diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..a85e0e9ed --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +# Local volumes +/config +/data +/db + +# Updated through volumes +Server/data +Server/worldprops + +# Env files containing secrets +/.env +/*.env + +# Log files +logs +*.log + +# Git files +.git +.gitattributes +.gitignore + +# Basic stuff +*.md +LICENSE +.gitlab-ci.yml + +# Docker files +docker-compose.yml +Dockerfile diff --git a/.gitignore b/.gitignore index 1c0e35d65..6ecd66985 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,8 @@ Server/data/global_kill_stats.json Server/ge_test.db Server/latestdump.txt Management-Server/managementprops/ -Server/worldprops/ +Server/worldprops/* +!Server/worldprops/default.conf **/.idea/workspace.xml **/.idea/tasks.xml @@ -31,3 +32,13 @@ gradle **/*.swo build/kotlin/sessions/ **/*.iml +Server/hasRan.txt + +# Local volumes +/config +/data +/db + +# Env files containing secrets +/.env +/*.env 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/.gitlab/.gitkeep b/.gitlab/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.gitlab/issue_templates/.gitkeep b/.gitlab/issue_templates/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 000000000..051106623 --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -0,0 +1,17 @@ +What I did: + +What I expected to happen: + +What actually happened: + +IDs of related NPCs/items: + +2009-era source (if relevant): + +Screenshots or video: + +LIVE SERVER username affected by this issue: + +**Bug reports are not accepted by SP users. SP is often out of date and results in invalid bug reports.** + +**If the bug is exploitable make sure you tick the confidential checkbox below** \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..5195e5bf4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Starting out with the openjdk-11-slim image +FROM maven:3-openjdk-11-slim + +# Set working directory to /app +WORKDIR /app + +# Copy all sources etc +COPY . . + +# Make sure ./run has permissions +RUN chmod +x run + +# Run it +CMD ["./run"] diff --git a/README.md b/README.md index 4bf8abdb3..2dd61ac3b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ * [Prerequisites](#prerequisites) * [Project Setup](#project-setup) * [Running the project](#running-the-project) + * [Browser WebSocket Transport](#browser-websocket-transport) * [License](#license) * [Contact](#contact) @@ -72,7 +73,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). @@ -80,6 +81,20 @@ There are many ways everyone can contribute! From the most seasoned programmers * **Code Contributors**: As a remake, we have massive amounts of content that need to be implemented or corrected. If you know how to program or are willing to learn, this is where you could be extremely helpful! We need everything from quests, to dialogue, to mini-games, to skills that still need to be corrected or implemented. This is perhaps one of the most valuable ways someone could help out the project! If you are interested in developing content, reach out in the development channel of the Discord. + +### Policy on Use of AI Tools + +You, the contributor, agree to submit quality code to the best of your ability which you have tested yourself and confirmed that it works as intended, and if it doesn’t work, you agree to take constructive feedback and fix it. + +You should be able to explain or defend how any part of your code works, without the use of AI or any other assistive tools. + +2009scape reserves the right to reject your MR on grounds of poor quality, poor functionality, poor code style, poor attitudes or unethical over-reliance on assistive tools. + +2009scape reserves the right to bar an individual from contributing due to patterns of aforementioned behavior, and close or delete all future MRs or issues or suggestions from said person without given explanation. + +AI may not be used to generate your MR description, your issue description, or any responses to any discourse present on the Gitlab at any time or in any fashion. If you use AI in any of these places, it will be assumed that your entire contribution is AI and that you have no clue how it functions, and the remainder of the policy outlined above will be applied to you immediately. + + ## Content Developers: Setting Up the Project. ### GitLab Setup **Note: This allows you to commit changes to the main repo (with approval)! Also, always stay up to date with the most recent updates by pulling into your copy when 2009Scape updates!** @@ -125,13 +140,119 @@ Start the game server with the included run script. Use `./run -h` for more info Start the game server with `run-server.bat` +#### Browser WebSocket Transport + +Enable the listener in `Server/worldprops/default.conf`: + +```properties +websocket_enabled = true +websocket_port = 0 +``` + +When `websocket_port = 0`, the listener uses `53594 + world_id`. For world `1`, that is `53595`. + +Both plain WebSocket (`ws://`) and secure WebSocket (`wss://`) are supported. Use `ws://` for local HTTP testing. PWA requires HTTPS/WSS. + +Enable WSS with a Java keystore: + +```properties +websocket_tls_enabled = true +websocket_tls_keystore_path = "certs/dev-wss.p12" +websocket_tls_keystore_password = "" +``` + +The keystore password is optional. Leave `websocket_tls_keystore_password` blank when using a PKCS12 file exported with an empty password: + +```bash +openssl pkcs12 -export \ + -in fullchain.pem \ + -inkey privkey.pem \ + -out certs/dev-wss.p12 \ + -name websocket \ + -passout pass: +``` + +For local WSS development, create a certificate for your hostname or LAN IP that the browser will use, then point the server at a PKCS12 keystore. For production, use a normal certificate from certbot. Plain WS does not require a certificate. + +#### Docker + +Make sure [Docker Engine](https://docs.docker.com/engine/install/) & [Docker Compose](https://docs.docker.com/compose/install/) plugin are installed first: + +Go to the project root where the git repository is cloned into: + +```bash +cd /path/to/project-dir +``` + +To configure the database, copy the mysql env file template to a env file in the project root: + +```bash +cp mysql.env.example mysql.env +``` + +Customize the env file however necessary. + +Create a new directory called 'config' into the project root. + +```bash +mkdir config +``` + +Copy the server config file template in the config directory: + +```bash +cp Server/worldprops/default.conf config/default.conf +``` + +Edit the server configuration file as per needed. + +Go to the project root directory and execute: + +```bash +docker compose up --build +``` + +Which will build the docker image using the local Dockerfile and starts the server and database containers. + +The previous up command should be run every time the server sources are modified to propagate the changes to the container. + +For the first time, the server compilation process takes a long time. Grab some coffee in the meanwhile. + +You can check (and follow) the containers logs by running the following in the project root directory (-f for follow and Ctrl+C to stop): + +```bash +docker compose logs -f +``` + +Any compilation or runtime errors will be logged here. + +If you have already compiled the server and made no changes to the sources, you can simply run without the build option (however it will be cached anyways): + +```bash +docker compose up +``` + +To later restart the server to apply simple configuration changes, in the project root directory run: + +```bash +docker compose restart server +``` + +To shut down / take down the server, in the project root directory run: + +```bash +docker compose up --build +``` + +The database files, build cache and config files will be persisted on the host filesystem for easy backup management. + ### License We use the AGPL 3.0 license, which can be found [here](https://www.gnu.org/licenses/agpl-3.0.en.html). Please be sure to read and understand the license. Failure to follow the guidelines outlined in the license will result in legal action. If you know or hear of anyone breaking this license, please send a report, with proof, to Red Bracket#8151, ceikry#2724, or woahscam#8535 on discord or email woahscam@hotmail.com. **We WILL NOT change the license to fit your needs.** ### 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 +270,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..bfd1c5167 100644 --- a/Server/data/ObjectParser.xml +++ b/Server/data/ObjectParser.xml @@ -11,7 +11,6 @@ - @@ -22,7 +21,6 @@ - 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/ammo_configs.json b/Server/data/configs/ammo_configs.json index 4cc7dbc0a..47da425c3 100644 --- a/Server/data/configs/ammo_configs.json +++ b/Server/data/configs/ammo_configs.json @@ -125,7 +125,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "813", @@ -133,7 +133,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "814", @@ -141,7 +141,7 @@ "start_graphic": "234,96", "darkbow_graphic": "", "projectile": "228,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "815", @@ -149,7 +149,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "816", @@ -157,7 +157,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "817", @@ -165,7 +165,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "825", @@ -221,7 +221,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "832", @@ -229,7 +229,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "833", @@ -237,7 +237,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "834", @@ -245,7 +245,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "835", @@ -253,7 +253,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "836", @@ -261,7 +261,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "863", @@ -325,7 +325,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "871", @@ -333,7 +333,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "872", @@ -341,7 +341,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "873", @@ -349,7 +349,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "874", @@ -357,7 +357,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "875", @@ -365,7 +365,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "876", @@ -373,7 +373,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "877", @@ -389,7 +389,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "879", @@ -421,7 +421,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "884", @@ -437,7 +437,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "886", @@ -453,7 +453,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "888", @@ -469,7 +469,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "890", @@ -485,7 +485,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "892", @@ -501,7 +501,7 @@ "start_graphic": "24,96", "darkbow_graphic": "1109,96", "projectile": "15,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "2532", @@ -565,7 +565,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "4160", @@ -725,7 +725,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5617", @@ -733,7 +733,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5618", @@ -741,7 +741,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5619", @@ -749,7 +749,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5620", @@ -757,7 +757,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5621", @@ -773,7 +773,7 @@ "start_graphic": "19,96", "darkbow_graphic": "1104,96", "projectile": "10,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5623", @@ -781,7 +781,7 @@ "start_graphic": "18,96", "darkbow_graphic": "1105,96", "projectile": "9,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5624", @@ -789,7 +789,7 @@ "start_graphic": "20,96", "darkbow_graphic": "1106,96", "projectile": "11,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5625", @@ -797,7 +797,7 @@ "start_graphic": "21,96", "darkbow_graphic": "1107,96", "projectile": "12,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5626", @@ -805,7 +805,7 @@ "start_graphic": "22,96", "darkbow_graphic": "1108,96", "projectile": "13,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5627", @@ -813,7 +813,7 @@ "start_graphic": "24,96", "darkbow_graphic": "1109,96", "projectile": "15,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5628", @@ -821,7 +821,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5629", @@ -829,7 +829,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5630", @@ -837,7 +837,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5631", @@ -845,7 +845,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5633", @@ -853,7 +853,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5634", @@ -861,7 +861,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5635", @@ -869,7 +869,7 @@ "start_graphic": "232,96", "darkbow_graphic": "", "projectile": "226,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5636", @@ -877,7 +877,7 @@ "start_graphic": "233,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5637", @@ -885,7 +885,7 @@ "start_graphic": "234,96", "darkbow_graphic": "", "projectile": "228,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5638", @@ -893,7 +893,7 @@ "start_graphic": "273,96", "darkbow_graphic": "", "projectile": "227,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5639", @@ -901,7 +901,7 @@ "start_graphic": "235,96", "darkbow_graphic": "", "projectile": "229,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5640", @@ -909,7 +909,7 @@ "start_graphic": "236,96", "darkbow_graphic": "", "projectile": "230,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5641", @@ -917,7 +917,7 @@ "start_graphic": "237,96", "darkbow_graphic": "", "projectile": "231,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5642", @@ -925,7 +925,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5643", @@ -933,7 +933,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5644", @@ -941,7 +941,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5645", @@ -949,7 +949,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5646", @@ -957,7 +957,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5647", @@ -965,7 +965,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5648", @@ -973,7 +973,7 @@ "start_graphic": "206,96", "darkbow_graphic": "", "projectile": "200,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5649", @@ -981,7 +981,7 @@ "start_graphic": "207,96", "darkbow_graphic": "", "projectile": "201,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5650", @@ -989,7 +989,7 @@ "start_graphic": "208,96", "darkbow_graphic": "", "projectile": "202,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5651", @@ -997,7 +997,7 @@ "start_graphic": "209,96", "darkbow_graphic": "", "projectile": "203,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5652", @@ -1005,7 +1005,7 @@ "start_graphic": "210,96", "darkbow_graphic": "", "projectile": "204,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5653", @@ -1013,7 +1013,7 @@ "start_graphic": "211,96", "darkbow_graphic": "", "projectile": "205,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5654", @@ -1021,7 +1021,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5655", @@ -1029,7 +1029,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5656", @@ -1037,7 +1037,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5657", @@ -1045,7 +1045,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5658", @@ -1053,7 +1053,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5659", @@ -1061,7 +1061,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5660", @@ -1069,7 +1069,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "5661", @@ -1077,7 +1077,7 @@ "start_graphic": "219,96", "darkbow_graphic": "", "projectile": "212,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5662", @@ -1085,7 +1085,7 @@ "start_graphic": "220,96", "darkbow_graphic": "", "projectile": "213,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5663", @@ -1093,7 +1093,7 @@ "start_graphic": "221,96", "darkbow_graphic": "", "projectile": "214,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5664", @@ -1101,7 +1101,7 @@ "start_graphic": "223,96", "darkbow_graphic": "", "projectile": "216,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5665", @@ -1109,7 +1109,7 @@ "start_graphic": "222,96", "darkbow_graphic": "", "projectile": "215,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5666", @@ -1117,7 +1117,7 @@ "start_graphic": "224,96", "darkbow_graphic": "", "projectile": "217,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "5667", @@ -1125,7 +1125,7 @@ "start_graphic": "225,96", "darkbow_graphic": "", "projectile": "218,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "6061", @@ -1133,7 +1133,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "6062", @@ -1141,7 +1141,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "6522", @@ -1293,7 +1293,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9287", @@ -1301,7 +1301,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9288", @@ -1309,7 +1309,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9289", @@ -1317,7 +1317,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9290", @@ -1325,7 +1325,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9291", @@ -1333,7 +1333,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "9293", @@ -1341,7 +1341,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9294", @@ -1349,7 +1349,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9295", @@ -1357,7 +1357,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9296", @@ -1365,7 +1365,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9297", @@ -1373,7 +1373,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9298", @@ -1381,7 +1381,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "9300", @@ -1389,7 +1389,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9301", @@ -1397,7 +1397,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9302", @@ -1405,7 +1405,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9303", @@ -1413,7 +1413,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9304", @@ -1421,7 +1421,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9305", @@ -1429,7 +1429,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "9335", @@ -1557,7 +1557,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "11228", @@ -1565,7 +1565,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "11229", @@ -1573,7 +1573,7 @@ "start_graphic": "1116,96", "darkbow_graphic": "1114,96", "projectile": "1120,40,36,41,46,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "11230", @@ -1589,7 +1589,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "11233", @@ -1597,7 +1597,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "11234", @@ -1605,7 +1605,7 @@ "start_graphic": "1123,96", "darkbow_graphic": "", "projectile": "1122,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13083", @@ -1621,7 +1621,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13085", @@ -1629,7 +1629,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13086", @@ -1637,7 +1637,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "27,38,36,41,32,5,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13280", @@ -1661,7 +1661,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13881", @@ -1669,7 +1669,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13882", @@ -1677,7 +1677,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13883", @@ -1701,7 +1701,7 @@ "start_graphic": "-1,0", "darkbow_graphic": "", "projectile": "1837,40,36,32,32,15,0", - "poison_damage": "28" + "poison_damage": "10" }, { "itemId": "13955", @@ -1709,7 +1709,7 @@ "start_graphic": "1837,96", "darkbow_graphic": "", "projectile": "1840,40,36,32,32,15,0", - "poison_damage": "38" + "poison_damage": "15" }, { "itemId": "13956", @@ -1717,7 +1717,7 @@ "start_graphic": "1837,96", "darkbow_graphic": "", "projectile": "1840,40,36,32,32,15,0", - "poison_damage": "48" + "poison_damage": "20" }, { "itemId": "13957", diff --git a/Server/data/configs/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..f342b1856 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,18 +332,18 @@ { "id": "2050", "replaceId": "15511", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "2051", "replaceId": "15513", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "2054", - "replaceId": "24375", + "replaceId": "2055", "fence": "false", "metal": "false" }, @@ -419,18 +419,6 @@ "fence": "false", "metal": "false" }, - { - "id": "2391", - "replaceId": "1561", - "fence": "false", - "metal": "true" - }, - { - "id": "2392", - "replaceId": "1561", - "fence": "false", - "metal": "true" - }, { "id": "2397", "replaceId": "24383", @@ -896,12 +884,12 @@ { "id": "3725", "replaceId": "3727", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "3726", - "replaceId": "3727", + "replaceId": "3728", "fence": "true", "metal": "false" }, @@ -925,7 +913,7 @@ }, { "id": "3747", - "replaceId": "1534", + "replaceId": "3748", "fence": "false", "metal": "false" }, @@ -943,7 +931,7 @@ }, { "id": "4148", - "replaceId": "4248", + "replaceId": "4246", "fence": "false", "metal": "false" }, @@ -979,19 +967,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 +1226,12 @@ { "id": "7049", "replaceId": "7051", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "7050", - "replaceId": "23919", + "replaceId": "7052", "fence": "true", "metal": "false" }, @@ -1340,7 +1328,7 @@ { "id": "8810", "replaceId": "8812", - "fence": "false", + "fence": "true", "metal": "false" }, { @@ -1439,6 +1427,12 @@ "fence": "false", "metal": "false" }, + { + "id": "11470", + "replaceId": "11471", + "fence": "false", + "metal": "false" + }, { "id": "11483", "replaceId": "11708", @@ -1759,7 +1753,7 @@ }, { "id": "14245", - "replaceId": "14248", + "replaceId": "14246", "fence": "true", "metal": "false" }, @@ -1983,7 +1977,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,18 +2146,18 @@ { "id": "24369", "replaceId": "24373", - "fence": "true", + "fence": "false", "metal": "false" }, { "id": "24370", "replaceId": "24374", - "fence": "true", + "fence": "false", "metal": "false" }, { "id": "24376", - "replaceId": "24377", + "replaceId": "24375", "fence": "false", "metal": "false" }, @@ -2177,18 +2179,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 +2347,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 +2511,6 @@ "fence": "false", "metal": "false" }, - { - "id": "28514", - "replaceId": "28518", - "fence": "true", - "metal": "true" - }, { "id": "28589", "replaceId": "28588", @@ -2671,7 +2669,7 @@ }, { "id": "31815", - "replaceId": "31816", + "replaceId": "31817", "fence": "false", "metal": "false" }, @@ -2798,7 +2796,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..2b5926e69 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", @@ -8380,16 +8021,9 @@ } ], "charm": [], - "ids": "128,1479,1875,4343", + "ids": "128,1479,1875", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7862", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -8622,7 +8256,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3186", + "id": "526", "maxAmount": "1" }, { @@ -8666,20 +8300,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 +8351,21 @@ ], "ids": "133", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7821", - "maxAmount": "1" - }, + "main": [] + }, + { + "default": [ { "minAmount": "1", "weight": "100.0", - "id": "0", + "id": "526", "maxAmount": "1" } - ] + ], + "charm": [], + "ids": "1329", + "description": "", + "main": [] }, { "default": [ @@ -10421,13 +10043,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 +11393,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 +11437,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 +11448,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 +11602,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 +11803,6 @@ "id": "12070", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -12353,7 +11927,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "526", + "id": "592", "maxAmount": "1" }, { @@ -13613,75 +13187,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 +14330,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 +14421,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 +15645,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 +15689,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 +15869,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 +16047,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 +17716,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 +17882,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 +18092,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": [ { @@ -19013,7 +18356,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "5733", + "id": "12070", "maxAmount": "1" }, { @@ -19085,20 +18428,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 +18497,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 +19006,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -19895,10 +19171,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -20422,7 +19698,14 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "100.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [], "ids": "1307", "description": "", @@ -20810,72 +20093,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 +20339,6 @@ "id": "3751", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "1.0", - "id": "3749", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "1.0", @@ -20904,75 +20353,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 +20795,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 +20861,8 @@ }, { "minAmount": "1", - "weight": "1400.0", - "id": "0", + "weight": "16.0", + "id": "14426", "maxAmount": "1" } ] @@ -22651,79 +22350,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 +22475,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 +22531,7 @@ { "minAmount": "1", "weight": "2.0", - "id": "4150", + "id": "4160", "maxAmount": "10" }, { @@ -23024,129 +22743,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 +23085,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 +23251,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 +23424,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 +23640,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 +23831,6 @@ "id": "4109", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "7977", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -24473,283 +24094,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 +25460,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 +25604,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 +26182,6 @@ "id": "686", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -26957,12 +26462,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -27591,12 +27090,6 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "100.0", - "id": "526", - "maxAmount": "2" } ], "charm": [], @@ -27650,12 +27143,6 @@ "weight": "50.0", "id": "995", "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7910", - "maxAmount": "1" } ] }, @@ -27939,7 +27426,7 @@ "maxAmount": "1" } ], - "ids": "1832,5750", + "ids": "5750", "description": "", "main": [ { @@ -28459,7 +27946,7 @@ "maxAmount": "1" } ], - "ids": "1960,5360", + "ids": "1958,1961,1960,5360", "description": "", "main": [ { @@ -28476,7 +27963,7 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "4675", "maxAmount": "1" }, @@ -28666,12 +28153,6 @@ "id": "5733", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -28947,20 +28428,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 +28466,7 @@ ], "ids": "2035", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -29022,60 +28483,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": [ @@ -30420,10 +29831,10 @@ "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "2", "weight": "100.0", "id": "12161", - "maxAmount": "1" + "maxAmount": "2" }, { "minAmount": "1", @@ -31291,13 +30702,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 +30718,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 +30813,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 +30829,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 +30906,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 +30922,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 +30993,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 +31009,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" } ] @@ -32647,7 +31674,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "532", + "id": "526", "maxAmount": "1" } ], @@ -32688,207 +31715,159 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1367", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "16.0", "id": "1313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "12.0", + "id": "1367", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "1361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1183", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1113", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1163", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "1.0", + "id": "11235", + "maxAmount": "1" + }, { "minAmount": "47", - "weight": "25.0", + "weight": "32.0", "id": "556", "maxAmount": "47" }, { "minAmount": "10", - "weight": "25.0", + "weight": "28.0", "id": "562", "maxAmount": "10" }, + { + "minAmount": "3", + "weight": "20.0", + "id": "560", + "maxAmount": "3" + }, { "minAmount": "7", - "weight": "25.0", + "weight": "16.0", "id": "565", "maxAmount": "7" }, { - "minAmount": "3", - "weight": "25.0", + "minAmount": "5", + "weight": "4.0", "id": "560", "maxAmount": "5" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "60.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "16.0", + "id": "14424", "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": "152", + "weight": "159.0", + "id": "995", + "maxAmount": "152" }, { "minAmount": "64", - "weight": "25.0", + "weight": "24.0", "id": "995", "maxAmount": "64" }, { "minAmount": "95", - "weight": "25.0", + "weight": "24.0", "id": "995", "maxAmount": "95" }, - { - "minAmount": "152", - "weight": "25.0", - "id": "995", - "maxAmount": "152" - }, { "minAmount": "220", - "weight": "25.0", + "weight": "20.0", "id": "995", "maxAmount": "220" }, { "minAmount": "1", - "weight": "25.0", - "id": "121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "12.0", "id": "385", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1456", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "2361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "10977", + "weight": "4.0", + "id": "1456", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "10976", + "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": "26.6", + "weight": "12.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "2.13", - "id": "11235", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "150.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -33950,207 +32929,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 +33137,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 +33350,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 +33886,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 +35754,7 @@ ] }, { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "592", - "maxAmount": "1" - } - ], + "default": [], "charm": [ { "minAmount": "1", @@ -36918,238 +35790,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" } ] @@ -37792,12 +36472,6 @@ "weight": "100.0", "id": "532", "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "100.0", - "id": "526", - "maxAmount": "3" } ], "charm": [], @@ -38439,7 +37113,7 @@ { "minAmount": "4", "weight": "25.0", - "id": "4150", + "id": "4160", "maxAmount": "5" }, { @@ -39045,12 +37719,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -41087,7 +39755,20 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9080", + "maxAmount": "1" + } + ], "charm": [ { "minAmount": "1", @@ -42515,7 +41196,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "1", + "id": "12070", "maxAmount": "1" }, { @@ -44723,280 +43404,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 +45178,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "5733", + "id": "12070", "maxAmount": "1" }, { @@ -46887,7 +45294,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "1", + "id": "12070", "maxAmount": "1" } ] @@ -48532,10 +46939,10 @@ "maxAmount": "2" }, { - "minAmount": "3", + "minAmount": "1", "weight": "500.0", "id": "7054", - "maxAmount": "1" + "maxAmount": "3" }, { "minAmount": "1", @@ -56130,30 +54537,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 +54621,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 +54699,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 +57515,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3179", + "id": "526", "maxAmount": "1" } ], @@ -59103,7 +57534,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 +58344,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "527", + "id": "526", "maxAmount": "1" } ], @@ -59958,7 +58389,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "617", + "id": "995", "maxAmount": "25" }, { @@ -59974,5 +58405,407 @@ "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": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "6287", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "4343", + "description": "", + "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..1893b6df3 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -1,618 +1,690 @@ [ { - "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,2866,2976,0,160}-{1,2854,2977,0,160}-{1,2839,2975,0,160}-{1,2943,2950,0,160}-{1,2757,2947,0,160}-{1,2755,2952,0,160}-{1,2786,2960,0,160}-{1,2782,2987,0,160}-{1,2771,2995,0,160}-{1,2761,3000,0,160}-{1,2945,3068,0,160}-{1,2975,3013,0,160}-{1,2955,3010,0,160}-{1,3829,3053,0,160}-{1,3813,3066,0,160}-{1,3814,3064,0,160}-{1,3808,3060,0,160}-{1,2755,3008,0,160}-{1,2754,3017,0,160}-{1,2756,3060,0,160}-{1,2755,3070,0,160}-{1,2896,3119,0,160}-{1,2914,3111,0,160}-{1,2926,3110,0,160}-{1,2939,3102,0,160}-{1,2938,3073,0,160}-{1,2756,3074,0,160}-{1,2806,3128,0,160}-{1,2764,3131,0,160}-{1,2753,3125,0,160}-{1,2757,3109,0,160}-{1,2864,3195,0,160}-{1,2810,3387,0,160}-{1,2809,3388,0,160}-{1,2809,3385,0,160}-{1,2807,3386,0,160}-{1,2804,3385,0,160}-{1,2807,3384,0,160}-{1,2805,3384,0,160}-{1,2802,3382,0,160}-{1,2805,3381,0,160}-{1,2804,3380,0,160}-{1,2693,3727,0,160}-{1,2698,3729,0,160}-{1,2700,3731,0,160}-{1,2708,3728,0,160}-{1,2725,3731,0,160}-{1,2721,3730,0,160}-{1,2719,3733,0,160}-{1,2714,3733,0,160}-{1,2712,3732,0,160}-" + }, + { + "item_id": "444", + "loc_data": "{1,3231,3739,0,250}-{1,3236,3741,0,250}-{1,3195,9821,0,150}-{1,2732,3224,0,250}-" + }, + { + "item_id": "480", + "loc_data": "{1,3298,3313,0,30}-" + }, + { + "item_id": "526", + "loc_data": "{1,2510,3080,0,30}-{1,2516,3084,0,30}-{1,2509,3090,0,30}-{1,2512,3084,0,30}-{1,2510,3084,0,30}-{1,2505,3114,0,30}-{1,2507,3116,0,30}-{1,2503,3118,0,30}-{1,2510,3114,0,30}-{1,3238,3606,0,100}-{1,3236,3605,0,100}-{1,3238,3603,0,100}-{1,3240,3603,0,100}-{1,3242,3604,0,100}-{1,3244,3611,0,100}-{1,3247,3614,0,100}-{1,3238,3610,0,100}-{1,3238,3608,0,100}-{1,3232,3948,0,100}-{1,3237,3938,0,100}-{1,3250,3952,0,100}-{1,2966,9772,0,90}-{1,2968,9771,0,90}-{1,2831,9766,0,90}-{1,2829,9764,0,90}-{1,2917,9796,0,90}-{1,2914,9796,0,90}-{1,2910,9797,0,90}-{1,2908,9794,0,90}-{1,2926,9801,0,90}-{1,2924,9801,0,90}-{1,2924,9804,0,90}-{1,2927,9805,0,90}-{1,2929,9807,0,90}-{1,2938,9792,0,90}-{1,2938,9796,0,90}-{1,2935,9799,0,90}-{1,2930,9794,0,90}-{1,2932,9792,0,90}-{1,2903,9826,0,90}-{1,2906,9823,0,90}-{1,2906,9825,0,90}-{1,2907,9824,0,90}-{1,2910,9825,0,90}-{1,3101,9825,0,90}-{1,3107,9823,0,90}-{1,3109,9823,0,90}-{1,3110,9825,0,90}-{1,3138,9880,0,90}-{1,3141,9879,0,90}-{1,3142,9880,0,90}-{1,3143,9878,0,90}-{1,3097,9902,0,90}-{1,3094,9907,0,90}-{1,3093,9884,0,90}-{1,3098,9886,0,90}-{1,3093,9879,0,90}-{1,3122,9891,0,90}-{1,3116,9891,0,90}-{1,3119,9894,0,90}-{1,3120,9894,0,90}-{1,3103,9953,0,90}-{1,3104,9950,0,90}-{1,3110,9952,0,90}-{1,3111,9956,0,90}-{1,3110,9958,0,90}-{1,3111,9959,0,90}-{1,3116,9950,0,90}-{1,3127,9957,0,90}-" + }, + { + "item_id": "528", + "loc_data": "{1,3211,3822,0,60}-{1,3212,3819,0,60}-{1,3217,3816,0,60}-{1,3218,3813,0,60}-{1,3182,3848,0,50}-{1,3178,3851,0,60}-{1,3179,3853,0,60}-{1,3185,3857,0,60}-{1,3187,3853,0,60}-" + }, + { + "item_id": "542", + "loc_data": "{1,3059,3488,1,90}-" + }, + { + "item_id": "544", + "loc_data": "{1,3059,3487,1,90}-" + }, + { + "item_id": "554", + "loc_data": "{1,3303,3311,0,30}-{1,3026,3636,0,200}-" + }, + { + "item_id": "555", + "loc_data": "{1,3297,3316,0,30}-{1,3028,3636,0,200}-{3,2960,3899,0,200}-" + }, + { + "item_id": "556", + "loc_data": "{1,2938,3158,0,110}-{1,3030,3636,0,200}-" + }, + { + "item_id": "557", + "loc_data": "{1,3032,3636,0,200}-" + }, + { + "item_id": "558", + "loc_data": "{1,3206,3208,0,40}-{1,3023,3640,0,200}-" + }, + { + "item_id": "559", + "loc_data": "{1,3233,3573,0,200}-{1,3234,3578,0,200}-{1,3230,3582,0,200}-{1,3021,3637,0,200}-{1,3089,3866,0,30}-" + }, + { + "item_id": "561", + "loc_data": "{1,2671,3737,0,200}-{1,2672,3738,0,200}-" + }, + { + "item_id": "562", + "loc_data": "{1,3021,3640,0,200}-{1,3137,3833,0,200}-{1,3139,3814,0,200}-{1,3145,3814,0,200}-{1,3144,3826,0,200}-{1,3149,3823,0,200}-{1,3151,3831,0,200}-" + }, + { + "item_id": "564", + "loc_data": "{3,2947,3898,0,200}-" + }, + { + "item_id": "590", + "loc_data": "{1,2368,3135,0,140}-{1,2431,3072,0,140}-{1,3112,3369,2,140}-{1,3209,3734,0,100}-" + }, + { + "item_id": "600", + "loc_data": "{1,2438,3187,0,200}-" + }, + { + "item_id": "677", + "loc_data": "{1,3369,3378,0,150}-" + }, + { + "item_id": "687", + "loc_data": "{1,2655,3424,0,80}-{1,2665,3424,0,80}-{1,2669,3423,0,80}-{1,2668,3419,0,80}-{1,2671,3420,0,80}-{1,2677,3423,0,80}-{1,2676,3425,0,80}-{1,2676,3422,0,80}-{1,2679,3424,0,80}-{1,2678,3426,0,80}-{1,2679,3423,0,80}-{1,2680,3424,0,80}-{1,2672,3427,0,80}-{1,2676,3430,0,80}-{1,2673,3428,0,80}-{1,2670,3437,0,80}-{1,2649,9854,0,80}-{1,2643,9853,0,80}-{1,2644,9851,0,80}-" + }, + { + "item_id": "708", + "loc_data": "{1,3368,9770,0,30}-{1,3364,9764,0,30}-{1,3363,9831,0,30}-{1,3370,9826,0,30}-" + }, + { + "item_id": "712", + "loc_data": "{1,2887,3412,0,30}-{1,2903,3441,0,30}-" + }, + { + "item_id": "767", + "loc_data": "{1,3245,3385,1,50}-{1,3243,3383,1,50}-" + }, + { + "item_id": "882", + "loc_data": "{1,3205,3227,0,140}-{1,2957,3205,0,30}-{1,2944,3332,0,30}-{1,3104,3599,0,100}-{1,3103,3596,0,100}-{1,3100,3593,0,100}-{1,3096,3595,0,100}-{1,3094,3598,0,100}-{1,3100,3609,0,100}-{1,3104,3610,0,100}-{1,3107,3611,0,100}-{1,3108,3603,0,100}-{1,3135,9916,0,140}-{3,3130,9903,0,140}-" + }, + { + "item_id": "946", + "loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,2700,3407,0,100}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,2653,9767,0,30}-{1,3218,9887,0,33}-" + }, + { + "item_id": "952", + "loc_data": "{1,3572,3312,0,30}-{1,3571,3310,0,30}-{1,3120,3359,0,110}-{1,2566,3330,0,100}-{1,2981,3370,0,90}-{1,3218,3412,1,30}-{1,1951,4964,0,30}-" + }, + { + "item_id": "954", + "loc_data": "{1,2094,3152,0,150}-{1,2785,3279,0,150}-{1,2786,3287,0,150}-{1,2786,3286,0,150}-" + }, + { + "item_id": "960", + "loc_data": "{1,2851,3239,0,160}-{1,2847,3238,0,160}-{1,2845,3232,0,160}-{1,2856,3231,0,160}-{1,2857,3236,0,160}-{1,2846,3384,0,160}-{1,2848,3383,0,160}-{1,2550,3575,0,200}-{1,2553,3576,0,200}-{1,2554,3575,0,200}-{1,2556,3573,0,200}-{1,3216,3665,0,200}-{1,3224,3668,0,200}-{1,3245,3678,0,200}-{1,3230,3686,0,200}-{1,3216,3677,0,200}-{1,3219,3680,0,200}-" + }, + { + "item_id": "966", + "loc_data": "{1,3237,3696,0,150}-" + }, + { + "item_id": "970", + "loc_data": "{1,3290,3033,1,90}-" + }, + { + "item_id": "983", + "loc_data": "{1,3131,9862,0,90}-" + }, + { + "item_id": "995", + "loc_data": "{2,3234,3560,0,100}-{2,3106,3547,0,100}-{1,3104,3558,0,100}-{1,3106,3534,0,100}-{2,3101,3564,0,100}-{4,3103,3579,0,100}-{384,3224,3830,0,50}-{384,3220,3824,0,50}-{1,2909,9800,0,150}-{4,2912,9801,0,150}-{8,2910,9803,0,150}-{5,2907,9807,0,150}-{6,2913,9806,0,150}-{1,2922,9820,0,150}-{1,2934,9834,0,150}-{1,2917,9850,0,150}-{1,2914,9849,0,150}-{3,3195,9834,0,150}-{4,3195,9820,0,150}-{66,3191,9821,0,150}-{56,3190,9819,0,150}-{26,3188,9819,0,150}-{26,3188,9820,0,150}-{35,3189,9819,0,150}-{4,3088,9898,0,150}-{1,3088,9899,0,150}-{1,3091,9899,0,150}-" + }, + { + "item_id": "1005", + "loc_data": "{1,3014,3227,0,30}-{1,3009,3204,0,30}-" + }, + { + "item_id": "1009", + "loc_data": "{1,3122,9881,0,30}-" + }, + { + "item_id": "1059", + "loc_data": "{1,3148,3177,0,30}-{1,3242,3385,1,90}-{1,3097,3486,0,90}-" + }, + { + "item_id": "1061", + "loc_data": "{1,3111,3159,0,190}-{1,3112,3155,0,100}-{1,3302,3190,0,30}-{1,3244,3386,1,190}-{1,3208,9620,0,190}-{1,3210,9615,0,190}-" + }, + { + "item_id": "1069", + "loc_data": "{1,3249,3740,0,90}-" + }, + { + "item_id": "1119", + "loc_data": "{1,3084,3859,0,60}-" + }, + { + "item_id": "1137", + "loc_data": "{1,3242,3688,0,150}-" + }, + { + "item_id": "1139", + "loc_data": "{1,3122,3360,0,190}-" + }, + { + "item_id": "1171", + "loc_data": "{1,3217,3514,0,30}-" + }, + { + "item_id": "1191", + "loc_data": "{1,3250,3797,0,150}-" + }, + { + "item_id": "1203", + "loc_data": "{1,3248,3245,0,180}-{1,3242,3383,1,240}-{1,3216,3695,0,150}-" + }, + { + "item_id": "1205", + "loc_data": "{1,3213,3216,1,190}-" + }, + { + "item_id": "1207", + "loc_data": "{1,3213,3682,0,100}-" + }, + { + "item_id": "1217", + "loc_data": "{1,3180,3821,0,60}-" + }, + { + "item_id": "1265", + "loc_data": "{1,3229,3218,2,100}-{1,2963,3216,0,30}-{1,3081,3429,0,50}-{1,3288,9442,0,90}-{1,3288,9431,0,90}-" + }, + { + "item_id": "1321", + "loc_data": "{1,2965,3211,1,30}-" + }, + { + "item_id": "1351", + "loc_data": "{1,2795,3161,0,240}-{1,2970,3376,1,30}-" + }, + { + "item_id": "1385", + "loc_data": "{1,3099,3862,0,131}-" + }, + { + "item_id": "1422", + "loc_data": "{1,3320,3137,0,150}-" + }, + { + "item_id": "1467", + "loc_data": "{1,2784,3289,0,0}-" + }, + { + "item_id": "1469", + "loc_data": "{1,2766,3277,0,100}-{1,2766,3289,0,100}-" + }, + { + "item_id": "1510", + "loc_data": "{1,2576,3334,0,100}-" + }, + { + "item_id": "1511", + "loc_data": "{1,3106,3160,0,160}-{1,3106,3159,0,160}-{1,3105,3159,0,160}-{1,3205,3226,2,160}-{1,3205,3224,2,160}-{1,3208,3225,2,160}-{1,3209,3224,2,160}-{1,2958,3205,0,30}-{1,2959,3205,0,30}-" + }, + { + "item_id": "1523", + "loc_data": "{1,2614,9570,0,30}-" + }, + { + "item_id": "1550", + "loc_data": "{1,2714,3478,0,30}-" + }, + { + "item_id": "1573", + "loc_data": "{1,2560,2974,0,10}-{1,2561,2973,0,10}-{1,2561,2976,0,10}-{1,2559,2975,0,10}-{1,2740,3637,0,30}-{1,2736,3638,0,30}-{1,2735,3636,0,30}-{1,2743,3636,0,30}-{1,2739,3634,0,30}-{1,2743,3640,0,30}-{1,2741,3639,0,30}-{1,2736,3641,0,30}-{1,2738,3641,0,30}-{1,2734,3640,0,30}-{1,2738,3636,0,30}-" + }, + { + "item_id": "1590", + "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": "1773", + "loc_data": "{1,2563,3261,0,100}-" + }, + { + "item_id": "1785", + "loc_data": "{1,2822,3355,0,150}-" + }, + { + "item_id": "1812", + "loc_data": "{1,2747,3579,0,100}-" + }, + { + "item_id": "1813", + "loc_data": "{1,2746,3578,0,100}-" + }, + { + "item_id": "1856", + "loc_data": "{1,2638,3292,0,150}-" + }, + { + "item_id": "1887", + "loc_data": "{1,3141,3452,1,90}-" + }, + { + "item_id": "1913", + "loc_data": "{1,2863,9875,0,44}-{1,2861,9878,0,44}-" + }, + { + "item_id": "1917", + "loc_data": "{1,3286,3033,1,90}-{1,2799,3156,0,150}-{1,2796,3165,0,150}-{1,3049,3257,0,150}-{1,3080,3438,0,45}-{1,3077,3439,0,30}-{1,3077,3443,0,30}-" + }, + { + "item_id": "1919", + "loc_data": "{1,2798,3156,0,150}-{1,2799,3155,0,150}-{1,2795,3160,0,150}-{1,2798,3160,0,150}-{1,2798,3161,0,150}-{1,2795,3165,0,150}-{1,2794,3165,0,150}-" + }, + { + "item_id": "1921", + "loc_data": "{1,3291,3034,0,90}-" + }, + { + "item_id": "1923", + "loc_data": "{1,3208,3214,0,80}-{1,3140,3452,1,90}-" + }, + { + "item_id": "1925", + "loc_data": "{1,2428,3079,0,80}-{1,2428,3080,0,80}-{1,2371,3128,0,80}-{1,2371,3127,0,80}-{1,3307,3195,0,30}-{1,3225,3294,0,80}-{1,3026,3289,0,80}-{1,3121,3359,0,60}-{1,2564,3332,0,100}-{1,3221,3497,1,80}-{1,3222,3491,1,80}-{1,2958,3510,0,80}-{1,2958,3510,0,80}-{1,1941,4956,0,30}-{1,3216,9625,0,80}-{1,2633,9908,0,100}-" + }, + { + "item_id": "1927", + "loc_data": "{1,2685,3683,0,174}-" + }, + { + "item_id": "1929", + "loc_data": "{1,2820,3452,0,90}-{1,2823,3449,0,90}-" + }, + { + "item_id": "1931", + "loc_data": "{1,3209,3214,0,80}-{1,3166,3310,0,90}-{1,3144,3449,2,90}-{1,2918,10191,1,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,3428,3322,0,45}-{1,3436,3311,0,45}-{1,3437,3298,0,45}-{1,3428,3293,0,45}-{1,3422,3286,0,45}-{1,3451,3320,0,45}-{1,3434,3277,0,45}-{1,3446,3281,0,45}-{1,3447,3269,0,45}-{1,3450,3359,0,45}-{1,3451,3359,0,45}-{1,3452,3379,0,45}-{1,3452,3380,0,45}-{1,3438,3379,0,45}-{1,3440,3377,0,45}-{1,3414,3367,0,45}-{1,3409,3363,0,45}-{1,3452,3390,0,45}-{1,3452,3391,0,45}-{1,3466,3377,0,45}-{1,3460,3374,0,45}-{1,3459,3370,0,45}-{1,3469,3363,0,45}-{1,3476,3365,0,45}-{1,3460,3353,0,45}-{1,3462,3349,0,45}-{1,3472,3344,0,45}-{1,3493,3408,0,30}-{1,3494,3402,0,30}-{1,3494,3396,0,30}-{1,3499,3401,0,30}-{1,3464,3432,0,45}-{1,3469,3434,0,45}-{1,3473,3435,0,45}-{1,3471,3430,0,45}-{1,3478,3440,0,45}-{1,3477,3444,0,45}-{1,3472,3447,0,45}-{1,3458,3418,0,45}-{1,3460,3414,0,45}-{1,3467,3417,0,45}-{1,3472,3419,0,45}-{1,3474,3415,0,45}-{1,3480,3419,0,45}-{1,3481,3427,0,45}-{1,3460,3406,0,45}-{1,3471,3403,0,45}-{1,3428,3437,0,45}-{1,3434,3433,0,45}-{1,3435,3425,0,45}-{1,3441,3438,0,45}-{1,3445,3435,0,45}-{1,3449,3434,0,45}-{1,3448,3430,0,45}-{1,3454,3441,0,45}-{1,3441,3415,0,45}-{1,3442,3414,0,45}-{1,3436,3403,0,45}-{1,3432,3402,0,45}-{1,3450,3416,0,45}-{1,3445,3405,0,45}-{1,3453,3402,0,45}-" + }, + { + "item_id": "1944", + "loc_data": "{1,3191,3276,0,35}-{1,3229,3299,0,35}-{1,3226,3301,0,35}-{1,3015,3295,0,30}-{1,3016,3295,0,30}-{1,2853,3370,0,35}-{1,2852,3369,0,35}-{1,2851,3372,0,35}-{1,2453,4476,0,1}-" + }, + { + "item_id": "1955", + "loc_data": "{1,3141,3447,1,90}-{1,3140,3447,1,90}-{1,3141,3447,2,90}-" + }, + { + "item_id": "1963", + "loc_data": "{1,2907,3146,0,150}-{1,3009,3207,0,150}-" + }, + { + "item_id": "1965", + "loc_data": "{1,3217,9622,0,90}-" + }, + { + "item_id": "1971", + "loc_data": "{1,2843,3369,0,160}-" + }, + { + "item_id": "1973", + "loc_data": "{1,3143,3453,0,40}-{1,2384,4440,0,40}-" + }, + { + "item_id": "1982", + "loc_data": "{1,2584,2966,0,10}-{1,3085,3261,0,150}-{1,3039,3706,0,15}-" + }, + { + "item_id": "1985", + "loc_data": "{1,3083,3260,0,150}-{1,3039,3707,0,15}-" + }, + { + "item_id": "1987", + "loc_data": "{1,3144,3450,2,90}-{1,3237,9761,0,90}-" + }, + { + "item_id": "1993", + "loc_data": "{1,3291,3033,1,90}-" + }, + { + "item_id": "2025", + "loc_data": "{1,2449,3510,1,90}-" + }, + { + "item_id": "2026", + "loc_data": "{1,2483,3482,1,90}-{1,2489,3489,1,90}-" + }, + { + "item_id": "2126", + "loc_data": "{1,2661,3478,0,100}-{1,2638,3480,0,100}-{1,2632,3492,0,100}-{1,2648,3498,0,100}-{1,2669,3497,0,100}-" }, { "item_id": "2128", - "loc_data": "{1,2648,2963,0,327690}-{1,2647,2956,0,327690}-{1,2638,2949,0,327690}" + "loc_data": "{1,2648,2963,0,10}-{1,2647,2956,0,10}-{1,2638,2949,0,10}-" }, { - "item_id": "4615", - "loc_data": "{1,3479,3092,0,0}" + "item_id": "2140", + "loc_data": "{1,2971,3382,1,30}-" + }, + { + "item_id": "2142", + "loc_data": "{1,3080,3443,0,30}-{1,3077,3441,0,30}-" + }, + { + "item_id": "2150", + "loc_data": "{1,2907,3393,0,30}-{1,2903,3400,0,30}-{1,2908,3410,0,30}-{1,2418,3511,0,30}-{1,2417,3512,0,30}-{1,2416,3512,0,30}-{1,2412,3511,0,30}-{1,2411,3519,0,30}-{1,2415,3518,0,30}-{1,2417,3516,0,30}-{1,2417,3515,0,30}-{1,2421,3519,0,30}-{1,2424,3517,0,30}-{1,2424,3514,0,30}-{1,2428,3510,0,30}-{1,2407,3516,0,30}-" + }, + { + "item_id": "2162", + "loc_data": "{1,2896,3414,0,30}-{1,2425,3515,0,30}-{1,2415,3517,0,30}-{1,2414,3518,0,30}-{1,2419,3511,0,30}-{1,2415,3511,0,30}-{1,2414,3510,0,30}-{1,2427,3508,0,30}-{1,2426,3507,0,30}-{1,2408,3518,0,30}-" + }, + { + "item_id": "2313", + "loc_data": "{1,2820,3455,0,90}-{1,3142,3447,1,90}-{1,3222,3494,0,90}-" }, { "item_id": "2333", - "loc_data": "{1,3042,3952,0,100}" + "loc_data": "{1,3042,3952,0,100}-" + }, + { + "item_id": "2347", + "loc_data": "{1,2934,3286,0,90}-{1,2975,3368,1,30}-{1,2579,3463,0,40}-{1,1953,4974,0,30}-" + }, + { + "item_id": "2357", + "loc_data": "{1,3192,9822,0,150}-" + }, + { + "item_id": "2398", + "loc_data": "{1,2517,3049,0,50}-{1,2511,3045,0,50}-" + }, + { + "item_id": "2407", + "loc_data": "{1,2935,3460,0,5}-" + }, + { + "item_id": "2408", + "loc_data": "{1,2903,3471,0,5}-" + }, + { + "item_id": "2957", + "loc_data": "{1,3443,9742,0,100}-{1,3443,9742,1,100}-" + }, + { + "item_id": "2964", + "loc_data": "{1,3437,3337,0,100}-" + }, + { + "item_id": "3109", + "loc_data": "{1,2893,3565,0,2}-" + }, + { + "item_id": "3110", + "loc_data": "{1,2893,3564,0,2}-" + }, + { + "item_id": "3111", + "loc_data": "{1,2893,3561,0,2}-" + }, + { + "item_id": "3112", + "loc_data": "{1,2893,3563,0,2}-" + }, + { + "item_id": "3113", + "loc_data": "{1,2893,3562,0,2}-" + }, + { + "item_id": "3138", + "loc_data": "{1,3461,9480,2,30}-{1,3460,9484,2,30}-{1,3465,9477,2,30}-{1,3467,9493,0,30}-{1,3486,9517,0,30}-{1,3474,9509,0,30}-{1,3470,9502,0,30}-{1,3480,9483,0,30}-" + }, + { + "item_id": "3711", + "loc_data": "{1,2660,3676,0,196}-" + }, + { + "item_id": "3803", + "loc_data": "{1,2658,3676,0,174}-" + }, + { + "item_id": "3805", + "loc_data": "{1,2659,3676,0,196}-" + }, + { + "item_id": "4199", + "loc_data": "{1,3492,3474,0,0}-" + }, + { + "item_id": "4615", + "loc_data": "{1,3479,3092,0,0}-" + }, + { + "item_id": "4619", + "loc_data": "{1,2713,4913,0,2}-" + }, + { + "item_id": "4707", + "loc_data": "{1,3571,3312,0,40}-" + }, + { + "item_id": "4838", + "loc_data": "{1,2593,3103,1,40}-" + }, + { + "item_id": "5008", + "loc_data": "{1,3230,9609,0,44}-" + }, + { + "item_id": "5523", + "loc_data": "{1,2935,3282,1,90}-" + }, + { + "item_id": "5586", + "loc_data": "{1,2473,4941,0,90}-" + }, + { + "item_id": "6291", + "loc_data": "{1,2681,3111,0,30}-{1,2673,3112,0,30}-{1,2674,3094,0,30}-{1,2671,3089,0,30}-" + }, + { + "item_id": "6662", + "loc_data": "{1,2984,3112,0,90}-" + }, + { + "item_id": "6663", + "loc_data": "{1,2984,3114,0,90}-" + }, + { + "item_id": "7510", + "loc_data": "{1,2867,9875,0,44}-" + }, + { + "item_id": "11065", + "loc_data": "{1,2928,3289,0,90}-" + }, + { + "item_id": "11656", + "loc_data": "{1,2438,3185,0,200}-" + }, + { + "item_id": "12494", + "loc_data": "{1,2762,2973,0,60}-" } -] +] \ No newline at end of file diff --git a/Server/data/configs/interface_configs.json b/Server/data/configs/interface_configs.json index bb3d2219d..4377daff0 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,18 @@ "walkable": "true", "tabIndex": "-1" }, + { + "id": "375", + "interfaceType": "3", + "walkable": "false", + "tabIndex": "-1" + }, + { + "id": "377", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "380", "interfaceType": "1", @@ -449,12 +605,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 +641,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 +685,7 @@ }, { "id": "532", - "interfaceType": "1", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -605,9 +797,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 +919,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..b928857e7 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -1,130745 +1,125300 @@ [ { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "The body of a Dwarf savaged by Goblins.", - "durability": null, + "id": "0", "name": "Dwarf remains", - "tradeable": "false", - "destroy": "true", + "examine": "The body of a Dwarf savaged by Goblins.", "archery_ticket_price": "0", - "two_handed": "true", - "id": "0" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "two_handed": "true" }, { - "destroy_message": "I got this from Captain Lawgof.", - "shop_price": "1", - "examine": "Good for repairing a broken cannon.", - "durability": null, + "id": "1", "name": "Toolkit", - "tradeable": "false", + "examine": "Good for repairing a broken cannon.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1" - }, - { - "shop_price": "5", - "ge_buy_limit": "10000", - "examine": "Ammo for the Dwarf Cannon.", - "grand_exchange_price": "381", + "destroy_message": "I got this from Captain Lawgof.", "durability": null, - "name": "Cannonball", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2" - }, - { "shop_price": "1", - "examine": "Construction notes for Dwarf cannon ammo.", - "durability": null, - "name": "Nulodion's notes", - "weight": "0.02", - "archery_ticket_price": "0", - "id": "3" + "tradeable": "false", + "weight": "0.4" }, { + "id": "2", + "name": "Cannonball", + "examine": "Ammo for the Dwarf Cannon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "3", + "name": "Nulodion's notes", + "examine": "Construction notes for Dwarf cannon ammo.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "0.02" + }, + { + "id": "4", + "name": "Ammo mould", + "examine": "Used to make cannon ammunition.", + "archery_ticket_price": "0", "requirements": "{13,35}", "shop_price": "5", - "examine": "Used to make cannon ammunition.", - "name": "Ammo mould", - "weight": "4.5", - "archery_ticket_price": "0", - "id": "4" + "weight": "4.5" }, { - "shop_price": "20", - "examine": "An old note book.", - "durability": null, + "id": "5", "name": "Instruction manual", - "weight": "0.51", + "examine": "An old note book.", "archery_ticket_price": "0", - "id": "5" + "durability": null, + "shop_price": "20", + "weight": "0.51" }, { - "shop_price": "187500", - "ge_buy_limit": "10", + "id": "6", + "name": "Cannon base", "examine": "The cannon is built on this.", - "grand_exchange_price": "189000", - "durability": null, - "name": "Cannon base", - "tradeable": "true", - "weight": "7", "archery_ticket_price": "0", - "id": "6" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true", + "weight": "7" }, { - "shop_price": "187500", - "ge_buy_limit": "10", - "grand_exchange_price": "189000", - "durability": null, + "id": "7", "name": "Cannon base", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true" }, { - "shop_price": "187500", - "ge_buy_limit": "10", + "id": "8", + "name": "Cannon stand", "examine": "The mounting for the multicannon.", - "grand_exchange_price": "185000", - "durability": null, - "name": "Cannon stand", - "tradeable": "true", - "weight": "7", "archery_ticket_price": "0", - "id": "8" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true", + "weight": "7" }, { - "shop_price": "187500", - "ge_buy_limit": "10", - "grand_exchange_price": "185000", - "durability": null, + "id": "9", "name": "Cannon stand", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true" }, { - "shop_price": "187500", - "ge_buy_limit": "10", + "id": "10", + "name": "Cannon barrels", "examine": "The barrels for the multicannon.", - "grand_exchange_price": "187000", - "durability": null, - "name": "Cannon barrels", - "tradeable": "true", - "weight": "7", "archery_ticket_price": "0", - "id": "10" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true", + "weight": "7" }, { - "shop_price": "187500", - "ge_buy_limit": "10", - "grand_exchange_price": "187000", - "durability": null, + "id": "11", "name": "Cannon barrels", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true" }, { - "shop_price": "187500", - "ge_buy_limit": "10", + "id": "12", + "name": "Cannon furnace", "examine": "This powers the multicannon.", - "grand_exchange_price": "175200", - "durability": null, - "name": "Cannon furnace", - "tradeable": "true", - "weight": "7", "archery_ticket_price": "0", - "id": "12" - }, - { - "shop_price": "187500", + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "175200", - "durability": null, + "shop_price": "187500", + "tradeable": "true", + "weight": "7" + }, + { + "id": "13", "name": "Cannon furnace", - "tradeable": "true", "archery_ticket_price": "0", - "id": "13" + "durability": null, + "ge_buy_limit": "10", + "shop_price": "187500", + "tradeable": "true" }, { - "shop_price": "1", - "examine": "A metal railing replacement.", - "durability": null, + "id": "14", "name": "Railing", + "examine": "A metal railing replacement.", "archery_ticket_price": "0", - "id": "14" + "durability": null, + "shop_price": "1" }, { - "examine": "A cloth given to me by Sir Galahad.", - "durability": null, + "id": "15", "name": "Holy table napkin", + "examine": "A cloth given to me by Sir Galahad.", "archery_ticket_price": "0", - "id": "15" + "durability": null }, { - "examine": "A small tin whistle.", - "durability": null, + "id": "16", "name": "Magic whistle", - "weight": "0.2", + "examine": "A small tin whistle.", "archery_ticket_price": "0", - "id": "16" + "durability": null, + "weight": "0.2" }, { - "examine": "I wonder what happens when I ring it?", - "durability": null, + "id": "17", "name": "Grail bell", + "examine": "I wonder what happens when I ring it?", "archery_ticket_price": "0", - "id": "17" + "durability": null }, { - "examine": "It will point the way for me.", - "durability": null, + "id": "18", "name": "Magic gold feather", + "examine": "It will point the way for me.", "archery_ticket_price": "0", - "id": "18" + "durability": null }, { - "examine": "A cog from some machinery.", - "durability": null, + "id": "20", "name": "White cog", - "weight": "1", + "examine": "A cog from some machinery.", "archery_ticket_price": "0", - "id": "20" + "durability": null, + "weight": "1" }, { - "examine": "A cog from some machinery.", - "durability": null, + "id": "21", "name": "Black cog", - "weight": "1", + "examine": "A cog from some machinery.", "archery_ticket_price": "0", - "id": "21" + "durability": null, + "weight": "1" }, { - "examine": "A cog from some machinery.", - "durability": null, + "id": "22", "name": "Blue cog", - "weight": "1", - "archery_ticket_price": "0", - "id": "22" - }, - { "examine": "A cog from some machinery.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "23", "name": "Red cog", - "weight": "1", + "examine": "A cog from some machinery.", "archery_ticket_price": "0", - "id": "23" + "durability": null, + "weight": "1" }, { - "examine": "Doesn't look very tasty.", - "durability": null, + "id": "24", "name": "Rat poison", - "weight": "0.1", + "examine": "Doesn't look very tasty.", "archery_ticket_price": "0", - "id": "24" + "durability": null, + "weight": "0.1" }, { - "examine": "Wormy.", - "durability": null, + "id": "25", "name": "Red vine worm", + "examine": "Wormy.", "archery_ticket_price": "0", - "id": "25" + "durability": null }, { - "examine": "Hemenster fishing contest trophy.", - "durability": null, + "id": "26", "name": "Fishing trophy", + "examine": "Hemenster fishing contest trophy.", "archery_ticket_price": "0", - "id": "26" + "durability": null }, { - "examine": "Pass to the Hemenster fishing contest.", - "durability": null, + "id": "27", "name": "Fishing pass", + "examine": "Pass to the Hemenster fishing contest.", "archery_ticket_price": "0", - "id": "27" + "durability": null }, { - "shop_price": "121", - "examine": "Drives away all known 6 legged creatures.", - "durability": null, + "id": "28", "name": "Insect repellent", + "examine": "Drives away all known 6 legged creatures.", "archery_ticket_price": "0", - "id": "28" + "durability": null, + "shop_price": "121" }, { - "examine": "It's a bucket of wax.", - "durability": null, + "id": "30", "name": "Bucket of wax", - "weight": "2.5", + "examine": "It's a bucket of wax.", "archery_ticket_price": "0", - "id": "30" + "durability": null, + "weight": "2.5" }, { - "examine": "You shouldn't have this.", + "id": "31", "name": "RDT Slot", - "id": "31" + "examine": "You shouldn't have this." }, { - "shop_price": "3", - "examine": "A spooky candle.", - "durability": null, + "id": "32", "name": "Lit black candle", + "examine": "A spooky candle.", "archery_ticket_price": "0", - "id": "32" - }, - { - "shop_price": "24", - "examine": "A candle.", - "grand_exchange_price": "104", "durability": null, - "name": "Lit candle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "33" + "shop_price": "3" }, { - "shop_price": "24", + "id": "33", + "name": "Lit candle", "examine": "A candle.", - "grand_exchange_price": "104", - "durability": null, - "name": "Lit candle", - "tradeable": "true", "archery_ticket_price": "0", - "id": "34" + "durability": null, + "shop_price": "24", + "tradeable": "true" }, { - "shop_price": "200", + "id": "34", + "name": "Lit candle", + "examine": "A candle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "24", + "tradeable": "true" + }, + { + "id": "35", + "name": "Excalibur", "examine": "This used to belong to King Arthur.", - "has_special": "true", - "durability": null, - "weight": "2.2", - "attack_speed": "5", - "weapon_interface": "6", - "render_anim": "2554", - "defence_anim": "397", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "390,390,381,390", "attack_audios": "2500,2500,2517,2500", - "name": "Excalibur", - "archery_ticket_price": "0", - "id": "35", - "bonuses": "20,29,-2,0,0,0,3,2,1,0,0,25,0,0,0" + "attack_speed": "5", + "bonuses": "20,29,-2,0,0,0,3,2,1,0,0,25,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "2554", + "shop_price": "200", + "weapon_interface": "6", + "weight": "2.2" }, { - "shop_price": "24", - "ge_buy_limit": "100", + "id": "36", + "name": "Candle", "examine": "A candle.", - "grand_exchange_price": "119", - "durability": null, - "name": "Candle", - "tradeable": "true", "archery_ticket_price": "0", - "id": "36" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "119", - "durability": null, + "shop_price": "24", + "tradeable": "true" + }, + { + "id": "37", "name": "Candle", - "tradeable": "true", "archery_ticket_price": "0", - "id": "37" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "3", - "examine": "A spooky candle.", - "durability": null, + "id": "38", "name": "Black candle", + "examine": "A spooky candle.", "archery_ticket_price": "0", - "id": "38" + "durability": null, + "shop_price": "3" }, { - "shop_price": "10", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "16", - "durability": null, + "id": "39", "name": "Bronze arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "39" - }, - { - "shop_price": "26", - "ge_buy_limit": "10000", "examine": "I can make an arrow with these.", - "grand_exchange_price": "23", - "durability": null, - "name": "Iron arrowtips", - "tradeable": "true", "archery_ticket_price": "0", - "id": "40" - }, - { - "shop_price": "36", + "durability": null, "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "63", - "durability": null, - "name": "Steel arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "41" - }, - { - "shop_price": "68", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "74", - "durability": null, - "name": "Mithril arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "42" - }, - { - "shop_price": "160", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "185", - "durability": null, - "name": "Adamant arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "43" - }, - { - "shop_price": "460", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "839", - "durability": null, - "name": "Rune arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "44" - }, - { - "ge_buy_limit": "10000", - "examine": "Opal bolt tips.", - "grand_exchange_price": "1", - "durability": null, - "name": "Opal bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "45" - }, - { - "ge_buy_limit": "10000", - "examine": "Pearl bolt tips.", - "grand_exchange_price": "5", - "durability": null, - "name": "Pearl bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "46" - }, - { - "shop_price": "114", - "ge_buy_limit": "10000", - "examine": "I can make bolts with these.", - "grand_exchange_price": "44", - "durability": null, - "name": "Barb bolttips", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "5", - "id": "47" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung longbow; I need a bowstring for this.", - "grand_exchange_price": "14", - "durability": null, - "name": "Longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "48" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "14", - "durability": null, - "name": "Longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "49" - }, - { - "ge_buy_limit": "10000", - "examine": "I need to find a string for this.", - "grand_exchange_price": "5", - "durability": null, - "name": "Shortbow (u)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "50" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "5", - "durability": null, - "name": "Shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "51" - }, - { - "ge_buy_limit": "20000", - "examine": "A wooden arrow shaft", - "grand_exchange_price": "21", - "durability": null, - "name": "Arrow shaft", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "52" - }, - { - "ge_buy_limit": "20000", - "examine": "A wooden arrow shaft with flights attached.", - "grand_exchange_price": "56", - "durability": null, - "name": "Headless arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "53" - }, - { - "shop_price": "50", - "ge_buy_limit": "10000", - "examine": "An unstrung oak bow; I need a bowstring for this.", - "grand_exchange_price": "7", - "durability": null, - "name": "Oak shortbow (u)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "54" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "7", - "durability": null, - "name": "Oak shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "55" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung oak longbow; I need a bowstring for this.", - "grand_exchange_price": "20", - "durability": null, - "name": "Oak longbow (u)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "56" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "20", - "durability": null, - "name": "Oak longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "57" - }, - { - "shop_price": "64", - "ge_buy_limit": "10000", - "examine": "An unstrung willow longbow; I need a bowstring for this.", - "grand_exchange_price": "46", - "durability": null, - "name": "Willow longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "58" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "46", - "durability": null, - "name": "Willow longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "59" - }, - { - "shop_price": "16", - "ge_buy_limit": "10000", - "examine": "An unstrung willow shortbow; I need a bowstring for this.", - "grand_exchange_price": "20", - "durability": null, - "name": "Willow shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "60" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "20", - "durability": null, - "name": "Willow shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "61" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung maple bow; I need a bowstring for this.", - "grand_exchange_price": "94", - "durability": null, - "name": "Maple longbow (u)", - "tradeable": "true", - "weight": "1.2", - "archery_ticket_price": "0", - "id": "62" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "94", - "durability": null, - "name": "Maple longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "63" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung maple bow; I need a bowstring for this.", - "grand_exchange_price": "56", - "durability": null, - "name": "Maple shortbow (u)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "64" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "56", - "durability": null, - "name": "Maple shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "65" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung yew longbow; I need a bowstring for this.", - "grand_exchange_price": "468", - "durability": null, - "name": "Yew longbow (u)", - "tradeable": "true", - "weight": "1.32", - "archery_ticket_price": "0", - "id": "66" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "468", - "durability": null, - "name": "Yew longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "67" - }, - { - "ge_buy_limit": "10000", - "examine": "An unstrung yew shortbow; I need a bowstring for this.", - "grand_exchange_price": "158", - "durability": null, - "name": "Yew shortbow (u)", - "tradeable": "true", - "weight": "1.32", - "archery_ticket_price": "0", - "id": "68" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "158", - "durability": null, - "name": "Yew shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "69" - }, - { - "shop_price": "931", - "ge_buy_limit": "10000", - "examine": "An unstrung magic longbow; I need a bowstring for this.", - "grand_exchange_price": "1246", - "durability": null, - "name": "Magic longbow (u)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "70" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1246", - "durability": null, - "name": "Magic longbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "71" - }, - { - "shop_price": "890", - "ge_buy_limit": "10000", - "examine": "An unstrung magic shortbow; I need a bowstring for this.", - "grand_exchange_price": "745", - "durability": null, - "name": "Magic shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "72" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "745", - "durability": null, - "name": "Magic shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "73" - }, - { - "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", - "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" + "tradeable": "true" }, { + "id": "40", + "name": "Iron arrowtips", + "examine": "I can make an arrow with these.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "41", + "name": "Steel arrowtips", + "examine": "I can make an arrow with these.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "36", + "tradeable": "true" + }, + { + "id": "42", + "name": "Mithril arrowtips", + "examine": "I can make an arrow with these.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "68", + "tradeable": "true" + }, + { + "id": "43", + "name": "Adamant arrowtips", + "examine": "I can make an arrow with these.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "160", + "tradeable": "true" + }, + { + "id": "44", + "name": "Rune arrowtips", + "examine": "I can make an arrow with these.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "460", + "tradeable": "true" + }, + { + "id": "45", + "name": "Opal bolt tips", + "examine": "Opal bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "46", + "name": "Pearl bolt tips", + "examine": "Pearl bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "47", + "name": "Barb bolttips", + "examine": "I can make bolts with these.", + "archery_ticket_price": "5", + "destroy": "true", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "114", + "tradeable": "false" + }, + { + "id": "48", + "name": "Longbow (u)", + "examine": "An unstrung longbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "49", + "name": "Longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "50", + "name": "Shortbow (u)", + "examine": "I need to find a string for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "51", + "name": "Shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "52", + "name": "Arrow shaft", + "examine": "A wooden arrow shaft", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "53", + "name": "Headless arrow", + "examine": "A wooden arrow shaft with flights attached.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "54", + "name": "Oak shortbow (u)", + "examine": "An unstrung oak bow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "50", + "tradeable": "true", + "weight": "1" + }, + { + "id": "55", + "name": "Oak shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "56", + "name": "Oak longbow (u)", + "examine": "An unstrung oak longbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "57", + "name": "Oak longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "58", + "name": "Willow longbow (u)", + "examine": "An unstrung willow longbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "64", + "tradeable": "true" + }, + { + "id": "59", + "name": "Willow longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "60", + "name": "Willow shortbow (u)", + "examine": "An unstrung willow shortbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "16", + "tradeable": "true" + }, + { + "id": "61", + "name": "Willow shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "62", + "name": "Maple longbow (u)", + "examine": "An unstrung maple bow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.2" + }, + { + "id": "63", + "name": "Maple longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "64", + "name": "Maple shortbow (u)", + "examine": "An unstrung maple bow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "65", + "name": "Maple shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "66", + "name": "Yew longbow (u)", + "examine": "An unstrung yew longbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.32" + }, + { + "id": "67", + "name": "Yew longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "68", + "name": "Yew shortbow (u)", + "examine": "An unstrung yew shortbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.32" + }, + { + "id": "69", + "name": "Yew shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "70", + "name": "Magic longbow (u)", + "examine": "An unstrung magic longbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "931", + "tradeable": "true", + "weight": "1" + }, + { + "id": "71", + "name": "Magic longbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "72", + "name": "Magic shortbow (u)", + "examine": "An unstrung magic shortbow; I need a bowstring for this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "890", + "tradeable": "true" + }, + { + "id": "73", + "name": "Magic shortbow (u)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "74", + "name": "Khazard helmet", + "examine": "A helmet, as worn by the minions of General Khazard.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,4,5,3,0,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "I can borrow another from the armoury near the Fight Arena.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10", + "tradeable": "false", + "weight": "2.7" + }, + { + "id": "75", + "name": "Khazard armour", + "examine": "Armour, as worn by the minions of General Khazard.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,11,10,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "I can borrow another from the armoury near the Fight Arena.", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "12", - "examine": "Armour, as worn by the minions of General Khazard.", - "durability": null, - "name": "Khazard armour", - "weight": "0.5", - "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" + "tradeable": "false", + "weight": "0.5" }, { - "destroy_message": "The lazy guard probably has a spare set...", - "examine": "These keys open the cells at the Khazard Fight Arena. (Fight Arena)", - "durability": null, + "id": "76", "name": "Khazard cell keys", - "tradeable": "false", - "destroy": "true", + "examine": "These keys open the cells at the Khazard Fight Arena. (Fight Arena)", "archery_ticket_price": "0", - "id": "76" + "destroy": "true", + "destroy_message": "The lazy guard probably has a spare set...", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Angor, the Khazard barman, has a stock of Khali brew.", - "shop_price": "5", - "examine": "A bottle of Khazard's worst brew.", - "durability": null, + "id": "77", "name": "Khali brew", - "tradeable": "false", + "examine": "A bottle of Khazard's worst brew.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "77" - }, - { - "destroy_message": "Can only be fired from yew, magic, dark or twisted bows.", - "shop_price": "2", - "examine": "Can only be fired from yew, magic, or dark bows.", + "destroy_message": "Angor, the Khazard barman, has a stock of Khali brew.", "durability": null, - "name": "Ice arrows", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "78", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "The power in this staff causes it to vibrate gently.", - "durability": null, - "name": "Staff of armadyl", - "tradeable": "false", - "destroy": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "84", - "render_anim": "2553" - }, - { - "examine": "It catches the light! (Temple of Ikov)", - "durability": null, - "name": "Shiny key", - "archery_ticket_price": "0", - "id": "85" - }, - { - "examine": "An amulet made by Lucien.", - "durability": null, - "name": "Pendant of lucien", - "archery_ticket_price": "0", - "id": "86", - "equipment_slot": "2" - }, - { - "examine": "Yet another amulet.", - "durability": null, - "name": "Armadyl pendant", - "archery_ticket_price": "0", - "id": "87", - "equipment_slot": "2" - }, - { - "examine": "Magic boots that make you lighter than normal.", - "durability": null, - "name": "Boots of lightness", - "weight": "-4.5", - "archery_ticket_price": "0", - "id": "88", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "Magic boots that make you lighter than normal.", - "durability": null, - "name": "Boots of lightness", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "89", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "It's very soft!", - "durability": null, - "name": "Child's blanket", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "90" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this guam potion.", - "grand_exchange_price": "285", - "durability": null, - "name": "Guam potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "91" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "285", - "durability": null, - "name": "Guam potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "92" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this marrentill potion.", - "grand_exchange_price": "43", - "durability": null, - "name": "Marrentill potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "93" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "43", - "durability": null, - "name": "Marrentill potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "94" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this tarromin potion.", - "grand_exchange_price": "287", - "durability": null, - "name": "Tarromin potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "95" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "287", - "durability": null, - "name": "Tarromin potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "96" - }, - { - "shop_price": "64", - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this potion.", - "grand_exchange_price": "435", - "durability": null, - "name": "Harralander potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "97" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "435", - "durability": null, - "name": "Harralander potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "98" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this ranarr potion.", - "grand_exchange_price": "4823", - "durability": null, - "name": "Ranarr potion(unf)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "99" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4823", - "durability": null, - "name": "Ranarr potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "100" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this irit potion.", - "grand_exchange_price": "2243", - "durability": null, - "name": "Irit potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "101" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2243", - "durability": null, - "name": "Irit potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "102" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this avantoe potion.", - "grand_exchange_price": "3831", - "durability": null, - "name": "Avantoe potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "103" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3831", - "durability": null, - "name": "Avantoe potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "104" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this kwuarm potion.", - "grand_exchange_price": "1833", - "durability": null, - "name": "Kwuarm potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "105" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1833", - "durability": null, - "name": "Kwuarm potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "106" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this cadantine potion.", - "grand_exchange_price": "1844", - "durability": null, - "name": "Cadantine potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "107" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1844", - "durability": null, - "name": "Cadantine potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "108" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this dwarf weed potion.", - "grand_exchange_price": "6740", - "durability": null, - "name": "Dwarf weed potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "109" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6740", - "durability": null, - "name": "Dwarf weed potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "110" - }, - { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this torstol potion.", - "grand_exchange_price": "14500", - "durability": null, - "name": "Torstol potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "111" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "14500", - "durability": null, - "name": "Torstol potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "112" - }, - { - "shop_price": "780", - "ge_buy_limit": "100", - "examine": "4 doses of Strength potion.", - "grand_exchange_price": "1041", - "durability": null, - "name": "Strength potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "113" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1041", - "durability": null, - "name": "Strength potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "114" - }, - { - "shop_price": "780", - "ge_buy_limit": "100", - "examine": "3 doses of Strength potion.", - "grand_exchange_price": "812", - "durability": null, - "name": "Strength potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "115" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "812", - "durability": null, - "name": "Strength potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "116" - }, - { - "shop_price": "780", - "ge_buy_limit": "100", - "examine": "2 doses of Strength potion.", - "grand_exchange_price": "656", - "durability": null, - "name": "Strength potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "117" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "656", - "durability": null, - "name": "Strength potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "118" - }, - { - "shop_price": "780", - "ge_buy_limit": "100", - "examine": "1 dose of Strength potion.", - "grand_exchange_price": "355", - "durability": null, - "name": "Strength potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "119" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "355", - "durability": null, - "name": "Strength potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "120" - }, - { - "shop_price": "560", - "ge_buy_limit": "100", - "examine": "3 doses of Attack potion.", - "grand_exchange_price": "195", - "durability": null, - "name": "Attack potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "121" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "195", - "durability": null, - "name": "Attack potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "122" - }, - { - "shop_price": "560", - "ge_buy_limit": "100", - "examine": "2 doses of Attack potion.", - "grand_exchange_price": "113", - "durability": null, - "name": "Attack potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "123" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "113", - "durability": null, - "name": "Attack potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "124" - }, - { - "shop_price": "560", - "ge_buy_limit": "100", - "examine": "1 dose of Attack potion.", - "grand_exchange_price": "197", - "durability": null, - "name": "Attack potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "125" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "197", - "durability": null, - "name": "Attack potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "126" - }, - { - "shop_price": "139", - "ge_buy_limit": "100", - "examine": "3 doses of restore potion.", - "grand_exchange_price": "59", - "durability": null, - "name": "Restore potion(3)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "127" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "59", - "durability": null, - "name": "Restore potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "128" - }, - { - "shop_price": "139", - "ge_buy_limit": "100", - "examine": "2 doses of restore potion.", - "grand_exchange_price": "35", - "durability": null, - "name": "Restore potion(2)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "129" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "35", - "durability": null, - "name": "Restore potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "130" - }, - { - "shop_price": "139", - "ge_buy_limit": "100", - "examine": "1 dose of restore potion.", - "grand_exchange_price": "21", - "durability": null, - "name": "Restore potion(1)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "131" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21", - "durability": null, - "name": "Restore potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "132" - }, - { - "shop_price": "114", - "ge_buy_limit": "100", - "examine": "3 doses of Defence Potion.", - "grand_exchange_price": "323", - "durability": null, - "name": "Defence potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "133" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "323", - "durability": null, - "name": "Defence potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "134" - }, - { - "shop_price": "114", - "ge_buy_limit": "100", - "examine": "2 doses of Defence Potion.", - "grand_exchange_price": "247", - "durability": null, - "name": "Defence potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "135" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "247", - "durability": null, - "name": "Defence potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "136" - }, - { - "shop_price": "114", - "ge_buy_limit": "100", - "examine": "1 dose of Defence Potion.", - "grand_exchange_price": "148", - "durability": null, - "name": "Defence potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "137" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "148", - "durability": null, - "name": "Defence potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "138" - }, - { - "ge_buy_limit": "100", - "examine": "3 doses of Prayer restore potion.", - "grand_exchange_price": "4134", - "durability": null, - "name": "Prayer potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "139" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4134", - "durability": null, - "name": "Prayer potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "140" - }, - { - "ge_buy_limit": "100", - "examine": "2 doses of Prayer restore potion.", - "grand_exchange_price": "2868", - "durability": null, - "name": "Prayer potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "141" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2868", - "durability": null, - "name": "Prayer potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "142" - }, - { - "ge_buy_limit": "100", - "examine": "1 dose of Prayer restore potion.", - "grand_exchange_price": "1353", - "durability": null, - "name": "Prayer potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "143" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1353", - "durability": null, - "name": "Prayer potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "144" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of super Attack potion.", - "grand_exchange_price": "691", - "durability": null, - "name": "Super attack(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "145" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "691", - "durability": null, - "name": "Super attack(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "146" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of super Attack potion.", - "grand_exchange_price": "451", - "durability": null, - "name": "Super attack(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "147" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "451", - "durability": null, - "name": "Super attack(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "148" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of super Attack potion.", - "grand_exchange_price": "243", - "durability": null, - "name": "Super attack(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "149" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "243", - "durability": null, - "name": "Super attack(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "150" - }, - { - "ge_buy_limit": "100", - "examine": "3 doses of Fishing potion.", - "grand_exchange_price": "40", - "durability": null, - "name": "Fishing potion(3)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "151" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "40", - "durability": null, - "name": "Fishing potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "152" - }, - { - "ge_buy_limit": "100", - "examine": "2 doses of Fishing potion.", - "grand_exchange_price": "56", - "durability": null, - "name": "Fishing potion(2)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "153" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "56", - "durability": null, - "name": "Fishing potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "154" - }, - { - "ge_buy_limit": "100", - "examine": "1 dose of Fishing potion.", - "grand_exchange_price": "87", - "durability": null, - "name": "Fishing potion(1)", - "tradeable": "true", - "weight": "0.03", - "archery_ticket_price": "0", - "id": "155" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "87", - "durability": null, - "name": "Fishing potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "156" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of super Strength potion.", - "grand_exchange_price": "1679", - "durability": null, - "name": "Super strength(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "157" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1679", - "durability": null, - "name": "Super strength(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "158" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of super Strength potion.", - "grand_exchange_price": "1164", - "durability": null, - "name": "Super strength(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "159" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1164", - "durability": null, - "name": "Super strength(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "160" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of super Strength potion.", - "grand_exchange_price": "603", - "durability": null, - "name": "Super strength(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "161" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "603", - "durability": null, - "name": "Super strength(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "162" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of super Defence potion.", - "grand_exchange_price": "113", - "durability": null, - "name": "Super defence(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "163" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "113", - "durability": null, - "name": "Super defence(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "164" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of super Defence potion.", - "grand_exchange_price": "65", - "durability": null, - "name": "Super defence(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "165" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "65", - "durability": null, - "name": "Super defence(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "166" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of super Defence potion.", - "grand_exchange_price": "41", - "durability": null, - "name": "Super defence(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "167" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "41", - "durability": null, - "name": "Super defence(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "168" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of ranging potion.", - "grand_exchange_price": "6224", - "durability": null, - "name": "Ranging potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "169" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "6224", - "durability": null, - "name": "Ranging potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "170" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of ranging potion.", - "grand_exchange_price": "4330", - "durability": null, - "name": "Ranging potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "171" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "4330", - "durability": null, - "name": "Ranging potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "172" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of ranging potion.", - "grand_exchange_price": "2068", - "durability": null, - "name": "Ranging potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "173" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2068", - "durability": null, - "name": "Ranging potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "174" - }, - { - "shop_price": "375", - "ge_buy_limit": "1000", - "examine": "3 doses of antipoison potion.", - "grand_exchange_price": "1239", - "durability": null, - "name": "Antipoison(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "175" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1239", - "durability": null, - "name": "Antipoison(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "176" - }, - { - "shop_price": "375", - "ge_buy_limit": "1000", - "examine": "2 doses of antipoison potion.", - "grand_exchange_price": "822", - "durability": null, - "name": "Antipoison(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "177" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "822", - "durability": null, - "name": "Antipoison(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "178" - }, - { - "shop_price": "375", - "ge_buy_limit": "1000", - "examine": "1 dose of antipoison potion.", - "grand_exchange_price": "460", - "durability": null, - "name": "Antipoison(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "179" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "460", - "durability": null, - "name": "Antipoison(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "180" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of super anti poison potion.", - "grand_exchange_price": "872", - "durability": null, - "name": "Super antipoison(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "181" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "872", - "durability": null, - "name": "Super antipoison(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "182" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of super anti poison potion.", - "grand_exchange_price": "509", - "durability": null, - "name": "Super antipoison(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "183" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "509", - "durability": null, - "name": "Super antipoison(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "184" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of super anti poison potion.", - "grand_exchange_price": "366", - "durability": null, - "name": "Super antipoison(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "185" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "366", - "durability": null, - "name": "Super antipoison(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "186" - }, - { - "ge_buy_limit": "1000", - "examine": "For use on daggers and projectiles.", - "grand_exchange_price": "33", - "durability": null, - "name": "Weapon poison", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "187" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "33", - "durability": null, - "name": "Weapon poison", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "188" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of Zamorak brew.", - "grand_exchange_price": "1818", - "durability": null, - "name": "Zamorak brew(3)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "189" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1818", - "durability": null, - "name": "Zamorak brew(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "190" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of Zamorak brew.", - "grand_exchange_price": "1004", - "durability": null, - "name": "Zamorak brew(2)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "191" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1004", - "durability": null, - "name": "Zamorak brew(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "192" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of Zamorak brew.", - "grand_exchange_price": "1175", - "durability": null, - "name": "Zamorak brew(1)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "193" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1175", - "durability": null, - "name": "Zamorak brew(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "194" - }, - { - "ge_buy_limit": "100", - "examine": "This is meant to be good for spots.", - "grand_exchange_price": "117", - "durability": null, - "name": "Potion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "195" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "117", - "durability": null, - "name": "Potion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "196" - }, - { - "shop_price": "39", - "ge_buy_limit": "100", - "examine": "Stankers gives out these strange cocktails for free.", - "grand_exchange_price": "124", - "durability": null, - "name": "Poison chalice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "197" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "124", - "durability": null, - "name": "Poison chalice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "198" - }, - { - "requirements": "{3,15}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "150", - "durability": null, - "name": "Grimy guam", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "199" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "155", - "durability": null, - "name": "Grimy guam", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "200" - }, - { - "requirements": "{5,15}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "150", - "durability": null, - "name": "Grimy marrentill", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "201" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "26", - "durability": null, - "name": "Grimy marrentill", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "202" - }, - { - "requirements": "{11,15}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "150", - "durability": null, - "name": "Grimy tarromin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "203" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "151", - "durability": null, - "name": "Grimy tarromin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "204" - }, - { - "requirements": "{20,15}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "300", - "durability": null, - "name": "Grimy harralander", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "205" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "275", - "durability": null, - "name": "Grimy harralander", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "206" - }, - { - "requirements": "{15,25}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "1300", - "durability": null, - "name": "Grimy ranarr", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "207" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "4909", - "durability": null, - "name": "Grimy ranarr", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "208" - }, - { - "requirements": "{15,40}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "900", - "durability": null, - "name": "Grimy irit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "209" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2034", - "durability": null, - "name": "Grimy irit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "210" - }, - { - "requirements": "{15,48}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "500", - "durability": null, - "name": "Grimy avantoe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "211" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "3789", - "durability": null, - "name": "Grimy avantoe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "212" - }, - { - "requirements": "{15,54}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "900", - "durability": null, - "name": "Grimy kwuarm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "213" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1665", - "durability": null, - "name": "Grimy kwuarm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "214" - }, - { - "requirements": "{15,65}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "900", - "durability": null, - "name": "Grimy cadantine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "215" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1582", - "durability": null, - "name": "Grimy cadantine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "216" - }, - { - "requirements": "{15,70}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "1300", - "durability": null, - "name": "Grimy dwarf weed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "217" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "6934", - "durability": null, - "name": "Grimy dwarf weed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "218" - }, - { - "requirements": "{15,75}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "3200", - "durability": null, - "name": "Grimy torstol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "219" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "35800", - "durability": null, - "name": "Grimy torstol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "220" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "It seems to be looking at me.", - "grand_exchange_price": "27", - "durability": null, - "name": "Eye of newt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "221" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "27", - "durability": null, - "name": "Eye of newt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "222" - }, - { - "ge_buy_limit": "10000", - "examine": "Ewww!", - "grand_exchange_price": "888", - "durability": null, - "name": "Red spiders' eggs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "223" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "888", - "durability": null, - "name": "Red spiders' eggs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "224" - }, - { - "ge_buy_limit": "10000", - "examine": "The root of a limpwurt plant.", - "grand_exchange_price": "1984", - "durability": null, - "name": "Limpwurt root", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "225" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1984", - "durability": null, - "name": "Limpwurt root", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "226" - }, - { - "shop_price": "10", - "ge_buy_limit": "10000", - "examine": "A glass vial containing water.", - "grand_exchange_price": "35", - "durability": null, - "name": "Vial of water", - "tradeable": "true", - "weight": "0.02", - "archery_ticket_price": "0", - "id": "227" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "35", - "durability": null, - "name": "Vial of water", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "228" - }, - { "shop_price": "5", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "78", + "name": "Ice arrows", + "examine": "Can only be fired from yew, magic, or dark bows.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", + "destroy": "true", + "destroy_message": "Can only be fired from yew, magic, dark or twisted bows.", + "durability": null, + "equipment_slot": "13", + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "83", + "name": "Lever", + "examine": "A lever to open something perhaps?", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "84", + "name": "Staff of armadyl", + "examine": "The power in this staff causes it to vibrate gently.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "render_anim": "2553", + "tradeable": "false", + "weight": "5" + }, + { + "id": "85", + "name": "Shiny key", + "examine": "It catches the light! (Temple of Ikov)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "86", + "name": "Pendant of lucien", + "examine": "An amulet made by Lucien.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2" + }, + { + "id": "87", + "name": "Armadyl pendant", + "examine": "Yet another amulet.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2" + }, + { + "id": "88", + "name": "Boots of lightness", + "examine": "Magic boots that make you lighter than normal.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "weight": "-4.5" + }, + { + "id": "89", + "name": "Boots of lightness", + "examine": "Magic boots that make you lighter than normal.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "weight": "0.3" + }, + { + "id": "90", + "name": "Child's blanket", + "examine": "It's very soft!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "91", + "name": "Guam potion(unf)", + "examine": "I need another ingredient to finish this guam potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "92", + "name": "Guam potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "93", + "name": "Marrentill potion(unf)", + "examine": "I need another ingredient to finish this marrentill potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "94", + "name": "Marrentill potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "95", + "name": "Tarromin potion(unf)", + "examine": "I need another ingredient to finish this tarromin potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "96", + "name": "Tarromin potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "97", + "name": "Harralander potion(unf)", + "examine": "I need another ingredient to finish this potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "64", + "tradeable": "true" + }, + { + "id": "98", + "name": "Harralander potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "99", + "name": "Ranarr potion(unf)", + "examine": "I need another ingredient to finish this ranarr potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "100", + "name": "Ranarr potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "101", + "name": "Irit potion(unf)", + "examine": "I need another ingredient to finish this irit potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "102", + "name": "Irit potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "103", + "name": "Avantoe potion(unf)", + "examine": "I need another ingredient to finish this avantoe potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "104", + "name": "Avantoe potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "105", + "name": "Kwuarm potion(unf)", + "examine": "I need another ingredient to finish this kwuarm potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "106", + "name": "Kwuarm potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "107", + "name": "Cadantine potion(unf)", + "examine": "I need another ingredient to finish this cadantine potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "108", + "name": "Cadantine potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "109", + "name": "Dwarf weed potion(unf)", + "examine": "I need another ingredient to finish this dwarf weed potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "110", + "name": "Dwarf weed potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "111", + "name": "Torstol potion(unf)", + "examine": "I need another ingredient to finish this torstol potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "112", + "name": "Torstol potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "113", + "name": "Strength potion(4)", + "examine": "4 doses of Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "780", + "tradeable": "true" + }, + { + "id": "114", + "name": "Strength potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "115", + "name": "Strength potion(3)", + "examine": "3 doses of Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "780", + "tradeable": "true" + }, + { + "id": "116", + "name": "Strength potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "117", + "name": "Strength potion(2)", + "examine": "2 doses of Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "780", + "tradeable": "true" + }, + { + "id": "118", + "name": "Strength potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "119", + "name": "Strength potion(1)", + "examine": "1 dose of Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "780", + "tradeable": "true" + }, + { + "id": "120", + "name": "Strength potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "121", + "name": "Attack potion(3)", + "examine": "3 doses of Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "560", + "tradeable": "true" + }, + { + "id": "122", + "name": "Attack potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "123", + "name": "Attack potion(2)", + "examine": "2 doses of Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "560", + "tradeable": "true" + }, + { + "id": "124", + "name": "Attack potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "125", + "name": "Attack potion(1)", + "examine": "1 dose of Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "560", + "tradeable": "true" + }, + { + "id": "126", + "name": "Attack potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "127", + "name": "Restore potion(3)", + "examine": "3 doses of restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "139", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "128", + "name": "Restore potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "129", + "name": "Restore potion(2)", + "examine": "2 doses of restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "139", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "130", + "name": "Restore potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "131", + "name": "Restore potion(1)", + "examine": "1 dose of restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "139", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "132", + "name": "Restore potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "133", + "name": "Defence potion(3)", + "examine": "3 doses of Defence Potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "114", + "tradeable": "true" + }, + { + "id": "134", + "name": "Defence potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "135", + "name": "Defence potion(2)", + "examine": "2 doses of Defence Potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "114", + "tradeable": "true" + }, + { + "id": "136", + "name": "Defence potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "137", + "name": "Defence potion(1)", + "examine": "1 dose of Defence Potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "114", + "tradeable": "true" + }, + { + "id": "138", + "name": "Defence potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "139", + "name": "Prayer potion(3)", + "examine": "3 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "140", + "name": "Prayer potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "141", + "name": "Prayer potion(2)", + "examine": "2 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "142", + "name": "Prayer potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "143", + "name": "Prayer potion(1)", + "examine": "1 dose of Prayer restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "144", + "name": "Prayer potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "145", + "name": "Super attack(3)", + "examine": "3 doses of super Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "146", + "name": "Super attack(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "147", + "name": "Super attack(2)", + "examine": "2 doses of super Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "148", + "name": "Super attack(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "149", + "name": "Super attack(1)", + "examine": "1 dose of super Attack potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "150", + "name": "Super attack(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "151", + "name": "Fishing potion(3)", + "examine": "3 doses of Fishing potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "152", + "name": "Fishing potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "153", + "name": "Fishing potion(2)", + "examine": "2 doses of Fishing potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "154", + "name": "Fishing potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "155", + "name": "Fishing potion(1)", + "examine": "1 dose of Fishing potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.03" + }, + { + "id": "156", + "name": "Fishing potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "157", + "name": "Super strength(3)", + "examine": "3 doses of super Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "158", + "name": "Super strength(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "159", + "name": "Super strength(2)", + "examine": "2 doses of super Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "160", + "name": "Super strength(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "161", + "name": "Super strength(1)", + "examine": "1 dose of super Strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "162", + "name": "Super strength(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "163", + "name": "Super defence(3)", + "examine": "3 doses of super Defence potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "164", + "name": "Super defence(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "165", + "name": "Super defence(2)", + "examine": "2 doses of super Defence potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "166", + "name": "Super defence(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "167", + "name": "Super defence(1)", + "examine": "1 dose of super Defence potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "168", + "name": "Super defence(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "169", + "name": "Ranging potion(3)", + "examine": "3 doses of ranging potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "170", + "name": "Ranging potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "171", + "name": "Ranging potion(2)", + "examine": "2 doses of ranging potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "172", + "name": "Ranging potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "173", + "name": "Ranging potion(1)", + "examine": "1 dose of ranging potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "174", + "name": "Ranging potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "175", + "name": "Antipoison(3)", + "examine": "3 doses of antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "375", + "tradeable": "true" + }, + { + "id": "176", + "name": "Antipoison(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "177", + "name": "Antipoison(2)", + "examine": "2 doses of antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "375", + "tradeable": "true" + }, + { + "id": "178", + "name": "Antipoison(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "179", + "name": "Antipoison(1)", + "examine": "1 dose of antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "375", + "tradeable": "true" + }, + { + "id": "180", + "name": "Antipoison(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "181", + "name": "Super antipoison(3)", + "examine": "3 doses of super anti poison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "182", + "name": "Super antipoison(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "183", + "name": "Super antipoison(2)", + "examine": "2 doses of super anti poison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "184", + "name": "Super antipoison(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "185", + "name": "Super antipoison(1)", + "examine": "1 dose of super anti poison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "186", + "name": "Super antipoison(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "187", + "name": "Weapon poison", + "examine": "For use on daggers and projectiles.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "188", + "name": "Weapon poison", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "189", + "name": "Zamorak brew(3)", + "examine": "3 doses of Zamorak brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "190", + "name": "Zamorak brew(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "191", + "name": "Zamorak brew(2)", + "examine": "2 doses of Zamorak brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "192", + "name": "Zamorak brew(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "193", + "name": "Zamorak brew(1)", + "examine": "1 dose of Zamorak brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "194", + "name": "Zamorak brew(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "195", + "name": "Potion", + "examine": "This is meant to be good for spots.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "196", + "name": "Potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "197", + "name": "Poison chalice", + "examine": "Stankers gives out these strange cocktails for free.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "39", + "tradeable": "true" + }, + { + "id": "198", + "name": "Poison chalice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "199", + "name": "Grimy guam", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", + "requirements": "{3,15}", + "tradeable": "true" + }, + { + "id": "200", + "name": "Grimy guam", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "201", + "name": "Grimy marrentill", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{5,15}", + "tradeable": "true" + }, + { + "id": "202", + "name": "Grimy marrentill", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "203", + "name": "Grimy tarromin", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{11,15}", + "tradeable": "true" + }, + { + "id": "204", + "name": "Grimy tarromin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "205", + "name": "Grimy harralander", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{20,15}", + "tradeable": "true" + }, + { + "id": "206", + "name": "Grimy harralander", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "207", + "name": "Grimy ranarr", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,25}", + "tradeable": "true" + }, + { + "id": "208", + "name": "Grimy ranarr", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "209", + "name": "Grimy irit", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,40}", + "tradeable": "true" + }, + { + "id": "210", + "name": "Grimy irit", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "211", + "name": "Grimy avantoe", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,48}", + "tradeable": "true" + }, + { + "id": "212", + "name": "Grimy avantoe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "213", + "name": "Grimy kwuarm", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,54}", + "tradeable": "true" + }, + { + "id": "214", + "name": "Grimy kwuarm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "215", + "name": "Grimy cadantine", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,65}", + "tradeable": "true" + }, + { + "id": "216", + "name": "Grimy cadantine", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "217", + "name": "Grimy dwarf weed", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,70}", + "tradeable": "true" + }, + { + "id": "218", + "name": "Grimy dwarf weed", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "219", + "name": "Grimy torstol", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{15,75}", + "tradeable": "true" + }, + { + "id": "220", + "name": "Grimy torstol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "221", + "name": "Eye of newt", + "examine": "It seems to be looking at me.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "222", + "name": "Eye of newt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "223", + "name": "Red spiders' eggs", + "examine": "Ewww!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "224", + "name": "Red spiders' eggs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "225", + "name": "Limpwurt root", + "examine": "The root of a limpwurt plant.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "226", + "name": "Limpwurt root", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "227", + "name": "Vial of water", + "examine": "A glass vial containing water.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "228", + "name": "Vial of water", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "229", + "name": "Vial", "examine": "An empty glass vial.", - "grand_exchange_price": "2", - "durability": null, - "name": "Vial", - "tradeable": "true", "archery_ticket_price": "0", - "id": "229" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "5", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2", - "durability": null, + "id": "230", "name": "Vial", - "tradeable": "true", "archery_ticket_price": "0", - "id": "230" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "67", - "ge_buy_limit": "10000", + "id": "231", + "name": "Snape grass", "examine": "Strange spiky grass.", - "grand_exchange_price": "318", - "durability": null, - "name": "Snape grass", - "tradeable": "true", "archery_ticket_price": "0", - "id": "231" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "318", - "durability": null, - "name": "Snape grass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "232" + "shop_price": "67", + "tradeable": "true" }, { - "shop_price": "4", - "ge_buy_limit": "1000", + "id": "232", + "name": "Snape grass", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "233", + "name": "Pestle and mortar", "examine": "I can grind things for potions in this.", - "grand_exchange_price": "38", - "durability": null, - "name": "Pestle and mortar", - "tradeable": "true", - "weight": "0.05", "archery_ticket_price": "0", - "id": "233" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "38", - "durability": null, + "shop_price": "4", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "234", "name": "Pestle and mortar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "234" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "235", + "name": "Unicorn horn dust", "examine": "Finely ground horn of Unicorn.", - "grand_exchange_price": "2460", - "durability": null, - "name": "Unicorn horn dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "235" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2460", - "durability": null, + "id": "236", "name": "Unicorn horn dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "236" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "237", + "name": "Unicorn horn", "examine": "This horn has restorative properties.", - "grand_exchange_price": "2481", - "durability": null, - "name": "Unicorn horn", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "237" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2481", - "durability": null, + "id": "238", "name": "Unicorn horn", - "tradeable": "true", "archery_ticket_price": "0", - "id": "238" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "239", + "name": "White berries", "examine": "Sour berries, used in potions.", - "grand_exchange_price": "1438", - "durability": null, - "name": "White berries", - "tradeable": "true", "archery_ticket_price": "0", - "id": "239" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1438", - "durability": null, + "id": "240", "name": "White berries", - "tradeable": "true", "archery_ticket_price": "0", - "id": "240" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "241", + "name": "Dragon scale dust", "examine": "Finely ground scale of Dragon.", - "grand_exchange_price": "1336", - "durability": null, - "name": "Dragon scale dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "241" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1336", - "durability": null, + "id": "242", "name": "Dragon scale dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "242" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "243", + "name": "Blue dragon scale", "examine": "A large shiny scale.", - "grand_exchange_price": "1244", - "durability": null, - "name": "Blue dragon scale", - "tradeable": "true", - "weight": "0.01", "archery_ticket_price": "0", - "id": "243" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.01" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1244", - "durability": null, + "id": "244", "name": "Blue dragon scale", - "tradeable": "true", "archery_ticket_price": "0", - "id": "244" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "245", + "name": "Wine of zamorak", "examine": "An evil wine that is often found in chaos temples.", - "grand_exchange_price": "2768", - "durability": null, - "name": "Wine of zamorak", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "245" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2768", - "durability": null, + "id": "246", "name": "Wine of zamorak", - "tradeable": "true", "archery_ticket_price": "0", - "id": "246" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "247", + "name": "Jangerberries", "examine": "They don't look very ripe.", - "grand_exchange_price": "83", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "248", "name": "Jangerberries", - "tradeable": "true", "archery_ticket_price": "0", - "id": "247" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "83", - "durability": null, - "name": "Jangerberries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "248" - }, - { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "182", - "durability": null, + "id": "249", "name": "Clean guam", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "249" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "182", - "durability": null, + "id": "250", "name": "Clean guam", - "tradeable": "true", "archery_ticket_price": "0", - "id": "250" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "23", - "durability": null, + "id": "251", "name": "Clean marrentill", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "251" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "23", - "durability": null, + "id": "252", "name": "Clean marrentill", - "tradeable": "true", "archery_ticket_price": "0", - "id": "252" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "144", - "durability": null, + "id": "253", "name": "Clean tarromin", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "253" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "144", - "durability": null, + "id": "254", "name": "Clean tarromin", - "tradeable": "true", "archery_ticket_price": "0", - "id": "254" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "275", - "durability": null, + "id": "255", "name": "Clean harralander", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "255" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "275", - "durability": null, + "id": "256", "name": "Clean harralander", - "tradeable": "true", "archery_ticket_price": "0", - "id": "256" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "4930", - "durability": null, + "id": "257", "name": "Clean ranarr", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "257" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "4930", - "durability": null, + "id": "258", "name": "Clean ranarr", - "tradeable": "true", "archery_ticket_price": "0", - "id": "258" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "2072", - "durability": null, + "id": "259", "name": "Clean irit", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "259" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2072", - "durability": null, + "id": "260", "name": "Clean irit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "260" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "3812", - "durability": null, + "id": "261", "name": "Clean avantoe", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "261" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "3812", - "durability": null, + "id": "262", "name": "Clean avantoe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "262" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "1641", - "durability": null, + "id": "263", "name": "Clean kwuarm", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "263" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1641", - "durability": null, + "id": "264", "name": "Clean kwuarm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "264" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "1585", - "durability": null, + "id": "265", "name": "Clean cadantine", - "tradeable": "true", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "265" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1585", - "durability": null, + "id": "266", "name": "Clean cadantine", - "tradeable": "true", "archery_ticket_price": "0", - "id": "266" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "6925", - "durability": null, + "id": "267", "name": "Clean dwarf weed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "267" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "6925", - "durability": null, - "name": "Clean dwarf weed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "268" - }, - { - "ge_buy_limit": "10000", "examine": "A fresh herb.", - "grand_exchange_price": "8400", - "durability": null, - "name": "Clean torstol", - "tradeable": "true", "archery_ticket_price": "0", - "id": "269" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "35900", - "durability": null, - "name": "Clean torstol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "270" + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It looks like part of a machine", + "id": "268", + "name": "Clean dwarf weed", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "269", + "name": "Clean torstol", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "270", + "name": "Clean torstol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "271", "name": "Pressure gauge", + "examine": "It looks like part of a machine", "archery_ticket_price": "0", - "id": "271" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A box of fish food / Keeps your pet fish strong and healthy.", - "durability": null, - "name": "Fish food", - "archery_ticket_price": "0", "id": "272", + "name": "Fish food", + "examine": "A box of fish food / Keeps your pet fish strong and healthy.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "destroy_message": "You'll have to make more in Draynor Manor.", - "examine": "This isn't good for fish.", - "durability": null, - "name": "Poisoned fish food", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "274" + "id": "273", + "name": "Poison", + "examine": "This stuff looks nasty." }, { - "destroy_message": "You can reclaim this item from the place you found it.", + "id": "274", + "name": "Poisoned fish food", + "examine": "This isn't good for fish.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to make more in Draynor Manor.", "durability": null, + "tradeable": "false" + }, + { + "id": "275", "name": "Key", "archery_ticket_price": "0", - "id": "275" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It's slightly charred.", - "durability": null, + "id": "276", "name": "Rubber tube", + "examine": "It's slightly charred.", "archery_ticket_price": "0", - "id": "276" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It's pretty full.", - "durability": null, + "id": "277", "name": "Oil can", - "tradeable": "false", + "examine": "It's pretty full.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "277" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "A sharp cattleprod.", - "durability": null, - "name": "Cattleprod", - "weight": "0.4", - "archery_ticket_price": "0", "id": "278", - "weapon_interface": "5", + "name": "Cattleprod", + "examine": "A sharp cattleprod.", + "archery_ticket_price": "0", "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "5", + "weight": "0.4" }, { - "examine": "Councillor Halgrive gave me this to kill some sheep.", - "durability": null, + "id": "279", "name": "Sheep feed", - "weight": "2.2", + "examine": "Councillor Halgrive gave me this to kill some sheep.", "archery_ticket_price": "0", - "id": "279" + "durability": null, + "weight": "2.2" }, { - "examine": "The suspicious-looking remains of a suspicious-looking sheep.", - "durability": null, + "id": "280", "name": "Sheep bones (1)", + "examine": "The suspicious-looking remains of a suspicious-looking sheep.", "archery_ticket_price": "0", - "id": "280" + "durability": null }, { - "examine": "The suspicious-looking remains of a suspicious-looking sheep.", - "durability": null, + "id": "281", "name": "Sheep bones (2)", + "examine": "The suspicious-looking remains of a suspicious-looking sheep.", "archery_ticket_price": "0", - "id": "281" + "durability": null }, { - "examine": "The suspicious-looking remains of a suspicious-looking sheep.", - "durability": null, + "id": "282", "name": "Sheep bones (3)", - "archery_ticket_price": "0", - "id": "282" - }, - { "examine": "The suspicious-looking remains of a suspicious-looking sheep.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "283", "name": "Sheep bones (4)", + "examine": "The suspicious-looking remains of a suspicious-looking sheep.", "archery_ticket_price": "0", - "id": "283" + "durability": null }, { - "remove_sleeves": "true", - "examine": "This should protect me from the plague, I hope!", - "durability": null, - "name": "Plague jacket", - "weight": "2.2", - "archery_ticket_price": "0", "id": "284", - "equipment_slot": "4" + "name": "Plague jacket", + "examine": "This should protect me from the plague, I hope!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.2" }, { - "examine": "A thick pair of leather trousers. or These should protect me from the plague, I hope!", - "durability": null, - "name": "Plague trousers", - "weight": "2.2", - "archery_ticket_price": "0", "id": "285", - "equipment_slot": "7" + "name": "Plague trousers", + "examine": "A thick pair of leather trousers. or These should protect me from the plague, I hope!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2.2" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Orange goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "286", - "equipment_slot": "4" + "name": "Orange goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Blue goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "287", - "equipment_slot": "4" - }, - { - "shop_price": "16", - "ge_buy_limit": "100", + "name": "Blue goblin mail", "examine": "Armour designed to fit goblins.", - "grand_exchange_price": "398", - "durability": null, - "name": "Goblin mail", - "tradeable": "true", - "weight": "3.6", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" + }, + { "id": "288", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "398", - "durability": null, "name": "Goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "16", "tradeable": "true", - "archery_ticket_price": "0", - "id": "289" + "weight": "3.6" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This contains some vital research results.", + "id": "289", + "name": "Goblin mail", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "290", "name": "Research package", - "weight": "1", + "examine": "This contains some vital research results.", "archery_ticket_price": "0", - "id": "290" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "weight": "1" + }, + { + "id": "291", "name": "Notes", "archery_ticket_price": "0", - "id": "291" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "A book on elven history in northern RuneScape.", - "durability": null, + "id": "292", "name": "Book on baxtorian", + "examine": "A book on elven history in northern 2009Scape.", "archery_ticket_price": "0", - "id": "292" + "durability": null }, { - "examine": "A small pebble with elven inscription.", - "durability": null, + "id": "293", + "name": "A key", + "examine": "This will unlock something. (Waterfall Quest)", + "archery_ticket_price": "0", + "tradeable": "false", + "weight": "0.01" + }, + { + "id": "294", "name": "Glarial's pebble", + "examine": "A small pebble with elven inscription.", "archery_ticket_price": "0", - "id": "294" + "durability": null }, { - "examine": "A bright green gem set in a necklace.", - "durability": null, - "name": "Glarial's amulet", - "archery_ticket_price": "0", "id": "295", + "name": "Glarial's amulet", + "examine": "A bright green gem set in a necklace.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "examine": "An urn containing Glarial's ashes./An empty urn made for Glarial's ashes.", - "durability": null, + "id": "296", "name": "Glarial's urn", - "archery_ticket_price": "0", - "id": "296" - }, - { "examine": "An urn containing Glarial's ashes./An empty urn made for Glarial's ashes.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "297", "name": "Glarial's urn", + "examine": "An urn containing Glarial's ashes./An empty urn made for Glarial's ashes.", "archery_ticket_price": "0", - "id": "297" + "durability": null }, { - "shop_price": "300", - "ge_buy_limit": "5000", - "examine": "Magical seeds in a mithril case.", - "grand_exchange_price": "623", - "durability": null, - "name": "Mithril seeds", - "tradeable": "true", + "id": "298", + "name": "A key", + "examine": "This will unlock something. (Waterfall dungeon)", "archery_ticket_price": "0", - "id": "299" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A bit of rat.", - "durability": null, - "name": "Rat's tail", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "300" + "weight": "0.01" }, { + "id": "299", + "name": "Mithril seeds", + "examine": "Magical seeds in a mithril case.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "300", + "name": "Rat's tail", + "examine": "A bit of rat.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "301", + "name": "Lobster pot", + "examine": "Useful for catching lobsters.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{10,40}", "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Useful for catching lobsters.", - "grand_exchange_price": "100", - "durability": null, - "name": "Lobster pot", "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "301" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "100", - "durability": null, + "id": "302", "name": "Lobster pot", - "tradeable": "true", "archery_ticket_price": "0", - "id": "302" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "40", - "ge_buy_limit": "100", + "id": "303", + "name": "Small fishing net", "examine": "Useful for catching small fish.", - "grand_exchange_price": "397", - "durability": null, - "name": "Small fishing net", - "tradeable": "true", - "weight": "4.5", "archery_ticket_price": "0", - "id": "303" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "397", - "durability": null, - "name": "Small fishing net", + "shop_price": "40", "tradeable": "true", - "archery_ticket_price": "0", - "id": "304" + "weight": "4.5" }, { + "id": "304", + "name": "Small fishing net", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "305", + "name": "Big fishing net", + "examine": "Useful for catching lots of fish.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{16,10}", "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Useful for catching lots of fish.", - "grand_exchange_price": "329", - "durability": null, - "name": "Big fishing net", "tradeable": "true", - "weight": "8.1", - "archery_ticket_price": "0", - "id": "305" + "weight": "8.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "329", - "durability": null, + "id": "306", "name": "Big fishing net", - "tradeable": "true", "archery_ticket_price": "0", - "id": "306" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "307", + "name": "Fishing rod", + "examine": "Useful for catching sardine or herring.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{5,10}", "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Useful for catching sardine or herring.", - "grand_exchange_price": "74", - "durability": null, - "name": "Fishing rod", "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "307" + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "74", - "durability": null, + "id": "308", "name": "Fishing rod", - "tradeable": "true", "archery_ticket_price": "0", - "id": "308" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "309", + "name": "Fly fishing rod", + "examine": "Useful for catching salmon or trout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{20,10}", "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Useful for catching salmon or trout.In Daemonhiem: Just add feathers.", - "grand_exchange_price": "123", - "durability": null, - "name": "Fly fishing rod", "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "309" + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "123", - "durability": null, + "id": "310", "name": "Fly fishing rod", - "tradeable": "true", "archery_ticket_price": "0", - "id": "310" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "311", + "name": "Harpoon", + "examine": "Useful for catching big fish.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", "requirements": "{10,35}", "shop_price": "45", - "ge_buy_limit": "5000", - "examine": "Useful for catching big fish.", - "grand_exchange_price": "235", - "durability": null, - "name": "Harpoon", "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "311" + "weight": "1.3" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "235", - "durability": null, + "id": "312", "name": "Harpoon", - "tradeable": "true", "archery_ticket_price": "0", - "id": "312" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "For use with a fishing rod.", - "grand_exchange_price": "3", - "durability": null, + "id": "313", "name": "Fishing bait", - "tradeable": "true", + "examine": "For use with a fishing rod.", "archery_ticket_price": "0", - "id": "313" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "10000", - "examine": "Used for fly fishing.", - "grand_exchange_price": "24", - "durability": null, + "id": "314", "name": "Feather", - "tradeable": "true", + "examine": "Used for fly fishing.", "archery_ticket_price": "0", - "id": "314" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "6", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "315", + "name": "Shrimps", "examine": "Some nicely cooked shrimp.", - "grand_exchange_price": "9", - "durability": null, - "name": "Shrimps", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "315" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "9", - "durability": null, - "name": "Shrimps", "tradeable": "true", - "archery_ticket_price": "0", - "id": "316" + "weight": "0.5" }, { - "shop_price": "6", - "ge_buy_limit": "20000", + "id": "316", + "name": "Shrimps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "317", + "name": "Raw shrimps", "examine": "I should try cooking this.", - "grand_exchange_price": "7", - "durability": null, - "name": "Raw shrimps", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "317" - }, - { + "durability": null, "ge_buy_limit": "20000", - "grand_exchange_price": "7", - "durability": null, - "name": "Raw shrimps", + "shop_price": "6", "tradeable": "true", - "archery_ticket_price": "0", - "id": "318" + "weight": "0.5" }, { - "ge_buy_limit": "10000", + "id": "318", + "name": "Raw shrimps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "319", + "name": "Anchovies", "examine": "Some nicely cooked anchovies.", - "grand_exchange_price": "26", - "durability": null, - "name": "Anchovies", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "319" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "26", - "durability": null, - "name": "Anchovies", "tradeable": "true", - "archery_ticket_price": "0", - "id": "320" + "weight": "0.1" }, { - "shop_price": "16", - "ge_buy_limit": "20000", + "id": "320", + "name": "Anchovies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "321", + "name": "Raw anchovies", "examine": "I should try cooking this.", - "grand_exchange_price": "11", - "durability": null, - "name": "Raw anchovies", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "321" - }, - { + "durability": null, "ge_buy_limit": "20000", - "grand_exchange_price": "11", - "durability": null, + "shop_price": "16", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "322", "name": "Raw anchovies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "322" - }, - { "durability": null, - "name": "Burnt fish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "324" + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "shop_price": "10", - "ge_buy_limit": "10000", + "id": "324", + "name": "Burnt fish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "325", + "name": "Sardine", "examine": "Some nicely cooked Sardines.", - "grand_exchange_price": "12", - "durability": null, - "name": "Sardine", - "tradeable": "true", - "weight": "0.12", "archery_ticket_price": "0", - "id": "325" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "12", - "durability": null, - "name": "Sardine", + "shop_price": "10", "tradeable": "true", - "archery_ticket_price": "0", - "id": "326" + "weight": "0.12" }, { - "shop_price": "7", - "ge_buy_limit": "20000", + "id": "326", + "name": "Sardine", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "327", + "name": "Raw sardine", "examine": "I should try cooking this.", - "grand_exchange_price": "31", - "durability": null, - "name": "Raw sardine", - "tradeable": "true", - "weight": "0.12", "archery_ticket_price": "0", - "id": "327" - }, - { + "durability": null, "ge_buy_limit": "20000", - "grand_exchange_price": "31", - "durability": null, - "name": "Raw sardine", + "shop_price": "7", "tradeable": "true", - "archery_ticket_price": "0", - "id": "328" + "weight": "0.12" }, { - "shop_price": "88", - "ge_buy_limit": "10000", + "id": "328", + "name": "Raw sardine", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "329", + "name": "Salmon", "examine": "Some nicely cooked salmon.", - "grand_exchange_price": "278", - "durability": null, - "name": "Salmon", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "329" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "278", - "durability": null, - "name": "Salmon", + "shop_price": "88", "tradeable": "true", - "archery_ticket_price": "0", - "id": "330" + "weight": "0.5" }, { + "id": "330", + "name": "Salmon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "331", + "name": "Raw salmon", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", "shop_price": "36", - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "172", - "durability": null, - "name": "Raw salmon", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "331" + "weight": "0.5" }, { - "ge_buy_limit": "20000", - "grand_exchange_price": "172", - "durability": null, + "id": "332", "name": "Raw salmon", - "tradeable": "true", "archery_ticket_price": "0", - "id": "332" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "shop_price": "7", - "ge_buy_limit": "10000", + "id": "333", + "name": "Trout", "examine": "Some nicely cooked trout.", - "grand_exchange_price": "172", - "durability": null, - "name": "Trout", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "333" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "172", - "durability": null, - "name": "Trout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "334" - }, - { "shop_price": "7", - "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "334", + "name": "Trout", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "335", + "name": "Raw trout", "examine": "I should try cooking this.", - "grand_exchange_price": "67", - "durability": null, - "name": "Raw trout", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "335" - }, - { + "durability": null, "ge_buy_limit": "20000", - "grand_exchange_price": "67", - "durability": null, - "name": "Raw trout", + "shop_price": "7", "tradeable": "true", - "archery_ticket_price": "0", - "id": "336" + "weight": "0.4" }, { - "examine": "Some nicely cooked giant carp.", + "id": "336", + "name": "Raw trout", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "337", "name": "Giant carp", + "examine": "Some nicely cooked giant carp.", "archery_ticket_price": "0", - "id": "337" + "durability": null }, { - "examine": "I should try cooking this.", - "durability": null, + "id": "338", "name": "Raw giant carp", + "examine": "I should try cooking this.", "archery_ticket_price": "0", - "id": "338" + "durability": null }, { - "shop_price": "49", - "ge_buy_limit": "10000", + "id": "339", + "name": "Cod", "examine": "Some nicely cooked fish.", - "grand_exchange_price": "19", - "durability": null, - "name": "Cod", - "tradeable": "true", - "weight": "0.33", "archery_ticket_price": "0", - "id": "339" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "19", - "durability": null, - "name": "Cod", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "340" - }, - { "shop_price": "49", - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "29", - "durability": null, - "name": "Raw cod", "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "341" + "weight": "0.33" }, { - "ge_buy_limit": "20000", - "grand_exchange_price": "29", - "durability": null, - "name": "Raw cod", - "tradeable": "true", + "id": "340", + "name": "Cod", "archery_ticket_price": "0", - "id": "342" - }, - { "durability": null, - "name": "Burnt fish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "344" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "63", - "durability": null, - "name": "Raw herring", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "345" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "63", - "durability": null, - "name": "Raw herring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "346" - }, - { - "shop_price": "6", "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "341", + "name": "Raw cod", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "shop_price": "49", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "342", + "name": "Raw cod", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "344", + "name": "Burnt fish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "345", + "name": "Raw herring", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "346", + "name": "Raw herring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "347", + "name": "Herring", "examine": "Some nicely cooked herring.", - "grand_exchange_price": "23", - "durability": null, - "name": "Herring", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "347" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "23", - "durability": null, - "name": "Herring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "348" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "72", - "durability": null, - "name": "Raw pike", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "349" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "72", - "durability": null, - "name": "Raw pike", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "350" - }, - { - "ge_buy_limit": "10000", - "examine": "Some nicely cooked pike.", - "grand_exchange_price": "94", - "durability": null, - "name": "Pike", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "351" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "94", - "durability": null, - "name": "Pike", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "352" - }, - { - "shop_price": "26", - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "14", - "durability": null, - "name": "Raw mackerel", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "353" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "14", - "durability": null, - "name": "Raw mackerel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "354" - }, - { - "shop_price": "23", - "ge_buy_limit": "10000", - "examine": "Some nicely cooked fish.", - "grand_exchange_price": "5", - "durability": null, - "name": "Mackerel", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "355" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "5", - "durability": null, - "name": "Mackerel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "356" - }, - { - "durability": null, - "name": "Burnt fish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "358" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "210", - "durability": null, - "name": "Raw tuna", - "tradeable": "true", - "weight": "0.379", - "archery_ticket_price": "0", - "id": "359" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "210", - "durability": null, - "name": "Raw tuna", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "360" - }, - { - "shop_price": "126", - "ge_buy_limit": "10000", - "examine": "Wow, this is a big fish.", - "grand_exchange_price": "281", - "durability": null, - "name": "Tuna", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "361" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "281", - "durability": null, - "name": "Tuna", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "362" - }, - { - "ge_buy_limit": "20000", - "examine": "Wow, this is a big fish.", - "grand_exchange_price": "490", - "durability": null, - "name": "Raw bass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "363" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "490", - "durability": null, - "name": "Raw bass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "364" - }, - { - "shop_price": "270", - "ge_buy_limit": "10000", - "examine": "Wow, this is a big fish.", - "grand_exchange_price": "330", - "durability": null, - "name": "Bass", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "365" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "330", - "durability": null, - "name": "Bass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "366" - }, - { - "durability": null, - "name": "Burnt fish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "368" - }, - { - "durability": null, - "name": "Burnt fish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "370" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "513", - "durability": null, - "name": "Raw swordfish", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "371" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "513", - "durability": null, - "name": "Raw swordfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "372" - }, - { - "shop_price": "400", - "ge_buy_limit": "10000", - "examine": "I'd better be careful eating this!", - "grand_exchange_price": "501", - "durability": null, - "name": "Swordfish", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "373" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "501", - "durability": null, - "name": "Swordfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "374" - }, - { - "durability": null, - "name": "Burnt swordfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "376" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "322", - "durability": null, - "name": "Raw lobster", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "377" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "322", - "durability": null, - "name": "Raw lobster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "378" - }, - { - "shop_price": "107", - "ge_buy_limit": "10000", - "examine": "This looks tricky to eat.", - "grand_exchange_price": "334", - "durability": null, - "name": "Lobster", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "379" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "334", - "durability": null, - "name": "Lobster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "380" - }, - { - "durability": null, - "name": "Burnt lobster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "382" - }, - { - "ge_buy_limit": "20000", - "examine": "I should try cooking this.", - "grand_exchange_price": "1615", - "durability": null, - "name": "Raw shark", - "tradeable": "true", - "weight": "0.7", - "archery_ticket_price": "0", - "id": "383" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "1615", - "durability": null, - "name": "Raw shark", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "384" - }, - { - "ge_buy_limit": "10000", - "examine": "I'd better be careful eating this.", - "grand_exchange_price": "1682", - "durability": null, - "name": "Shark", - "tradeable": "true", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "385" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1682", - "durability": null, - "name": "Shark", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "386" - }, - { - "durability": null, - "name": "Burnt shark", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "388" - }, - { - "ge_buy_limit": "20000", - "examine": "Tastier when cooked.", - "grand_exchange_price": "2114", - "durability": null, - "name": "Raw manta ray", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "389" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "2114", - "durability": null, - "name": "Raw manta ray", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "390" - }, - { - "ge_buy_limit": "10000", - "examine": "Mmmm...this looks tasty!", - "grand_exchange_price": "2552", - "durability": null, - "name": "Manta ray", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "391" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2552", - "durability": null, - "name": "Manta ray", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "392" - }, - { - "durability": null, - "name": "Burnt manta ray", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "394" - }, - { - "ge_buy_limit": "20000", - "examine": "Looks wrinkly.", - "grand_exchange_price": "2397", - "durability": null, - "name": "Raw sea turtle", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "395" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "2397", - "durability": null, - "name": "Raw sea turtle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "396" - }, - { - "ge_buy_limit": "10000", - "examine": "Tasty!", - "grand_exchange_price": "2486", - "durability": null, - "name": "Sea turtle", - "tradeable": "true", - "weight": "0.35", - "archery_ticket_price": "0", - "id": "397" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2486", - "durability": null, - "name": "Sea turtle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "398" - }, - { - "durability": null, - "name": "Burnt sea turtle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "400" - }, - { - "shop_price": "5", - "ge_buy_limit": "10000", - "examine": "Slightly damp seaweed.", - "grand_exchange_price": "780", - "durability": null, - "name": "Seaweed", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "401" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "780", - "durability": null, - "name": "Seaweed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "402" - }, - { - "ge_buy_limit": "10000", - "examine": "Slightly damp seaweed.", - "grand_exchange_price": "291", - "durability": null, - "name": "Edible seaweed", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "403" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "291", - "durability": null, - "name": "Edible seaweed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "404" - }, - { - "ge_buy_limit": "100", - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "4162", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "405" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4162", - "durability": null, - "name": "Casket", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "406" - }, - { - "ge_buy_limit": "500", - "examine": "Its a rare oyster.", - "grand_exchange_price": "23", - "durability": null, - "name": "Oyster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "407" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "23", - "durability": null, - "name": "Oyster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "408" - }, - { - "shop_price": "5", - "examine": "Aww, it's empty.", - "durability": null, - "name": "Empty oyster", - "tradeable": "true", - "weight": "0.08", - "archery_ticket_price": "0", - "id": "409" - }, - { - "durability": null, - "name": "Empty oyster", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "410" - }, - { - "shop_price": "44", - "ge_buy_limit": "500", - "examine": "I could work wonders with a chisel on this pearl.", - "grand_exchange_price": "18", - "durability": null, - "name": "Oyster pearl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "411", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "18", - "durability": null, - "name": "Oyster pearl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "412" - }, - { - "ge_buy_limit": "500", - "examine": "I could work wonders with a chisel on these pearls.", - "grand_exchange_price": "639", - "durability": null, - "name": "Oyster pearls", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "413", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "639", - "durability": null, - "name": "Oyster pearls", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "414" - }, - { - "examine": "An expensive colourless liquid.", - "durability": null, - "name": "Ethenea", - "archery_ticket_price": "0", - "id": "415" - }, - { - "examine": "This isn't worth much.", - "durability": null, - "name": "Liquid honey", - "archery_ticket_price": "0", - "id": "416" - }, - { - "examine": "It's highly poisonous.", - "durability": null, - "name": "Sulphuric broline", - "weight": "1", - "archery_ticket_price": "0", - "id": "417" - }, - { - "examine": "Probably best I don't keep this too long.", - "durability": null, - "name": "Plague sample", - "archery_ticket_price": "0", - "id": "418" - }, - { - "examine": "A special kind of paper.", - "durability": null, - "name": "Touch paper", - "archery_ticket_price": "0", - "id": "419" - }, - { - "examine": "Apparently it distills.", - "durability": null, - "name": "Distillator", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "420" - }, - { - "examine": "Yup. It's an amulet.", - "durability": null, - "name": "Lathas' amulet", - "archery_ticket_price": "0", - "id": "421", - "equipment_slot": "2" - }, - { - "examine": "Birds love this stuff!", - "durability": null, - "name": "Bird feed", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "422" - }, - { - "examine": "(empty) It's empty... (full) It's full of pigeons.", - "durability": null, - "name": "Pigeon cage", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "424" - }, - { - "examine": "(empty) It's empty... (full) It's full of pigeons.", - "durability": null, - "name": "Pigeon cage", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "425" - }, - { - "ge_buy_limit": "100", - "shop_price": "5", - "examine": "Top half of a priest suit.", - "durability": null, - "weight": "2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "55", - "name": "Priest gown", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "426", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,3,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "55", - "durability": null, - "name": "Priest gown", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "427" - }, - { - "ge_buy_limit": "100", - "shop_price": "5", - "examine": "Bottom half of a priest suit.", - "grand_exchange_price": "14", - "durability": null, - "name": "Priest gown", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "428", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,3,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "14", - "durability": null, - "name": "Priest gown", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "429" - }, - { - "remove_sleeves": "true", - "examine": "Medical looking.", - "durability": null, - "name": "Doctors' gown", - "weight": "1", - "archery_ticket_price": "0", - "id": "430", - "equipment_slot": "4" - }, - { - "shop_price": "27", - "examine": "A very strong spirit brewed in Karamja.", - "durability": null, - "name": "Karamjan rum", - "archery_ticket_price": "0", - "id": "431" - }, - { - "examine": "Pirates don't have the best handwriting...", - "durability": null, - "name": "Pirate message", - "weight": "1", - "archery_ticket_price": "0", - "id": "433" - }, - { - "ge_buy_limit": "25000", - "examine": "Some hard dry clay.", - "grand_exchange_price": "150", - "durability": null, - "name": "Clay", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "434" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "406", - "durability": null, - "name": "Clay", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "435" - }, - { - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "80", - "tokkul_price": "4", - "durability": null, - "name": "Copper ore", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "436" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "58", - "durability": null, - "name": "Copper ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "437" - }, - { "shop_price": "6", - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "80", - "tokkul_price": "4", - "durability": null, - "name": "Tin ore", "tradeable": "true", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "438" + "weight": "0.5" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "46", + "id": "348", + "name": "Herring", + "archery_ticket_price": "0", "durability": null, - "name": "Tin ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "439" - }, - { - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "204", - "tokkul_price": "25", - "durability": null, - "name": "Iron ore", - "tradeable": "true", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "440" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "396", - "durability": null, - "name": "Iron ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "441" - }, - { - "shop_price": "82", - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "180", - "tokkul_price": "112", - "durability": null, - "name": "Silver ore", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "442" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "205", - "durability": null, - "name": "Silver ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "443" - }, - { - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "311", - "tokkul_price": "225", - "durability": null, - "name": "Gold ore", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "444" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "319", - "durability": null, - "name": "Gold ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "445" - }, - { - "examine": "This needs refining.", - "durability": null, - "name": "'perfect' gold ore", - "archery_ticket_price": "0", - "id": "446" - }, - { - "shop_price": "243", - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "420", - "tokkul_price": "243", - "durability": null, - "name": "Mithril ore", - "tradeable": "true", - "weight": "1.75", - "archery_ticket_price": "0", - "id": "447" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "369", - "durability": null, - "name": "Mithril ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "448" - }, - { - "shop_price": "1300", - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "1160", - "tokkul_price": "600", - "durability": null, - "name": "Adamantite ore", - "tradeable": "true", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "449" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "1622", - "durability": null, - "name": "Adamantite ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "450" - }, - { - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "grand_exchange_price": "10300", - "tokkul_price": "4800", - "durability": null, - "name": "Runite ore", - "tradeable": "true", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "451" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "10300", - "durability": null, - "name": "Runite ore", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "452" - }, - { - "ge_buy_limit": "25000", - "examine": "Hmm a non-renewable energy source!", - "grand_exchange_price": "320", - "tokkul_price": "67", - "durability": null, - "name": "Coal", - "tradeable": "true", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "453" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "315", - "durability": null, - "name": "Coal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "454" - }, - { - "examine": "The official Alfred Grimhand bar crawl card.", - "durability": null, - "name": "Barcrawl card", - "archery_ticket_price": "0", - "id": "455" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "456" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "457" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "458" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "459" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "460" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "461" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "462" - }, - { - "destroy_message": "You will need to speak to Thormac to get another cage.", - "examine": "It's empty!", - "durability": null, - "name": "Scorpion cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "463" - }, - { "ge_buy_limit": "10000", - "examine": "Found on strange plants that grow around Karamja.", - "grand_exchange_price": "403", - "durability": null, - "name": "Strange fruit", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "464" + "tradeable": "true" }, { + "id": "349", + "name": "Raw pike", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "350", + "name": "Raw pike", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "351", + "name": "Pike", + "examine": "Some nicely cooked pike.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "403", - "durability": null, - "name": "Strange fruit", "tradeable": "true", - "archery_ticket_price": "0", - "id": "465" + "weight": "0.5" }, { - "shop_price": "1", - "examine": "Useless without the head.", - "attack_audios": "2508,0,0,0", + "id": "352", + "name": "Pike", + "archery_ticket_price": "0", "durability": null, - "name": "Pickaxe handle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "466", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Pickaxe handle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "467" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "468", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "469" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "470", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "471" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "472", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "473" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "474", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "475" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "476", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "477" - }, - { - "examine": "Nurmof can fix this for me.", - "attack_audios": "2508,0,0,0", - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "478", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "479" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Bronze pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "480" - }, - { - "durability": null, - "name": "Bronze pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "481" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Iron pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "482" - }, - { - "durability": null, - "name": "Iron pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "483" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Steel pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "484" - }, - { - "durability": null, - "name": "Steel pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "485" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Mithril pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "486" - }, - { - "durability": null, - "name": "Mithril pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "487" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Adamant pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "488" - }, - { - "durability": null, - "name": "Adamant pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "489" - }, - { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, - "name": "Rune pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "490" - }, - { - "durability": null, - "name": "Rune pick head", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "491" - }, - { - "examine": "Useless without the head.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "492", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "493" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "494", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "495" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "496", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "497" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "498", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "499" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "500", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "501" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "502", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "503" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "504", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "505" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "506", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "507" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "508" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "509" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "510" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "511" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "512" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "513" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "514" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "515" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "516" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "517" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "518" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "519" - }, - { - "examine": "A piece of ancient goblin axe.", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "520" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "521" - }, - { - "examine": "I don't fancy eating this now.", - "durability": null, - "name": "Enchanted beef", - "archery_ticket_price": "0", - "id": "522" - }, - { - "examine": "I don't fancy eating this now.", - "durability": null, - "name": "Enchanted rat meat", - "archery_ticket_price": "0", - "id": "523" - }, - { - "examine": "I don't fancy eating this now.", - "durability": null, - "name": "Enchanted bear meat", - "archery_ticket_price": "0", - "id": "524" - }, - { - "examine": "I don't fancy eating this now.", - "durability": null, - "name": "Enchanted chicken", - "weight": "1", - "archery_ticket_price": "0", - "id": "525" - }, - { - "shop_price": "190", "ge_buy_limit": "10000", - "grand_exchange_price": "190", - "examine": "Bones are for burying!", - "durability": null, - "name": "Bones", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "526" + "tradeable": "true" }, { - "shop_price": "190", + "id": "353", + "name": "Raw mackerel", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "shop_price": "26", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "354", + "name": "Raw mackerel", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "355", + "name": "Mackerel", + "examine": "Some nicely cooked fish.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "190", - "durability": null, - "name": "Bones", + "shop_price": "23", "tradeable": "true", - "archery_ticket_price": "0", - "id": "527" + "weight": "0.3" }, { + "id": "356", + "name": "Mackerel", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "Bones are for burying!", - "grand_exchange_price": "329", - "durability": null, - "name": "Burnt bones", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "528" + "tradeable": "true" }, { + "id": "358", + "name": "Burnt fish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "359", + "name": "Raw tuna", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.379" + }, + { + "id": "360", + "name": "Raw tuna", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "361", + "name": "Tuna", + "examine": "Wow, this is a big fish.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "329", - "durability": null, - "name": "Burnt bones", + "shop_price": "126", "tradeable": "true", - "archery_ticket_price": "0", - "id": "529" + "weight": "0.4" }, { + "id": "362", + "name": "Tuna", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "Ew it's a pile of bones.", - "grand_exchange_price": "354", - "durability": null, - "name": "Bat bones", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "530" + "tradeable": "true" }, { + "id": "363", + "name": "Raw bass", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "364", + "name": "Raw bass", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "365", + "name": "Bass", + "examine": "Wow, this is a big fish.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "354", - "durability": null, - "name": "Bat bones", + "shop_price": "270", "tradeable": "true", - "archery_ticket_price": "0", - "id": "531" + "weight": "0.3" }, { - "shop_price": "593", + "id": "366", + "name": "Bass", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "Normal: Ew, it's a pile of bones.", - "grand_exchange_price": "593", - "durability": null, - "name": "Big bones", - "tradeable": "true", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "532" + "tradeable": "true" }, { - "shop_price": "593", + "id": "368", + "name": "Burnt fish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "370", + "name": "Burnt fish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "371", + "name": "Raw swordfish", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "372", + "name": "Raw swordfish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "373", + "name": "Swordfish", + "examine": "I'd better be careful eating this!", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "593", - "durability": null, - "name": "Big bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "533" - }, - { - "ge_buy_limit": "10000", - "examine": "Ew, it's a pile of bones.", - "grand_exchange_price": "1357", - "durability": null, - "name": "Babydragon bones", - "tradeable": "true", - "weight": "0.7", - "archery_ticket_price": "0", - "id": "534" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1357", - "durability": null, - "name": "Babydragon bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "535" - }, - { - "ge_buy_limit": "10000", - "examine": "These would feed a dog for months.", - "grand_exchange_price": "1300", - "durability": null, - "name": "Dragon bones", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "536" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "4065", - "durability": null, - "name": "Dragon bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "537" - }, - { - "ge_buy_limit": "100", - "examine": "Keeps a druid's knees nice and warm.", - "grand_exchange_price": "168", - "durability": null, - "name": "Druid's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "538", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "168", - "durability": null, - "name": "Druid's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "539" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "examine": "I feel closer to the gods when I am wearing this.", - "grand_exchange_price": "328", - "durability": null, - "name": "Druid's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "540", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "328", - "durability": null, - "name": "Druid's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "541" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "528", - "durability": null, - "name": "Monk's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "542", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "528", - "durability": null, - "name": "Monk's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "543" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "1495", - "durability": null, - "name": "Monk's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "544", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,6,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1495", - "durability": null, - "name": "Monk's robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "545" - }, - { - "remove_sleeves": "true", - "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", - "durability": null, - "name": "Shade robe", - "weight": "2", - "archery_ticket_price": "0", - "id": "546", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "4" - }, - { - "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", - "durability": null, - "name": "Shade robe", - "weight": "2", - "archery_ticket_price": "0", - "id": "548", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "Issued by RuneScape Council to all new citizens.", - "grand_exchange_price": "123", - "durability": null, - "name": "Newcomer map", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "550" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "123", - "durability": null, - "name": "Newcomer map", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "551" - }, - { - "examine": "It lets me talk to ghosts.", - "durability": null, - "name": "Ghostspeak amulet", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "552", - "equipment_slot": "2" - }, - { - "destroy_message": "If you get rid of the skull you'll have to search for it again.", - "examine": "Ooooh spooky!", - "durability": null, - "name": "Ghost's skull", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "553" - }, - { - "shop_price": "17", - "ge_buy_limit": "25000", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "18", - "tokkul_price": "6", - "durability": null, - "name": "Fire rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "554" - }, - { - "shop_price": "17", - "ge_buy_limit": "25000", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "18", - "tokkul_price": "6", - "durability": null, - "name": "Water rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "555" - }, - { - "shop_price": "17", - "ge_buy_limit": "25000", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "18", - "tokkul_price": "6", - "durability": null, - "name": "Air rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "556" - }, - { - "shop_price": "17", - "ge_buy_limit": "25000", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "18", - "tokkul_price": "6", - "durability": null, - "name": "Earth rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "557" - }, - { - "shop_price": "17", - "ge_buy_limit": "25000", - "examine": "Used for basic missile spells.", - "grand_exchange_price": "18", - "tokkul_price": "4", - "durability": null, - "name": "Mind rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "558" - }, - { - "shop_price": "16", - "ge_buy_limit": "25000", - "examine": "Used for Curse spells", - "grand_exchange_price": "16", - "tokkul_price": "4", - "durability": null, - "name": "Body rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "559" - }, - { - "shop_price": "310", - "ge_buy_limit": "25000", - "examine": "Used for medium missile spells.", - "grand_exchange_price": "320", - "durability": null, - "tokkul_price": "270", - "name": "Death rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "560" - }, - { - "shop_price": "372", - "ge_buy_limit": "25000", - "examine": "Used for alchemy spells.", - "grand_exchange_price": "380", - "durability": null, - "name": "Nature rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "561" - }, - { - "shop_price": "140", - "ge_buy_limit": "25000", - "examine": "Used for small missile spells.", - "grand_exchange_price": "140", - "durability": null, - "tokkul_price": "135", - "name": "Chaos rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "562" - }, - { - "shop_price": "378", - "ge_buy_limit": "25000", - "examine": "Used for teleport spells.", - "grand_exchange_price": "380", - "durability": null, - "name": "Law rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "563" - }, - { - "shop_price": "232", - "ge_buy_limit": "25000", - "examine": "Used for enchant spells.", - "grand_exchange_price": "240", - "durability": null, - "name": "Cosmic rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "564" - }, - { - "shop_price": "550", - "ge_buy_limit": "25000", - "examine": "Used for large missile spells.", - "grand_exchange_price": "560", - "durability": null, - "name": "Blood rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "565" - }, - { "shop_price": "400", - "ge_buy_limit": "25000", - "examine": "Used for high level curse spells.", - "grand_exchange_price": "420", - "durability": null, - "name": "Soul rune", "tradeable": "true", - "archery_ticket_price": "0", - "id": "566" + "weight": "0.5" }, { - "ge_buy_limit": "5000", - "examine": "I'd prefer it if it was powered.", - "grand_exchange_price": "561", - "durability": null, - "name": "Unpowered orb", - "tradeable": "true", - "weight": "0.4", + "id": "374", + "name": "Swordfish", "archery_ticket_price": "0", - "id": "567" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "561", - "durability": null, - "name": "Unpowered orb", - "tradeable": "true", + "id": "376", + "name": "Burnt swordfish", "archery_ticket_price": "0", - "id": "568" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "A magic glowing orb.", - "grand_exchange_price": "2346", - "durability": null, - "name": "Fire orb", - "tradeable": "true", - "weight": "0.4", + "id": "377", + "name": "Raw lobster", + "examine": "I should try cooking this.", "archery_ticket_price": "0", - "id": "569" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2346", - "durability": null, - "name": "Fire orb", - "tradeable": "true", + "id": "378", + "name": "Raw lobster", "archery_ticket_price": "0", - "id": "570" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "A magic glowing orb.", - "grand_exchange_price": "2024", - "durability": null, - "name": "Water orb", - "tradeable": "true", - "weight": "0.4", + "id": "379", + "name": "Lobster", + "examine": "This looks tricky to eat.", "archery_ticket_price": "0", - "id": "571" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "107", + "tradeable": "true", + "weight": "0.3" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2024", - "durability": null, - "name": "Water orb", - "tradeable": "true", + "id": "380", + "name": "Lobster", "archery_ticket_price": "0", - "id": "572" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "A magic glowing orb.", - "grand_exchange_price": "2162", - "durability": null, - "name": "Air orb", - "tradeable": "true", - "weight": "0.4", + "id": "382", + "name": "Burnt lobster", "archery_ticket_price": "0", - "id": "573" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2162", - "durability": null, - "name": "Air orb", - "tradeable": "true", + "id": "383", + "name": "Raw shark", + "examine": "I should try cooking this.", "archery_ticket_price": "0", - "id": "574" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.7" }, { - "ge_buy_limit": "5000", - "examine": "A magic glowing orb.", - "grand_exchange_price": "2208", - "durability": null, - "name": "Earth orb", - "tradeable": "true", - "weight": "0.4", + "id": "384", + "name": "Raw shark", + "examine": "", "archery_ticket_price": "0", - "id": "575" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2208", - "durability": null, - "name": "Earth orb", - "tradeable": "true", + "id": "385", + "name": "Shark", + "examine": "I'd better be careful eating this.", "archery_ticket_price": "0", - "id": "576" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.6" }, { - "shop_price": "4", - "ge_buy_limit": "100", - "examine": "I can do magic better in this.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1840", - "name": "Wizard robe", - "tradeable": "true", + "id": "386", + "name": "Shark", "archery_ticket_price": "0", - "id": "577", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1840", - "durability": null, - "name": "Wizard robe", - "tradeable": "true", + "id": "388", + "name": "Burnt shark", "archery_ticket_price": "0", - "id": "578" + "durability": null, + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "durability": null, - "weight": "0.45", - "equipment_slot": "0", - "grand_exchange_price": "361", - "name": "Wizard hat", - "tradeable": "true", + "id": "389", + "name": "Raw manta ray", + "examine": "A rare catch.", "archery_ticket_price": "0", - "hat": "true", - "id": "579", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "2", - "ge_buy_limit": "100", - "grand_exchange_price": "361", - "durability": null, - "name": "Wizard hat", - "tradeable": "true", + "id": "390", + "name": "Raw manta ray", "archery_ticket_price": "0", - "id": "580" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { + "id": "391", + "name": "Manta ray", + "examine": "Mmmm...this looks tasty!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "392", + "name": "Manta ray", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "394", + "name": "Burnt manta ray", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "395", + "name": "Raw sea turtle", + "examine": "A rare catch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "396", + "name": "Raw sea turtle", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "397", + "name": "Sea turtle", + "examine": "Tasty!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.35" + }, + { + "id": "398", + "name": "Sea turtle", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "400", + "name": "Burnt sea turtle", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "401", + "name": "Seaweed", + "examine": "Slightly damp seaweed.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", "shop_price": "5", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "402", + "name": "Seaweed", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "403", + "name": "Edible seaweed", + "examine": "Slightly damp seaweed.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "404", + "name": "Edible seaweed", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "405", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "examine": "I can do magic better in this.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1364", - "name": "Black robe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "581", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "weight": "5" }, { + "id": "406", + "name": "Casket", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1364", + "tradeable": "true" + }, + { + "id": "407", + "name": "Oyster", + "examine": "Its a rare oyster.", + "archery_ticket_price": "0", "durability": null, - "name": "Black robe", + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "408", + "name": "Oyster", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "409", + "name": "Empty oyster", + "examine": "Aww, it's empty.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5", "tradeable": "true", - "archery_ticket_price": "0", - "id": "582" + "weight": "0.08" }, { - "shop_price": "10", - "examine": "Empty: It's a bailing bucket. Full: It's a bailing bucket full of salty water.", - "durability": null, - "name": "Bailing bucket", - "tradeable": "true", - "weight": "1", + "id": "410", + "name": "Empty oyster", "archery_ticket_price": "0", - "id": "583" + "durability": null, + "tradeable": "true" }, { - "durability": null, - "name": "Bailing bucket", - "tradeable": "true", + "id": "411", + "name": "Oyster pearl", + "examine": "I could work wonders with a chisel on this pearl.", "archery_ticket_price": "0", - "id": "584" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "500", + "shop_price": "44", + "tradeable": "true" }, { - "shop_price": "10", - "examine": "Empty: It's a bailing bucket. Full: It's a bailing bucket full of salty water.", - "durability": null, - "name": "Bailing bucket", - "tradeable": "true", - "weight": "1", + "id": "412", + "name": "Oyster pearl", "archery_ticket_price": "0", - "id": "585" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "durability": null, - "name": "Bailing bucket", - "tradeable": "true", + "id": "413", + "name": "Oyster pearls", + "examine": "I could work wonders with a chisel on these pearls.", "archery_ticket_price": "0", - "id": "586" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "500", + "tradeable": "true" }, { - "examine": "A strange glowing green orb.", - "durability": null, - "name": "Orb of protection", - "weight": "1", + "id": "414", + "name": "Oyster pearls", "archery_ticket_price": "0", - "id": "587" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "examine": "Two strange glowing green orbs.", - "durability": null, - "name": "Orbs of protection", + "id": "415", + "name": "Ethenea", + "examine": "An expensive colourless liquid.", "archery_ticket_price": "0", - "id": "588" + "durability": null }, { - "examine": "It's an amulet of protection given to me by the Gnomes.", - "durability": null, - "name": "Gnome amulet", + "id": "416", + "name": "Liquid honey", + "examine": "This isn't worth much.", "archery_ticket_price": "0", - "id": "589", - "bonuses": "0,0,0,0,0,13,13,13,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "417", + "name": "Sulphuric broline", + "examine": "It's highly poisonous.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "418", + "name": "Plague sample", + "examine": "Probably best I don't keep this too long.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "419", + "name": "Touch paper", + "examine": "A special kind of paper.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "420", + "name": "Distillator", + "examine": "Apparently it distills.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "421", + "name": "Lathas' amulet", + "examine": "Yup. It's an amulet.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "shop_price": "1", - "ge_buy_limit": "5000", - "examine": "Useful for lighting a fire.", - "grand_exchange_price": "118", - "durability": null, - "name": "Tinderbox", - "tradeable": "true", - "weight": "0.05", + "id": "422", + "name": "Bird feed", + "examine": "Birds love this stuff!", "archery_ticket_price": "0", - "id": "590" + "durability": null, + "weight": "0.1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "118", - "durability": null, - "name": "Tinderbox", - "tradeable": "true", + "id": "424", + "name": "Pigeon cage", + "examine": "(empty) It's empty... (full) It's full of pigeons.", "archery_ticket_price": "0", - "id": "591" + "durability": null, + "weight": "0.1" }, { + "id": "425", + "name": "Pigeon cage", + "examine": "(empty) It's empty... (full) It's full of pigeons.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "426", + "name": "Priest gown", + "examine": "Top half of a priest suit.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,3,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "5", + "tradeable": "true", + "weight": "2" + }, + { + "id": "427", + "name": "Priest gown", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "428", + "name": "Priest gown", + "examine": "Bottom half of a priest suit.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,3,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "2" + }, + { + "id": "429", + "name": "Priest gown", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "430", + "name": "Doctors' gown", + "examine": "Medical looking.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "1" + }, + { + "id": "431", + "name": "Karamjan rum", + "examine": "A very strong spirit brewed in Karamja.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "27" + }, + { + "id": "433", + "name": "Pirate message", + "examine": "Pirates don't have the best handwriting...", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "434", + "name": "Clay", + "examine": "Some hard dry clay.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "435", + "name": "Clay", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "436", + "name": "Copper ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tokkul_price": "4", + "tradeable": "true", + "weight": "2" + }, + { + "id": "437", + "name": "Copper ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "438", + "name": "Tin ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "6", + "tokkul_price": "4", + "tradeable": "true", + "weight": "2.25" + }, + { + "id": "439", + "name": "Tin ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "440", + "name": "Iron ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tokkul_price": "25", + "tradeable": "true", + "weight": "2.25" + }, + { + "id": "441", + "name": "Iron ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "442", + "name": "Silver ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "82", + "tokkul_price": "112", + "tradeable": "true", + "weight": "2" + }, + { + "id": "443", + "name": "Silver ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "444", + "name": "Gold ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tokkul_price": "225", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "445", + "name": "Gold ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "446", + "name": "'perfect' gold ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "447", + "name": "Mithril ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "243", + "tokkul_price": "243", + "tradeable": "true", + "weight": "1.75" + }, + { + "id": "448", + "name": "Mithril ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "449", + "name": "Adamantite ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "1300", + "tokkul_price": "600", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "450", + "name": "Adamantite ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "451", + "name": "Runite ore", + "examine": "This needs refining.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tokkul_price": "4800", + "tradeable": "true", + "weight": "2.25" + }, + { + "id": "452", + "name": "Runite ore", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "453", + "name": "Coal", + "examine": "Hmm a non-renewable energy source!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tokkul_price": "67", + "tradeable": "true", + "weight": "2.25" + }, + { + "id": "454", + "name": "Coal", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "455", + "name": "Barcrawl card", + "examine": "The official Alfred Grimhand bar crawl card.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "456", + "name": "Scorpion cage", + "examine": "It's empty!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.15" + }, + { + "id": "457", + "name": "Scorpion cage", + "examine": "There is 1 scorpion inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.175" + }, + { + "id": "458", + "name": "Scorpion cage", + "examine": "There are 2 scorpions inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "459", + "name": "Scorpion cage", + "examine": "There is 1 scorpion inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.175" + }, + { + "id": "460", + "name": "Scorpion cage", + "examine": "There are 2 scorpions inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "461", + "name": "Scorpion cage", + "examine": "There is 1 scorpion inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.175" + }, + { + "id": "462", + "name": "Scorpion cage", + "examine": "There are 2 scorpions inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "463", + "name": "Scorpion cage", + "examine": "There are 3 scorpions inside.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Thormac to get another cage.", + "durability": null, + "tradeable": "false", + "weight": "0.225" + }, + { + "id": "464", + "name": "Strange fruit", + "examine": "Found on strange plants that grow around Karamja.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "A heap of ashes.", - "grand_exchange_price": "368", - "durability": null, - "name": "Ashes", "tradeable": "true", - "weight": "0.056", - "archery_ticket_price": "0", - "id": "592" + "weight": "1" }, { + "id": "465", + "name": "Strange fruit", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "368", - "durability": null, - "name": "Ashes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "593" - }, - { - "examine": "A lit home-made torch.", - "durability": null, - "name": "Lit torch", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "594" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "An unlit home-made torch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Unlit torch", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "596" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Unlit torch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "597" - }, - { - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with bronze heads and oil-soaked cloth. lit: An easy to make, bronze-headed fire arrow.", - "grand_exchange_price": "162", - "durability": null, - "name": "Bronze fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "598", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A book on the history of astronomy in RuneScape.", - "grand_exchange_price": "658", - "durability": null, - "name": "Astronomy book", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "600" - }, - { - "destroy_message": "You'll have to find another, down in the dungeon.", - "examine": "A small key for a large door.", - "durability": null, - "name": "Goblin kitchen key", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "601" - }, - { - "destroy_message": "You'll have to get another down in the Dungeon", - "examine": "An unusual clay mould in the shape of a disc.", - "durability": null, - "name": "Lens mould", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "602" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A perfectly circular disc of glass.", - "durability": null, - "name": "Observatory lens", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "603" - }, - { - "shop_price": "160", - "examine": "A slender bone shard given to you by Zadimus.", - "durability": null, - "name": "Bone shard", - "archery_ticket_price": "0", - "id": "604" - }, - { - "shop_price": "100", - "examine": "A key fashioned from a shard of bone. (Shilo Village)", - "durability": null, - "name": "Bone key", - "archery_ticket_price": "0", - "id": "605" - }, - { - "examine": "A stone plaque with carved letters in it.", - "durability": null, - "name": "Stone-plaque", - "archery_ticket_price": "0", - "id": "606" - }, - { - "examine": "An ancient tattered scroll.", - "durability": null, - "name": "Tattered scroll", - "archery_ticket_price": "0", - "id": "607" - }, - { - "shop_price": "100", - "examine": "An ancient crumpled scroll.", - "durability": null, - "name": "Crumpled scroll", - "archery_ticket_price": "0", - "id": "608" - }, - { - "examine": "The remains of the Zombie Queen.", - "durability": null, - "name": "Rashiliyia corpse", - "archery_ticket_price": "0", - "id": "609" - }, - { - "examine": "The remains of Zadimus.", - "durability": null, - "name": "Zadimus corpse", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "610" - }, - { - "examine": "A magical crystal sphere.", - "durability": null, - "name": "Locating crystal", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "611" - }, - { - "examine": "A magical crystal sphere.", - "durability": null, - "name": "Locating crystal", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "612" - }, - { - "examine": "A magical crystal sphere.", - "durability": null, - "name": "Locating crystal", - "weight": "0.9", + "id": "466", + "name": "Pickaxe handle", + "examine": "Useless without the head.", "archery_ticket_price": "0", - "id": "613" - }, - { - "examine": "A magical crystal sphere.", - "durability": null, - "name": "Locating crystal", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "614" - }, - { - "examine": "A magical crystal sphere.", - "durability": null, - "name": "Locating crystal", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "615" - }, - { - "destroy_message": "You made the beads of the dead during the Shilo Village quest. You can get another from Yanni Salika in Shilo Village for 1200 gold.", - "shop_price": "1200", - "examine": "A curious looking neck ornament.", - "durability": null, - "name": "Beads of the dead", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "616", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,1,0,0", - "equipment_slot": "2" - }, - { - "bankable": "false", - "examine": "Lovely money!", - "durability": null, - "name": "Coins", - "archery_ticket_price": "0", - "id": "617" - }, - { - "examine": "Beads carved out of a bone.", - "durability": null, - "name": "Bone beads", - "archery_ticket_price": "0", - "id": "618" - }, - { - "shop_price": "35", - "examine": "Allows you to rest in the luxurious Paramayer[sic] Inn.", - "durability": null, - "name": "Paramaya ticket", - "archery_ticket_price": "0", - "id": "619" - }, - { - "shop_price": "35", - "examine": "Allows you to rest in the luxurious Paramayer[sic] Inn.", - "durability": null, - "name": "Paramaya ticket", - "archery_ticket_price": "0", - "id": "620" - }, - { - "examine": "Sold in Shilo Village for passage on the 'Lady of the Waves'.", - "durability": null, - "name": "Ship ticket", - "archery_ticket_price": "0", - "id": "621" - }, - { - "examine": "An ivory sword pommel.", - "durability": null, - "name": "Sword pommel", - "archery_ticket_price": "0", - "id": "623" - }, - { - "examine": "Notes taken from the tomb of Bervirius.", - "durability": null, - "name": "Bervirius notes", - "archery_ticket_price": "0", - "id": "624" - }, - { - "examine": "A decorated belt used to trade information between distant villages.", - "durability": null, - "name": "Wampum belt", - "archery_ticket_price": "0", - "id": "625" - }, - { - "ge_buy_limit": "100", - "shop_price": "200", - "examine": "They're soft, silky and pink.", - "grand_exchange_price": "558", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "626", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "558", - "durability": null, - "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "627" - }, - { - "ge_buy_limit": "100", - "shop_price": "200", - "examine": "They're soft, silky and green.", - "grand_exchange_price": "397", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "628", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "397", - "durability": null, - "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "629" - }, - { - "ge_buy_limit": "100", - "shop_price": "200", - "examine": "They're soft, silky and blue.", - "grand_exchange_price": "612", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "630", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "612", - "durability": null, - "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "631" - }, - { - "ge_buy_limit": "100", - "shop_price": "200", - "examine": "They're soft, silky and cream.", - "grand_exchange_price": "496", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "632", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "496", - "durability": null, - "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "633" - }, - { - "ge_buy_limit": "100", - "shop_price": "200", - "examine": "They're soft, silky and turquoise.", - "grand_exchange_price": "226", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "634", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "226", - "durability": null, - "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "635" - }, - { - "shop_price": "180", - "ge_buy_limit": "100", - "examine": "The ultimate in gnome design, now in pink.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "265", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "636", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "265", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "637" - }, - { - "shop_price": "180", - "ge_buy_limit": "100", - "examine": "The ultimate in gnome design, now in green.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "235", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "638", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "235", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "639" - }, - { - "shop_price": "180", - "ge_buy_limit": "100", - "examine": "The ultimate in gnome design, now in blue.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "447", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "640", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "447", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "641" - }, - { - "shop_price": "180", - "ge_buy_limit": "100", - "examine": "The ultimate in gnome design, now in cream.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "396", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "642", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "396", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "643" - }, - { - "shop_price": "180", - "ge_buy_limit": "100", - "examine": "The ultimate in gnome design, now in turquoise.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "70", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "644", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "70", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "645" - }, - { - "ge_buy_limit": "100", - "shop_price": "180", - "examine": "Made by tree gnomes with a thing for pink.", - "grand_exchange_price": "249", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "646", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "249", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "647" - }, - { - "ge_buy_limit": "100", - "shop_price": "180", - "examine": "Made by tree gnomes with a thing for green.", - "grand_exchange_price": "210", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "648", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "210", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "649" - }, - { - "ge_buy_limit": "100", - "shop_price": "180", - "examine": "Made by tree gnomes with a thing for blue.", - "grand_exchange_price": "453", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "650", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "453", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "651" - }, - { - "ge_buy_limit": "100", - "shop_price": "180", - "examine": "Made by tree gnomes with a thing for cream.", - "grand_exchange_price": "348", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "652", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "348", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "653" - }, - { - "ge_buy_limit": "100", - "shop_price": "180", - "examine": "Made by tree gnomes with a thing for turquoise.", - "grand_exchange_price": "92", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "654", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "92", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "655" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "677", - "examine": "A silly, pink pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.4", - "hat": "true", - "id": "656", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "677", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "657" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "480", - "examine": "A silly, green pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.4", - "hat": "true", - "id": "658", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "480", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "659" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "421", - "examine": "A silly, blue pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.4", - "hat": "true", - "id": "660", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "421", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "661" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1102", - "examine": "A silly, cream pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.4", - "hat": "true", - "id": "662", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1102", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "663" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "295", - "examine": "A silly, turquoise pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.4", - "hat": "true", - "id": "664", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "295", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "665" - }, - { - "examine": "Picture of a posing Paladin.", - "durability": null, - "name": "Portrait", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "666" - }, - { - "turn90cw_anim": "821", - "examine": "A Faladian Knight's sword.", - "walk_anim": "819", + "attack_audios": "2508,0,0,0", "durability": null, - "weight": "1", - "turn90ccw_anim": "822", - "attack_speed": "5", - "weapon_interface": "6", - "turn180_anim": "820", - "render_anim": "292", - "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "stand_anim": "809", - "attack_audios": "2500,2500,2517,2500", - "name": "Blurite sword", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "667", - "stand_turn_anim": "823", - "bonuses": "9,14,-2,0,0,0,3,2,0,0,0,10,0,0,0" + "shop_price": "1", + "tradeable": "true" }, { - "examine": "Definitely blue.", - "durability": null, - "name": "Blurite ore", - "weight": "2.2", + "id": "467", + "name": "Pickaxe handle", "archery_ticket_price": "0", - "id": "668" + "durability": null, + "tradeable": "true" }, { - "examine": "A receptacle for specimens!", - "durability": null, - "name": "Specimen jar", + "id": "468", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", "archery_ticket_price": "0", - "id": "669" - }, - { - "examine": "A small brush used to clean rock samples.", + "attack_audios": "2508,0,0,0", "durability": null, - "name": "Specimen brush", - "archery_ticket_price": "0", - "id": "670" - }, - { - "examine": "A carefully-kept-safe skull sample.", - "durability": null, - "name": "Animal skull", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "671" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A special cup.", - "durability": null, - "name": "Special cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "672" - }, - { - "examine": "A lucky mascot.", - "durability": null, - "name": "Teddy", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "673" - }, - { - "examine": "A roughly shaped piece of rock.", - "durability": null, - "name": "Cracked sample", + "equipment_slot": "3", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "674" + "weight": "0.2" }, { - "examine": "A small pick for digging.", - "durability": null, - "name": "Rock pick", + "id": "469", + "name": "Broken pickaxe", "archery_ticket_price": "0", - "id": "675", - "equipment_slot": "3" + "durability": null, + "tradeable": "true" }, { - "examine": "Used for digging!", - "durability": null, - "name": "Trowel", + "id": "470", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", "archery_ticket_price": "0", - "id": "676" - }, - { - "examine": "An empty tray for panning.", + "attack_audios": "2508,0,0,0", "durability": null, - "name": "Panning tray", - "archery_ticket_price": "0", - "id": "677" - }, - { - "examine": "This tray contains gold nuggets.", - "durability": null, - "name": "Panning tray", - "archery_ticket_price": "0", - "id": "678" - }, - { - "examine": "This tray contains mud.", - "durability": null, - "name": "Panning tray", - "archery_ticket_price": "0", - "id": "679" - }, - { - "examine": "Pure, lovely gold!", - "durability": null, - "name": "Nuggets", - "archery_ticket_price": "0", - "id": "680" - }, - { - "examine": "An unusual symbol as yet unidentified by the archaeological expert.", - "durability": null, - "name": "Ancient talisman", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "681" - }, - { - "examine": "A letter waiting to be stamped.", - "durability": null, - "name": "Unstamped letter", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "682" - }, - { - "examine": "A sealed letter of recommendation.", - "durability": null, - "name": "Sealed letter", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "683" - }, - { - "examine": "Used to hold up trousers!", - "durability": null, - "name": "Belt buckle", - "weight": "1", - "archery_ticket_price": "0", - "id": "684" - }, - { - "examine": "Phew!", - "durability": null, - "name": "Old boot", - "archery_ticket_price": "0", - "id": "685" - }, - { - "examine": "A decent-enough weapon gone rusty.", - "durability": null, - "name": "Rusty sword", - "weight": "2", - "archery_ticket_price": "0", - "id": "686" - }, - { - "examine": "This must have been shot at high speed.", - "durability": null, - "name": "Broken arrow", - "archery_ticket_price": "0", - "id": "687" - }, - { - "examine": "Not Dick Whittington's helper at all!", - "durability": null, - "name": "Buttons", - "archery_ticket_price": "0", - "id": "688" - }, - { - "examine": "I pity the poor person beaten with this!", - "attack_audios": "2555,0,0,0", - "durability": null, - "name": "Broken staff", + "equipment_slot": "3", "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "689" + "weight": "0.2" }, { - "examine": "Smashed glass.", + "id": "471", + "name": "Broken pickaxe", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "472", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", + "archery_ticket_price": "0", + "attack_audios": "2508,0,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "473", + "name": "Broken pickaxe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "474", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", + "archery_ticket_price": "0", + "attack_audios": "2508,0,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "475", + "name": "Broken pickaxe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "476", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", + "archery_ticket_price": "0", + "attack_audios": "2508,0,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "477", + "name": "Broken pickaxe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "478", + "name": "Broken pickaxe", + "examine": "Nurmof can fix this for me.", + "archery_ticket_price": "0", + "attack_audios": "2508,0,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "479", + "name": "Broken pickaxe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "480", + "name": "Bronze pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "481", + "name": "Bronze pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "482", + "name": "Iron pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "483", + "name": "Iron pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "484", + "name": "Steel pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "485", + "name": "Steel pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "486", + "name": "Mithril pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "487", + "name": "Mithril pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "488", + "name": "Adamant pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "489", + "name": "Adamant pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "490", + "name": "Rune pick head", + "examine": "It's missing a handle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "491", + "name": "Rune pick head", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "492", + "name": "Picture", + "examine": "Useless without the head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "493", "name": "Picture", "archery_ticket_price": "0", - "id": "690" - }, - { - "examine": "The owner has passed Earth Sciences Level 1 exam.", "durability": null, - "name": "Level 1 certificate", - "archery_ticket_price": "0", - "id": "691" + "tradeable": "true" }, { - "examine": "The owner has passed Earth Sciences Level 2 exam.", + "id": "494", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Level 2 certificate", - "archery_ticket_price": "0", - "id": "692" + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "The owner has passed Earth Sciences Level 3 exam.", + "id": "495", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, - "name": "Level 3 certificate", - "archery_ticket_price": "0", - "id": "693" + "tradeable": "true" }, { - "examine": "Smashing!", + "id": "496", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Ceramic remains", - "archery_ticket_price": "0", - "id": "694" + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "Now, if I could just find a tooth fairy to sell this to...", + "id": "497", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, - "name": "Old tooth", - "archery_ticket_price": "0", - "id": "695" + "tradeable": "true" }, { - "examine": "A letter inviting me to use the private digging shafts.", + "id": "498", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Invitation letter", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "696" + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "It would be hard to repair this!", + "id": "499", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, - "name": "Damaged armour", - "weight": "7.75", - "archery_ticket_price": "0", - "id": "697" + "tradeable": "true" }, { - "examine": "Armour left over from a great fight.", + "id": "500", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Broken armour", - "weight": "8", - "archery_ticket_price": "0", - "id": "698" + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "An old stone slab with writing on it.", + "id": "501", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, - "name": "Stone tablet", - "archery_ticket_price": "0", - "id": "699" + "tradeable": "true" }, { - "examine": "An acrid chemical.", + "id": "502", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Chemical powder", - "archery_ticket_price": "0", - "id": "700" + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "An acrid chemical.", + "id": "503", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, - "name": "Ammonium nitrate", - "archery_ticket_price": "0", - "id": "701" + "tradeable": "true" }, { - "examine": "A strong chemical.", + "id": "504", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", "durability": null, - "name": "Unidentified liquid", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "702" + "equipment_slot": "3", + "weight": "2.5" }, { + "id": "505", + "name": "Broken axe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "506", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "2.5" + }, + { + "id": "507", + "name": "Broken axe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "508", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "509", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "510", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "511", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "512", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "513", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "514", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "515", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "516", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "517", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "518", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "519", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "520", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "521", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "522", + "name": "Enchanted beef", + "examine": "I don't fancy eating this now.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "523", + "name": "Enchanted rat meat", + "examine": "I don't fancy eating this now.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "524", + "name": "Enchanted bear meat", + "examine": "I don't fancy eating this now.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "525", + "name": "Enchanted chicken", + "examine": "I don't fancy eating this now.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "526", + "name": "Bones", + "examine": "Bones are for burying!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "190", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "527", + "name": "Bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "190", + "tradeable": "true" + }, + { + "id": "528", + "name": "Burnt bones", + "examine": "Bones are for burying!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "529", + "name": "Burnt bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "530", + "name": "Bat bones", + "examine": "Ew it's a pile of bones.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "531", + "name": "Bat bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "532", + "name": "Big bones", + "examine": "Normal: Ew, it's a pile of bones.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "593", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "533", + "name": "Big bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "593", + "tradeable": "true" + }, + { + "id": "534", + "name": "Babydragon bones", + "examine": "Ew, it's a pile of bones.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.7" + }, + { + "id": "535", + "name": "Babydragon bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "536", + "name": "Dragon bones", + "examine": "These would feed a dog for months!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "537", + "name": "Dragon bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "538", + "name": "Druid's robe", + "examine": "Keeps a druid's knees nice and warm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "539", + "name": "Druid's robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "540", + "name": "Druid's robe", + "examine": "I feel closer to the gods when I am wearing this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true" + }, + { + "id": "541", + "name": "Druid's robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "542", + "name": "Monk's robe", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "543", + "name": "Monk's robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "544", + "name": "Monk's robe", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,6,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true" + }, + { + "id": "545", + "name": "Monk's robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "546", + "name": "Shade robe", + "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" + }, + { + "id": "548", + "name": "Shade robe", + "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,4,0,0", + "durability": null, + "equipment_slot": "7", + "weight": "2" + }, + { + "id": "550", + "name": "Newcomer map", + "examine": "Issued by 2009Scape Council to all new citizens.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "1", - "examine": "A strong chemical.", - "durability": null, - "name": "Nitroglycerin", - "weight": "1", - "archery_ticket_price": "0", - "id": "703" - }, - { - "examine": "Charcoal - crushed to small pieces!", - "durability": null, - "name": "Ground charcoal", - "archery_ticket_price": "0", - "id": "704" - }, - { - "examine": "A mixture of strong chemicals.", - "durability": null, - "name": "Chemical compound", - "weight": "2", - "archery_ticket_price": "0", - "id": "707" - }, - { - "examine": "The root of an arcenia plant.", - "durability": null, - "name": "Arcenia root", - "archery_ticket_price": "0", - "id": "708" - }, - { - "examine": "An unusually shaped vase. You can see something glinting inside.", - "durability": null, - "name": "Vase", - "archery_ticket_price": "0", - "id": "710" - }, - { - "examine": "It's about chemicals, judging from its cover.", - "durability": null, - "name": "Book on chemicals", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "711" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "712" + "weight": "0.5" }, { - "examine": "Notes given to you by Radimus Erkle, it includes a partially completed map.", - "durability": null, - "name": "Radimus notes", + "id": "551", + "name": "Newcomer map", "archery_ticket_price": "0", - "id": "714" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "Notes given to you by Radimus Erkle, it includes a partially completed map.", - "durability": null, - "name": "Radimus notes", + "id": "552", + "name": "Ghostspeak amulet", + "examine": "It lets me talk to ghosts.", "archery_ticket_price": "0", - "id": "715" + "durability": null, + "equipment_slot": "2", + "tradeable": "false" }, { - "examine": "It makes a loud but interesting sound when swung in the air.", - "durability": null, - "name": "Bull roarer", + "id": "553", + "name": "Ghost's skull", + "examine": "Ooooh spooky!", "archery_ticket_price": "0", - "id": "716" - }, - { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "Scrawled note", - "archery_ticket_price": "0", - "id": "717" - }, - { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "A scribbled note", - "archery_ticket_price": "0", - "id": "718" - }, - { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "Scrumpled note", - "archery_ticket_price": "0", - "id": "719" - }, - { - "examine": "A rough sketch of a bowl shaped vessel given to you by Gujuo.", - "durability": null, - "name": "Sketch", - "archery_ticket_price": "0", - "id": "720" - }, - { - "examine": "A specially made bowl constructed out of pure gold.", - "durability": null, - "name": "Gold bowl", - "archery_ticket_price": "0", - "id": "721" - }, - { - "examine": "A specially made bowl constructed out of pure gold and blessed.", - "durability": null, - "name": "Blessed gold bowl", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "722" - }, - { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "weight": "1", - "archery_ticket_price": "0", - "id": "723" - }, - { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "weight": "1", - "archery_ticket_price": "0", - "id": "724" - }, - { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "weight": "1", - "archery_ticket_price": "0", - "id": "725" - }, - { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "weight": "1", - "archery_ticket_price": "0", - "id": "726" - }, - { - "examine": "One of nature's pipes.", - "durability": null, - "name": "Hollow reed", - "weight": "0.012", - "archery_ticket_price": "0", - "id": "727" - }, - { - "examine": "One of nature's pipes.", - "durability": null, - "name": "Hollow reed", - "weight": "0.012", - "archery_ticket_price": "0", - "id": "728" - }, - { - "examine": "It looks like the Shamans personal notes...", - "durability": null, - "name": "Shamans tome", - "archery_ticket_price": "0", - "id": "729" - }, - { - "examine": "An ancient tome on Demonology.", - "durability": null, - "name": "Binding book", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "730" - }, - { - "shop_price": "160", - "examine": "An enchanted empty glass vial.", - "durability": null, - "name": "Enchanted vial", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "731" + "destroy_message": "If you get rid of the skull you'll have to search for it again.", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of holy water, good against certain demons.", - "durability": null, - "name": "Holy water", + "id": "554", + "name": "Fire rune", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "732", - "weapon_interface": "18", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,24", - "equipment_slot": "3" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "17", + "tokkul_price": "6", + "tradeable": "true" }, { - "examine": "Fragments of a broken container.", - "durability": null, - "name": "Smashed glass", + "id": "555", + "name": "Water rune", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "733" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "17", + "tokkul_price": "6", + "tradeable": "true" }, { - "examine": "These need to be germinated before they can be used. / These are germinated and ready to be planted in fertile soil.", - "durability": null, - "name": "Yommi tree seeds", + "id": "556", + "name": "Air rune", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "735" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "17", + "tokkul_price": "6", + "tradeable": "true" }, { - "examine": "These need to be germinated before they can be used. / These are germinated and ready to be planted in fertile soil.", - "durability": null, - "name": "Yommi tree seeds", + "id": "557", + "name": "Earth rune", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "736" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "17", + "tokkul_price": "6", + "tradeable": "true" }, { - "examine": "A Snakeweed mixture, needs another ingredient.", - "durability": null, - "name": "Snakeweed mixture", - "weight": "1", + "id": "558", + "name": "Mind rune", + "examine": "Used for basic missile spells.", "archery_ticket_price": "0", - "id": "737" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "17", + "tokkul_price": "4", + "tradeable": "true" }, { - "examine": "It's a mixture of ardrigal and water. Needs another ingredient.", - "durability": null, - "name": "Ardrigal mixture", + "id": "559", + "name": "Body rune", + "examine": "Used for Curse spells", "archery_ticket_price": "0", - "id": "738" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "16", + "tokkul_price": "4", + "tradeable": "true" }, { - "examine": "A bravery potion for which Gujuo gave you the details, let's hope it works.", - "durability": null, - "name": "Bravery potion", + "id": "560", + "name": "Death rune", + "examine": "Used for medium missile spells.", "archery_ticket_price": "0", - "id": "739" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "310", + "tokkul_price": "270", + "tradeable": "true" }, { - "shop_price": "160", - "examine": "A silly, blue pointed hat.", - "grand_exchange_price": "472", + "id": "561", + "name": "Nature rune", + "examine": "Used for alchemy spells.", + "archery_ticket_price": "0", "durability": null, - "name": "Blue hat", + "ge_buy_limit": "25000", + "shop_price": "372", + "tradeable": "true" + }, + { + "id": "562", + "name": "Chaos rune", + "examine": "Used for small missile spells.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "140", + "tokkul_price": "135", + "tradeable": "true" + }, + { + "id": "563", + "name": "Law rune", + "examine": "Used for teleport spells.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "378", + "tradeable": "true" + }, + { + "id": "564", + "name": "Cosmic rune", + "examine": "Used for enchant spells.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "232", + "tradeable": "true" + }, + { + "id": "565", + "name": "Blood rune", + "examine": "Used for large missile spells.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "550", + "tradeable": "true" + }, + { + "id": "566", + "name": "Soul rune", + "examine": "Used for high level curse spells.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "400", + "tradeable": "true" + }, + { + "id": "567", + "name": "Unpowered orb", + "examine": "I'd prefer it if it was powered.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", "tradeable": "true", - "weight": "0.4", + "weight": "0.4" + }, + { + "id": "568", + "name": "Unpowered orb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "569", + "name": "Fire orb", + "examine": "A magic glowing orb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "570", + "name": "Fire orb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "571", + "name": "Water orb", + "examine": "A magic glowing orb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "572", + "name": "Water orb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "573", + "name": "Air orb", + "examine": "A magic glowing orb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "574", + "name": "Air orb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "575", + "name": "Earth orb", + "examine": "A magic glowing orb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "576", + "name": "Earth orb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "577", + "name": "Wizard robe", + "examine": "I can do magic better in this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "4", + "tradeable": "true", + "weight": "1" + }, + { + "id": "578", + "name": "Wizard robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "579", + "name": "Wizard hat", + "examine": "A silly pointed hat.", "archery_ticket_price": "0", - "hat": "true", - "id": "740", "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "examine": "It looks like it's been snapped off of something.", "durability": null, - "name": "Chunk of crystal", - "archery_ticket_price": "0", - "id": "741" - }, - { - "examine": "It looks like it's been snapped off of something.", - "durability": null, - "name": "Hunk of crystal", - "archery_ticket_price": "0", - "id": "742" - }, - { - "examine": "It looks like it's been snapped off of something.", - "durability": null, - "name": "Lump of crystal", - "archery_ticket_price": "0", - "id": "743" - }, - { - "examine": "A heart shaped crystal.", - "durability": null, - "name": "Heart crystal", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "744" - }, - { - "examine": "A heart shaped crystal.", - "durability": null, - "name": "Heart crystal", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "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, - "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" - }, - { - "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", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,400,400", - "attack_audios": "2517,2517,2500,2517", - "name": "Glowing dagger", - "archery_ticket_price": "0", - "id": "747", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,0,0,0,0" - }, - { - "examine": "A powerful spell for good.", - "durability": null, - "name": "Holy force", - "archery_ticket_price": "0", - "id": "748" - }, - { - "examine": "A well carved totem pole made from the trunk of a Yommi tree.", - "durability": null, - "name": "Yommi totem", - "weight": "3", - "archery_ticket_price": "0", - "id": "749" - }, - { - "examine": "A gilded totem pole from the Kharazi tribe.", - "durability": null, - "name": "Gilded totem", - "archery_ticket_price": "0", - "id": "750" - }, - { - "examine": "A common gnomeball, obtained by playing gnomeball.", - "durability": null, - "name": "Gnomeball", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "751", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Gnomeball", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "752" - }, - { + "equipment_slot": "0", "ge_buy_limit": "100", - "examine": "Poisonous berries.", - "grand_exchange_price": "42", - "durability": null, - "name": "Cadava berries", + "hat": "true", + "shop_price": "2", "tradeable": "true", - "archery_ticket_price": "0", - "id": "753" + "weight": "0.45" }, { + "id": "580", + "name": "Wizard hat", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "42", - "durability": null, - "name": "Cadava berries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "754" + "shop_price": "2", + "tradeable": "true" }, { + "id": "581", + "name": "Black robe", + "examine": "I can do magic better in this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "5", + "tradeable": "true", + "weight": "1" + }, + { + "id": "582", + "name": "Black robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "583", + "name": "Bailing bucket", + "examine": "Empty: It's a bailing bucket. Full: It's a bailing bucket full of salty water.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "1" + }, + { + "id": "584", + "name": "Bailing bucket", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "585", + "name": "Bailing bucket", + "examine": "Empty: It's a bailing bucket. Full: It's a bailing bucket full of salty water.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "1" + }, + { + "id": "586", + "name": "Bailing bucket", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "587", + "name": "Orb of protection", + "examine": "A strange glowing green orb.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "588", + "name": "Orbs of protection", + "examine": "Two strange glowing green orbs.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "589", + "name": "Gnome amulet", + "examine": "It's an amulet of protection given to me by the Gnomes.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,13,13,13,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "2" + }, + { + "id": "590", + "name": "Tinderbox", + "examine": "Useful for lighting a fire.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", "shop_price": "1", - "examine": "This potion will most certainly almost kill you.", - "durability": null, - "name": "Cadava potion", - "archery_ticket_price": "0", - "id": "756" - }, - { - "examine": "Phoenix Gang weapon store key.", - "durability": null, - "name": "Weapon store key", "tradeable": "true", - "archery_ticket_price": "0", - "id": "759" + "weight": "0.05" }, { + "id": "591", + "name": "Tinderbox", + "archery_ticket_price": "0", "durability": null, - "name": "Weapon store key", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "592", + "name": "Ashes", + "examine": "A heap of ashes.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "760" + "weight": "0.056" }, { - "destroy_message": "You'll have to kill Jonny the beard in order to get another one.", - "examine": "An intelligence report.", + "id": "593", + "name": "Ashes", + "archery_ticket_price": "0", "durability": null, - "name": "Intel report", - "destroy": "true", - "archery_ticket_price": "0", - "id": "761" + "ge_buy_limit": "10000", + "tradeable": "true" }, { + "id": "594", + "name": "Lit torch", + "examine": "A lit home-made torch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "596", + "name": "Unlit torch", + "examine": "An unlit home-made torch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "597", + "name": "Unlit torch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "598", + "name": "Bronze fire arrows", + "examine": "unlit: Arrows with bronze heads and oil-soaked cloth. lit: An easy to make, bronze-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "600", + "name": "Astronomy book", + "examine": "A book on the history of astronomy in 2009Scape.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "601", + "name": "Goblin kitchen key", + "examine": "A small key for a large door.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another, down in the dungeon.", + "durability": null, + "tradeable": "false" + }, + { + "id": "602", + "name": "Lens mould", + "examine": "An unusual clay mould in the shape of a disc.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to get another down in the dungeon.", + "durability": null, + "tradeable": "false" + }, + { + "id": "603", + "name": "Observatory lens", + "examine": "A perfectly circular disc of glass.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Half of the Shield of Arrav.", "durability": null, - "name": "Broken shield", - "destroy": "true", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "763" + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Half of the Shield of Arrav.", - "durability": null, - "name": "Broken shield", - "destroy": "true", - "weight": "2.7", + "id": "604", + "name": "Bone shard", + "examine": "A slender bone shard given to you by Zadimus.", "archery_ticket_price": "0", - "id": "765" + "durability": null, + "shop_price": "160" }, { - "turn90cw_anim": "821", - "examine": "Second-rate crossbow, former property of the Phoenix Gang.", - "walk_anim": "4226", + "id": "605", + "name": "Bone key", + "examine": "A key fashioned from a shard of bone. (Shilo Village)", + "archery_ticket_price": "0", "durability": null, - "weight": "3", - "turn90ccw_anim": "822", - "attack_speed": "6", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "defence_anim": "424", + "shop_price": "100" + }, + { + "id": "606", + "name": "Stone-plaque", + "examine": "A stone plaque with carved letters in it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "607", + "name": "Tattered scroll", + "examine": "An ancient tattered scroll.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "608", + "name": "Crumpled scroll", + "examine": "An ancient crumpled scroll.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "100" + }, + { + "id": "609", + "name": "Rashiliyia corpse", + "examine": "The remains of the Zombie Queen.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "610", + "name": "Zadimus corpse", + "examine": "The remains of Zadimus.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "611", + "name": "Locating crystal", + "examine": "A magical crystal sphere.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "612", + "name": "Locating crystal", + "examine": "A magical crystal sphere.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "613", + "name": "Locating crystal", + "examine": "A magical crystal sphere.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "614", + "name": "Locating crystal", + "examine": "A magical crystal sphere.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "615", + "name": "Locating crystal", + "examine": "A magical crystal sphere.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "616", + "name": "Beads of the dead", + "examine": "A curious looking neck ornament.", + "archery_ticket_price": "0", + "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,1,0,0", + "destroy": "true", + "destroy_message": "You made the beads of the dead during the Shilo Village quest. You can get another from Yanni Salika in Shilo Village for 1200 gold.", + "durability": null, + "equipment_slot": "2", + "shop_price": "1200", + "tradeable": "false" + }, + { + "id": "617", + "name": "Coins", + "examine": "Lovely money!", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "618", + "name": "Bone beads", + "examine": "Beads carved out of a bone.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "619", + "name": "Paramaya ticket", + "examine": "Allows you to rest in the luxurious Paramayer Inn.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "35" + }, + { + "id": "620", + "name": "Paramaya ticket", + "examine": "Allows you to rest in the luxurious Paramayer Inn.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "35" + }, + { + "id": "621", + "name": "Ship ticket", + "examine": "Sold in Shilo Village for passage on the 'Lady of the Waves'.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "623", + "name": "Sword pommel", + "examine": "An ivory sword pommel.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "624", + "name": "Bervirius notes", + "examine": "Notes taken from the tomb of Bervirius.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "625", + "name": "Wampum belt", + "examine": "A decorated belt used to trade information between distant villages.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "626", + "name": "Boots", + "examine": "They're soft, silky and pink.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "627", + "name": "Boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "628", + "name": "Boots", + "examine": "They're soft, silky and green.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "629", + "name": "Boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "630", + "name": "Boots", + "examine": "They're soft, silky and blue.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "631", + "name": "Boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "632", + "name": "Boots", + "examine": "They're soft, silky and cream.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "633", + "name": "Boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "634", + "name": "Boots", + "examine": "They're soft, silky and turquoise.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "635", + "name": "Boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "636", + "name": "Robe top", + "examine": "The ultimate in gnome design, now in pink.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "637", + "name": "Robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "638", + "name": "Robe top", + "examine": "The ultimate in gnome design, now in green.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "639", + "name": "Robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "640", + "name": "Robe top", + "examine": "The ultimate in gnome design, now in blue.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "641", + "name": "Robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "642", + "name": "Robe top", + "examine": "The ultimate in gnome design, now in cream.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "643", + "name": "Robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "644", + "name": "Robe top", + "examine": "The ultimate in gnome design, now in turquoise.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "645", + "name": "Robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "646", + "name": "Robe bottoms", + "examine": "Made by tree gnomes with a thing for pink.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "647", + "name": "Robe bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "648", + "name": "Robe bottoms", + "examine": "Made by tree gnomes with a thing for green.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "649", + "name": "Robe bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "650", + "name": "Robe bottoms", + "examine": "Made by tree gnomes with a thing for blue.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "651", + "name": "Robe bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "652", + "name": "Robe bottoms", + "examine": "Made by tree gnomes with a thing for cream.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "653", + "name": "Robe bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "654", + "name": "Robe bottoms", + "examine": "Made by tree gnomes with a thing for turquoise.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "180", + "tradeable": "true", + "weight": "1" + }, + { + "id": "655", + "name": "Robe bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "656", + "name": "Hat", + "examine": "A silly, pink pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "657", + "name": "Hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "658", + "name": "Hat", + "examine": "A silly, green pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "659", + "name": "Hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "660", + "name": "Hat", + "examine": "A silly, blue pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "661", + "name": "Hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "662", + "name": "Hat", + "examine": "A silly, cream pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "663", + "name": "Hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "664", + "name": "Hat", + "examine": "A silly, turquoise pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "665", + "name": "Hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "666", + "name": "Portrait", + "examine": "Picture of a posing Paladin.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "667", + "name": "Blurite sword", + "examine": "A Faladian Knight's sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,0,0,0,10,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "name": "Phoenix crossbow", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "767", + "render_anim": "292", + "run_anim": "824", + "stand_anim": "809", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" }, { - "durability": null, - "name": "Phoenix crossbow", - "tradeable": "true", + "id": "668", + "name": "Blurite ore", + "examine": "Definitely blue.", "archery_ticket_price": "0", - "id": "768" + "durability": null, + "weight": "2.2" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Entrance certificate to the Imperial Guard.", - "durability": null, - "name": "Certificate", + "id": "669", + "name": "Specimen jar", + "examine": "A receptacle for specimens!", "archery_ticket_price": "0", - "id": "769" + "durability": null }, { - "destroy_message": "You can get another branch if you chop the dramen tree underneath Entrana again.", - "examine": "A limb of the fabled Dramen tree.", - "durability": null, - "name": "Dramen branch", - "tradeable": "false", + "id": "670", + "name": "Specimen brush", + "examine": "A small brush used to clean rock samples.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "671", + "name": "Animal skull", + "examine": "A carefully-kept-safe skull sample.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "771" + "durability": null, + "tradeable": "false" }, { - "turn90cw_anim": "1207", - "examine": "Crafted from a Dramen tree branch.", - "walk_anim": "1205", + "id": "672", + "name": "Special cup", + "examine": "A special cup.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "673", + "name": "Teddy", + "examine": "A lucky mascot.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "674", + "name": "Cracked sample", + "examine": "A roughly shaped piece of rock.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "675", + "name": "Rock pick", + "examine": "A small pick for digging.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "676", + "name": "Trowel", + "examine": "Used for digging!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "677", + "name": "Panning tray", + "examine": "An empty tray for panning.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "678", + "name": "Panning tray", + "examine": "This tray contains gold nuggets.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "679", + "name": "Panning tray", + "examine": "This tray contains mud.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "680", + "name": "Nuggets", + "examine": "Pure, lovely gold!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "681", + "name": "Ancient talisman", + "examine": "An unusual symbol as yet unidentified by the archaeological expert.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "682", + "name": "Unstamped letter", + "examine": "A letter waiting to be stamped.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Dramen staff", "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "772", - "stand_turn_anim": "1209", - "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0" + "weight": "1" }, { - "examine": "A perfect ruby ring.", - "durability": null, - "name": "'perfect' ring", + "id": "683", + "name": "Sealed letter", + "examine": "A sealed letter of recommendation.", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "684", + "name": "Belt buckle", + "examine": "Used to hold up trousers!", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "685", + "name": "Old boot", + "examine": "Phew!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "686", + "name": "Rusty sword", + "examine": "A decent-enough weapon gone rusty.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "687", + "name": "Broken arrow", + "examine": "This must have been shot at high speed.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "688", + "name": "Buttons", + "examine": "Not Dick Whittington's helper at all!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "689", + "name": "Broken staff", + "examine": "I pity the poor person beaten with this!", + "archery_ticket_price": "0", + "attack_audios": "2555,0,0,0", + "durability": null, + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "690", + "name": "Picture", + "examine": "Smashed glass.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "691", + "name": "Level 1 certificate", + "examine": "The owner has passed Earth Sciences Level 1 exam.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "692", + "name": "Level 2 certificate", + "examine": "The owner has passed Earth Sciences Level 2 exam.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "693", + "name": "Level 3 certificate", + "examine": "The owner has passed Earth Sciences Level 3 exam.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "694", + "name": "Ceramic remains", + "examine": "Smashing!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "695", + "name": "Old tooth", + "examine": "Now, if I could just find a tooth fairy to sell this to...", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "696", + "name": "Invitation letter", + "examine": "A letter inviting me to use the private digging shafts.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "697", + "name": "Damaged armour", + "examine": "It would be hard to repair this!", + "archery_ticket_price": "0", + "durability": null, + "weight": "7.75" + }, + { + "id": "698", + "name": "Broken armour", + "examine": "Armour left over from a great fight.", + "archery_ticket_price": "0", + "durability": null, + "weight": "8" + }, + { + "id": "699", + "name": "Stone tablet", + "examine": "An old stone slab with writing on it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "700", + "name": "Chemical powder", + "examine": "An acrid chemical.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "701", + "name": "Ammonium nitrate", + "examine": "An acrid chemical.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "702", + "name": "Unidentified liquid", + "examine": "A strong chemical.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "703", + "name": "Nitroglycerin", + "examine": "A strong chemical.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "1" + }, + { + "id": "704", + "name": "Ground charcoal", + "examine": "Charcoal - crushed to small pieces!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "705", + "name": "Mixed chemicals", + "examine": "A mixture of strong chemicals.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "0.03" + }, + { + "id": "706", + "name": "Mixed chemicals", + "examine": "A mixture of strong chemicals.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "0.035" + }, + { + "id": "707", + "name": "Chemical compound", + "examine": "A mixture of strong chemicals.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.035" + }, + { + "id": "708", + "name": "Arcenia root", + "examine": "The root of an arcenia plant.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "709", + "name": "Chest key", + "examine": "This key opens a chest in the Carnillean household." + }, + { + "id": "710", + "name": "Vase", + "examine": "An unusually shaped vase. You can see something glinting inside.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "711", + "name": "Book on chemicals", + "examine": "It's about chemicals, judging from its cover.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "712", + "name": "Cup of tea", + "examine": "A nice cup of tea.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "714", + "name": "Radimus notes", + "examine": "Notes given to you by Radimus Erkle, it includes a partially completed map.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "715", + "name": "Radimus notes", + "examine": "Notes given to you by Radimus Erkle, it includes a partially completed map.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "716", + "name": "Bull roarer", + "examine": "It makes a loud but interesting sound when swung in the air.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "717", + "name": "Scrawled note", + "examine": "A scrawled note with spidery writing on it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "718", + "name": "A scribbled note", + "examine": "A scrawled note with spidery writing on it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "719", + "name": "Scrumpled note", + "examine": "A scrawled note with spidery writing on it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "720", + "name": "Sketch", + "examine": "A rough sketch of a bowl shaped vessel given to you by Gujuo.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "721", + "name": "Gold bowl", + "examine": "A specially made bowl constructed out of pure gold.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "722", + "name": "Blessed gold bowl", + "examine": "A specially made bowl constructed out of pure gold and blessed.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.8" + }, + { + "id": "723", + "name": "Golden bowl", + "examine": "A blessed golden bowl. It has pure sacred water in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "724", + "name": "Golden bowl", + "examine": "A blessed golden bowl. It has pure sacred water in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "725", + "name": "Golden bowl", + "examine": "A blessed golden bowl. It has pure sacred water in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "726", + "name": "Golden bowl", + "examine": "A blessed golden bowl. It has pure sacred water in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "727", + "name": "Hollow reed", + "examine": "One of nature's pipes.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.012" + }, + { + "id": "728", + "name": "Hollow reed", + "examine": "One of nature's pipes.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.012" + }, + { + "id": "729", + "name": "Shamans tome", + "examine": "It looks like the Shamans personal notes...", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "730", + "name": "Binding book", + "examine": "An ancient tome on Demonology.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "731", + "name": "Enchanted vial", + "examine": "An enchanted empty glass vial.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "shop_price": "160", + "tradeable": "false" + }, + { + "id": "732", + "name": "Holy water", + "examine": "A vial of holy water, good against certain demons.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,24", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "18" + }, + { + "id": "733", + "name": "Smashed glass", + "examine": "Fragments of a broken container.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "735", + "name": "Yommi tree seeds", + "examine": "These need to be germinated before they can be used. / These are germinated and ready to be planted in fertile soil.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "736", + "name": "Yommi tree seeds", + "examine": "These need to be germinated before they can be used. / These are germinated and ready to be planted in fertile soil.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "737", + "name": "Snakeweed mixture", + "examine": "A Snakeweed mixture, needs another ingredient.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "738", + "name": "Ardrigal mixture", + "examine": "It's a mixture of ardrigal and water. Needs another ingredient.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "739", + "name": "Bravery potion", + "examine": "A bravery potion for which Gujuo gave you the details, let's hope it works.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "740", + "name": "Blue hat", + "examine": "A silly, blue pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "hat": "true", + "shop_price": "160", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "741", + "name": "Chunk of crystal", + "examine": "It looks like it's been snapped off of something.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "742", + "name": "Hunk of crystal", + "examine": "It looks like it's been snapped off of something.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "743", + "name": "Lump of crystal", + "examine": "It looks like it's been snapped off of something.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "744", + "name": "Heart crystal", + "examine": "A heart shaped crystal.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "745", + "name": "Heart crystal", + "examine": "A heart shaped crystal.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "746", + "name": "Dark dagger", + "examine": "A black obsidian dagger, it has a strange aura about it.", + "archery_ticket_price": "0", + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,0,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "747", + "name": "Glowing dagger", + "examine": "A black obsidian dagger, it has a strange aura about it - it seems to be glowing.", + "archery_ticket_price": "0", + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "6", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,0,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "748", + "name": "Holy force", + "examine": "A powerful spell for good.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "749", + "name": "Yommi totem", + "examine": "A well carved totem pole made from the trunk of a Yommi tree.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "750", + "name": "Gilded totem", + "examine": "A gilded totem pole from the Kharazi tribe.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "751", + "name": "Gnomeball", + "examine": "A common gnomeball, obtained by playing gnomeball.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "752", + "name": "Gnomeball", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "753", + "name": "Cadava berries", + "examine": "Poisonous berries.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "754", + "name": "Cadava berries", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "756", + "name": "Cadava potion", + "examine": "This potion will most certainly almost kill you.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "759", + "name": "Weapon store key", + "examine": "Phoenix Gang weapon store key.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "760", + "name": "Weapon store key", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "761", + "name": "Intel report", + "examine": "An intelligence report.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to kill Jonny the beard in order to get another one.", + "durability": null + }, + { + "id": "763", + "name": "Broken shield", + "examine": "Half of the Shield of Arrav.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "2.7" + }, + { + "id": "765", + "name": "Broken shield", + "examine": "Half of the Shield of Arrav.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "2.7" + }, + { + "id": "767", + "name": "Phoenix crossbow", + "examine": "Second-rate crossbow, former property of the Phoenix Gang.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "175", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "3" + }, + { + "id": "768", + "name": "Phoenix crossbow", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "769", + "name": "Certificate", + "examine": "Entrance certificate to the Imperial Guard.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "771", + "name": "Dramen branch", + "examine": "A limb of the fabled Dramen tree.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another branch if you chop the dramen tree underneath Entrana again.", + "durability": null, + "tradeable": "false", + "weight": "2.2" + }, + { + "id": "772", + "name": "Dramen staff", + "examine": "Crafted from a Dramen tree branch.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "4", + "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1.8" + }, + { "id": "773", + "name": "'perfect' ring", + "examine": "A perfect ruby ring.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "12" }, { - "examine": "A perfect ruby necklace.", - "durability": null, - "name": "'perfect' necklace", - "archery_ticket_price": "0", "id": "774", + "name": "'perfect' necklace", + "examine": "A perfect ruby necklace.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "destroy_message": "You can obtain another set of Cooking gauntlets from Dimintheis.", - "examine": "These gauntlets empower with a greater ability to cook fish.", - "durability": null, - "name": "Cooking gauntlets", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "775", + "name": "Cooking gauntlets", + "examine": "These gauntlets empower with a greater ability to cook fish.", + "archery_ticket_price": "0", "bonuses": "2,2,2,0,0,8,9,7,0,0,3,2,0,0,0", - "equipment_slot": "9" + "destroy": "true", + "destroy_message": "You can obtain another set of Cooking gauntlets from Dimintheis.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false", + "weight": "0.2" }, { - "destroy_message": "You can obtain another set of Goldsmith gauntlets from Dimintheis.", - "examine": "These gauntlets empower the bearer whilst making gold.", - "durability": null, - "name": "Goldsmith gauntlets", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "776", + "name": "Goldsmith gauntlets", + "examine": "These gauntlets empower the bearer whilst making gold.", + "archery_ticket_price": "0", "bonuses": "2,2,2,0,0,8,9,7,0,0,3,2,0,0,0", - "equipment_slot": "9" + "destroy": "true", + "destroy_message": "You can obtain another set of Goldsmith gauntlets from Dimintheis.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false" }, { - "destroy_message": "You can obtain another set of Chaos gauntlets from Dimintheis.", - "examine": "These gauntlets empower spell casters.", - "durability": null, - "name": "Chaos gauntlets", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "777", + "name": "Chaos gauntlets", + "examine": "These gauntlets empower spell casters.", + "archery_ticket_price": "0", "bonuses": "2,2,2,0,0,8,9,7,0,0,3,2,0,0,0", - "equipment_slot": "9" + "destroy": "true", + "destroy_message": "You can obtain another set of Chaos gauntlets from Dimintheis.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "My reward for assisting the Fitzharmon family.", - "durability": null, - "name": "Family gauntlets", - "weight": "1", - "archery_ticket_price": "0", "id": "778", + "name": "Family gauntlets", + "examine": "My reward for assisting the Fitzharmon family.", + "archery_ticket_price": "0", "bonuses": "2,2,2,0,0,8,9,7,0,0,0,2,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "A fragment of the Fitzharmon family crest.", - "durability": null, - "name": "Crest part", - "archery_ticket_price": "0", - "id": "779" - }, - { - "examine": "A fragment of the Fitzharmon family crest.", - "durability": null, - "name": "Crest part", - "archery_ticket_price": "0", - "id": "780" - }, - { - "examine": "A fragment of the Fitzharmon family crest.", - "durability": null, - "name": "Crest part", - "archery_ticket_price": "0", - "id": "781" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A sample of the bark from the Grand Tree.", "durability": null, + "equipment_slot": "9", + "weight": "1" + }, + { + "id": "779", + "name": "Crest part", + "examine": "A fragment of the Fitzharmon family crest.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "780", + "name": "Crest part", + "examine": "A fragment of the Fitzharmon family crest.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "781", + "name": "Crest part", + "examine": "A fragment of the Fitzharmon family crest.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "783", "name": "Bark sample", + "examine": "A sample of the bark from the Grand Tree.", "archery_ticket_price": "0", - "id": "783" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A book to translate the ancient gnome language into English.", - "durability": null, + "id": "784", "name": "Translation book", + "examine": "A book to translate the ancient gnome language into English.", "archery_ticket_price": "0", - "id": "784" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Perhaps I should read it and see what Glough is up to!", - "durability": null, + "id": "785", "name": "Glough's journal", + "examine": "Perhaps I should read it and see what Glough is up to!", "archery_ticket_price": "0", - "id": "785" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Hazelmere wrote something down on this scroll.", - "durability": null, + "id": "786", "name": "Hazelmere's scroll", + "examine": "Hazelmere wrote something down on this scroll.", "archery_ticket_price": "0", - "id": "786" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "An order from the Karamja shipyard.", - "durability": null, + "id": "787", "name": "Lumber order", + "examine": "An order from the Karamja shipyard.", "archery_ticket_price": "0", - "id": "787" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "The key to Glough's chest.", - "durability": null, + "id": "788", "name": "Glough's key", - "weight": "1", + "examine": "The key to Glough's chest.", "archery_ticket_price": "0", - "id": "788" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Twigs bound together in the shape of a (Z/U/T/O).", - "durability": null, + "id": "789", "name": "Twigs", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "789" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", "examine": "Twigs bound together in the shape of a (Z/U/T/O).", - "durability": null, - "name": "Twigs", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "790" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", + "id": "790", + "name": "Twigs", "examine": "Twigs bound together in the shape of a (Z/U/T/O).", - "durability": null, - "name": "Twigs", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "791" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", + "id": "791", + "name": "Twigs", "examine": "Twigs bound together in the shape of a (Z/U/T/O).", - "durability": null, - "name": "Twigs", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "792" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { + "id": "792", + "name": "Twigs", + "examine": "Twigs bound together in the shape of a (Z/U/T/O).", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "An ancient rock with strange magical properties.", "durability": null, + "tradeable": "false" + }, + { + "id": "793", "name": "Daconia rock", - "tradeable": "false", - "destroy": "true", - "weight": "3", + "examine": "An ancient rock with strange magical properties.", "archery_ticket_price": "0", - "id": "793" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "These are plans for an invasion!", - "durability": null, + "id": "794", "name": "Invasion plans", - "tradeable": "false", + "examine": "These are plans for an invasion!", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "794" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A model of a Karamja warship", "durability": null, + "tradeable": "false" + }, + { + "id": "795", "name": "War ship", - "weight": "0.01", + "examine": "A model of a Karamja warship", "archery_ticket_price": "0", - "id": "795" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "0.01" + }, + { + "id": "799", + "name": "C. Ele Minor Drop Table", + "examine": "Item container for C. Ele Minor Drop Table. You should not be able to obtain this item." }, { - "shop_price": "4", - "ge_buy_limit": "1000", - "examine": "A finely balanced throwing axe.", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "3", - "name": "Bronze thrownaxe", - "tradeable": "true", - "archery_ticket_price": "0", "id": "800", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,5" - }, - { - "shop_price": "13", - "ge_buy_limit": "1000", + "name": "Bronze thrownaxe", "examine": "A finely balanced throwing axe.", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "14", - "name": "Iron thrownaxe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "801", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,7" + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,5", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "4", + "tradeable": "true", + "weapon_interface": "18" }, { + "id": "801", + "name": "Iron thrownaxe", + "examine": "A finely balanced throwing axe.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "13", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "802", + "name": "Steel thrownaxe", + "examine": "A finely balanced throwing axe.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,11", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,5}", "shop_price": "38", - "ge_buy_limit": "1000", - "examine": "A finely balanced throwing axe.", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "60", - "name": "Steel thrownaxe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "802", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,11" + "weapon_interface": "18" }, { + "id": "803", + "name": "Mithril thrownaxe", + "examine": "A finely balanced throwing axe.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,16", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,20}", "shop_price": "105", - "ge_buy_limit": "1000", - "examine": "A finely balanced throwing axe.", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "135", - "name": "Mithril thrownaxe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "803", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,16" + "weapon_interface": "18" }, { + "id": "804", + "name": "Adamant thrownaxe", + "examine": "A finely balanced throwing axe.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,23", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,30}", "shop_price": "262", - "ge_buy_limit": "1000", - "examine": "A finely balanced throwing axe.", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "138", - "name": "Adamant thrownaxe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "804", - "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,23" + "weapon_interface": "18" }, { + "id": "805", + "name": "Rune thrownaxe", + "examine": "A finely balanced throwing axe.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2706,2706,2706", + "attack_speed": "5", + "bonuses": "0,0,0,0,26,0,0,0,0,0,0,0,0,0,36", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "has_special": "true", "requirements": "{4,40}", "shop_price": "698", - "ge_buy_limit": "1000", - "examine": "A finely balanced throwing axe.", - "has_special": "true", - "durability": null, - "attack_speed": "5", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "243", - "name": "Rune thrownaxe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "805", - "bonuses": "0,0,0,0,26,0,0,0,0,0,0,0,0,0,36" + "weapon_interface": "18" }, { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a bronze tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "53", - "attack_audios": "2547,0,0,0", - "name": "Bronze dart", - "tradeable": "true", - "archery_ticket_price": "0", "id": "806", - "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1" - }, - { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an iron tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "59", - "attack_audios": "2547,0,0,0", - "name": "Iron dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "807", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "requirements": "{4,5}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a steel tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "50", - "attack_audios": "2547,0,0,0", - "name": "Steel dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "808", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a mithril tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "68", - "attack_audios": "2547,0,0,0", - "name": "Mithril dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "809", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an adamantite tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "96", - "attack_audios": "2547,0,0,0", - "name": "Adamant dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "810", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a rune tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "122", - "attack_audios": "2547,0,0,0", - "name": "Rune dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "811", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "ge_buy_limit": "1000", + "name": "Bronze dart", "examine": "A deadly throwing dart with a bronze tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "66", - "attack_audios": "2547,0,0,0", - "name": "Bronze dart(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "812", - "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1" + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" }, { - "ge_buy_limit": "1000", + "id": "807", + "name": "Iron dart", "examine": "A deadly throwing dart with an iron tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "66", - "attack_audios": "2547,0,0,0", - "name": "Iron dart(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "813", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" }, { - "requirements": "{4,5}", - "ge_buy_limit": "1000", + "id": "808", + "name": "Steel dart", "examine": "A deadly throwing dart with a steel tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "75", - "attack_audios": "2547,0,0,0", - "name": "Steel dart(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "814", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "tradeable": "true", + "weapon_interface": "18" }, { - "requirements": "{4,20}", - "ge_buy_limit": "1000", + "id": "809", + "name": "Mithril dart", "examine": "A deadly throwing dart with a mithril tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "65", - "attack_audios": "2547,0,0,0", - "name": "Mithril dart(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "815", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", + "id": "810", + "name": "Adamant dart", "examine": "A deadly throwing dart with an adamantite tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "75", - "attack_audios": "2547,0,0,0", - "name": "Adamant dart(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "816", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,40}", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a rune tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "110", - "attack_audios": "2547,0,0,0", - "name": "Rune dart(p)", + "requirements": "{4,30}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "817", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" + "weapon_interface": "18" }, { + "id": "811", + "name": "Rune dart", + "examine": "A deadly throwing dart with a rune tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "812", + "name": "Bronze dart(p)", + "examine": "A deadly throwing dart with a bronze tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "813", + "name": "Iron dart(p)", + "examine": "A deadly throwing dart with an iron tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "814", + "name": "Steel dart(p)", + "examine": "A deadly throwing dart with a steel tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "815", + "name": "Mithril dart(p)", + "examine": "A deadly throwing dart with a mithril tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "816", + "name": "Adamant dart(p)", + "examine": "A deadly throwing dart with an adamantite tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "817", + "name": "Rune dart(p)", + "examine": "A deadly throwing dart with a rune tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "818", "name": "Poisoned dart(p)", "archery_ticket_price": "0", - "id": "818", + "attack_audios": "2547,0,0,0", + "durability": null, + "equip_audio": "2244", "equipment_slot": "3" }, { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of bronze - needs feathers for flight.", - "grand_exchange_price": "24", - "attack_audios": "2547,0,0,0", - "durability": null, + "id": "819", "name": "Bronze dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of bronze - needs feathers for flight.", "archery_ticket_price": "0", - "id": "819" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of iron - needs feathers for flight.", - "grand_exchange_price": "19", "attack_audios": "2547,0,0,0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "820", "name": "Iron dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of iron - needs feathers for flight.", "archery_ticket_price": "0", - "id": "820" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of steel - needs feathers for flight.", - "grand_exchange_price": "66", "attack_audios": "2547,0,0,0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "821", "name": "Steel dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of steel - needs feathers for flight.", "archery_ticket_price": "0", - "id": "821" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of mithril - needs feathers for flight.", - "grand_exchange_price": "97", "attack_audios": "2547,0,0,0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "822", "name": "Mithril dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of mithril - needs feathers for flight.", "archery_ticket_price": "0", - "id": "822" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of adamantite - needs feathers for flight.", - "grand_exchange_price": "212", "attack_audios": "2547,0,0,0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "823", "name": "Adamant dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of adamantite - needs feathers for flight.", "archery_ticket_price": "0", - "id": "823" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dart tip made of runite - needs feathers for flight.", - "grand_exchange_price": "498", "attack_audios": "2547,0,0,0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "824", "name": "Rune dart tip", - "tradeable": "true", + "examine": "A deadly looking dart tip made of runite - needs feathers for flight.", "archery_ticket_price": "0", - "id": "824" + "attack_audios": "2547,0,0,0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "1000", - "examine": "A bronze-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "6", - "name": "Bronze javelin", - "tradeable": "true", - "archery_ticket_price": "0", "id": "825", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6" - }, - { - "shop_price": "8", - "ge_buy_limit": "1000", - "examine": "An iron-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "2", - "name": "Iron javelin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "826", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,5}", - "shop_price": "37", - "ge_buy_limit": "1000", - "examine": "A steel-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "19", - "name": "Steel javelin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "827", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12" - }, - { - "requirements": "{4,20}", - "shop_price": "79", - "ge_buy_limit": "1000", - "examine": "A mithril tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "25", - "name": "Mithril javelin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "828", - "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18" - }, - { - "requirements": "{4,30}", - "shop_price": "100", - "ge_buy_limit": "1000", - "examine": "An adamant tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "51", - "name": "Adamant javelin", - "tradeable": "true", - "archery_ticket_price": "100", - "id": "829", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28" - }, - { - "requirements": "{4,40}", - "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "186", - "name": "Rune javelin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "830", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42" - }, - { - "shop_price": "6", - "ge_buy_limit": "1000", + "name": "Bronze javelin", "examine": "A bronze-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "19", - "name": "Bronze javelin(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "831", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6" - }, - { - "shop_price": "8", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", "ge_buy_limit": "1000", - "examine": "An iron-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "19", - "name": "Iron javelin(p)", + "shop_price": "6", "tradeable": "true", - "archery_ticket_price": "0", - "id": "832", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10" + "weapon_interface": "18" }, { + "id": "826", + "name": "Iron javelin", + "examine": "An iron-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "8", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "827", + "name": "Steel javelin", + "examine": "A steel-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,5}", "shop_price": "37", - "ge_buy_limit": "1000", - "examine": "A steel-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "34", - "name": "Steel javelin(p)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "833", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12" + "weapon_interface": "18" }, { + "id": "828", + "name": "Mithril javelin", + "examine": "A mithril tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,20}", "shop_price": "79", - "ge_buy_limit": "1000", - "examine": "A mithril tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "44", - "name": "Mithril javelin(p)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "834", - "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18" + "weapon_interface": "18" }, { + "id": "829", + "name": "Adamant javelin", + "examine": "An adamant tipped javelin.", + "archery_ticket_price": "100", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,30}", "shop_price": "100", - "ge_buy_limit": "1000", - "examine": "An adamant tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "88", - "name": "Adamant javelin(p)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "835", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28" + "weapon_interface": "18" }, { + "id": "830", + "name": "Rune javelin", + "examine": "A rune tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{4,40}", "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "831", + "name": "Bronze javelin(p)", + "examine": "A bronze-tipped javelin.", + "archery_ticket_price": "0", "attack_anims": "806,806,806,806", - "grand_exchange_price": "256", - "name": "Rune javelin(p)", + "attack_speed": "6", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "6", "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "832", + "name": "Iron javelin(p)", + "examine": "An iron-tipped javelin.", "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "8", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "833", + "name": "Steel javelin(p)", + "examine": "A steel-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "shop_price": "37", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "834", + "name": "Mithril javelin(p)", + "examine": "A mithril tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "shop_price": "79", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "835", + "name": "Adamant javelin(p)", + "examine": "An adamant tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "shop_price": "100", + "tradeable": "true", + "weapon_interface": "18" + }, + { "id": "836", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42" + "name": "Rune javelin(p)", + "examine": "A rune tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "shop_price": "624", + "tradeable": "true", + "weapon_interface": "18" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "This fires crossbow bolts.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "276", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", "id": "837", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0", - "shop_price": "70", - "durability": null, - "weight": "8", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Crossbow" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "276", - "durability": null, "name": "Crossbow", - "tradeable": "true", + "examine": "This fires crossbow bolts.", "archery_ticket_price": "0", - "id": "838" - }, - { - "ge_buy_limit": "5000", - "turn90cw_anim": "821", - "examine": "A nice sturdy bow.", - "walk_anim": "819", - "turn90ccw_anim": "822", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "820", + "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0", "defence_anim": "424", + "durability": null, + "equip_audio": "2244", "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "55", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "839", + "ge_buy_limit": "100", + "render_anim": "175", + "run_anim": "4228", + "shop_price": "70", + "stand_anim": "4591", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0", - "shop_price": "80", - "durability": null, - "weight": "1.8", - "weapon_interface": "16", - "render_anim": "1", - "attack_audios": "2700,0,0,0", - "name": "Longbow" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "55", - "durability": null, - "name": "Longbow", "tradeable": "true", - "archery_ticket_price": "0", - "id": "840" - }, - { - "ge_buy_limit": "5000", - "turn90cw_anim": "821", - "examine": "A shortbow made of wood.", - "walk_anim": "819", + "turn180_anim": "4227", "turn90ccw_anim": "822", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "820", - "defence_anim": "424", - "equipment_slot": "3", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "8" + }, + { + "id": "838", + "name": "Crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "839", + "name": "Longbow", + "examine": "A nice sturdy bow.", + "archery_ticket_price": "0", "attack_anims": "426,426,426,426", - "grand_exchange_price": "79", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "841", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0", - "shop_price": "50", - "durability": null, - "weight": "1.33", - "weapon_interface": "16", - "render_anim": "1", "attack_audios": "2700,0,0,0", - "name": "Shortbow" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "79", + "attack_speed": "6", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", "durability": null, - "name": "Shortbow", + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "1", + "run_anim": "824", + "shop_price": "80", + "stand_anim": "808", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "842" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "819", + "weapon_interface": "16", + "weight": "1.8" }, { + "id": "840", + "name": "Longbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "841", + "name": "Shortbow", + "examine": "A shortbow made of wood.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "1", + "run_anim": "824", + "shop_price": "50", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "819", + "weapon_interface": "16", + "weight": "1.33" + }, + { + "id": "842", + "name": "Shortbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "843", + "name": "Oak shortbow", + "examine": "A shortbow made out of oak, still effective.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "2588", "requirements": "{4,5}", "shop_price": "164", - "ge_buy_limit": "5000", - "examine": "A shortbow made out of oak, still effective.", - "durability": null, - "weight": "1.8", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "54", - "attack_audios": "2700,0,0,0", - "name": "Oak shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "843", - "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "54", + "id": "844", + "name": "Oak shortbow", + "archery_ticket_price": "0", "durability": null, - "name": "Oak shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "844" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "845", + "name": "Oak longbow", + "examine": "A nice sturdy bow made out of oak.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "2588", "requirements": "{4,5}", "shop_price": "160", - "ge_buy_limit": "5000", - "examine": "A nice sturdy bow made out of oak.", - "durability": null, - "weight": "2", - "attack_speed": "6", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "132", - "attack_audios": "2700,0,0,0", - "name": "Oak longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "845", - "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "132", + "id": "846", + "name": "Oak longbow", + "archery_ticket_price": "0", "durability": null, - "name": "Oak longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "846" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "847", + "name": "Willow longbow", + "examine": "A nice sturdy bow made out of willow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "2588", "requirements": "{4,20}", "shop_price": "320", - "ge_buy_limit": "5000", - "examine": "A nice sturdy bow made out of willow.", - "durability": null, - "weight": "1.5", - "attack_speed": "6", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "132", - "attack_audios": "2700,0,0,0", - "name": "Willow longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "847", - "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,0" + "weight": "1.5" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "132", + "id": "848", + "name": "Willow longbow", + "archery_ticket_price": "0", "durability": null, - "name": "Willow longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "848" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "849", + "name": "Willow shortbow", + "examine": "A shortbow made out of willow, still effective.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "render_anim": "2588", "requirements": "{4,20}", "shop_price": "200", - "ge_buy_limit": "5000", - "examine": "A shortbow made out of willow, still effective.", - "durability": null, - "weight": "1", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "106", - "attack_audios": "2700,0,0,0", - "name": "Willow shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "849", - "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "106", + "id": "850", + "name": "Willow shortbow", + "archery_ticket_price": "0", "durability": null, - "name": "Willow shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "850" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "851", + "name": "Maple longbow", + "examine": "A nice sturdy bow made out of maple.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "lendable": "true", + "render_anim": "2588", "requirements": "{4,30}", "shop_price": "640", - "ge_buy_limit": "5000", - "examine": "A nice sturdy bow made out of maple.", - "durability": null, - "weight": "1.8", - "attack_speed": "6", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "320", - "attack_audios": "2700,0,0,0", - "name": "Maple longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "851", - "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "320", + "id": "852", + "name": "Maple longbow", + "archery_ticket_price": "0", "durability": null, - "name": "Maple longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "852" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "853", + "name": "Maple shortbow", + "examine": "A shortbow made out of maple, still effective.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "lendable": "true", + "render_anim": "2588", "requirements": "{4,30}", "shop_price": "400", - "ge_buy_limit": "5000", - "examine": "A shortbow made out of maple, still effective.", - "durability": null, - "weight": "1", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "290", - "attack_audios": "2700,0,0,0", - "name": "Maple shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "853", - "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "290", + "id": "854", + "name": "Maple shortbow", + "archery_ticket_price": "0", "durability": null, - "name": "Maple shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "854" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "855", + "name": "Yew longbow", + "examine": "A nice sturdy bow made out of yew.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "lendable": "true", + "render_anim": "2588", "requirements": "{4,40}", "shop_price": "674", - "ge_buy_limit": "5000", - "examine": "A nice sturdy bow made out of yew.", - "durability": null, - "weight": "1.8", - "attack_speed": "6", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "706", - "attack_audios": "2700,0,0,0", - "name": "Yew longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "855", - "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "706", - "durability": null, + "id": "856", "name": "Yew longbow", - "tradeable": "true", "archery_ticket_price": "0", - "id": "856" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{4,40}", - "ge_buy_limit": "5000", - "examine": "A shortbow made out of yew, still effective.", - "durability": null, - "weight": "1", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "422", - "attack_audios": "2700,0,0,0", - "name": "Yew shortbow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "857", - "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "422", - "durability": null, "name": "Yew shortbow", - "tradeable": "true", + "examine": "A shortbow made out of yew, still effective.", "archery_ticket_price": "0", - "id": "858" + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "lendable": "true", + "render_anim": "2588", + "requirements": "{4,40}", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16", + "weight": "1" }, { + "id": "858", + "name": "Yew shortbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "859", + "name": "Magic longbow", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "has_special": "true", + "lendable": "true", + "render_anim": "2588", "requirements": "{4,50}", "shop_price": "1270", - "ge_buy_limit": "5000", - "examine": "A nice sturdy magical bow.", - "has_special": "true", - "durability": null, - "weight": "1.8", - "attack_speed": "6", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "1437", - "attack_audios": "2700,0,0,0", - "name": "Magic longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "859", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1437", + "id": "860", + "name": "Magic longbow", + "archery_ticket_price": "0", "durability": null, - "name": "Magic longbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "860" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "861", + "name": "Magic shortbow", + "examine": "Short and magical, but still effective.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "5000", + "has_special": "true", + "lendable": "true", + "render_anim": "2588", "requirements": "{4,50}", "shop_price": "5600", - "ge_buy_limit": "5000", - "examine": "Short and magical, but still effective.", - "has_special": "true", - "durability": null, - "weight": "1.3", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "959", - "attack_audios": "2700,0,0,0", - "name": "Magic shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "861", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" + "weight": "1.3" }, { + "id": "862", + "name": "Magic shortbow", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "959", - "durability": null, - "name": "Magic shortbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "862" + "tradeable": "true" }, { - "shop_price": "58", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "87", - "attack_audios": "2704,0,0,0", - "name": "Iron knife", - "tradeable": "true", - "archery_ticket_price": "0", "id": "863", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "shop_price": "14", - "ge_buy_limit": "10000", + "name": "Iron knife", "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "73", - "attack_audios": "2704,0,0,0", - "name": "Bronze knife", - "tradeable": "true", "archery_ticket_price": "0", - "id": "864", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "requirements": "{4,5}", - "shop_price": "68", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "136", - "attack_audios": "2704,0,0,0", - "name": "Steel knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "865", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "201", - "attack_audios": "2704,0,0,0", - "name": "Mithril knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "866", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "421", - "attack_audios": "2704,0,0,0", - "name": "Adamant knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "867", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1132", - "attack_audios": "2704,0,0,0", - "name": "Rune knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "868", - "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24" - }, - { - "requirements": "{4,10}", - "shop_price": "15", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "95", - "attack_audios": "2704,0,0,0", - "name": "Black knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "869", - "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8" - }, - { - "shop_price": "14", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "73", - "attack_audios": "2704,0,0,0", - "name": "Bronze knife(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "870", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { "shop_price": "58", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "88", - "attack_audios": "2704,0,0,0", - "name": "Iron knife(p)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "871", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" + "weapon_interface": "18" }, { + "id": "864", + "name": "Bronze knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "14", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "865", + "name": "Steel knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", "requirements": "{4,5}", "shop_price": "68", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "866", + "name": "Mithril knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "124", - "attack_audios": "2704,0,0,0", - "name": "Steel knife(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "872", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { "requirements": "{4,20}", - "ge_buy_limit": "100", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "226", - "attack_audios": "2704,0,0,0", - "name": "Mithril knife(p)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "873", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" + "weapon_interface": "18" }, { + "id": "867", + "name": "Adamant knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "868", + "name": "Rune knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "869", + "name": "Black knife", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", "requirements": "{4,10}", "shop_price": "15", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "69", - "attack_audios": "2704,0,0,0", - "name": "Black knife(p)", "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "870", + "name": "Bronze knife(p)", + "examine": "A finely balanced throwing knife.", "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "14", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "871", + "name": "Iron knife(p)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "58", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "872", + "name": "Steel knife(p)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "68", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "873", + "name": "Mithril knife(p)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { "id": "874", - "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8" + "name": "Black knife(p)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,10}", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "18" }, { - "requirements": "{4,30}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "503", - "attack_audios": "2704,0,0,0", - "name": "Adamant knife(p)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "875", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", + "name": "Adamant knife(p)", "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "806,806,806,806", - "grand_exchange_price": "1374", "attack_audios": "2704,0,0,0", - "name": "Rune knife(p)", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,30}", "tradeable": "true", - "archery_ticket_price": "0", + "weapon_interface": "18" + }, + { "id": "876", - "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24" + "name": "Rune knife(p)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" }, { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "Bronze crossbow bolts.", - "grand_exchange_price": "12", - "durability": null, - "name": "Bronze bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "877", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", + "name": "Bronze bolts", "examine": "Bronze crossbow bolts.", - "grand_exchange_price": "38", - "durability": null, - "name": "Bronze bolts(p)", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" + }, + { "id": "878", + "name": "Bronze bolts(p)", + "examine": "Bronze crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" }, { - "ge_buy_limit": "25000", - "examine": "Opal tipped bronze crossbow bolts", - "grand_exchange_price": "5", - "durability": null, - "name": "Opal bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "879", + "name": "Opal bolts", + "examine": "Opal tipped bronze crossbow bolts", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,14", - "equipment_slot": "13" - }, - { - "requirements": "{4,26}", + "durability": null, + "equipment_slot": "13", "ge_buy_limit": "25000", - "examine": "Pearl tipped Iron crossbow bolts.", - "grand_exchange_price": "32", - "durability": null, - "name": "Pearl bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "880", + "name": "Pearl bolts", + "examine": "Pearl tipped Iron crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,48", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,26}", + "tradeable": "true" }, { - "shop_price": "80", - "ge_buy_limit": "10000", - "examine": "Great if you have a crossbow!", - "grand_exchange_price": "42", - "durability": null, - "name": "Barbed bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "881", + "name": "Barbed bolts", + "examine": "Great if you have a crossbow!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,12", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "80", + "tradeable": "true" }, { - "shop_price": "7", - "ge_buy_limit": "10000", - "examine": "Arrows with bronze heads.", - "grand_exchange_price": "10", - "durability": null, - "name": "Bronze arrow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "882", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" - }, - { - "shop_price": "7", - "ge_buy_limit": "10000", + "name": "Bronze arrow", "examine": "Arrows with bronze heads.", - "grand_exchange_price": "39", - "durability": null, - "name": "Bronze arrow(p)", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "7", + "tradeable": "true" + }, + { "id": "883", + "name": "Bronze arrow(p)", + "examine": "Arrows with bronze heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "7", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "grand_exchange_price": "10", - "durability": null, - "name": "Iron arrow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "884", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", + "name": "Iron arrow", "examine": "Arrows with iron heads.", - "grand_exchange_price": "41", - "durability": null, - "name": "Iron arrow(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "885", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" }, { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "grand_exchange_price": "25", - "durability": null, - "name": "Steel arrow", - "tradeable": "true", + "id": "885", + "name": "Iron arrow(p)", + "examine": "Arrows with iron heads.", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" + }, + { "id": "886", + "name": "Steel arrow", + "examine": "Arrows with steel heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,5}", "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "grand_exchange_price": "69", - "durability": null, - "name": "Steel arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "887", + "name": "Steel arrow(p)", + "examine": "Arrows with steel heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "46", + "tradeable": "true" }, { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "grand_exchange_price": "30", - "durability": null, - "name": "Mithril arrow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "888", + "name": "Mithril arrow", + "examine": "Arrows with mithril heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,20}", "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "grand_exchange_price": "69", - "durability": null, - "name": "Mithril arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "889", + "name": "Mithril arrow(p)", + "examine": "Arrows with mithril heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "shop_price": "76", + "tradeable": "true" }, { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "grand_exchange_price": "111", - "durability": null, - "name": "Adamant arrow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "890", + "name": "Adamant arrow", + "examine": "Arrows with adamantite heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,30}", "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "grand_exchange_price": "109", - "durability": null, - "name": "Adamant arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "891", + "name": "Adamant arrow(p)", + "examine": "Arrows with adamantite heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "shop_price": "172", + "tradeable": "true" }, { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "grand_exchange_price": "212", - "durability": null, - "name": "Rune arrow", - "tradeable": "true", - "archery_ticket_price": "40", "id": "892", + "name": "Rune arrow", + "examine": "Arrows with rune heads.", + "archery_ticket_price": "40", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,40}", "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "grand_exchange_price": "257", - "durability": null, - "name": "Rune arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "893", + "name": "Rune arrow(p)", + "examine": "Arrows with rune heads.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", "ge_buy_limit": "10000", - "examine": "unlit: Arrows with bronze heads and oil-soaked cloth. lit: An easy to make, bronze-headed fire arrow.", - "grand_exchange_price": "228", - "durability": null, - "name": "Bronze fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{4,40}", + "shop_price": "510", + "tradeable": "true" + }, + { "id": "942", + "name": "Bronze fire arrows", + "examine": "unlit: Arrows with bronze heads and oil-soaked cloth. lit: An easy to make, bronze-headed fire arrow.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "0", + "id": "943", + "name": "Worm", "examine": "Ugh! It's wriggling!", - "durability": null, - "name": "Worm", - "tradeable": "true", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "943" + "durability": null, + "shop_price": "0", + "tradeable": "true" }, { - "durability": null, + "id": "944", "name": "Worm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "944" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "945", "name": "Throwing rope", "archery_ticket_price": "0", - "id": "945", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "25", - "ge_buy_limit": "100", + "id": "946", + "name": "Knife", "examine": "A dangerous looking knife.", - "grand_exchange_price": "32", - "durability": null, - "name": "Knife", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "946" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "32", - "durability": null, - "name": "Knife", + "shop_price": "25", "tradeable": "true", - "archery_ticket_price": "0", - "id": "947" + "weight": "0.4" }, { + "id": "947", + "name": "Knife", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "948", + "name": "Bear fur", + "examine": "This would make warm clothing.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "4", - "ge_buy_limit": "100", - "examine": "This would make warm clothing.", - "grand_exchange_price": "9", - "durability": null, - "name": "Bear fur", "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "948" + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "9", - "durability": null, + "id": "949", "name": "Bear fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "949" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "21", - "ge_buy_limit": "100", + "id": "950", + "name": "Silk", "examine": "It's a sheet of silk.", - "grand_exchange_price": "27", - "durability": null, - "name": "Silk", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "950" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "21", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "27", - "durability": null, + "id": "951", "name": "Silk", - "tradeable": "true", "archery_ticket_price": "0", - "id": "951" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "5", - "ge_buy_limit": "100", + "id": "952", + "name": "Spade", "examine": "A slightly muddy spade. / Popular with farmers and treasure hunters.", - "grand_exchange_price": "117", - "durability": null, - "name": "Spade", - "tradeable": "true", - "weight": "1.8", "archery_ticket_price": "0", - "id": "952" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "117", - "durability": null, + "id": "953", "name": "Spade", - "tradeable": "true", "archery_ticket_price": "0", - "id": "953" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "18", - "ge_buy_limit": "100", + "id": "954", + "name": "Rope", "examine": "A coil of rope.", - "grand_exchange_price": "96", - "durability": null, - "name": "Rope", - "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", - "id": "954" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "96", - "durability": null, - "name": "Rope", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "955" - }, - { - "examine": "Get your axes from Bob's Axes.", - "durability": null, - "name": "Flier", - "archery_ticket_price": "0", - "id": "956" - }, - { - "shop_price": "70", - "ge_buy_limit": "100", - "examine": "This would make warm clothing.", - "grand_exchange_price": "34", - "durability": null, - "name": "Grey wolf fur", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "958" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "34", - "durability": null, - "name": "Grey wolf fur", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "959" - }, - { - "shop_price": "100", - "ge_buy_limit": "10000", - "examine": "A plank of wood!", - "grand_exchange_price": "234", - "durability": null, - "name": "Plank", - "tradeable": "true", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "960" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "234", - "durability": null, - "name": "Plank", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "961" - }, - { - "ge_buy_limit": "2", - "examine": "I need to pull this.", - "grand_exchange_price": "15", - "durability": null, - "name": "Christmas cracker", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "962" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1900000000", - "durability": null, - "name": "Christmas cracker", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "963" - }, - { - "examine": "A fraction of a roof.", - "durability": null, - "name": "Tile", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "966" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "Used for making notes.", - "grand_exchange_price": "153", - "durability": null, - "name": "Papyrus", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "970" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "153", - "durability": null, - "name": "Papyrus", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "971" - }, - { - "shop_price": "15", - "examine": "Used for making notes.", - "grand_exchange_price": "163", - "durability": null, - "name": "Papyrus", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "972" - }, - { - "shop_price": "45", - "ge_buy_limit": "100", - "examine": "A lump of charcoal.", - "grand_exchange_price": "272", - "durability": null, - "name": "Charcoal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "973" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "272", - "durability": null, - "name": "Charcoal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "974" - }, - { - "shop_price": "52", - "ge_buy_limit": "100", - "examine": "A jungle specific slashing device.", - "durability": null, - "weight": "1.3", - "attack_speed": "5", - "weapon_interface": "6", - "render_anim": "2554", - "equipment_slot": "3", - "grand_exchange_price": "619", - "name": "Machete", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "975", - "bonuses": "0,6,-2,0,0,0,0,0,0,0,0,5,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "619", - "durability": null, - "name": "Machete", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "976" - }, - { - "durability": null, - "name": "Cooking pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "978" - }, - { - "shop_price": "10", - "ge_buy_limit": "2", - "examine": "Used to get out of Thordur's blackhole.", - "grand_exchange_price": "298000000", - "durability": null, - "name": "Disk of returning", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "981" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "298000000", - "durability": null, - "name": "Disk of returning", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "982" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "A key found on the floor of Edgeville Dungeon.", - "grand_exchange_price": "547", - "durability": null, - "name": "Brass key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "983" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "547", - "durability": null, - "name": "Brass key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "984" - }, - { - "ge_buy_limit": "100", - "examine": "The tooth end of the mysterious Crystal key. Can you find the other half?", - "grand_exchange_price": "24900", - "durability": null, - "name": "Tooth half of a key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "985" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24900", - "durability": null, - "name": "Tooth half of a key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "986" - }, - { - "ge_buy_limit": "100", - "examine": "The loop end of the mysterious Crystal key. Can you find the other half?", - "grand_exchange_price": "21100", - "durability": null, - "name": "Loop half of a key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "987" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21100", - "durability": null, - "name": "Loop half of a key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "988" - }, - { - "ge_buy_limit": "100", - "examine": "A mysterious key for a mysterious chest.", - "grand_exchange_price": "48700", - "durability": null, - "name": "Crystal key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "989" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "48700", - "durability": null, - "name": "Crystal key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "990" - }, - { - "ge_buy_limit": "100", - "examine": "It looks like the key to a chest", - "grand_exchange_price": "4971", - "durability": null, - "name": "Muddy key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "991" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4971", - "durability": null, - "name": "Muddy key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "992" - }, - { - "ge_buy_limit": "100", - "examine": "You get a sense of dread from this key.", - "grand_exchange_price": "54600", - "durability": null, - "name": "Sinister key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "993" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "54600", - "durability": null, - "name": "Sinister key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "994" - }, - { - "examine": "Lovely money!", - "durability": null, - "name": "Coins", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "995" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A mostly clean apron.", - "grand_exchange_price": "93", - "durability": null, - "name": "White apron", - "tradeable": "true", - "weight": "0.45", - "archery_ticket_price": "0", - "id": "1005", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "93", - "durability": null, - "name": "White apron", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1006" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A bright red cape.", - "grand_exchange_price": "187", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1007", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1008" - }, - { - "ge_buy_limit": "100", - "examine": "I'd prefer a gold one.", - "grand_exchange_price": "65", - "durability": null, - "name": "Brass necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1009", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "65", - "durability": null, - "name": "Brass necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1010" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "Leg covering favored by women and wizards.", - "grand_exchange_price": "208", - "durability": null, - "name": "Blue skirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1011", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "208", - "durability": null, - "name": "Blue skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1012" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A ladies skirt.", - "grand_exchange_price": "216", - "durability": null, - "name": "Pink skirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1013", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "216", - "durability": null, - "name": "Pink skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1014" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "Clothing favoured by women and dark wizards.", - "grand_exchange_price": "30", - "durability": null, - "name": "Black skirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1015", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "30", - "durability": null, - "name": "Black skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1016" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "grand_exchange_price": "280", - "durability": null, - "name": "Wizard hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": "true", - "id": "1017", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "280", - "durability": null, - "name": "Wizard hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1018" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A warm black cape.", - "grand_exchange_price": "209", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1019", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "209", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1020" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick blue cape.", - "grand_exchange_price": "504", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1021", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "504", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1022" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick yellow cape.", - "grand_exchange_price": "439", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1023", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "439", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1024" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A black piece of cloth on a string.", - "grand_exchange_price": "111", - "durability": null, - "name": "Eyepatch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1025", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "111", - "durability": null, - "name": "Eyepatch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1026" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick green cape.", - "grand_exchange_price": "1061", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1027", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1061", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1028" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick purple cape.", - "grand_exchange_price": "955", - "durability": null, - "name": "Cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1029", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "955", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1030" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick orange cape.", - "durability": null, - "destroy": "true", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1656", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1031", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1656", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1032" - }, - { - "shop_price": "30", - "ge_buy_limit": "2", - "examine": "A robe worn by worshippers of Zamorak.", - "grand_exchange_price": "2481", - "durability": null, - "name": "Zamorak robe", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1033", - "bonuses": "0,0,0,2,0,0,0,0,3,0,0,0,3,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2481", - "durability": null, - "name": "Zamorak robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1034" - }, - { - "shop_price": "40", - "ge_buy_limit": "2", - "examine": "A robe worn by worshippers of Zamorak.", - "durability": null, - "weight": "0.9", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1259", - "name": "Zamorak robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1035", - "bonuses": "0,0,0,2,0,0,0,0,3,0,3,0,3,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1259", - "durability": null, - "name": "Zamorak robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1036" - }, - { - "destroy_message": "You can get a replacement from Diango.", - "shop_price": "1", - "examine": "A rabbit-like adornment.", - "durability": null, - "name": "Bunny ears", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1037", - "equipment_slot": "0" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "Red partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1038", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1500000000", - "durability": null, - "name": "Red partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1039" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "Yellow partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1040", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000000", - "durability": null, - "name": "Yellow partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1041" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "Blue partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1042", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2100000000", - "durability": null, - "name": "Blue partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1043" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "Green partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1044", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1300000000", - "durability": null, - "name": "Green partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1045" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "Purple partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1046", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1000000000", - "durability": null, - "name": "Purple partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1047" - }, - { - "lendable": "true", - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "10", - "durability": null, - "name": "White partyhat", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1048", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2000000000", - "durability": null, - "name": "White partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1049" - }, - { - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "It's a Santa hat.", - "durability": null, - "weight": "0.1", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5", - "name": "Santa hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": "true", - "id": "1050" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "112600000", - "durability": null, - "name": "Santa hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1051" - }, - { - "shop_price": "450", - "examine": "The cape worn by members of the Legends Guild.", - "durability": null, - "name": "Cape of legends", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "1052", - "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0", - "equipment_slot": "1" - }, - { - "remove_head": "true", - "shop_price": "15", - "ge_buy_limit": "2", - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "98000000", - "name": "Green h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1053" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "98000000", - "durability": null, - "name": "Green h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1054" - }, - { - "remove_head": "true", - "shop_price": "15", - "ge_buy_limit": "2", - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "115900000", - "name": "Blue h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1055" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "115900000", - "durability": null, - "name": "Blue h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1056" - }, - { - "remove_head": "true", - "shop_price": "15", - "ge_buy_limit": "2", - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "154300000", - "name": "Red h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1057" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "154300000", - "durability": null, - "name": "Red h'ween mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1058" - }, - { - "shop_price": "6", - "ge_buy_limit": "5000", - "examine": "These will keep my hands warm!", - "grand_exchange_price": "12", - "durability": null, - "name": "Leather gloves", - "tradeable": "true", - "weight": "0.23", - "archery_ticket_price": "0", - "id": "1059", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "12", - "durability": null, - "name": "Leather gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1060" - }, - { - "shop_price": "6", - "ge_buy_limit": "5000", - "examine": "Comfortable leather boots.", - "grand_exchange_price": "144", - "durability": null, - "name": "Leather boots", - "tradeable": "true", - "weight": "0.34", - "archery_ticket_price": "0", - "id": "1061", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "144", - "durability": null, - "name": "Leather boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1062" - }, - { "shop_price": "18", - "ge_buy_limit": "5000", - "examine": "Better than no armour!", - "grand_exchange_price": "13", - "durability": null, - "name": "Leather vambraces", "tradeable": "true", - "weight": "0.22", + "weight": "1.3" + }, + { + "id": "955", + "name": "Rope", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "956", + "name": "Flier", + "examine": "Get your axes from Bob's Axes.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "958", + "name": "Grey wolf fur", + "examine": "This would make warm clothing.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "70", + "tradeable": "true", + "weight": "3" + }, + { + "id": "959", + "name": "Grey wolf fur", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "960", + "name": "Plank", + "examine": "A plank of wood!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "100", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "961", + "name": "Plank", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "962", + "name": "Christmas cracker", + "examine": "I need to pull this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "963", + "name": "Christmas cracker", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "966", + "name": "Tile", + "examine": "A fraction of a roof.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.6" + }, + { + "id": "970", + "name": "Papyrus", + "examine": "Used for making notes.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "971", + "name": "Papyrus", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "972", + "name": "Papyrus", + "examine": "Used for making notes.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "973", + "name": "Charcoal", + "examine": "A lump of charcoal.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "45", + "tradeable": "true" + }, + { + "id": "974", + "name": "Charcoal", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "975", + "name": "Machete", + "examine": "A jungle specific slashing device.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,6,-2,0,0,0,0,0,0,0,0,5,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2554", + "shop_price": "52", + "tradeable": "true", + "weapon_interface": "6", + "weight": "1.3" + }, + { + "id": "976", + "name": "Machete", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "978", + "name": "Cooking pot", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "981", + "name": "Disk of returning", + "examine": "Used to get out of Thordur's blackhole.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "shop_price": "10", + "tradeable": "true", + "weight": "2" + }, + { + "id": "982", + "name": "Disk of returning", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "983", + "name": "Brass key", + "examine": "A key found on the floor of Edgeville Dungeon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "984", + "name": "Brass key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "985", + "name": "Tooth half of a key", + "examine": "The tooth end of the mysterious Crystal key. Can you find the other half?", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "986", + "name": "Tooth half of a key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "987", + "name": "Loop half of a key", + "examine": "The loop end of the mysterious Crystal key. Can you find the other half?", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "988", + "name": "Loop half of a key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "989", + "name": "Crystal key", + "examine": "A mysterious key for a mysterious chest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "990", + "name": "Crystal key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "991", + "name": "Muddy key", + "examine": "It looks like the key to a chest", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "992", + "name": "Muddy key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "993", + "name": "Sinister key", + "examine": "You get a sense of dread from this key.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "994", + "name": "Sinister key", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "995", + "name": "Coins", + "examine": "Lovely money!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1005", + "name": "White apron", + "examine": "A mostly clean apron.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "0.45" + }, + { + "id": "1006", + "name": "White apron", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1007", + "name": "Cape", + "examine": "A bright red cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1008", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1009", + "name": "Brass necklace", + "examine": "I'd prefer a gold one.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1010", + "name": "Brass necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1011", + "name": "Blue skirt", + "examine": "Leg covering favored by women and wizards.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1012", + "name": "Blue skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1013", + "name": "Pink skirt", + "examine": "A ladies skirt.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1014", + "name": "Pink skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1015", + "name": "Black skirt", + "examine": "Clothing favoured by women and dark wizards.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1016", + "name": "Black skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1017", + "name": "Wizard hat", + "examine": "A silly pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "1018", + "name": "Wizard hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1019", + "name": "Cape", + "examine": "A warm black cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1020", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1021", + "name": "Cape", + "examine": "A thick blue cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1022", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1023", + "name": "Cape", + "examine": "A thick yellow cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1024", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1025", + "name": "Eyepatch", + "examine": "A black piece of cloth on a string.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "1026", + "name": "Eyepatch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1027", + "name": "Cape", + "examine": "A thick green cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1028", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1029", + "name": "Cape", + "examine": "A thick purple cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1030", + "name": "Cape", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1031", + "name": "Cape", + "examine": "A thick orange cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1032", + "name": "Cape", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1033", + "name": "Zamorak robe", + "examine": "A robe worn by worshippers of Zamorak.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,3,0,0,0,3,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "shop_price": "30", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1034", + "name": "Zamorak robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1035", + "name": "Zamorak robe", + "examine": "A robe worn by worshippers of Zamorak.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,3,0,3,0,3,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "shop_price": "40", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1036", + "name": "Zamorak robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1037", + "name": "Bunny ears", + "examine": "A rabbit-like adornment.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get a replacement from Diango.", + "durability": null, + "equipment_slot": "0", + "shop_price": "1", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "1038", + "name": "Red partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1039", + "name": "Red partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1040", + "name": "Yellow partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1041", + "name": "Yellow partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1042", + "name": "Blue partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1043", + "name": "Blue partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1044", + "name": "Green partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1045", + "name": "Green partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1046", + "name": "Purple partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1047", + "name": "Purple partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1048", + "name": "White partyhat", + "examine": "A nice hat from a cracker.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1049", + "name": "White partyhat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1050", + "name": "Santa hat", + "examine": "It's a Santa hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": "true", + "lendable": "true", + "shop_price": "1", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1051", + "name": "Santa hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1052", + "name": "Cape of legends", + "examine": "The cape worn by members of the Legends Guild.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "shop_price": "450", + "weight": "1.8" + }, + { + "id": "1053", + "name": "Green h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "1054", + "name": "Green h'ween mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1055", + "name": "Blue h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "1056", + "name": "Blue h'ween mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1057", + "name": "Red h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "1058", + "name": "Red h'ween mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1059", + "name": "Leather gloves", + "examine": "These will keep my hands warm!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "6", + "tradeable": "true", + "weight": "0.23" + }, + { + "id": "1060", + "name": "Leather gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1061", + "name": "Leather boots", + "examine": "Comfortable leather boots.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "5000", + "shop_price": "6", + "tradeable": "true", + "weight": "0.34" + }, + { + "id": "1062", + "name": "Leather boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "1063", - "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "13", - "durability": null, "name": "Leather vambraces", - "tradeable": "true", + "examine": "Better than no armour!", "archery_ticket_price": "0", - "id": "1064" + "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "18", + "tradeable": "true", + "weight": "0.22" }, { + "id": "1064", + "name": "Leather vambraces", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1065", + "name": "Green d'hide vambraces", + "examine": "Made from 100% real dragonhide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,3,2,4,2,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "5000", + "lendable": "true", "requirements": "{4,40}", "shop_price": "2500", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "0.28", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "1407", - "name": "Green d'hide vambraces", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1065", - "bonuses": "0,0,0,-10,8,3,2,4,2,0,0,0,0,0,0" + "weight": "0.28" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1407", - "durability": null, + "id": "1066", "name": "Green d'hide vambraces", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1066" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "280", - "ge_buy_limit": "100", - "examine": "These look pretty heavy.", - "durability": null, - "weight": "9", - "absorb": "1,0,1", - "equipment_slot": "7", - "grand_exchange_price": "55", - "name": "Iron platelegs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1067", - "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "55", - "durability": null, "name": "Iron platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1068" - }, - { - "requirements": "{1,5}", - "shop_price": "1000", - "ge_buy_limit": "100", "examine": "These look pretty heavy.", - "durability": null, - "weight": "9", "absorb": "1,0,1", - "equipment_slot": "7", - "grand_exchange_price": "426", - "name": "Steel platelegs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1069", - "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "426", - "durability": null, - "name": "Steel platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1070" - }, - { - "requirements": "{1,20}", - "shop_price": "2600", - "ge_buy_limit": "100", - "examine": "These look pretty heavy.", - "durability": null, - "weight": "7.7", - "absorb": "1,0,2", - "equipment_slot": "7", - "grand_exchange_price": "1391", - "name": "Mithril platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1071", - "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1391", - "durability": null, - "name": "Mithril platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1072" - }, - { - "requirements": "{1,30}", - "shop_price": "6464", - "ge_buy_limit": "100", - "examine": "These look pretty heavy.", - "durability": null, - "weight": "10", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "3649", - "name": "Adamant platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1073", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3649", - "durability": null, - "name": "Adamant platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1074" - }, - { - "shop_price": "80", - "ge_buy_limit": "100", - "examine": "These look pretty heavy.", - "grand_exchange_price": "34", - "durability": null, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "34", - "durability": null, - "name": "Bronze platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1076" - }, - { - "requirements": "{1,10}", - "shop_price": "1920", - "ge_buy_limit": "100", - "examine": "These look pretty heavy", - "durability": null, - "weight": "9", - "absorb": "1,0,2", - "equipment_slot": "7", - "grand_exchange_price": "4995", - "name": "Black platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1077", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4995", - "durability": null, - "name": "Black platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1078" - }, - { - "requirements": "{1,40}", - "shop_price": "64000", - "ge_buy_limit": "100", - "examine": "These look pretty heavy.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "38500", - "name": "Rune platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1079", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "38500", - "durability": null, - "name": "Rune platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1080" - }, - { - "shop_price": "280", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", - "grand_exchange_price": "86", - "durability": null, - "name": "Iron plateskirt", - "tradeable": "true", - "weight": "8.1", - "archery_ticket_price": "0", - "id": "1081", "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "86", "durability": null, - "name": "Iron plateskirt", + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "280", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1082" + "weight": "9" }, { + "id": "1068", + "name": "Iron platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1069", + "name": "Steel platelegs", + "examine": "These look pretty heavy.", + "absorb": "1,0,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{1,5}", "shop_price": "1000", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", + "tradeable": "true", + "weight": "9" + }, + { + "id": "1070", + "name": "Steel platelegs", + "archery_ticket_price": "0", "durability": null, - "weight": "8.1", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1071", + "name": "Mithril platelegs", + "examine": "These look pretty heavy.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0", + "durability": null, + "equip_audio": "2242", "equipment_slot": "7", - "grand_exchange_price": "514", - "name": "Steel plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1083", - "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "514", - "durability": null, - "name": "Steel plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1084" - }, - { "requirements": "{1,20}", "shop_price": "2600", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", + "tradeable": "true", + "weight": "7.7" + }, + { + "id": "1072", + "name": "Mithril platelegs", + "archery_ticket_price": "0", "durability": null, - "weight": "7.2", - "absorb": "1,0,2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1073", + "name": "Adamant platelegs", + "examine": "These look pretty heavy.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equip_audio": "2242", "equipment_slot": "7", - "grand_exchange_price": "1339", - "name": "Mithril plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1085", - "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1339", - "durability": null, - "name": "Mithril plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1086" - }, - { - "shop_price": "80", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", - "grand_exchange_price": "113", - "durability": null, - "name": "Bronze plateskirt", - "tradeable": "true", - "weight": "8.1", - "archery_ticket_price": "0", - "id": "1087", - "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "113", - "durability": null, - "name": "Bronze plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1088" - }, - { - "requirements": "{1,10}", - "shop_price": "1920", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", - "durability": null, - "weight": "8", - "absorb": "1,0,2", - "equipment_slot": "7", - "grand_exchange_price": "2180", - "name": "Black plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1089", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2180", - "durability": null, - "name": "Black plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1090" - }, - { "requirements": "{1,30}", "shop_price": "6464", - "ge_buy_limit": "100", - "examine": "Designer leg protection.", + "tradeable": "true", + "weight": "10" + }, + { + "id": "1074", + "name": "Adamant platelegs", + "archery_ticket_price": "0", "durability": null, - "weight": "9", - "absorb": "1,0,3", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1075", + "name": "Bronze platelegs", + "examine": "These look pretty heavy.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0", + "durability": null, + "equip_audio": "2242", "equipment_slot": "7", - "grand_exchange_price": "3610", - "name": "Adamant plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1091", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "3610", - "durability": null, - "name": "Adamant plateskirt", + "shop_price": "80", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1092" + "weight": "9" }, { + "id": "1076", + "name": "Bronze platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1077", + "name": "Black platelegs", + "examine": "These look pretty heavy", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,10}", + "shop_price": "1920", + "tradeable": "true", + "weight": "9" + }, + { + "id": "1078", + "name": "Black platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1079", + "name": "Rune platelegs", + "examine": "These look pretty heavy.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{1,40}", "shop_price": "64000", + "tradeable": "true", + "weight": "9" + }, + { + "id": "1080", + "name": "Rune platelegs", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1081", + "name": "Iron plateskirt", "examine": "Designer leg protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0", "durability": null, - "weight": "8", - "absorb": "2,0,4", "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "38100", - "name": "Rune plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1093", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "38100", + "shop_price": "280", + "tradeable": "true", + "weight": "8.1" + }, + { + "id": "1082", + "name": "Iron plateskirt", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1083", + "name": "Steel plateskirt", + "examine": "Designer leg protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,5}", + "shop_price": "1000", + "tradeable": "true", + "weight": "8.1" + }, + { + "id": "1084", + "name": "Steel plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1085", + "name": "Mithril plateskirt", + "examine": "Designer leg protection.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,20}", + "shop_price": "2600", + "tradeable": "true", + "weight": "7.2" + }, + { + "id": "1086", + "name": "Mithril plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1087", + "name": "Bronze plateskirt", + "examine": "Designer leg protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "80", + "tradeable": "true", + "weight": "8.1" + }, + { + "id": "1088", + "name": "Bronze plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1089", + "name": "Black plateskirt", + "examine": "Designer leg protection.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,10}", + "shop_price": "1920", + "tradeable": "true", + "weight": "8" + }, + { + "id": "1090", + "name": "Black plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1091", + "name": "Adamant plateskirt", + "examine": "Designer leg protection.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,30}", + "shop_price": "6464", + "tradeable": "true", + "weight": "9" + }, + { + "id": "1092", + "name": "Adamant plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1093", "name": "Rune plateskirt", - "tradeable": "true", + "examine": "Designer leg protection.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "1094" + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}", + "shop_price": "64000", + "tradeable": "true", + "weight": "8" }, { - "shop_price": "23", - "ge_buy_limit": "5000", - "examine": "Better than no armour!", - "grand_exchange_price": "5", - "durability": null, - "name": "Leather chaps", - "tradeable": "true", - "weight": "3", + "id": "1094", + "name": "Rune plateskirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1095", - "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "5", - "durability": null, "name": "Leather chaps", - "tradeable": "true", + "examine": "Better than no armour!", "archery_ticket_price": "0", - "id": "1096" + "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "5000", + "shop_price": "23", + "tradeable": "true", + "weight": "3" }, { + "id": "1096", + "name": "Leather chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1097", + "name": "Studded chaps", + "examine": "Those studs should provide a bit more protection.", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "1000", "requirements": "{4,20}", "shop_price": "750", - "ge_buy_limit": "1000", - "examine": "Those studs should provide a bit more protection.", + "tradeable": "true", + "weight": "4.5" + }, + { + "id": "1098", + "name": "Studded chaps", + "archery_ticket_price": "0", "durability": null, - "weight": "4.5", - "absorb": "0,2,1", + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1099", + "name": "Green d'hide chaps", + "examine": "100% real dragonhide.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "7", - "grand_exchange_price": "377", - "name": "Studded chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1097", - "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "377", - "durability": null, - "name": "Studded chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1098" - }, - { + "ge_buy_limit": "5000", + "lendable": "true", "requirements": "{4,40}", "shop_price": "3900", - "ge_buy_limit": "5000", - "examine": "100% real dragonhide.", - "durability": null, - "weight": "5.4", - "absorb": "0,4,2", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "2228", - "name": "Green d'hide chaps", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1099", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0" + "weight": "5.4" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2228", - "durability": null, + "id": "1100", "name": "Green d'hide chaps", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1100" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "210", - "ge_buy_limit": "100", - "examine": "A series of connected metal rings.", - "grand_exchange_price": "76", - "durability": null, - "name": "Iron chainbody", - "tradeable": "true", - "weight": "6.8", - "archery_ticket_price": "0", "id": "1101", - "bonuses": "0,0,0,-15,0,10,15,19,-3,12,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "76", - "durability": null, "name": "Iron chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1102" - }, - { - "shop_price": "60", - "ge_buy_limit": "100", "examine": "A series of connected metal rings.", - "grand_exchange_price": "37", - "durability": null, - "name": "Bronze chainbody", - "tradeable": "true", - "weight": "6.8", "archery_ticket_price": "0", - "id": "1103", - "bonuses": "0,0,0,-15,0,7,11,13,-3,9,5,0,0,0,0", - "equipment_slot": "4" - }, - { + "bonuses": "0,0,0,-15,0,10,15,19,-3,12,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", "ge_buy_limit": "100", - "grand_exchange_price": "37", - "durability": null, - "name": "Bronze chainbody", + "shop_price": "210", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1104" + "weight": "6.8" }, { + "id": "1102", + "name": "Iron chainbody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1103", + "name": "Bronze chainbody", + "examine": "A series of connected metal rings.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,7,11,13,-3,9,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "60", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "1104", + "name": "Bronze chainbody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1105", + "name": "Steel chainbody", + "examine": "A series of connected metal rings.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,17,25,30,-3,19,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "requirements": "{1,5}", "shop_price": "750", + "tradeable": "true", + "weight": "6" + }, + { + "id": "1106", + "name": "Steel chainbody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1107", + "name": "Black chainbody", "examine": "A series of connected metal rings.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,22,32,39,-3,24,10,0,0,0,0", "durability": null, - "weight": "6", "equipment_slot": "4", - "grand_exchange_price": "265", - "name": "Steel chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1105", - "bonuses": "0,0,0,-15,0,17,25,30,-3,19,5,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "265", - "durability": null, - "name": "Steel chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1106" - }, - { "requirements": "{1,10}", "shop_price": "1440", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "1108", + "name": "Black chainbody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1109", + "name": "Mithril chainbody", "examine": "A series of connected metal rings.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,25,35,42,-3,27,20,0,0,0,0", "durability": null, - "weight": "6.8", - "absorb": "1,0,2", "equipment_slot": "4", - "grand_exchange_price": "961", - "name": "Black chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1107", - "bonuses": "0,0,0,-15,0,22,32,39,-3,24,10,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "961", - "durability": null, - "name": "Black chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1108" - }, - { "requirements": "{1,20}", "shop_price": "1950", + "tradeable": "true", + "weight": "5.8" + }, + { + "id": "1110", + "name": "Mithril chainbody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1111", + "name": "Adamant chainbody", "examine": "A series of connected metal rings.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,36,50,61,-3,38,30,0,0,0,0", "durability": null, - "weight": "5.8", - "absorb": "1,0,3", "equipment_slot": "4", - "grand_exchange_price": "963", - "name": "Mithril chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1109", - "bonuses": "0,0,0,-15,0,25,35,42,-3,27,20,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "963", - "durability": null, - "name": "Mithril chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1110" - }, - { "requirements": "{1,30}", "shop_price": "4800", + "tradeable": "true", + "weight": "7.7" + }, + { + "id": "1112", + "name": "Adamant chainbody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1113", + "name": "Rune chainbody", "examine": "A series of connected metal rings.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0", "durability": null, - "weight": "7.7", - "absorb": "2,0,4", "equipment_slot": "4", - "grand_exchange_price": "2731", - "name": "Adamant chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1111", - "bonuses": "0,0,0,-15,0,36,50,61,-3,38,30,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "2731", - "durability": null, - "name": "Adamant chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1112" - }, - { + "lendable": "true", "requirements": "{1,40}", "shop_price": "50000", - "ge_buy_limit": "100", - "examine": "A series of connected metal rings.", - "durability": null, - "weight": "6.8", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "29800", - "name": "Rune chainbody", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1113", - "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0" + "weight": "6.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "29800", - "durability": null, + "id": "1114", "name": "Rune chainbody", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1114" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "560", - "ge_buy_limit": "500", - "examine": "Provides excellent protection.", - "durability": null, - "weight": "9.95", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "434", - "name": "Iron platebody", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1115", - "bonuses": "0,0,0,-30,-10,21,20,12,-6,20,5,0,0,0,0" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "434", - "durability": null, "name": "Iron platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1116" - }, - { - "shop_price": "160", - "ge_buy_limit": "500", "examine": "Provides excellent protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,21,20,12,-6,20,5,0,0,0,0", "durability": null, - "weight": "9.5", + "equip_audio": "2239", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "36", - "name": "Bronze platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1117", - "bonuses": "0,0,0,-30,-10,15,14,9,-6,14,5,0,0,0,0" - }, - { "ge_buy_limit": "500", - "grand_exchange_price": "36", - "durability": null, - "name": "Bronze platebody", + "remove_sleeves": "true", + "shop_price": "560", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1118" + "weight": "9.95" }, { + "id": "1116", + "name": "Iron platebody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1117", + "name": "Bronze platebody", + "examine": "Provides excellent protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,15,14,9,-6,14,5,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "500", + "remove_sleeves": "true", + "shop_price": "160", + "tradeable": "true", + "weight": "9.5" + }, + { + "id": "1118", + "name": "Bronze platebody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1119", + "name": "Steel platebody", + "examine": "Provides excellent protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "500", + "remove_sleeves": "true", "requirements": "{1,5}", "shop_price": "2000", + "tradeable": "true", + "weight": "9.9" + }, + { + "id": "1120", + "name": "Steel platebody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1121", + "name": "Mithril platebody", "examine": "Provides excellent protection.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,0,0,0", "durability": null, - "weight": "9.9", + "equip_audio": "2239", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1077", - "name": "Steel platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1119", - "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0" - }, - { "ge_buy_limit": "500", - "grand_exchange_price": "1077", - "durability": null, - "name": "Steel platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1120" - }, - { + "remove_sleeves": "true", "requirements": "{1,20}", "shop_price": "5200", + "tradeable": "true", + "weight": "8.6" + }, + { + "id": "1122", + "name": "Mithril platebody", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1123", + "name": "Adamant platebody", "examine": "Provides excellent protection.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", "durability": null, - "weight": "8.6", - "absorb": "1,0,3", + "equip_audio": "2239", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "2965", - "name": "Mithril platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1121", - "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,0,0,0" - }, - { "ge_buy_limit": "500", - "grand_exchange_price": "2965", - "durability": null, - "name": "Mithril platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1122" - }, - { + "remove_sleeves": "true", "requirements": "{1,30}", "shop_price": "16256", - "ge_buy_limit": "500", - "examine": "Provides excellent protection.", + "tradeable": "true", + "weight": "11" + }, + { + "id": "1124", + "name": "Adamant platebody", + "archery_ticket_price": "0", "durability": null, - "weight": "11", - "absorb": "2,0,4", + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1125", + "name": "Black platebody", + "examine": "Provides Excellent protection.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "9790", - "name": "Adamant platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1123", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0" - }, - { "ge_buy_limit": "500", - "grand_exchange_price": "9790", - "durability": null, - "name": "Adamant platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1124" - }, - { + "remove_sleeves": "true", "requirements": "{1,10}", "shop_price": "3840", - "ge_buy_limit": "500", - "examine": "Provides Excellent protection.", + "tradeable": "true", + "weight": "9.9" + }, + { + "id": "1126", + "name": "Black platebody", + "archery_ticket_price": "0", "durability": null, - "weight": "9.9", - "absorb": "1,0,2", + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1127", + "name": "Rune platebody", + "examine": "Provides excellent protection.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "4915", - "name": "Black platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1125", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "4915", - "durability": null, - "name": "Black platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1126" - }, - { "requirements": "{1,40}", "shop_price": "65000", - "ge_buy_limit": "100", - "examine": "Provides excellent protection.", - "durability": null, - "weight": "9.9", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "38800", - "name": "Rune platebody", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1127", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" + "weight": "9.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "38800", - "durability": null, + "id": "1128", "name": "Rune platebody", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1128" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "1000", - "examine": "Better than no armour!", - "grand_exchange_price": "34", - "durability": null, - "name": "Leather body", - "tradeable": "true", - "weight": "2.7", - "archery_ticket_price": "0", "id": "1129", - "bonuses": "0,0,0,-2,2,8,9,10,4,9,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "34", - "durability": null, "name": "Leather body", - "tradeable": "true", + "examine": "Better than no armour!", "archery_ticket_price": "0", - "id": "1130" + "bonuses": "0,0,0,-2,2,8,9,10,4,9,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "1000", + "shop_price": "20", + "tradeable": "true", + "weight": "2.7" }, { + "id": "1130", + "name": "Leather body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1131", + "name": "Hardleather body", + "examine": "Harder than normal leather.", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-4,8,12,15,18,6,15,10,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "1000", "requirements": "{1,10}", "shop_price": "170", - "ge_buy_limit": "1000", - "examine": "Harder than normal leather.", + "tradeable": "true", + "weight": "3.6" + }, + { + "id": "1132", + "name": "Hardleather body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1133", + "name": "Studded body", + "examine": "These studs should provide a bit more protection.", + "absorb": "0,3,1", + "archery_ticket_price": "150", + "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0", "durability": null, - "weight": "3.6", - "absorb": "0,2,1", "equipment_slot": "4", - "grand_exchange_price": "43", - "name": "Hardleather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1131", - "bonuses": "0,0,0,-4,8,12,15,18,6,15,10,0,0,0,0" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "43", - "durability": null, - "name": "Hardleather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1132" - }, - { "requirements": "{1,20}-{4,20}", "shop_price": "850", - "ge_buy_limit": "1000", - "examine": "These studs should provide a bit more protection.", - "durability": null, - "weight": "5", - "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "414", - "name": "Studded body", "tradeable": "true", - "archery_ticket_price": "150", - "id": "1133", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0" + "weight": "5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "414", - "durability": null, + "id": "1134", "name": "Studded body", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1134" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "1135", + "name": "Green d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "2400", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "5000", + "lendable": "true", "requirements": "{1,40}-{4,40}", "shop_price": "7800", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "6.8", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "4544", - "name": "Green d'hide body", "tradeable": "true", - "archery_ticket_price": "2400", - "id": "1135", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0" + "weight": "6.8" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "4544", - "durability": null, + "id": "1136", "name": "Green d'hide body", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1136" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "1137", + "name": "Iron med helm", + "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "shop_price": "84", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1138", + "name": "Iron med helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1139", + "name": "Bronze med helm", "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,3,4,2,-1,3,0,0,0,0,0", "durability": null, - "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "22", - "name": "Iron med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1137", - "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "22", - "durability": null, - "name": "Iron med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1138" - }, - { "remove_head": "true", "shop_price": "24", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1140", + "name": "Bronze med helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1141", + "name": "Steel med helm", "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,0", "durability": null, - "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "39", - "name": "Bronze med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1139", - "bonuses": "0,0,0,-3,-1,3,4,2,-1,3,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "39", - "durability": null, - "name": "Bronze med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1140" - }, - { "remove_head": "true", "requirements": "{1,5}", "shop_price": "300", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1142", + "name": "Steel med helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1143", + "name": "Mithril med helm", "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,0", "durability": null, - "weight": "1.8", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "52", - "name": "Steel med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1141", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "52", - "durability": null, - "name": "Steel med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1142" - }, - { "remove_head": "true", "requirements": "{1,20}", "shop_price": "780", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "1144", + "name": "Mithril med helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1145", + "name": "Adamant med helm", "examine": "A medium sized helmet.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0", "durability": null, - "weight": "1.3", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "272", - "name": "Mithril med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1143", - "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "272", - "durability": null, - "name": "Mithril med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1144" - }, - { "remove_head": "true", "requirements": "{1,30}", "shop_price": "1920", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1146", + "name": "Adamant med helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1147", + "name": "Rune med helm", "examine": "A medium sized helmet.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,22,23,21,-1,22,7,0,0,0,0", "durability": null, - "weight": "1.8", - "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "1024", - "name": "Adamant med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1145", - "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1024", - "durability": null, - "name": "Adamant med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1146" - }, - { + "lendable": "true", "remove_head": "true", "requirements": "{1,40}", "shop_price": "19200", - "ge_buy_limit": "100", - "examine": "A medium sized helmet.", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1148", + "name": "Rune med helm", + "archery_ticket_price": "0", "durability": null, - "weight": "1.8", - "absorb": "1,0,3", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1149", + "name": "Dragon med helm", + "examine": "Makes the wearer pretty intimidating.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "11400", - "name": "Rune med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1147", - "bonuses": "0,0,0,-3,-1,22,23,21,-1,22,7,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "11400", - "durability": null, - "name": "Rune med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1148" - }, - { + "lendable": "true", "remove_head": "true", "requirements": "{1,60}", - "ge_buy_limit": "100", - "examine": "Makes the wearer pretty intimidating.", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "1150", + "name": "Dragon med helm", + "archery_ticket_price": "0", "durability": null, - "weight": "1.3", - "absorb": "2,0,4", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1151", + "name": "Black med helm", + "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "60500", - "name": "Dragon med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1149", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "60500", - "durability": null, - "name": "Dragon med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1150" - }, - { "remove_head": "true", "requirements": "{1,10}", "shop_price": "530", - "ge_buy_limit": "100", - "examine": "A medium sized helmet.", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "1152", + "name": "Black med helm", + "archery_ticket_price": "0", "durability": null, - "weight": "1.8", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1153", + "name": "Iron full helm", + "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,6,7,5,-1,6,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "242", - "name": "Black med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1151", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,4,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "242", - "durability": null, - "name": "Black med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1152" - }, - { + "remove_beard": "true", "remove_head": "true", "shop_price": "61", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1154", + "name": "Iron full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1155", + "name": "Bronze full helm", "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,4,5,3,-1,4,0,0,0,0,0", "durability": null, - "weight": "2.7", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "56", - "name": "Iron full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1153", - "bonuses": "0,0,0,-6,-2,6,7,5,-1,6,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "56", - "durability": null, - "name": "Iron full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1154" - }, - { + "remove_beard": "true", "remove_head": "true", "shop_price": "44", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1156", + "name": "Bronze full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1157", + "name": "Steel full helm", "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", "durability": null, - "weight": "2.7", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "67", - "name": "Bronze full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1155", - "bonuses": "0,0,0,-6,-2,4,5,3,-1,4,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "67", - "durability": null, - "name": "Bronze full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1156" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,5}", "shop_price": "550", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1158", + "name": "Steel full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1159", + "name": "Mithril full helm", "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,13,14,11,-1,13,5,0,0,0,0", "durability": null, - "weight": "2.7", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "195", - "name": "Steel full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1157", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "195", - "durability": null, - "name": "Steel full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1158" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,20}", "shop_price": "1430", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "1160", + "name": "Mithril full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1161", + "name": "Adamant full helm", "examine": "A full face helmet.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", "durability": null, - "weight": "2.2", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "675", - "name": "Mithril full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1159", - "bonuses": "0,0,0,-6,-2,13,14,11,-1,13,5,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "675", - "durability": null, - "name": "Mithril full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1160" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,30}", "shop_price": "3520", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1162", + "name": "Adamant full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1163", + "name": "Rune full helm", "examine": "A full face helmet.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", "durability": null, - "weight": "2.7", - "absorb": "1,0,2", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "1911", - "name": "Adamant full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1161", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1911", - "durability": null, - "name": "Adamant full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1162" - }, - { + "lendable": "true", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1164", + "name": "Rune full helm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1165", + "name": "Black full helm", "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "remove_beard": "true", + "equip_audio": "2240", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "20900", - "name": "Rune full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1163", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "20900", - "durability": null, - "name": "Rune full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1164" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,10}", "shop_price": "1372", - "ge_buy_limit": "100", - "examine": "A full face helmet.", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1166", + "name": "Black full helm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1167", + "name": "Leather cowl", + "examine": "Better than no armour!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,1,2,3,4,2,3,0,0,0,0,0", "durability": null, - "weight": "2.7", - "remove_beard": "true", "equipment_slot": "0", - "grand_exchange_price": "731", - "name": "Black full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1165", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "731", - "durability": null, - "name": "Black full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1166" - }, - { + "ge_buy_limit": "5000", "remove_head": "true", "shop_price": "24", - "ge_buy_limit": "5000", - "examine": "Better than no armour!", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1168", + "name": "Leather cowl", + "archery_ticket_price": "0", "durability": null, - "weight": "0.9", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1169", + "name": "Coif", + "examine": "Light weight head protection.", + "archery_ticket_price": "100", + "bonuses": "0,0,0,-1,2,4,6,8,4,4,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", "equipment_slot": "0", - "grand_exchange_price": "9", - "name": "Leather cowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1167", - "bonuses": "0,0,0,0,1,2,3,4,2,3,0,0,0,0,0" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "9", - "durability": null, - "name": "Leather cowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1168" - }, - { "remove_head": "true", "requirements": "{4,20}", "shop_price": "200", - "ge_buy_limit": "5000", - "examine": "Light weight head protection.", - "durability": null, - "weight": "0.9", - "equipment_slot": "0", - "grand_exchange_price": "56", - "name": "Coif", "tradeable": "true", - "archery_ticket_price": "100", - "id": "1169", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,5,0,0,0,0" + "weight": "0.9" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "56", - "durability": null, + "id": "1170", "name": "Coif", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1170" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A solid wooden shield.", - "grand_exchange_price": "483", - "durability": null, - "name": "Wooden shield", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1171", - "bonuses": "0,0,0,0,0,4,5,3,1,4,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "483", - "durability": null, "name": "Wooden shield", - "tradeable": "true", + "examine": "A solid wooden shield.", "archery_ticket_price": "0", - "id": "1172" + "bonuses": "0,0,0,0,0,4,5,3,1,4,0,0,0,0,0", + "durability": null, + "equip_audio": "2250", + "equipment_slot": "5", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true" }, { - "shop_price": "48", - "ge_buy_limit": "100", - "examine": "A medium square shield.", - "grand_exchange_price": "12", - "durability": null, - "name": "Bronze sq shield", - "tradeable": "true", - "weight": "3", + "id": "1172", + "name": "Wooden shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1173", - "bonuses": "0,0,0,-6,-2,5,6,4,0,5,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12", - "durability": null, "name": "Bronze sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1174" - }, - { - "shop_price": "168", - "ge_buy_limit": "100", "examine": "A medium square shield.", - "grand_exchange_price": "39", - "durability": null, - "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,5,6,4,0,5,0,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", "ge_buy_limit": "100", - "grand_exchange_price": "39", - "durability": null, - "name": "Iron sq shield", + "shop_price": "48", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1176" + "weight": "3" }, { + "id": "1174", + "name": "Bronze sq shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1175", + "name": "Iron sq shield", + "examine": "A medium square shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,8,9,7,0,8,1,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", + "shop_price": "168", + "tradeable": "true", + "weight": "3.6" + }, + { + "id": "1176", + "name": "Iron sq shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1177", + "name": "Steel sq shield", + "examine": "A medium square shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,12,13,11,0,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{1,5}", "shop_price": "600", + "tradeable": "true", + "weight": "3" + }, + { + "id": "1178", + "name": "Steel sq shield", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1179", + "name": "Black sq shield", "examine": "A medium square shield.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,15,16,14,0,15,9,0,0,0,0", "durability": null, - "weight": "3", + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "185", - "name": "Steel sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1177", - "bonuses": "0,0,0,-6,-2,12,13,11,0,12,4,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "185", - "durability": null, - "name": "Steel sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1178" - }, - { "requirements": "{1,10}", "shop_price": "1463", + "tradeable": "true", + "weight": "4" + }, + { + "id": "1180", + "name": "Black sq shield", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1181", + "name": "Mithril sq shield", "examine": "A medium square shield.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,17,19,15,0,17,18,0,0,0,0", "durability": null, - "weight": "4", - "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "511", - "name": "Black sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1179", - "bonuses": "0,0,0,-6,-2,15,16,14,0,15,9,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "511", - "durability": null, - "name": "Black sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1180" - }, - { "requirements": "{1,20}", "shop_price": "1560", - "ge_buy_limit": "100", - "examine": "A medium square shield.", - "durability": null, - "weight": "3.1", - "absorb": "2,0,4", - "equipment_slot": "5", - "grand_exchange_price": "744", - "name": "Mithril sq shield", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1181", - "bonuses": "0,0,0,-6,-2,17,19,15,0,17,18,0,0,0,0" + "weight": "3.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "744", - "durability": null, + "id": "1182", "name": "Mithril sq shield", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1182" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,30}", - "ge_buy_limit": "100", - "examine": "A medium square shield.", - "durability": null, - "weight": "4", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2136", - "name": "Adamant sq shield", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1183", - "bonuses": "0,0,0,-6,-2,24,26,22,0,24,25,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2136", - "durability": null, "name": "Adamant sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1184" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "100", "examine": "A medium square shield.", - "durability": null, - "weight": "3.64", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "22800", - "name": "Rune sq shield", - "tradeable": "true", + "absorb": "3,0,6", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,24,26,22,0,24,25,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "1184", + "name": "Adamant sq shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1185", - "bonuses": "0,0,0,-6,-2,38,40,36,0,38,35,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "22800", - "durability": null, "name": "Rune sq shield", - "tradeable": "true", + "examine": "A medium square shield.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "1186" - }, - { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "An ancient and powerful looking Dragon Square shield.", + "bonuses": "0,0,0,-6,-2,38,40,36,0,38,35,0,0,0,0", "durability": null, - "weight": "3", - "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", + "ge_buy_limit": "100", "lendable": "true", - "grand_exchange_price": "431900", - "name": "Dragon sq shield", + "requirements": "{1,40}", "tradeable": "true", + "weight": "3.64" + }, + { + "id": "1186", + "name": "Rune sq shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1187", - "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "431900", - "durability": null, "name": "Dragon sq shield", - "tradeable": "true", + "examine": "An ancient and powerful looking Dragon Square shield.", + "absorb": "5,0,11", "archery_ticket_price": "0", - "id": "1188" + "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "10", + "lendable": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "3" }, { - "shop_price": "86", - "ge_buy_limit": "100", - "examine": "A large metal shield.", - "grand_exchange_price": "18", - "durability": null, - "name": "Bronze kiteshield", - "tradeable": "true", - "weight": "5.4", + "id": "1188", + "name": "Dragon sq shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "1189", - "bonuses": "0,0,0,-8,-2,5,7,6,-1,6,1,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, "name": "Bronze kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1190" - }, - { - "shop_price": "233", - "ge_buy_limit": "100", "examine": "A large metal shield.", - "grand_exchange_price": "57", - "durability": null, - "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,5,7,6,-1,6,1,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", "ge_buy_limit": "100", - "grand_exchange_price": "57", - "durability": null, - "name": "Iron kiteshield", + "shop_price": "86", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1192" + "weight": "5.4" }, { + "id": "1190", + "name": "Bronze kiteshield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1191", + "name": "Iron kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,8,10,9,-1,9,2,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", + "shop_price": "233", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "1192", + "name": "Iron kiteshield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1193", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{1,5}", "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "1194", + "name": "Steel kiteshield", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1195", + "name": "Black kiteshield", "examine": "A large metal shield.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", "durability": null, - "weight": "5.4", + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "354", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1193", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "354", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1194" - }, - { "requirements": "{1,10}", "shop_price": "2121", - "ge_buy_limit": "100", - "examine": "A large metal shield.", - "durability": null, - "weight": "5.4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "3311", - "name": "Black kiteshield", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1195", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" + "weight": "5.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3311", - "durability": null, + "id": "1196", "name": "Black kiteshield", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1196" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,20}", - "ge_buy_limit": "100", - "examine": "A large metal shield.", - "durability": null, - "weight": "4.5", - "absorb": "2,0,4", - "equipment_slot": "5", - "grand_exchange_price": "1128", - "name": "Mithril kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1197", - "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1128", - "durability": null, "name": "Mithril kiteshield", - "tradeable": "true", + "examine": "A large metal shield.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "1198" + "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,20}", + "tradeable": "true", + "weight": "4.5" }, { - "requirements": "{1,30}", - "ge_buy_limit": "100", - "examine": "A large metal shield.", - "durability": null, - "weight": "5.8", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3066", - "name": "Adamant kiteshield", - "tradeable": "true", + "id": "1198", + "name": "Mithril kiteshield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1199", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3066", - "durability": null, "name": "Adamant kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1200" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "100", "examine": "A large metal shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", "durability": null, - "weight": "5.4", - "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "35600", - "name": "Rune kiteshield", + "ge_buy_limit": "100", + "requirements": "{1,30}", "tradeable": "true", + "weight": "5.8" + }, + { + "id": "1200", + "name": "Adamant kiteshield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1201", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "35600", - "durability": null, "name": "Rune kiteshield", - "tradeable": "true", + "examine": "A large metal shield.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "1202" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "38", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "35", - "attack_audios": "2517,2517,2500,2517", - "name": "Iron dagger", - "tradeable": "true", + "id": "1202", + "name": "Rune kiteshield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1203", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "35", - "durability": null, "name": "Iron dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1204" - }, - { - "shop_price": "11", - "ge_buy_limit": "100", "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "400,400,401,400", - "grand_exchange_price": "105", "attack_audios": "2517,2517,2500,2517", - "name": "Bronze dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1205", - "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "105", - "durability": null, - "name": "Bronze dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1206" - }, - { - "requirements": "{0,5}", - "shop_price": "135", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", "defence_anim": "378", + "durability": null, + "equip_audio": "2248", "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "17", - "attack_audios": "2517,2517,2500,2517", - "name": "Steel dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1207", - "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "17", - "durability": null, - "name": "Steel dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1208" - }, - { - "requirements": "{0,20}", - "shop_price": "130", - "ge_buy_limit": "100", - "examine": "A dangerous dagger.", - "durability": null, - "weight": "0.3", - "attack_speed": "4", - "weapon_interface": "5", "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "71", - "attack_audios": "2517,2517,2500,2517", - "name": "Mithril dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1209", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "71", - "durability": null, - "name": "Mithril dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1210" - }, - { - "requirements": "{0,30}", - "shop_price": "816", - "ge_buy_limit": "100", - "examine": "Short and deadly.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "300", - "attack_audios": "2517,2517,2500,2517", - "name": "Adamant dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1211", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "300", - "durability": null, - "name": "Adamant dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1212" - }, - { - "requirements": "{0,40}", - "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "lendable": "true", - "grand_exchange_price": "4642", - "attack_audios": "2517,2517,2500,2517", - "name": "Rune dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1213", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4642", - "durability": null, - "name": "Rune dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1214" - }, - { - "requirements": "{0,60}", - "shop_price": "60000", - "ge_buy_limit": "10", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "lendable": "true", - "grand_exchange_price": "17700", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1215", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "17700", - "durability": null, - "name": "Dragon dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1216" - }, - { - "requirements": "{0,10}", - "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious black dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "79", - "attack_audios": "2517,2517,2500,2517", - "name": "Black dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1217", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "79", - "durability": null, - "name": "Black dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1218" - }, - { "shop_price": "38", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "56", - "attack_audios": "2517,2517,2500,2517", - "name": "Iron dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1219", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "56", + "id": "1204", + "name": "Iron dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Iron dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1220" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1205", + "name": "Bronze dagger", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "shop_price": "11", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "86", - "attack_audios": "2517,2517,2500,2517", - "name": "Bronze dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1221", - "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "86", + "id": "1206", + "name": "Bronze dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Bronze dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1222" + "equip_audio": "", + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1207", + "name": "Steel dagger", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,5}", "shop_price": "135", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "98", - "attack_audios": "2517,2517,2500,2517", - "name": "Steel dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1223", - "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "98", + "id": "1208", + "name": "Steel dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Steel dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1224" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1209", + "name": "Mithril dagger", + "examine": "A dangerous dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,20}", "shop_price": "130", - "ge_buy_limit": "100", - "examine": "A dangerous dagger.", - "durability": null, - "weight": "0.3", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "129", - "attack_audios": "2517,2517,2500,2517", - "name": "Mithril dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1225", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0" + "weight": "0.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "129", + "id": "1210", + "name": "Mithril dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Mithril dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1226" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1211", + "name": "Adamant dagger", + "examine": "Short and deadly.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,30}", "shop_price": "816", - "ge_buy_limit": "100", - "examine": "Short and deadly.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "365", - "attack_audios": "2517,2517,2500,2517", - "name": "Adamant dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1227", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "365", + "id": "1212", + "name": "Adamant dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Adamant dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1228" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1213", + "name": "Rune dagger", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "2584", "requirements": "{0,40}", "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "lendable": "true", - "grand_exchange_price": "4831", - "attack_audios": "2517,2517,2500,2517", - "name": "Rune dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1229", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4831", + "id": "1214", + "name": "Rune dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Rune dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1230" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1215", + "name": "Dragon dagger", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "2584", "requirements": "{0,60}", "shop_price": "60000", - "ge_buy_limit": "10", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "lendable": "true", - "grand_exchange_price": "17600", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1231", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "17600", + "id": "1216", + "name": "Dragon dagger", + "archery_ticket_price": "0", "durability": null, - "name": "Dragon dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1232" + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "1217", + "name": "Black dagger", + "examine": "A vicious black dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,10}", "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious black dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "grand_exchange_price": "67", - "attack_audios": "2517,2517,2500,2517", - "name": "Black dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1233", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0" + "weight": "0.4" }, { + "id": "1218", + "name": "Black dagger", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "67", - "durability": null, - "name": "Black dagger(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1234" + "tradeable": "true" }, { + "id": "1219", + "name": "Iron dagger(p)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "defence_anim": "378", "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "38", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1220", + "name": "Iron dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1221", + "name": "Bronze dagger(p)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "11", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1222", + "name": "Bronze dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1223", + "name": "Steel dagger(p)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,5}", + "shop_price": "135", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1224", + "name": "Steel dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1225", + "name": "Mithril dagger(p)", + "examine": "A dangerous dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,20}", + "shop_price": "130", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.3" + }, + { + "id": "1226", + "name": "Mithril dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1227", + "name": "Adamant dagger(p)", + "examine": "Short and deadly.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,30}", + "shop_price": "816", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1228", + "name": "Adamant dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1229", + "name": "Rune dagger(p)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,40}", + "shop_price": "8000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1230", + "name": "Rune dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1231", + "name": "Dragon dagger(p)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,60}", + "shop_price": "60000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1232", + "name": "Dragon dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "1233", + "name": "Black dagger(p)", + "examine": "A vicious black dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,10}", + "shop_price": "240", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "1234", + "name": "Black dagger(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1235", "name": "Poisoned dagger(p)", "archery_ticket_price": "0", - "id": "1235", + "durability": null, "render_anim": "2584" }, { - "durability": null, + "id": "1236", "name": "Poisoned dagger(p)", "archery_ticket_price": "0", - "id": "1236" + "durability": null }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "858", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "1237", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", - "shop_price": "26", - "durability": null, - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Bronze spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "858", - "durability": null, "name": "Bronze spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1238" - }, - { - "ge_buy_limit": "100", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "209", - "durability": null, - "name": "Iron spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1240" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "340", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1241", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "name": "Steel spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "340", - "durability": null, - "name": "Steel spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1242" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "353", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1243", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", - "requirements": "{0,20}", - "durability": null, - "weight": "1.8", - "weapon_interface": "14", - "render_anim": "28", - "name": "Mithril spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "353", - "durability": null, - "name": "Mithril spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1244" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "1191", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1245", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", - "requirements": "{0,30}", - "durability": null, - "weight": "2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Adamant spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1191", - "durability": null, - "name": "Adamant spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1246" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "12200", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1247", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "lendable": "true", - "name": "Rune spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12200", - "durability": null, - "name": "Rune spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1248" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A dragon tipped spear.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "37200", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1249", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "lendable": "true", - "name": "Dragon spear" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "37200", - "durability": null, - "name": "Dragon spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1250", - "defence_anim": "2079" - }, - { - "ge_buy_limit": "100", - "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", + "archery_ticket_price": "0", "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "389", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "defence_anim": "2079", "durability": null, - "name": "Bronze spear(p)", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "26", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1252" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { + "id": "1238", + "name": "Bronze spear", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "turn90cw_anim": "1207", + "tradeable": "true" + }, + { + "id": "1239", + "name": "Iron spear", "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", + "archery_ticket_price": "0", "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "212", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "defence_anim": "2079", "durability": null, - "name": "Iron spear(p)", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1254" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { - "requirements": "{0,5}", + "id": "1240", + "name": "Iron spear", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "turn90cw_anim": "1207", + "tradeable": "true" + }, + { + "id": "1241", + "name": "Steel spear", "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", + "archery_ticket_price": "0", "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "136", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "defence_anim": "2079", "durability": null, - "name": "Steel spear(p)", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1256" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { + "id": "1242", + "name": "Steel spear", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "turn90cw_anim": "1207", + "tradeable": "true" + }, + { + "id": "1243", + "name": "Mithril spear", "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "350", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1257", - "stand_turn_anim": "1209", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", "requirements": "{0,20}", - "durability": null, - "weight": "1.8", - "weapon_interface": "14", - "render_anim": "28", - "name": "Mithril spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "350", - "durability": null, - "name": "Mithril spear(p)", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1258" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" }, { + "id": "1244", + "name": "Mithril spear", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "turn90cw_anim": "1207", + "tradeable": "true" + }, + { + "id": "1245", + "name": "Adamant spear", "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "1004", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1259", - "stand_turn_anim": "1209", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", "requirements": "{0,30}", - "durability": null, - "weight": "2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Adamant spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1004", - "durability": null, - "name": "Adamant spear(p)", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1260" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" }, { + "id": "1246", + "name": "Adamant spear", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "turn90cw_anim": "1207", + "tradeable": "true" + }, + { + "id": "1247", + "name": "Rune spear", "examine": "A rune tipped spear.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "12400", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1261", - "stand_turn_anim": "1209", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", "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", - "render_anim": "28", - "lendable": "true", - "name": "Rune spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12400", - "durability": null, - "name": "Rune spear(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1262" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A dragon tipped spear.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "39700", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1263", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "lendable": "true", - "name": "Dragon spear(p)" + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { + "id": "1248", + "name": "Rune spear", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1249", + "name": "Dragon spear", + "examine": "A dragon tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", "ge_buy_limit": "10", - "grand_exchange_price": "39700", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "1250", + "name": "Dragon spear", + "archery_ticket_price": "0", + "defence_anim": "2079", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "1251", + "name": "Bronze spear(p)", + "examine": "A bronze tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "1252", + "name": "Bronze spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1253", + "name": "Iron spear(p)", + "examine": "An iron tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "1254", + "name": "Iron spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1255", + "name": "Steel spear(p)", + "examine": "A steel tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "1256", + "name": "Steel spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1257", + "name": "Mithril spear(p)", + "examine": "A mithril tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "1258", + "name": "Mithril spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1259", + "name": "Adamant spear(p)", + "examine": "An adamant tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "1260", + "name": "Adamant spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1261", + "name": "Rune spear(p)", + "examine": "A rune tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "1262", + "name": "Rune spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1263", "name": "Dragon spear(p)", - "tradeable": "true", + "examine": "A dragon tipped spear.", "archery_ticket_price": "0", - "id": "1264" + "attack_anims": "428,2081,429,428", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" }, { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "Used for mining.", - "durability": null, - "weight": "2.25", - "attack_speed": "5", - "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "17", - "attack_audios": "2508,2508,2508,2508", - "name": "Bronze pickaxe", - "tradeable": "true", + "id": "1264", + "name": "Dragon spear(p)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "1265", - "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17", - "durability": null, "name": "Bronze pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1266" - }, - { - "shop_price": "140", - "ge_buy_limit": "100", "examine": "Used for mining.", - "durability": null, - "weight": "2.25", - "attack_speed": "5", - "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "401,401,400,401", - "grand_exchange_price": "200", "attack_audios": "2508,2508,2508,2508", - "name": "Iron pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1267", - "bonuses": "5,-2,3,0,0,0,1,0,0,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "200", + "attack_speed": "5", + "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0", + "defence_anim": "403", "durability": null, - "name": "Iron pickaxe", + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "1", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1268" + "weapon_interface": "4", + "weight": "2.25" }, { + "id": "1266", + "name": "Bronze pickaxe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1267", + "name": "Iron pickaxe", + "examine": "Used for mining.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,2508,2508", + "attack_speed": "5", + "bonuses": "5,-2,3,0,0,0,1,0,0,0,0,7,0,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "140", + "tradeable": "true", + "weapon_interface": "4", + "weight": "2.25" + }, + { + "id": "1268", + "name": "Iron pickaxe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1269", + "name": "Steel pickaxe", + "examine": "Used for mining.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,2508,2508", + "attack_speed": "5", + "bonuses": "8,-2,6,0,0,0,1,0,0,0,0,9,0,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,5}", "shop_price": "500", - "ge_buy_limit": "100", - "examine": "Used for mining.", - "durability": null, - "weight": "2.25", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "571", - "attack_audios": "2508,2508,2508,2508", - "name": "Steel pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1269", - "bonuses": "8,-2,6,0,0,0,1,0,0,0,0,9,0,0,0" + "weight": "2.25" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "571", + "id": "1270", + "name": "Steel pickaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Steel pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1270" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1271", + "name": "Adamant pickaxe", + "examine": "Used for mining.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,2508,2508", + "attack_speed": "5", + "bonuses": "17,-2,15,0,0,0,1,0,0,0,0,19,0,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,30}-{14,31}", "shop_price": "3200", - "ge_buy_limit": "100", - "examine": "Used for mining.", - "durability": null, - "weight": "2.7", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "1782", - "attack_audios": "2508,2508,2508,2508", - "name": "Adamant pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1271", - "bonuses": "17,-2,15,0,0,0,1,0,0,0,0,19,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1782", + "id": "1272", + "name": "Adamant pickaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Adamant pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1272" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1273", + "name": "Mithril pickaxe", + "examine": "Used for mining.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,2508,2508", + "attack_speed": "5", + "bonuses": "12,-2,10,0,0,0,1,0,0,0,0,13,0,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,20}", "shop_price": "1300", - "ge_buy_limit": "100", - "examine": "Used for mining.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "611", - "attack_audios": "2508,2508,2508,2508", - "name": "Mithril pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1273", - "bonuses": "12,-2,10,0,0,0,1,0,0,0,0,13,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "611", + "id": "1274", + "name": "Mithril pickaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Mithril pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1274" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1275", + "name": "Rune pickaxe", + "examine": "Used for mining.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,2508,2508", + "attack_speed": "5", + "bonuses": "26,-2,24,0,0,0,1,0,0,0,0,29,0,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{0,40}-{14,41}", "shop_price": "32000", - "ge_buy_limit": "100", - "examine": "Used for mining.", - "durability": null, - "weight": "2.2", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "4", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "lendable": "true", - "grand_exchange_price": "19500", - "attack_audios": "2508,2508,2508,2508", - "name": "Rune pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1275", - "bonuses": "26,-2,24,0,0,0,1,0,0,0,0,29,0,0,0" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "19500", + "id": "1276", + "name": "Rune pickaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Rune pickaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1276" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "13", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", "id": "1277", - "stand_turn_anim": "823", - "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", - "shop_price": "33", - "durability": null, - "weight": "1", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Bronze sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, "name": "Bronze sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1278" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1381", + "run_anim": "824", + "shop_price": "33", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "17", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", + "id": "1278", + "name": "Bronze sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1279", - "stand_turn_anim": "823", - "bonuses": "6,4,-2,0,0,0,2,1,0,0,0,7,0,0,0", - "shop_price": "91", - "durability": null, - "weight": "1.8", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Iron sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17", - "durability": null, "name": "Iron sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1280" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "6,4,-2,0,0,0,2,1,0,0,0,7,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1381", + "run_anim": "824", + "shop_price": "91", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "79", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", + "id": "1280", + "name": "Iron sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1281", - "stand_turn_anim": "823", - "bonuses": "11,8,-2,0,0,0,2,1,0,0,0,12,0,0,0", - "requirements": "{0,5}", - "shop_price": "412", - "durability": null, - "weight": "1", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Steel sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "79", - "durability": null, "name": "Steel sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1282" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "11,8,-2,0,0,0,2,1,0,0,0,12,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1381", + "requirements": "{0,5}", + "run_anim": "824", + "shop_price": "412", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "197", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", + "id": "1282", + "name": "Steel sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1283", - "stand_turn_anim": "823", - "bonuses": "14,10,-2,0,0,0,2,1,0,0,0,12,0,0,0", - "requirements": "{0,10}", - "shop_price": "624", - "durability": null, - "weight": "1", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Black sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "197", - "durability": null, "name": "Black sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1284" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "14,10,-2,0,0,0,2,1,0,0,0,12,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1381", + "requirements": "{0,10}", + "run_anim": "824", + "shop_price": "624", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "313", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", + "id": "1284", + "name": "Black sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1285", - "stand_turn_anim": "823", - "bonuses": "16,11,-2,0,0,0,2,1,0,0,0,17,0,0,0", - "requirements": "{0,20}", - "shop_price": "266", - "durability": null, - "weight": "1.5", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Mithril sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "313", - "durability": null, "name": "Mithril sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1286" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "16,11,-2,0,0,0,2,1,0,0,0,17,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1381", + "requirements": "{0,20}", + "run_anim": "824", + "shop_price": "266", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1.5" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "1046", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", + "id": "1286", + "name": "Mithril sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1287", - "stand_turn_anim": "823", - "bonuses": "23,18,-2,0,0,0,2,1,0,0,0,24,0,0,0", - "requirements": "{0,30}", - "shop_price": "2080", - "durability": null, - "weight": "1", - "weapon_interface": "5", - "render_anim": "1381", - "attack_audios": "2500,2500,2517,2500", - "name": "Adamant sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1046", - "durability": null, "name": "Adamant sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1288" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "381,381,390,381", - "grand_exchange_price": "12300", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "1289", - "stand_turn_anim": "823", - "bonuses": "38,26,-2,0,0,0,2,1,0,0,0,39,0,0,0", - "requirements": "{0,40}", - "shop_price": "20800", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "23,18,-2,0,0,0,2,1,0,0,0,24,0,0,0", + "defence_anim": "397", "durability": null, - "weight": "1.8", - "weapon_interface": "5", + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "1381", - "lendable": "true", - "attack_audios": "2500,2500,2517,2500", - "name": "Rune sword" + "requirements": "{0,30}", + "run_anim": "824", + "shop_price": "2080", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "12300", + "id": "1288", + "name": "Adamant sword", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1289", "name": "Rune sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "1290" + "attack_anims": "381,381,390,381", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "38,26,-2,0,0,0,2,1,0,0,0,39,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "1381", + "requirements": "{0,40}", + "run_anim": "824", + "shop_price": "20800", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "14", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1290", + "name": "Rune sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1291", - "stand_turn_anim": "823", - "bonuses": "4,5,-2,0,0,0,3,2,0,0,0,7,0,0,0", - "shop_price": "23", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Bronze longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "14", - "durability": null, "name": "Bronze longsword", - "tradeable": "true", + "examine": "A razor sharp longsword.", "archery_ticket_price": "0", - "id": "1292" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "4,5,-2,0,0,0,3,2,0,0,0,7,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "run_anim": "824", + "shop_price": "23", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "22", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1292", + "name": "Bronze longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1293", - "stand_turn_anim": "823", - "bonuses": "6,8,-2,0,0,0,3,2,0,0,0,10,0,0,0", - "shop_price": "177", - "durability": null, - "weight": "2", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Iron longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "22", - "durability": null, "name": "Iron longsword", - "tradeable": "true", + "examine": "A razor sharp longsword.", "archery_ticket_price": "0", - "id": "1294" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "6,8,-2,0,0,0,3,2,0,0,0,10,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "run_anim": "824", + "shop_price": "177", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "147", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1294", + "name": "Iron longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1295", - "stand_turn_anim": "823", - "bonuses": "9,14,-2,0,0,0,3,2,0,0,0,16,0,0,0", - "requirements": "{0,5}", - "shop_price": "500", - "durability": null, - "weight": "2", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Steel longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "147", - "durability": null, "name": "Steel longsword", - "tradeable": "true", + "examine": "A razor sharp longsword.", "archery_ticket_price": "0", - "id": "1296" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,0,0,0,16,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,5}", + "run_anim": "824", + "shop_price": "500", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "384", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1296", + "name": "Steel longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1297", - "stand_turn_anim": "823", - "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,0,0,0", - "requirements": "{0,10}", - "shop_price": "960", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Black longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "384", - "durability": null, "name": "Black longsword", - "tradeable": "true", + "examine": "A razor sharp longsword.", "archery_ticket_price": "0", - "id": "1298" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,10}", + "run_anim": "824", + "shop_price": "960", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "602", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1298", + "name": "Black longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1299", - "stand_turn_anim": "823", - "bonuses": "15,20,-2,0,0,0,3,2,0,0,0,22,0,0,0", - "requirements": "{0,20}", - "shop_price": "1074", - "durability": null, - "weight": "1.5", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Mithril longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "602", - "durability": null, "name": "Mithril longsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1300" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "1808", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "15,20,-2,0,0,0,3,2,0,0,0,22,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,20}", + "run_anim": "824", + "shop_price": "1074", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.5" + }, + { + "id": "1300", + "name": "Mithril longsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1301", - "stand_turn_anim": "823", - "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0", - "requirements": "{0,30}", - "shop_price": "3200", - "durability": null, - "weight": "2", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,2500,2517,2500", - "name": "Adamant longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1808", - "durability": null, "name": "Adamant longsword", - "tradeable": "true", + "examine": "A razor sharp longsword.", "archery_ticket_price": "0", - "id": "1302" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,30}", + "run_anim": "824", + "shop_price": "3200", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "19000", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1302", + "name": "Adamant longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1303", - "stand_turn_anim": "823", - "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0", - "requirements": "{0,40}", - "shop_price": "32000", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "lendable": "true", - "attack_audios": "2500,2500,2517,2500", - "name": "Rune longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "19000", - "durability": null, "name": "Rune longsword", - "tradeable": "true", + "examine": "A razor sharp longsword", "archery_ticket_price": "0", - "id": "1304" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A very powerful sword.", - "walk_anim": "1146", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", "attack_anims": "390,390,381,390", - "grand_exchange_price": "60200", - "stand_anim": "809", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1305", - "stand_turn_anim": "823", - "bonuses": "58,69,-2,0,0,0,3,2,0,0,0,71,0,0,0", - "requirements": "{0,60}", - "shop_price": "100000", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1426", - "lendable": "true", "attack_audios": "2500,2500,2517,2500", - "name": "Dragon longsword" + "attack_speed": "5", + "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "1", + "requirements": "{0,40}", + "run_anim": "824", + "shop_price": "32000", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "60200", + "id": "1304", + "name": "Rune longsword", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1305", "name": "Dragon longsword", - "tradeable": "true", + "examine": "A very powerful sword.", "archery_ticket_price": "0", - "id": "1306" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "58,69,-2,0,0,0,3,2,0,0,0,71,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "1426", + "requirements": "{0,60}", + "run_anim": "1210", + "shop_price": "100000", + "stand_anim": "809", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "6", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two-handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "89", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", + "id": "1306", + "name": "Dragon longsword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "1307", - "stand_turn_anim": "7040", - "bonuses": "-4,9,8,-4,0,0,0,0,0,-1,0,10,0,0,0", - "shop_price": "160", - "durability": null, - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Bronze 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "89", - "durability": null, "name": "Bronze 2h sword", - "tradeable": "true", + "examine": "A two-handed sword.", "archery_ticket_price": "0", - "id": "1308" + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,9,8,-4,0,0,0,0,0,-1,0,10,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "run_anim": "7039", + "shop_price": "160", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "32", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", + "id": "1308", + "name": "Bronze 2h sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1309", - "stand_turn_anim": "7040", - "bonuses": "-4,13,10,-4,0,0,0,0,0,-1,0,14,0,0,0", - "shop_price": "260", - "durability": null, - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Iron 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "32", - "durability": null, "name": "Iron 2h sword", - "tradeable": "true", + "examine": "A two handed sword.", "archery_ticket_price": "0", - "id": "1310" + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,13,10,-4,0,0,0,0,0,-1,0,14,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "run_anim": "7039", + "shop_price": "260", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "411", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", + "id": "1310", + "name": "Iron 2h sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1311", - "stand_turn_anim": "7040", - "bonuses": "-4,21,16,-4,0,0,0,0,0,-1,0,22,0,0,0", - "requirements": "{0,5}", - "shop_price": "1000", - "durability": null, - "weight": "3", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Steel 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "411", - "durability": null, "name": "Steel 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1312" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two-handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "1031", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "1313", - "stand_turn_anim": "7040", - "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,26,0,0,0", - "requirements": "{0,10}", - "shop_price": "2400", - "durability": null, - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Black 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1031", - "durability": null, - "name": "Black 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1314" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two-handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "1396", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "1315", - "stand_turn_anim": "7040", - "bonuses": "-4,30,24,-4,0,0,0,0,0,-1,0,31,0,0,0", - "requirements": "{0,20}", - "shop_price": "3000", - "durability": null, - "weight": "3.1", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Mithril 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1396", - "durability": null, - "name": "Mithril 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1316" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two-handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "3577", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "1317", - "stand_turn_anim": "7040", - "bonuses": "-4,43,30,-4,0,0,0,0,0,-1,0,44,0,0,0", - "requirements": "{0,30}", - "shop_price": "6400", - "durability": null, - "weight": "4", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "Adamant 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3577", - "durability": null, - "name": "Adamant 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1318" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", "examine": "A two handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "38200", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "1319", - "stand_turn_anim": "7040", - "bonuses": "-4,69,50,-4,0,0,0,0,0,-1,0,70,0,0,0", - "requirements": "{0,40}", - "shop_price": "40000", - "durability": null, - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", "attack_audios": "2503,0,2504,0", - "name": "Rune 2h sword" + "attack_speed": "7", + "bonuses": "-4,21,16,-4,0,0,0,0,0,-1,0,22,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,5}", + "run_anim": "7039", + "shop_price": "1000", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "38200", + "id": "1312", + "name": "Steel 2h sword", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1313", + "name": "Black 2h sword", + "examine": "A two-handed sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,26,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,10}", + "run_anim": "7039", + "shop_price": "2400", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" + }, + { + "id": "1314", + "name": "Black 2h sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1315", + "name": "Mithril 2h sword", + "examine": "A two-handed sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,30,24,-4,0,0,0,0,0,-1,0,31,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,20}", + "run_anim": "7039", + "shop_price": "3000", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.1" + }, + { + "id": "1316", + "name": "Mithril 2h sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1317", + "name": "Adamant 2h sword", + "examine": "A two-handed sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,43,30,-4,0,0,0,0,0,-1,0,44,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,30}", + "run_anim": "7039", + "shop_price": "6400", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "4" + }, + { + "id": "1318", + "name": "Adamant 2h sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1319", "name": "Rune 2h sword", - "tradeable": "true", + "examine": "A two handed sword.", "archery_ticket_price": "0", - "id": "1320" + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,69,50,-4,0,0,0,0,0,-1,0,70,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "124", + "requirements": "{0,40}", + "run_anim": "7039", + "shop_price": "40000", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "13", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1320", + "name": "Rune 2h sword", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1321", - "stand_turn_anim": "823", - "bonuses": "1,7,-2,0,0,0,1,0,0,0,0,6,0,0,0", - "shop_price": "40", - "durability": null, - "weight": "1", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "Bronze scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, "name": "Bronze scimitar", - "tradeable": "true", + "examine": "A vicious, curved sword.", "archery_ticket_price": "0", - "id": "1322" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "1,7,-2,0,0,0,1,0,0,0,0,6,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "run_anim": "824", + "shop_price": "40", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "30", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1322", + "name": "Bronze scimitar", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1323", - "stand_turn_anim": "823", - "bonuses": "2,10,-2,0,0,0,1,0,0,0,0,9,0,0,0", - "shop_price": "112", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "Iron scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "30", - "durability": null, "name": "Iron scimitar", - "tradeable": "true", + "examine": "A vicious, curved sword.", "archery_ticket_price": "0", - "id": "1324" + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "2,10,-2,0,0,0,1,0,0,0,0,9,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "run_anim": "824", + "shop_price": "112", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "101", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", + "id": "1324", + "name": "Iron scimitar", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1325", - "stand_turn_anim": "823", - "bonuses": "3,15,-2,0,0,0,1,0,0,0,0,14,0,0,0", - "requirements": "{0,5}", - "shop_price": "400", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "Steel scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "101", - "durability": null, "name": "Steel scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1326" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "390,390,381,390", - "grand_exchange_price": "4498", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "1327", - "stand_turn_anim": "823", - "bonuses": "4,19,-2,0,0,0,1,0,0,0,0,14,0,0,0", - "requirements": "{0,10}", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", "attack_audios": "2500,0,2517,0", - "name": "Black scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4498", - "durability": null, - "name": "Black scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1328" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", "attack_speed": "4", - "turn180_anim": "820", + "bonuses": "3,15,-2,0,0,0,1,0,0,0,0,14,0,0,0", "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "469", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "1329", - "stand_turn_anim": "823", - "bonuses": "5,21,-2,0,0,0,1,0,0,0,0,20,0,0,0", - "requirements": "{0,20}", - "shop_price": "1040", "durability": null, - "weight": "1.5", - "weapon_interface": "6", + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "Mithril scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "469", - "durability": null, - "name": "Mithril scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1330" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A viciously curved sword", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "1525", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "1331", - "stand_turn_anim": "823", - "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0", - "requirements": "{0,30}", - "shop_price": "2304", - "durability": null, - "weight": "2", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "Adamant scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1525", - "durability": null, - "name": "Adamant scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1332" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "15200", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "1333", - "stand_turn_anim": "823", - "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0", - "requirements": "{0,40}", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "lendable": "true", - "attack_audios": "2500,0,2517,0", - "name": "Rune scimitar" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15200", - "durability": null, - "name": "Rune scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1334" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "20", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1335", - "stand_turn_anim": "823", - "bonuses": "-4,-4,11,-4,0,0,0,0,0,0,0,9,0,0,0", - "shop_price": "224", - "durability": null, - "weight": "1.8", - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Iron warhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Iron warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1336" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "16", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1337", - "stand_turn_anim": "823", - "bonuses": "-4,-4,10,-4,0,0,0,0,0,0,0,8,0,0,0", - "shop_price": "59", - "durability": null, - "weight": "1", - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Bronze warhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16", - "durability": null, - "name": "Bronze warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1338" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "283", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1339", - "stand_turn_anim": "823", - "bonuses": "-4,-4,18,-4,0,0,0,0,0,0,0,16,0,0,0", "requirements": "{0,5}", - "shop_price": "832", - "durability": null, - "weight": "1", - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Steel warhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "283", - "durability": null, - "name": "Steel warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1340" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "562", + "run_anim": "824", + "shop_price": "400", "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1341", "stand_turn_anim": "823", - "bonuses": "-4,-4,22,-4,0,0,0,0,0,0,0,19,0,0,0", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "1326", + "name": "Steel scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1327", + "name": "Black scimitar", + "examine": "A vicious, curved sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "4,19,-2,0,0,0,1,0,0,0,0,14,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", "requirements": "{0,10}", - "shop_price": "1274", - "durability": null, - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Black warhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "562", - "durability": null, - "name": "Black warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1342" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "1071", + "run_anim": "824", "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1343", "stand_turn_anim": "823", - "bonuses": "-4,-4,25,-4,0,0,0,0,0,0,0,20,0,0,0", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "1328", + "name": "Black scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1329", + "name": "Mithril scimitar", + "examine": "A vicious, curved sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "5,21,-2,0,0,0,1,0,0,0,0,20,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", "requirements": "{0,20}", - "shop_price": "2158", - "durability": null, - "weight": "1.5", - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Mithril warhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1071", - "durability": null, - "name": "Mithril warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1344" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "2996", + "run_anim": "824", + "shop_price": "1040", "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1345", "stand_turn_anim": "823", - "bonuses": "-4,-4,33,-4,0,0,0,0,0,0,0,31,0,0,0", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.5" + }, + { + "id": "1330", + "name": "Mithril scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1331", + "name": "Adamant scimitar", + "examine": "A viciously curved sword", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", "requirements": "{0,30}", - "shop_price": "5356", - "durability": null, - "weight": "2", - "weapon_interface": "10", - "render_anim": "1430", - "attack_audios": "2504,0,0,0", - "name": "Adamant warhammer" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2996", - "durability": null, - "name": "Adamant warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1346" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "I don't think it's intended for joinery.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "24600", + "run_anim": "824", + "shop_price": "2304", "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1347", "stand_turn_anim": "823", - "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0", - "requirements": "{0,40}", - "shop_price": "41000", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "2" + }, + { + "id": "1332", + "name": "Adamant scimitar", + "archery_ticket_price": "0", "durability": null, - "weight": "1.8", - "weapon_interface": "10", - "render_anim": "1430", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1333", + "name": "Rune scimitar", + "examine": "A vicious, curved sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", "lendable": "true", + "render_anim": "1", + "requirements": "{0,40}", + "run_anim": "824", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "1334", + "name": "Rune scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1335", + "name": "Iron warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", "attack_audios": "2504,0,0,0", - "name": "Rune warhammer" + "attack_speed": "6", + "bonuses": "-4,-4,11,-4,0,0,0,0,0,0,0,9,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1430", + "run_anim": "1210", + "shop_price": "224", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "24600", + "id": "1336", + "name": "Iron warhammer", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1337", + "name": "Bronze warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,10,-4,0,0,0,0,0,0,0,8,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1430", + "run_anim": "1210", + "shop_price": "59", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1" + }, + { + "id": "1338", + "name": "Bronze warhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1339", + "name": "Steel warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,18,-4,0,0,0,0,0,0,0,16,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1430", + "requirements": "{0,5}", + "run_anim": "1210", + "shop_price": "832", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1" + }, + { + "id": "1340", + "name": "Steel warhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1341", + "name": "Black warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,22,-4,0,0,0,0,0,0,0,19,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1430", + "requirements": "{0,10}", + "run_anim": "1210", + "shop_price": "1274", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10" + }, + { + "id": "1342", + "name": "Black warhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1343", + "name": "Mithril warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,25,-4,0,0,0,0,0,0,0,20,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1430", + "requirements": "{0,20}", + "run_anim": "1210", + "shop_price": "2158", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1.5" + }, + { + "id": "1344", + "name": "Mithril warhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1345", + "name": "Adamant warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,33,-4,0,0,0,0,0,0,0,31,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "render_anim": "1430", + "requirements": "{0,30}", + "run_anim": "1210", + "shop_price": "5356", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "2" + }, + { + "id": "1346", + "name": "Adamant warhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1347", "name": "Rune warhammer", - "tradeable": "true", + "examine": "I don't think it's intended for joinery.", "archery_ticket_price": "0", - "id": "1348" + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "1430", + "requirements": "{0,40}", + "run_anim": "1210", + "shop_price": "41000", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1.8" }, { - "shop_price": "56", - "ge_buy_limit": "100", - "examine": "A woodcutter's axe.", - "durability": null, - "weight": "1.3", - "attack_speed": "5", - "weapon_interface": "2", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "8", - "name": "Iron axe", - "tradeable": "true", + "id": "1348", + "name": "Rune warhammer", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1349", - "bonuses": "-2,5,3,0,0,0,1,0,0,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, "name": "Iron axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1350" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", "examine": "A woodcutter's axe.", - "durability": null, - "weight": "1.35", - "attack_speed": "5", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "395,395,401,395", - "grand_exchange_price": "44", - "name": "Bronze axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1351", - "bonuses": "-2,4,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "44", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,5,3,0,0,0,1,0,0,0,0,7,0,0,0", + "defence_anim": "397", "durability": null, - "name": "Bronze axe", + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "56", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1352" + "weapon_interface": "2", + "weight": "1.3" }, { + "id": "1350", + "name": "Iron axe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1351", + "name": "Bronze axe", + "examine": "A woodcutter's axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,4,2,0,0,0,1,0,0,0,0,5,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", + "shop_price": "20", + "tradeable": "true", + "weapon_interface": "2", + "weight": "1.35" + }, + { + "id": "1352", + "name": "Bronze axe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1353", + "name": "Steel axe", + "examine": "A woodcutter's axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,8,6,0,0,0,1,0,0,0,0,9,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,5}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A woodcutter's axe.", - "durability": null, - "weight": "1.3", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "36", - "name": "Steel axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1353", - "bonuses": "-2,8,6,0,0,0,1,0,0,0,0,9,0,0,0" + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "36", + "id": "1354", + "name": "Steel axe", + "archery_ticket_price": "0", "durability": null, - "name": "Steel axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1354" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1355", + "name": "Mithril axe", + "examine": "A powerful axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,12,10,0,0,0,1,0,0,0,0,13,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,20}", "shop_price": "660", - "ge_buy_limit": "100", - "examine": "A powerful axe.", - "durability": null, - "weight": "1.1", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "141", - "name": "Mithril axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1355", - "bonuses": "-2,12,10,0,0,0,1,0,0,0,0,13,0,0,0" + "weight": "1.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "141", + "id": "1356", + "name": "Mithril axe", + "archery_ticket_price": "0", "durability": null, - "name": "Mithril axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1356" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1357", + "name": "Adamant axe", + "examine": "A powerful axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,17,15,0,0,0,1,0,0,0,0,19,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,30}-{8,31}", "shop_price": "1625", - "ge_buy_limit": "100", - "examine": "A powerful axe.", - "durability": null, - "weight": "2", - "attack_speed": "5", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "592", - "name": "Adamant axe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1357", - "bonuses": "-2,17,15,0,0,0,1,0,0,0,0,19,0,0,0" + "weapon_interface": "2", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "592", - "durability": null, + "id": "1358", "name": "Adamant axe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1358" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,40}-{8,41}", - "ge_buy_limit": "100", - "examine": "A powerful axe.", - "durability": null, - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "7495", - "name": "Rune axe", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1359", - "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7495", - "durability": null, "name": "Rune axe", - "tradeable": "true", + "examine": "A powerful axe.", "archery_ticket_price": "0", - "id": "1360" + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", + "requirements": "{0,40}-{8,41}", + "tradeable": "true", + "weapon_interface": "2" }, { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "examine": "A sinister looking axe.", - "durability": null, - "weight": "2", - "attack_speed": "5", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "335", - "name": "Black axe", - "tradeable": "true", + "id": "1360", + "name": "Rune axe", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1361", - "bonuses": "-2,10,8,0,0,0,1,0,0,0,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "335", - "durability": null, "name": "Black axe", - "tradeable": "true", + "examine": "A sinister looking axe.", "archery_ticket_price": "0", - "id": "1362" - }, - { - "shop_price": "182", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2.7", - "attack_speed": "6", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", "attack_anims": "395,395,401,395", - "grand_exchange_price": "21", - "name": "Iron battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1363", - "bonuses": "-2,8,5,0,0,0,0,0,0,-1,0,13,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,10,8,0,0,0,1,0,0,0,0,12,0,0,0", + "defence_anim": "397", "durability": null, - "name": "Iron battleaxe", + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", + "requirements": "{0,10}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1364" + "weapon_interface": "2", + "weight": "2" }, { + "id": "1362", + "name": "Black axe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1363", + "name": "Iron battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,8,5,0,0,0,0,0,0,-1,0,13,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", + "shop_price": "182", + "tradeable": "true", + "weapon_interface": "2", + "weight": "2.7" + }, + { + "id": "1364", + "name": "Iron battleaxe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1365", + "name": "Steel battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,16,11,0,0,0,0,0,0,-1,0,20,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,5}", "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2.7", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "208", - "name": "Steel battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1365", - "bonuses": "-2,16,11,0,0,0,0,0,0,-1,0,20,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "208", + "id": "1366", + "name": "Steel battleaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Steel battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1366" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1367", + "name": "Black battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,20,15,0,0,0,0,0,0,-1,0,24,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,10}", "shop_price": "1248", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2.7", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "556", - "name": "Black battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1367", - "bonuses": "-2,20,15,0,0,0,0,0,0,-1,0,24,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "556", + "id": "1368", + "name": "Black battleaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Black battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1368" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1369", + "name": "Mithril battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,22,17,0,0,0,0,0,0,-1,0,29,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,20}", "shop_price": "1690", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "809", - "name": "Mithril battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1369", - "bonuses": "-2,22,17,0,0,0,0,0,0,-1,0,29,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "809", + "id": "1370", + "name": "Mithril battleaxe", + "archery_ticket_price": "0", "durability": null, - "name": "Mithril battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1370" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1371", + "name": "Adamant battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,31,26,0,0,0,0,0,0,-1,0,41,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,30}", "shop_price": "4160", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "3", - "attack_speed": "6", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "grand_exchange_price": "2311", - "name": "Adamant battleaxe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1371", - "bonuses": "-2,31,26,0,0,0,0,0,0,-1,0,41,0,0,0" + "weapon_interface": "2", + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2311", - "durability": null, + "id": "1372", "name": "Adamant battleaxe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1372" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2", - "attack_speed": "6", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "lendable": "true", - "grand_exchange_price": "24800", - "name": "Rune battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1373", - "bonuses": "-2,48,43,0,0,0,0,0,0,-1,0,64,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24800", - "durability": null, "name": "Rune battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1374" - }, - { - "shop_price": "52", - "ge_buy_limit": "100", "examine": "A vicious looking axe.", - "durability": null, - "weight": "2.7", - "attack_speed": "6", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "395,395,401,395", - "grand_exchange_price": "15", - "name": "Bronze battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1375", - "bonuses": "-2,6,3,0,0,0,0,0,0,-1,0,9,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,48,43,0,0,0,0,0,0,-1,0,64,0,0,0", + "defence_anim": "397", "durability": null, - "name": "Bronze battleaxe", + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "2586", + "requirements": "{0,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1376" + "weapon_interface": "2", + "weight": "2" }, { + "id": "1374", + "name": "Rune battleaxe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1375", + "name": "Bronze battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,6,3,0,0,0,0,0,0,-1,0,9,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", + "shop_price": "52", + "tradeable": "true", + "weapon_interface": "2", + "weight": "2.7" + }, + { + "id": "1376", + "name": "Bronze battleaxe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1377", + "name": "Dragon battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "2586", "requirements": "{0,60}", "shop_price": "200000", - "ge_buy_limit": "10", - "examine": "A vicious looking axe.", - "has_special": "true", - "durability": null, - "weight": "2.7", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "lendable": "true", - "grand_exchange_price": "123200", - "name": "Dragon battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1377", - "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0" + "weight": "2.7" }, { - "requirements": "{0,60}", - "ge_buy_limit": "10", - "grand_exchange_price": "123200", - "durability": null, - "name": "Dragon battleaxe", - "tradeable": "true", - "weight": "2.7", - "archery_ticket_price": "0", "id": "1378", - "defence_anim": "397" + "name": "Dragon battleaxe", + "archery_ticket_price": "0", + "defence_anim": "397", + "durability": null, + "ge_buy_limit": "10", + "requirements": "{0,60}", + "tradeable": "true", + "weight": "2.7" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "37", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "1379", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", - "shop_price": "15", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "37", - "durability": null, "name": "Staff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "1380" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "15", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "784", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "1380", + "name": "Staff", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1381", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,10,0,2,3,1,10,0,0,3,0,0,0", - "shop_price": "1500", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Staff of air" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "784", - "durability": null, "name": "Staff of air", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1382" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", "examine": "A Magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "890", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1383", - "stand_turn_anim": "1209", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", "bonuses": "0,-1,7,10,0,2,3,1,10,0,0,3,0,0,0", - "shop_price": "1500", + "defence_anim": "420", "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Staff of water" + "run_anim": "1210", + "shop_price": "1500", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "890", + "id": "1382", + "name": "Staff of air", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1383", "name": "Staff of water", - "tradeable": "true", + "examine": "A Magical staff.", "archery_ticket_price": "0", - "id": "1384" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "0,-1,7,10,0,2,3,1,10,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "1500", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "879", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "1384", + "name": "Staff of water", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1385", - "stand_turn_anim": "1209", - "bonuses": "1,-1,9,10,0,2,3,1,10,0,0,5,0,0,0", - "shop_price": "1500", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Staff of earth" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "879", - "durability": null, "name": "Staff of earth", - "tradeable": "true", + "examine": "A Magical staff.", "archery_ticket_price": "0", - "id": "1386" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "1,-1,9,10,0,2,3,1,10,0,0,5,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "1500", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "1330", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "1386", + "name": "Staff of earth", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1387", - "stand_turn_anim": "1209", - "bonuses": "3,-1,9,10,0,2,3,1,10,0,0,6,0,0,0", - "shop_price": "1500", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Staff of fire" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1330", - "durability": null, "name": "Staff of fire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1388" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", "examine": "A Magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "58", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1389", - "stand_turn_anim": "1209", - "bonuses": "2,-1,10,10,0,2,3,1,10,0,0,7,0,0,0", - "shop_price": "200", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", + "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", - "name": "Magic staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "58", - "durability": null, - "name": "Magic staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1390" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", "attack_speed": "5", - "turn180_anim": "1206", + "bonuses": "3,-1,9,10,0,2,3,1,10,0,0,6,0,0,0", "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "7963", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1391", - "stand_turn_anim": "1209", - "bonuses": "7,-1,25,12,0,2,3,1,12,0,0,32,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "7000", "durability": null, - "weight": "2.25", - "weapon_interface": "1", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Battlestaff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7963", - "durability": null, - "name": "Battlestaff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1392" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "9113", - "stand_anim": "813", - "tradeable": "true", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1393", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "19600", - "durability": null, - "weight": "2.25", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Fire battlestaff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9113", - "durability": null, - "name": "Fire battlestaff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1394" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "9122", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1395", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Water battlestaff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9122", - "durability": null, - "name": "Water battlestaff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1396" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "8982", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1397", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "7000", - "durability": null, - "weight": "2.25", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Air battlestaff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8982", - "durability": null, - "name": "Air battlestaff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1398" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "9041", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1399", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", "shop_price": "1500", - "durability": null, - "weight": "2.2", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Earth battlestaff" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "9041", + "id": "1388", + "name": "Staff of fire", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1389", + "name": "Magic staff", + "examine": "A Magical staff.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "2,-1,10,10,0,2,3,1,10,0,0,7,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "200", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { + "id": "1390", + "name": "Magic staff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1391", + "name": "Battlestaff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,25,12,0,2,3,1,12,0,0,32,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "shop_price": "7000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.25" + }, + { + "id": "1392", + "name": "Battlestaff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1393", + "name": "Fire battlestaff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "shop_price": "19600", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.25" + }, + { + "id": "1394", + "name": "Fire battlestaff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1395", + "name": "Water battlestaff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { + "id": "1396", + "name": "Water battlestaff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1397", + "name": "Air battlestaff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "shop_price": "7000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.25" + }, + { + "id": "1398", + "name": "Air battlestaff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1399", "name": "Earth battlestaff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "1400" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "shop_price": "1500", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "25400", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "1400", + "name": "Earth battlestaff", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1401", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic fire staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25400", - "durability": null, "name": "Mystic fire staff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "1402" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "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", + "id": "1402", + "name": "Mystic fire staff", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1403", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24800", - "durability": null, "name": "Mystic water staff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "1404" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "25100", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "1404", + "name": "Mystic water staff", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1405", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic air staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25100", - "durability": null, "name": "Mystic air staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1406" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "25400", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1407", - "stand_turn_anim": "1209", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", "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.2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic earth staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25400", - "durability": null, - "name": "Mystic earth staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1408" - }, - { - "turn90cw_anim": "1207", - "examine": "An ancient staff, formerly the property of Iban.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", "defence_anim": "420", + "durability": null, + "equip_audio": "2247", "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Iban's staff", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "1409", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0" + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "examine": "An ancient staff, formerly the property of Iban.", + "id": "1406", + "name": "Mystic air staff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1407", + "name": "Mystic earth staff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "defence_anim": "420", "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "shop_price": "40000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { + "id": "1408", + "name": "Mystic earth staff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1409", "name": "Iban's staff", - "weight": "2.2", + "examine": "An ancient staff, formerly the property of Iban.", "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", "attack_speed": "5", + "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { "id": "1410", - "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0" + "name": "Iban's staff", + "examine": "An ancient staff, formerly the property of Iban.", + "archery_ticket_price": "0", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0", + "durability": null, + "weight": "2.2" }, { - "durability": null, + "id": "1412", "name": "Farmer's fork", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1412" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "1413", "name": "Halberd", "archery_ticket_price": "0", "attack_speed": "7", - "id": "1413" + "durability": null }, { - "durability": null, + "id": "1414", "name": "Halberd", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1414" + "durability": null, + "tradeable": "true" }, { + "id": "1415", + "name": "Warhammer", + "archery_ticket_price": "0", "attack_audios": "2504,0,0,0", - "durability": null, - "name": "Warhammer", - "archery_ticket_price": "0", "attack_speed": "6", - "id": "1415" + "durability": null }, { - "durability": null, + "id": "1416", "name": "Warhammer", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1416" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "1417", "name": "Javelin", "archery_ticket_price": "0", "attack_speed": "6", - "id": "1417" + "durability": null }, { - "durability": null, + "id": "1418", "name": "Javelin", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1418" + "durability": null, + "tradeable": "true" }, { - "turn90cw_anim": "821", - "examine": "It's a Scythe.", - "walk_anim": "819", - "durability": null, - "weight": "3", - "turn90ccw_anim": "822", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "22", - "turn180_anim": "820", - "render_anim": "1383", - "defence_anim": "383", - "equipment_slot": "3", - "attack_anims": "382,382,382,382", - "stand_anim": "847", - "name": "Scythe", - "run_anim": "824", - "archery_ticket_price": "0", "id": "1419", + "name": "Scythe", + "examine": "It's a Scythe.", + "archery_ticket_price": "0", + "attack_anims": "440,440,438,382", + "attack_audios": "2524,2524,2522,2524", + "attack_speed": "7", + "bonuses": "3,8,3,0,0,0,3,1,0,0,0,10,0,0,0", + "defence_anim": "383", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "1383", + "run_anim": "824", + "stand_anim": "847", "stand_turn_anim": "823", - "bonuses": "3,8,3,0,0,0,3,1,0,0,0,10,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "819", + "weapon_interface": "22", + "weight": "3" }, { - "shop_price": "80", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "7", - "name": "Iron mace", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1420", - "bonuses": "4,-2,9,0,0,0,0,0,0,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7", - "durability": null, "name": "Iron mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1421" - }, - { - "shop_price": "18", - "ge_buy_limit": "100", "examine": "A spiky mace.", - "durability": null, - "weight": "1.79", - "attack_speed": "5", - "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "401,401,400,401", - "grand_exchange_price": "12", - "name": "Bronze mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1422", - "bonuses": "1,-2,6,0,0,0,0,0,0,0,0,5,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "4,-2,9,0,0,0,0,0,0,0,0,7,1,0,0", + "defence_anim": "403", "durability": null, - "name": "Bronze mace", + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "80", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1423" + "weapon_interface": "8", + "weight": "1.8" }, { + "id": "1421", + "name": "Iron mace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1422", + "name": "Bronze mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "1,-2,6,0,0,0,0,0,0,0,0,5,1,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "18", + "tradeable": "true", + "weapon_interface": "8", + "weight": "1.79" + }, + { + "id": "1423", + "name": "Bronze mace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1424", + "name": "Steel mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "7,-2,13,0,0,0,0,0,0,0,0,11,2,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,5}", "shop_price": "225", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.79", - "attack_speed": "5", - "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "31", - "name": "Steel mace", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1424", - "bonuses": "7,-2,13,0,0,0,0,0,0,0,0,11,2,0,0" + "weapon_interface": "8", + "weight": "1.79" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "31", - "durability": null, + "id": "1425", "name": "Steel mace", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1425" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.79", - "attack_speed": "5", - "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "954", - "name": "Black mace", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1426", - "bonuses": "8,-2,16,0,0,0,0,0,0,0,0,13,2,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "954", - "durability": null, "name": "Black mace", - "tradeable": "true", + "examine": "A spiky mace.", "archery_ticket_price": "0", - "id": "1427" + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "8,-2,16,0,0,0,0,0,0,0,0,13,2,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,10}", + "tradeable": "true", + "weapon_interface": "8", + "weight": "1.79" }, { + "id": "1427", + "name": "Black mace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1428", + "name": "Mithril mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "11,-2,18,0,0,0,0,0,0,0,0,16,3,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,20}", "shop_price": "585", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.5", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "157", - "name": "Mithril mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1428", - "bonuses": "11,-2,18,0,0,0,0,0,0,0,0,16,3,0,0" + "weight": "1.5" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "157", + "id": "1429", + "name": "Mithril mace", + "archery_ticket_price": "0", "durability": null, - "name": "Mithril mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1429" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1430", + "name": "Adamant mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "13,-2,25,0,0,0,0,0,0,0,0,23,3,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,30}", "shop_price": "1440", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "2", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "grand_exchange_price": "673", - "name": "Adamant mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1430", - "bonuses": "13,-2,25,0,0,0,0,0,0,0,0,23,3,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "673", + "id": "1431", + "name": "Adamant mace", + "archery_ticket_price": "0", "durability": null, - "name": "Adamant mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1431" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1432", + "name": "Rune mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,4,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{0,40}", "shop_price": "14400", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.79", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "lendable": "true", - "grand_exchange_price": "8380", - "name": "Rune mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1432", - "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,4,0,0" + "weight": "1.79" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8380", + "id": "1433", + "name": "Rune mace", + "archery_ticket_price": "0", "durability": null, - "name": "Rune mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1433" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "1434", + "name": "Dragon mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "40,-2,60,0,0,0,0,0,0,0,0,55,5,0,0", + "defence_anim": "403", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", "requirements": "{0,60}", "shop_price": "50000", - "ge_buy_limit": "10", - "examine": "A spiky mace.", - "has_special": "true", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,400,401", - "lendable": "true", - "grand_exchange_price": "29300", - "name": "Dragon mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1434", - "bonuses": "40,-2,60,0,0,0,0,0,0,0,0,55,5,0,0" + "weight": "1.8" }, { - "requirements": "{0,60}", + "id": "1435", + "name": "Dragon mace", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "29300", - "durability": null, - "name": "Dragon mace", + "requirements": "{0,60}", "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "1435" + "weight": "1.8" }, { - "shop_price": "1", - "ge_buy_limit": "25000", + "id": "1436", + "name": "Rune essence", "examine": "An uncharged Rune Stone.", - "grand_exchange_price": "40", - "durability": null, - "name": "Rune essence", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1436" - }, - { + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "40", - "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "1437", "name": "Rune essence", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1437" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "shop_price": "50", - "ge_buy_limit": "500", + "id": "1438", + "name": "Air talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "404", - "durability": null, - "name": "Air talisman", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "1438" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "404", - "durability": null, - "name": "Air talisman", + "shop_price": "50", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1439" + "weight": "1" }, { + "id": "1439", + "name": "Air talisman", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1440", + "name": "Earth talisman", + "examine": "A mysterious power emanates from the talisman...", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{9,20}", "shop_price": "50", + "tradeable": "true" + }, + { + "id": "1441", + "name": "Earth talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1442", + "name": "Fire talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "785", - "durability": null, - "name": "Earth talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1440" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "785", - "durability": null, - "name": "Earth talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1441" - }, - { "requirements": "{14,20}", "shop_price": "50", + "tradeable": "true" + }, + { + "id": "1443", + "name": "Fire talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1444", + "name": "Water talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "4140", - "durability": null, - "name": "Fire talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1442" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "4140", - "durability": null, - "name": "Fire talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1443" - }, - { "requirements": "{5,20}", "shop_price": "50", + "tradeable": "true" + }, + { + "id": "1445", + "name": "Water talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1446", + "name": "Body talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "7975", - "durability": null, - "name": "Water talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1444" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "7975", - "durability": null, - "name": "Water talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1445" - }, - { "requirements": "{20,20}", "shop_price": "50", + "tradeable": "true" + }, + { + "id": "1447", + "name": "Body talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1448", + "name": "Mind talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "1373", - "durability": null, - "name": "Body talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1446" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "1373", - "durability": null, - "name": "Body talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1447" - }, - { "requirements": "{2,20}", "shop_price": "50", + "tradeable": "true" + }, + { + "id": "1449", + "name": "Mind talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1450", + "name": "Blood talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "285", - "durability": null, - "name": "Mind talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1448" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "285", - "durability": null, - "name": "Mind talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1449" - }, - { "requirements": "{20,77}", + "tradeable": "true" + }, + { + "id": "1451", + "name": "Blood talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1452", + "name": "Chaos talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "3178", - "durability": null, - "name": "Blood talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1450" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "3178", - "durability": null, - "name": "Blood talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1451" - }, - { "requirements": "{20,35}", "shop_price": "125", + "tradeable": "true" + }, + { + "id": "1453", + "name": "Chaos talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1454", + "name": "Cosmic talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "605", - "durability": null, - "name": "Chaos talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1452" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "605", - "durability": null, - "name": "Chaos talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1453" - }, - { "requirements": "{20,27}", "shop_price": "125", + "tradeable": "true" + }, + { + "id": "1455", + "name": "Cosmic talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1456", + "name": "Death talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "785", - "durability": null, - "name": "Cosmic talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1454" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "785", - "durability": null, - "name": "Cosmic talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1455" - }, - { "requirements": "{20,65}", + "tradeable": "true" + }, + { + "id": "1457", + "name": "Death talisman", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1458", + "name": "Law talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "2501", - "durability": null, - "name": "Death talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1456" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "2501", - "durability": null, - "name": "Death talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1457" - }, - { "requirements": "{20,54}", "shop_price": "125", - "ge_buy_limit": "500", - "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "1219", - "durability": null, - "name": "Law talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1458" + "tradeable": "true" }, { - "ge_buy_limit": "500", - "grand_exchange_price": "1219", - "durability": null, + "id": "1459", "name": "Law talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1459" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "durability": null, + "id": "1461", "name": "Soul talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1461" + "durability": null, + "tradeable": "true" }, { + "id": "1462", + "name": "Nature talisman", + "examine": "A mysterious power emanates from the talisman...", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{20,44}", "shop_price": "125", - "ge_buy_limit": "500", - "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "1596", - "durability": null, - "name": "Nature talisman", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1462" + "tradeable": "true" }, { - "ge_buy_limit": "500", - "grand_exchange_price": "1596", - "durability": null, + "id": "1463", "name": "Nature talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1463" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "I can exchange this for equipment.", - "grand_exchange_price": "9", - "durability": null, + "id": "1464", "name": "Archery ticket", - "tradeable": "true", + "examine": "I can exchange this for equipment.", "archery_ticket_price": "0", - "id": "1464" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "For use on daggers and projectiles.", - "grand_exchange_price": "36", - "durability": null, + "id": "1465", "name": "Weapon poison", - "tradeable": "true", + "examine": "For use on daggers and projectiles.", "archery_ticket_price": "0", - "id": "1465" + "durability": null, + "tradeable": "true" }, { - "examine": "Some damp wooden sticks.", - "durability": null, + "id": "1467", "name": "Damp sticks", + "examine": "Some damp wooden sticks.", "archery_ticket_price": "0", - "id": "1467" + "durability": null }, { - "examine": "Some dry wooden sticks.", - "durability": null, + "id": "1468", "name": "Dry sticks", + "examine": "Some dry wooden sticks.", "archery_ticket_price": "0", - "id": "1468" + "durability": null }, { - "examine": "Smashed glass.", - "durability": null, + "id": "1469", "name": "Broken glass", + "examine": "Smashed glass.", "archery_ticket_price": "0", - "id": "1469" + "durability": null }, { - "ge_buy_limit": "100", + "id": "1470", + "name": "Red bead", "examine": "A small round red bead.", - "grand_exchange_price": "169", - "durability": null, - "name": "Red bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1470" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "169", - "durability": null, + "id": "1471", "name": "Red bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1471" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "1472", + "name": "Yellow bead", "examine": "A small round yellow bead.", - "grand_exchange_price": "249", - "durability": null, - "name": "Yellow bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1472" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "249", - "durability": null, + "id": "1473", "name": "Yellow bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1473" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "1474", + "name": "Black bead", "examine": "A small round black bead.", - "grand_exchange_price": "229", - "durability": null, - "name": "Black bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1474" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "229", - "durability": null, + "id": "1475", "name": "Black bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1475" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "1476", + "name": "White bead", "examine": "A small round white bead.", - "grand_exchange_price": "1530", - "durability": null, - "name": "White bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1476" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1530", - "durability": null, + "id": "1477", "name": "White bead", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1477" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "1000", - "ge_buy_limit": "100", - "examine": "Wizard Mizgog sells these aim-enhancing amulets.", - "grand_exchange_price": "2870", - "durability": null, - "name": "Amulet of accuracy", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1478", - "bonuses": "4,4,4,4,4,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2870", - "durability": null, "name": "Amulet of accuracy", - "tradeable": "true", + "examine": "Wizard Mizgog sells these aim-enhancing amulets.", "archery_ticket_price": "0", - "id": "1479" + "bonuses": "4,4,4,4,4,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "shop_price": "1000", + "tradeable": "true" }, { - "examine": "A magical sphere that glimmers within.", + "id": "1479", + "name": "Amulet of accuracy", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1480", + "name": "Rock", + "examine": "A chunk of rock." + }, + { + "id": "1481", "name": "Orb of light", - "weight": "5", - "archery_ticket_price": "0", - "id": "1481" - }, - { "examine": "A magical sphere that glimmers within.", - "durability": null, - "name": "Orb of light", - "weight": "5", "archery_ticket_price": "0", - "id": "1482" + "durability": null, + "weight": "5" }, { + "id": "1482", + "name": "Orb of light", "examine": "A magical sphere that glimmers within.", - "durability": null, - "name": "Orb of light", - "weight": "5", "archery_ticket_price": "0", - "id": "1483" + "durability": null, + "weight": "5" }, { + "id": "1483", + "name": "Orb of light", "examine": "A magical sphere that glimmers within.", - "durability": null, - "name": "Orb of light", - "weight": "5", "archery_ticket_price": "0", - "id": "1484" + "durability": null, + "weight": "5" }, { - "examine": "A damp, wet cloth.", + "id": "1484", + "name": "Orb of light", + "examine": "A magical sphere that glimmers within.", + "archery_ticket_price": "0", "durability": null, + "weight": "5" + }, + { + "id": "1485", "name": "Damp cloth", + "examine": "A damp, wet cloth.", "archery_ticket_price": "0", - "id": "1485" + "durability": null }, { - "examine": "A broken piece of railing.", - "durability": null, + "id": "1486", "name": "Piece of railing", - "weight": "2", + "examine": "A broken piece of railing.", "archery_ticket_price": "0", - "id": "1486" + "durability": null, + "weight": "2" }, { - "examine": "This horn has restorative properties.", - "grand_exchange_price": "1159", - "durability": null, + "id": "1487", "name": "Unicorn horn", + "examine": "This horn has restorative properties.", + "archery_ticket_price": "0", + "durability": null, "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1487" + "weight": "1" }, { - "examine": "A coat of arms of the Ardougne Paladins.", - "durability": null, + "id": "1488", "name": "Paladin's badge", - "archery_ticket_price": "0", - "id": "1488" - }, - { "examine": "A coat of arms of the Ardougne Paladins.", - "durability": null, - "name": "Paladin's badge", "archery_ticket_price": "0", - "id": "1489" + "durability": null }, { + "id": "1489", + "name": "Paladin's badge", "examine": "A coat of arms of the Ardougne Paladins.", - "durability": null, - "name": "Paladin's badge", "archery_ticket_price": "0", - "id": "1490" + "durability": null }, { - "examine": "On the ground: Curiosity has yet to kill this one...", - "durability": null, + "id": "1490", + "name": "Paladin's badge", + "examine": "A coat of arms of the Ardougne Paladins.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1491", "name": "Witch's cat", - "weight": "1.2", + "examine": "On the ground: Curiosity has yet to kill this one...", "archery_ticket_price": "0", - "id": "1491" + "durability": null, + "weight": "1.2" }, { - "examine": "A simple doll with Iban's likeness.", - "durability": null, + "id": "1492", "name": "Doll of iban", - "weight": "1", + "examine": "A simple doll with Iban's likeness.", "archery_ticket_price": "0", - "id": "1492" + "durability": null, + "weight": "1" }, { - "examine": "An account of the last times of someone.", - "durability": null, + "id": "1493", "name": "Old journal", + "examine": "An account of the last times of someone.", "archery_ticket_price": "0", - "id": "1493" + "durability": null }, { - "examine": "The tale of Iban.", - "durability": null, + "id": "1494", "name": "History of iban", - "weight": "0.2", + "examine": "The tale of Iban.", "archery_ticket_price": "0", - "id": "1494" + "durability": null, + "weight": "0.2" }, { - "shop_price": "5000", - "examine": "Strong dwarvish gloves.", - "durability": null, - "name": "Klank's gauntlets", - "weight": "0.2", - "archery_ticket_price": "0", "id": "1495", + "name": "Klank's gauntlets", + "examine": "Strong dwarvish gloves.", + "archery_ticket_price": "0", "bonuses": "2,2,2,0,0,8,9,7,0,0,5,2,0,0,0", - "equipment_slot": "9" + "durability": null, + "equipment_slot": "9", + "shop_price": "5000", + "weight": "0.2" }, { - "examine": "I thought you only saw these in pairs?", - "durability": null, + "id": "1496", "name": "Iban's dove", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "I thought you only saw these in pairs?", "archery_ticket_price": "0", - "id": "1496" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "A mystical demonic amulet.", - "durability": null, + "id": "1497", "name": "Amulet of othanian", + "examine": "A mystical demonic amulet.", "archery_ticket_price": "0", - "id": "1497" + "durability": null }, { - "examine": "A mystical demonic amulet.", - "durability": null, + "id": "1498", "name": "Amulet of doomion", - "archery_ticket_price": "0", - "id": "1498" - }, - { "examine": "A mystical demonic amulet.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1499", "name": "Amulet of holthion", + "examine": "A mystical demonic amulet.", "archery_ticket_price": "0", - "id": "1499" + "durability": null }, { - "examine": "A strange dark liquid.", - "durability": null, + "id": "1500", "name": "Iban's shadow", + "examine": "A strange dark liquid.", "archery_ticket_price": "0", - "id": "1500" + "durability": null, + "equip_audio": "" }, { - "examine": "Smells stronger than most spirits.", - "durability": null, + "id": "1501", "name": "Dwarf brew", + "examine": "Smells stronger than most spirits.", "archery_ticket_price": "0", - "id": "1501" + "durability": null }, { - "examine": "The burnt remains of Iban.", - "durability": null, + "id": "1502", "name": "Iban's ashes", - "weight": "0.056", + "examine": "The burnt remains of Iban.", "archery_ticket_price": "0", - "id": "1502" + "durability": null, + "weight": "0.056" }, { - "examine": "A search warrant for a house in West Ardougne.", - "durability": null, + "id": "1503", "name": "Warrant", + "examine": "A search warrant for a house in West Ardougne.", "archery_ticket_price": "0", - "id": "1503" + "durability": null }, { - "examine": "It doesn't look very tasty.", - "durability": null, + "id": "1504", "name": "Hangover cure", - "weight": "2", + "examine": "It doesn't look very tasty.", "archery_ticket_price": "0", - "id": "1504" + "durability": null, + "weight": "2" }, { - "examine": "Maybe I should read this...", - "durability": null, + "id": "1505", "name": "A magic scroll", + "examine": "Maybe I should read this...", "archery_ticket_price": "0", - "id": "1505" + "durability": null, + "tradeable": "false" }, { - "remove_head": "true", - "examine": "Stops me from breathing nasty stuff!", - "durability": null, - "name": "Gas mask", - "weight": "0.25", - "archery_ticket_price": "0", "id": "1506", + "name": "Gas mask", + "examine": "Stops me from breathing nasty stuff!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "weight": "0.25" }, { - "examine": "Quite a small key (Plague City).", - "durability": null, + "id": "1507", "name": "A small key", + "examine": "Quite a small key.", "archery_ticket_price": "0", - "id": "1507" + "durability": null }, { - "examine": "It seems to say \"hongorer lure\"...", - "durability": null, + "id": "1508", "name": "A scruffy note", + "examine": "It seems to say \"hongorer lure\"...", "archery_ticket_price": "0", - "id": "1508" + "durability": null }, { - "examine": "A picture of a lady called Elena.", - "durability": null, + "id": "1509", + "name": "Book", + "examine": "Turnip growing for beginners.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1510", "name": "Picture", - "weight": "0.4", + "examine": "A picture of a lady called Elena.", "archery_ticket_price": "0", - "id": "1510" + "durability": null, + "weight": "0.4" }, { - "shop_price": "4", - "ge_buy_limit": "25000", + "id": "1511", + "name": "Logs", "examine": "A number of wooden logs.", - "grand_exchange_price": "120", - "durability": null, - "name": "Logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1511" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "4", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "279", - "durability": null, + "id": "1512", "name": "Logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1512" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "1513", + "name": "Magic logs", "examine": "Logs cut from a magic tree.", - "grand_exchange_price": "680", - "durability": null, - "name": "Magic logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1513" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "1581", - "durability": null, + "id": "1514", "name": "Magic logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1514" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "1515", + "name": "Yew logs", "examine": "Logs cut from a yew tree.", - "grand_exchange_price": "320", - "durability": null, - "name": "Yew logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1515" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "532", - "durability": null, + "id": "1516", "name": "Yew logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1516" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "1517", + "name": "Maple logs", "examine": "Logs cut from a maple tree.", - "grand_exchange_price": "160", - "durability": null, - "name": "Maple logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1517" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "20", - "durability": null, + "id": "1518", "name": "Maple logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1518" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "1519", + "name": "Willow logs", "examine": "Logs cut from a willow tree.", - "grand_exchange_price": "80", - "durability": null, - "name": "Willow logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1519" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "11", - "durability": null, + "id": "1520", "name": "Willow logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1520" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "1521", + "name": "Oak logs", "examine": "Logs cut from an Oak Tree.", - "grand_exchange_price": "140", - "durability": null, - "name": "Oak logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "1521" - }, - { + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "153", - "durability": null, + "tradeable": "true", + "weight": "2" + }, + { + "id": "1522", "name": "Oak logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1522" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "100", + "id": "1523", + "name": "Lockpick", "examine": "For picking tough locks.", - "grand_exchange_price": "1311", - "durability": null, - "name": "Lockpick", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1523" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1311", - "durability": null, - "name": "Lockpick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1524" + "shop_price": "20", + "tradeable": "true" }, { - "examine": "I need to clean this herb before I can use it.", + "id": "1524", + "name": "Lockpick", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1525", "name": "Grimy snake weed", + "examine": "I need to clean this herb before I can use it.", "archery_ticket_price": "0", - "id": "1525" + "durability": null }, { - "examine": "A fresh herb.", - "durability": null, + "id": "1526", "name": "Clean snake weed", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "1526" + "durability": null }, { - "examine": "I need to clean this herb before I can use it.", - "durability": null, + "id": "1527", "name": "Grimy ardrigal", - "weight": "0.2", + "examine": "I need to clean this herb before I can use it.", "archery_ticket_price": "0", - "id": "1527" + "durability": null, + "weight": "0.2" }, { - "examine": "A fresh herb.", - "durability": null, + "id": "1528", "name": "Clean ardrigal", - "weight": "0.2", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "1528" + "durability": null, + "weight": "0.2" }, { - "examine": "I need to clean this herb before I can use it.", - "durability": null, + "id": "1529", "name": "Grimy sito foil", + "examine": "I need to clean this herb before I can use it.", "archery_ticket_price": "0", - "id": "1529" + "durability": null }, { - "examine": "A fresh herb.", - "durability": null, + "id": "1530", "name": "Clean sito foil", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "1530" + "durability": null }, { - "examine": "I need to clean this herb before I can use it.", - "durability": null, + "id": "1531", "name": "Grimy volencia moss", - "archery_ticket_price": "0", - "id": "1531" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean volencia moss", - "archery_ticket_price": "0", - "id": "1532" - }, - { "examine": "I need to clean this herb before I can use it.", - "durability": null, - "name": "Grimy rogue's purse", "archery_ticket_price": "0", - "id": "1533" + "durability": null }, { + "id": "1532", + "name": "Clean volencia moss", "examine": "A fresh herb.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1533", + "name": "Grimy rogue's purse", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1534", "name": "Clean rogue's purse", + "examine": "A fresh herb.", "archery_ticket_price": "0", - "id": "1534" + "durability": null }, { - "examine": "A piece of map.", - "durability": null, + "id": "1535", "name": "Map part", - "archery_ticket_price": "0", - "id": "1535" - }, - { "examine": "A piece of map.", - "durability": null, - "name": "Map part", "archery_ticket_price": "0", - "id": "1536" + "durability": null }, { + "id": "1536", + "name": "Map part", "examine": "A piece of map.", - "durability": null, - "name": "Map part", "archery_ticket_price": "0", - "id": "1537" + "durability": null }, { - "examine": "A map of the route to Crandor.", - "durability": null, + "id": "1537", + "name": "Map part", + "examine": "A piece of map.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1538", "name": "Crandor map", + "examine": "A map of the route to Crandor.", "archery_ticket_price": "0", - "id": "1538" + "durability": null }, { - "shop_price": "52", - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "59", - "durability": null, + "id": "1539", "name": "Steel nails", - "tradeable": "true", + "examine": "Keeps things in place fairly permanently.", "archery_ticket_price": "0", - "id": "1539" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "52", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This provides partial protection from dragon-breath attacks.", - "grand_exchange_price": "637", - "durability": null, - "name": "Anti-dragon shield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "1540", - "bonuses": "0,0,0,0,0,7,9,8,2,8,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "637", - "durability": null, "name": "Anti-dragon shield", - "tradeable": "true", + "examine": "This provides partial protection from dragon-breath attacks.", "archery_ticket_price": "0", - "id": "1541" + "bonuses": "0,0,0,0,0,7,9,8,2,8,0,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "1", - "examine": "A key to Melzar's Maze.", + "id": "1541", + "name": "Anti-dragon shield", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1542", "name": "Maze key", + "examine": "A key to Melzar's Maze.", "archery_ticket_price": "0", - "id": "1542" + "durability": null, + "shop_price": "1" }, { - "destroy_message": "You can get another from Dr Harlow in the Blue Moon Inn of Varrock.", - "examine": "A very pointy stick.", - "durability": null, - "name": "Stake", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "1549", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "examine": "Deters vampires.", - "grand_exchange_price": "20", - "durability": null, - "name": "Garlic", - "tradeable": "true", + "name": "Stake", + "examine": "A very pointy stick.", "archery_ticket_price": "0", - "id": "1550" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Garlic", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1551" - }, - { - "examine": "Sardine flavoured with doogle leaves.", - "durability": null, - "name": "Doogle sardine", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "1552" + "destroy_message": "You can get another from Dr Harlow in the Blue Moon Inn of Varrock.", + "durability": null, + "equipment_slot": "5", + "tradeable": "false" }, { - "examine": "This kitten seems to like you.", + "id": "1550", + "name": "Garlic", + "examine": "Deters vampires.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1551", + "name": "Garlic", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1552", + "name": "Doogle sardine", + "examine": "Sardine flavoured with doogle leaves.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1555", "name": "Pet kitten", - "archery_ticket_price": "0", - "id": "1555" - }, - { "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", "archery_ticket_price": "0", - "id": "1556" + "durability": null }, { + "id": "1556", + "name": "Pet kitten", "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", "archery_ticket_price": "0", - "id": "1557" + "durability": null }, { + "id": "1557", + "name": "Pet kitten", "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", "archery_ticket_price": "0", - "id": "1558" + "durability": null }, { + "id": "1558", + "name": "Pet kitten", "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", "archery_ticket_price": "0", - "id": "1559" + "durability": null }, { + "id": "1559", + "name": "Pet kitten", "examine": "This kitten seems to like you.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1560", "name": "Pet kitten", + "examine": "This kitten seems to like you.", "archery_ticket_price": "0", - "id": "1560" + "durability": null }, { - "examine": "This cat definitely likes you.", - "durability": null, + "id": "1561", "name": "Pet cat", - "archery_ticket_price": "0", - "id": "1561" - }, - { "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", "archery_ticket_price": "0", - "id": "1562" + "durability": null }, { + "id": "1562", + "name": "Pet cat", "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", "archery_ticket_price": "0", - "id": "1563" + "durability": null }, { + "id": "1563", + "name": "Pet cat", "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", "archery_ticket_price": "0", - "id": "1564" + "durability": null }, { + "id": "1564", + "name": "Pet cat", "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", "archery_ticket_price": "0", - "id": "1565" + "durability": null }, { + "id": "1565", + "name": "Pet cat", "examine": "This cat definitely likes you.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1566", "name": "Pet cat", + "examine": "This cat definitely likes you.", "archery_ticket_price": "0", - "id": "1566" + "durability": null }, { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, + "id": "1567", "name": "Overgrown cat", - "archery_ticket_price": "0", - "id": "1567" - }, - { "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", "archery_ticket_price": "0", - "id": "1568" + "durability": null }, { + "id": "1568", + "name": "Overgrown cat", "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", "archery_ticket_price": "0", - "id": "1569" + "durability": null }, { + "id": "1569", + "name": "Overgrown cat", "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", "archery_ticket_price": "0", - "id": "1570" + "durability": null }, { + "id": "1570", + "name": "Overgrown cat", "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", "archery_ticket_price": "0", - "id": "1571" + "durability": null }, { + "id": "1571", + "name": "Overgrown cat", "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", "archery_ticket_price": "0", - "id": "1572" + "durability": null }, { - "examine": "A tasty herb good for seasoning.", - "durability": null, + "id": "1572", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1573", "name": "Doogle leaves", + "examine": "A tasty herb good for seasoning.", "archery_ticket_price": "0", - "id": "1573" + "durability": null }, { - "examine": "For feline training expertise.", - "durability": null, - "name": "Cat training medal", - "archery_ticket_price": "0", "id": "1575", + "name": "Cat training medal", + "examine": "For feline training expertise.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { + "id": "1577", + "name": "Pete's candlestick", "examine": "Candlestick used during Heroes' Quest.", - "durability": null, - "name": "Pete's candlestick", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1577" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "1578", "name": "Pete's candlestick", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1578" + "durability": null, + "tradeable": "true" }, { - "examine": "This denotes a Master Thief.", - "durability": null, + "id": "1579", "name": "Thieves' armband", + "examine": "This denotes a Master Thief.", "archery_ticket_price": "0", - "id": "1579" + "durability": null }, { - "examine": "These will keep my hands cold!", - "durability": null, - "name": "Ice gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "1580", + "name": "Ice gloves", + "examine": "These will keep my hands cold!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,3,4,0,2,2,0,0,0,0", - "equipment_slot": "9" + "durability": null, + "equipment_slot": "9", + "weight": "0.2" }, { - "examine": "Yuck.", - "durability": null, + "id": "1581", "name": "Blamish snail slime", - "weight": "1", + "examine": "Yuck.", "archery_ticket_price": "0", - "id": "1581" + "durability": null, + "weight": "1" }, { - "examine": "Made from the finest snail slime.", - "durability": null, + "id": "1582", "name": "Blamish oil", + "examine": "Made from the finest snail slime.", "archery_ticket_price": "0", - "id": "1582" + "durability": null }, { - "examine": "Firebird feather.", - "durability": null, + "id": "1583", "name": "Fire feather", + "examine": "Firebird feather.", "archery_ticket_price": "0", - "id": "1583" + "durability": null }, { - "examine": "Apparently my name is Hartigan", - "durability": null, + "id": "1584", "name": "Id papers", - "tradeable": "false", + "examine": "Apparently my name is Hartigen.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "1584" + "durability": null, + "tradeable": "false" }, { - "examine": "Useful for catching lava eels.", - "durability": null, + "id": "1585", "name": "Oily fishing rod", - "weight": "1", + "examine": "Useful for catching lava eels.", "archery_ticket_price": "0", - "id": "1585" + "durability": null, + "weight": "1" }, { + "id": "1586", + "name": "Miscellaneous key", "examine": "This is of use in completing Heroes' Quest. (Heroes' Quest)", - "durability": null, - "name": "Miscellaneous key", - "tradeable": "true", - "weight": "0.01", "archery_ticket_price": "0", - "id": "1586" + "durability": null, + "tradeable": "true", + "weight": "0.01" }, { - "durability": null, + "id": "1587", "name": "Miscellaneous key", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1587" + "durability": null, + "tradeable": "true" }, { - "examine": "The key I got from Grip. (Heroes' Quest)", - "durability": null, + "id": "1588", "name": "Grips' key ring", + "examine": "The key I got from Grip. (Heroes' Quest)", "archery_ticket_price": "0", - "id": "1588" + "durability": null }, { - "shop_price": "1", - "examine": "I wonder what this unlocks. (Heroes' Quest)", - "durability": null, + "id": "1590", "name": "Dusty key", + "examine": "I wonder what this unlocks. (Heroes' Quest)", "archery_ticket_price": "0", - "id": "1590" + "durability": null, + "shop_price": "1" }, { - "examine": "Key to a cell. (Heroes' Quest)", - "durability": null, + "id": "1591", "name": "Jail key", + "examine": "Key to a cell. (Heroes' Quest)", "archery_ticket_price": "0", - "id": "1591" + "durability": null }, { - "shop_price": "5", - "ge_buy_limit": "100", + "id": "1592", + "name": "Ring mould", "examine": "Used to make gold rings.", - "grand_exchange_price": "316", - "durability": null, - "name": "Ring mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "1592" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "316", - "durability": null, - "name": "Ring mould", + "shop_price": "5", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1593" + "weight": "0.4" }, { - "examine": "Used to make unholy symbols.", + "id": "1593", + "name": "Ring mould", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1594", "name": "Unholy mould", + "examine": "Used to make unholy symbols.", "archery_ticket_price": "0", - "id": "1594" + "durability": null }, { - "shop_price": "5", - "ge_buy_limit": "100", + "id": "1595", + "name": "Amulet mould", "examine": "Used to make gold amulets.", - "grand_exchange_price": "288", - "durability": null, - "name": "Amulet mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "1595" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "288", - "durability": null, - "name": "Amulet mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1596" - }, - { "shop_price": "5", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1596", + "name": "Amulet mould", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1597", + "name": "Necklace mould", "examine": "Used to make gold necklaces.", - "grand_exchange_price": "269", - "durability": null, - "name": "Necklace mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "1597" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "269", - "durability": null, - "name": "Necklace mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1598" - }, - { "shop_price": "5", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1598", + "name": "Necklace mould", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1599", + "name": "Holy mould", "examine": "Used to make holy symbols of Saradomin.", - "grand_exchange_price": "504", - "durability": null, - "name": "Holy mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "1599" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "504", - "durability": null, + "shop_price": "5", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1600", "name": "Holy mould", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1600" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "500", + "id": "1601", + "name": "Diamond", "examine": "This looks valuable.", - "grand_exchange_price": "14800", - "durability": null, - "name": "Diamond", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1601" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "14800", - "durability": null, - "name": "Diamond", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1602" + "tradeable": "true" }, { + "id": "1602", + "name": "Diamond", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1603", + "name": "Ruby", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", "shop_price": "400", + "tradeable": "true" + }, + { + "id": "1604", + "name": "Ruby", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1605", + "name": "Emerald", "examine": "This looks valuable.", - "grand_exchange_price": "1598", - "durability": null, - "name": "Ruby", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1603" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "1598", - "durability": null, - "name": "Ruby", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1604" - }, - { "shop_price": "350", + "tradeable": "true" + }, + { + "id": "1606", + "name": "Emerald", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1607", + "name": "Sapphire", "examine": "This looks valuable.", - "grand_exchange_price": "853", - "durability": null, - "name": "Emerald", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1605" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "853", - "durability": null, - "name": "Emerald", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1606" - }, - { "shop_price": "175", - "ge_buy_limit": "1000", - "examine": "This looks valuable.", - "grand_exchange_price": "520", - "durability": null, - "name": "Sapphire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1607" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "520", - "durability": null, + "id": "1608", "name": "Sapphire", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1608" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "1609", + "name": "Opal", + "examine": "A semi precious stone.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", "shop_price": "100", - "examine": "A semi precious stone.", - "grand_exchange_price": "44", - "durability": null, - "name": "Opal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1609" + "tradeable": "true" }, { + "id": "1610", + "name": "Opal", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "41", - "durability": null, - "name": "Opal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1610" + "tradeable": "true" }, { + "id": "1611", + "name": "Jade", + "examine": "A semi precious stone.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", "shop_price": "150", - "grand_exchange_price": "84", - "examine": "A semi precious stone.", - "durability": null, - "name": "Jade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1611" + "tradeable": "true" }, { + "id": "1612", + "name": "Jade", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "83", - "durability": null, - "name": "Jade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1612" + "tradeable": "true" }, { + "id": "1613", + "name": "Red topaz", + "examine": "A semi precious stone.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", "shop_price": "200", - "examine": "A semi precious stone.", - "grand_exchange_price": "120", - "durability": null, - "name": "Red topaz", "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1613" + "weight": "0.2" }, { + "id": "1614", + "name": "Red topaz", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "103", - "durability": null, - "name": "Red topaz", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1614" + "tradeable": "true" }, { - "shop_price": "75000", - "ge_buy_limit": "100", + "id": "1615", + "name": "Dragonstone", "examine": "This looks valuable.", - "grand_exchange_price": "27400", - "durability": null, - "name": "Dragonstone", - "tradeable": "true", - "weight": "0.3", "archery_ticket_price": "0", - "id": "1615" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "27400", - "durability": null, - "name": "Dragonstone", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1616" - }, - { - "ge_buy_limit": "500", - "examine": "An uncut diamond.", - "grand_exchange_price": "2160", - "tokkul_price": "300", - "durability": null, - "name": "Uncut diamond", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1617" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "20000", - "durability": null, - "name": "Uncut diamond", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1618" - }, - { - "ge_buy_limit": "1000", - "examine": "An uncut ruby.", - "grand_exchange_price": "1140", - "tokkul_price": "150", - "durability": null, - "name": "Uncut ruby", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1619" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "4970", - "durability": null, - "name": "Uncut ruby", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1620" - }, - { - "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "An uncut emerald.", - "grand_exchange_price": "580", - "tokkul_price": "75", - "durability": null, - "name": "Uncut emerald", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1621" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2621", - "durability": null, - "name": "Uncut emerald", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1622" - }, - { - "shop_price": "25", - "ge_buy_limit": "1000", - "examine": "An uncut sapphire.", - "grand_exchange_price": "420", - "tokkul_price": "37", - "durability": null, - "name": "Uncut sapphire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1623" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1851", - "durability": null, - "name": "Uncut sapphire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1624" - }, - { - "ge_buy_limit": "1000", - "examine": "An uncut opal.", - "grand_exchange_price": "65", - "durability": null, - "name": "Uncut opal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1625" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "65", - "durability": null, - "name": "Uncut opal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1626" - }, - { - "ge_buy_limit": "1000", - "examine": "An uncut jade.", - "grand_exchange_price": "138", - "durability": null, - "name": "Uncut jade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1627" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "138", - "durability": null, - "name": "Uncut jade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1628" - }, - { - "ge_buy_limit": "1000", - "examine": "An uncut red topaz.", - "grand_exchange_price": "279", - "durability": null, - "name": "Uncut red topaz", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1629" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "279", - "durability": null, - "name": "Uncut red topaz", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1630" - }, - { "shop_price": "75000", - "ge_buy_limit": "100", - "examine": "An uncut dragonstone.", - "grand_exchange_price": "31700", - "tokkul_price": "1500", - "durability": null, - "name": "Uncut dragonstone", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1631" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "31700", - "durability": null, - "name": "Uncut dragonstone", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1632" - }, - { - "examine": "This gem is crushed and broken.", - "durability": null, - "name": "Crushed gem", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1633" - }, - { - "durability": null, - "name": "Crushed gem", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1634" - }, - { - "ge_buy_limit": "5000", - "examine": "A valuable ring.", - "grand_exchange_price": "165", - "durability": null, - "name": "Gold ring", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "id": "1635", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "165", - "durability": null, - "name": "Gold ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1636" - }, - { - "ge_buy_limit": "5000", - "examine": "A valuable ring.", - "grand_exchange_price": "561", - "durability": null, - "name": "Sapphire ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1637", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "561", - "durability": null, - "name": "Sapphire ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1638" - }, - { - "ge_buy_limit": "5000", - "examine": "A valuable ring.", - "grand_exchange_price": "884", - "durability": null, - "name": "Emerald ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1639", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "884", - "durability": null, - "name": "Emerald ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1640" - }, - { - "shop_price": "2025", - "ge_buy_limit": "5000", - "examine": "A valuable ring.", - "grand_exchange_price": "1110", - "durability": null, - "name": "Ruby ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1641", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1110", - "durability": null, - "name": "Ruby ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1642" - }, - { - "shop_price": "3172", - "ge_buy_limit": "5000", - "examine": "A valuable ring.", - "grand_exchange_price": "4416", - "durability": null, - "name": "Diamond ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1643", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "4416", - "durability": null, - "name": "Diamond ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1644" - }, - { - "shop_price": "7750", - "ge_buy_limit": "100", - "examine": "A valuable ring.", - "grand_exchange_price": "26700", - "durability": null, - "name": "Dragonstone ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1645", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26700", - "durability": null, - "name": "Dragonstone ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1646" - }, - { - "examine": "A valuable ring.", - "grand_exchange_price": "547", - "durability": null, - "name": "Sapphire ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1649" - }, - { - "examine": "A valuable ring.", - "grand_exchange_price": "993", - "durability": null, - "name": "Emerald ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1650" - }, - { - "shop_price": "2025", - "examine": "A valuable ring.", - "grand_exchange_price": "1125", - "durability": null, - "name": "Ruby ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1651" - }, - { - "shop_price": "3172", - "examine": "A valuable ring.", - "grand_exchange_price": "3622", - "durability": null, - "name": "Diamond ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1652" - }, - { - "shop_price": "7750", - "examine": "A valuable ring.", - "grand_exchange_price": "19606", - "durability": null, - "name": "Dragonstone ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1653" - }, - { - "shop_price": "450", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "185", - "durability": null, - "name": "Gold necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1654", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "185", - "durability": null, - "name": "Gold necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1655" - }, - { - "shop_price": "945", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "534", - "durability": null, - "name": "Sapphire necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1656", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "534", - "durability": null, - "name": "Sapphire necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1657" - }, - { - "shop_price": "1425", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "781", - "durability": null, - "name": "Emerald necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1658", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "781", - "durability": null, - "name": "Emerald necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1659" - }, - { - "shop_price": "870", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Ruby necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1660", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1276", - "durability": null, - "name": "Ruby necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1661" - }, - { - "shop_price": "3307", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "14300", - "durability": null, - "name": "Diamond necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1662", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "14300", - "durability": null, - "name": "Diamond necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1663" - }, - { - "ge_buy_limit": "100", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "26000", - "durability": null, - "name": "Dragon necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1664", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26000", - "durability": null, - "name": "Dragon necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1665" - }, - { - "shop_price": "945", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "532", - "durability": null, - "name": "Sapphire necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1668" - }, - { - "shop_price": "1425", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "741", - "durability": null, - "name": "Emerald necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1669" - }, - { - "shop_price": "870", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "1194", - "durability": null, - "name": "Ruby necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1670" - }, - { - "shop_price": "3307", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "9795", - "durability": null, - "name": "Diamond necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1671" - }, - { - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "23010", - "durability": null, - "name": "Dragon necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1672" - }, - { - "shop_price": "350", - "ge_buy_limit": "5000", - "examine": "A plain gold amulet.", - "grand_exchange_price": "172", - "durability": null, - "name": "Gold amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1673" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "172", - "durability": null, - "name": "Gold amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1674" - }, - { - "shop_price": "810", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "473", - "durability": null, - "name": "Sapphire amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1675" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "473", - "durability": null, - "name": "Sapphire amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1676" - }, - { - "shop_price": "1147", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "695", - "durability": null, - "name": "Emerald amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1677" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "695", - "durability": null, - "name": "Emerald amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1678" - }, - { - "shop_price": "2025", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "1268", - "durability": null, - "name": "Ruby amulet", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1679" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1268", - "durability": null, - "name": "Ruby amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1680" - }, - { - "shop_price": "3307", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "3359", - "durability": null, - "name": "Diamond amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1681" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3359", - "durability": null, - "name": "Diamond amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1682" - }, - { - "ge_buy_limit": "100", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "26900", - "durability": null, - "name": "Dragonstone ammy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1683" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26900", - "durability": null, - "name": "Dragonstone ammy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1684" - }, - { - "shop_price": "810", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "417", - "durability": null, - "name": "Sapphire amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1687" - }, - { - "shop_price": "1147", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "648", - "durability": null, - "name": "Emerald amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1688" - }, - { - "shop_price": "2025", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "1098", - "durability": null, - "name": "Ruby amulet", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1689" - }, - { - "shop_price": "3307", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "1993", - "durability": null, - "name": "Diamond amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1690" - }, - { - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "18436", - "durability": null, - "name": "Dragonstone ammy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1691" - }, - { - "shop_price": "350", - "ge_buy_limit": "5000", - "examine": "A plain gold amulet.", - "grand_exchange_price": "137", - "durability": null, - "name": "Gold amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1692", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "137", - "durability": null, - "name": "Gold amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1693" - }, - { - "shop_price": "810", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "435", - "durability": null, - "name": "Sapphire amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1694", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "435", - "durability": null, - "name": "Sapphire amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1695" - }, - { - "shop_price": "1147", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "659", - "durability": null, - "name": "Emerald amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1696", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "659", - "durability": null, - "name": "Emerald amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1697" - }, - { - "shop_price": "2025", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "1115", - "durability": null, - "name": "Ruby amulet", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1698", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1115", - "durability": null, - "name": "Ruby amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1699" - }, - { - "shop_price": "3307", - "ge_buy_limit": "5000", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "2706", - "durability": null, - "name": "Diamond amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1700", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2706", - "durability": null, - "name": "Diamond amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1701" - }, - { - "ge_buy_limit": "100", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "27100", - "durability": null, - "name": "Dragonstone ammy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1702", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "27100", - "durability": null, - "name": "Dragonstone ammy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1703" - }, - { - "ge_buy_limit": "100", - "examine": "A very powerful dragonstone amulet.", - "grand_exchange_price": "28000", - "durability": null, - "name": "Amulet of glory", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1704", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "28000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1705" - }, - { - "examine": "A dragonstone amulet with 1 magic charge.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1706", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1707" - }, - { - "examine": "A dragonstone amulet with 2 magic charges.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1708", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1709" - }, - { - "examine": "A dragonstone amulet with 3 magic charges.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1710", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1711" - }, - { - "ge_buy_limit": "100", - "examine": "A dragonstone amulet with 4 magic charges.", - "grand_exchange_price": "29300", - "durability": null, - "name": "Amulet of glory(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1712", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "29300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1713" - }, - { - "shop_price": "200", - "ge_buy_limit": "5000", - "examine": "It needs a string so I can wear it.", - "grand_exchange_price": "83", - "durability": null, - "name": "Unstrung symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1714" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "83", - "durability": null, - "name": "Unstrung symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1715" - }, - { - "shop_price": "120", - "ge_buy_limit": "5000", - "examine": "A symbol of Saradomin.", - "grand_exchange_price": "29", - "durability": null, - "name": "Unblessed symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1716", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "29", - "durability": null, - "name": "Unblessed symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1717" - }, - { - "shop_price": "300", - "ge_buy_limit": "5000", - "examine": "A blessed holy symbol of Saradomin.", - "grand_exchange_price": "88", - "durability": null, - "name": "Holy symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1718", - "bonuses": "0,0,0,0,0,2,2,2,2,2,3,0,8,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "88", - "durability": null, - "name": "Holy symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1719" - }, - { - "ge_buy_limit": "5000", - "examine": "It needs a string so I can wear it.", - "grand_exchange_price": "178", - "durability": null, - "name": "Unstrung emblem", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1720" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "178", - "durability": null, - "name": "Unstrung emblem", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1721" - }, - { - "shop_price": "120", - "ge_buy_limit": "5000", - "examine": "An unholy symbol of Zamorak.", - "grand_exchange_price": "24", - "durability": null, - "name": "Unpowered symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1722", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "24", - "durability": null, - "name": "Unpowered symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1723" - }, - { - "shop_price": "135", - "ge_buy_limit": "5000", - "examine": "An unholy symbol of Zamorak.", - "grand_exchange_price": "57", - "durability": null, - "name": "Unholy symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1724", - "bonuses": "2,2,2,2,2,0,0,0,0,0,0,0,8,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "examine": "An enchanted ruby amulet.", - "grand_exchange_price": "1247", - "durability": null, - "name": "Amulet of strength", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1725", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,10,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1247", - "durability": null, - "name": "Amulet of strength", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1726" - }, - { - "shop_price": "900", - "ge_buy_limit": "5000", - "examine": "An enchanted sapphire amulet of magic.", - "grand_exchange_price": "519", - "durability": null, - "name": "Amulet of magic", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1727", - "bonuses": "0,0,0,10,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "519", - "durability": null, - "name": "Amulet of magic", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1728" - }, - { - "shop_price": "1275", - "ge_buy_limit": "5000", - "examine": "An enchanted emerald amulet of protection.", - "grand_exchange_price": "721", - "durability": null, - "name": "Amulet of defence", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1729", - "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "721", - "durability": null, - "name": "Amulet of defence", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1730" - }, - { - "shop_price": "3525", - "ge_buy_limit": "5000", - "examine": "An enchanted diamond amulet of power.", - "grand_exchange_price": "2573", - "durability": null, - "name": "Amulet of power", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1731", - "bonuses": "6,6,6,6,6,6,6,6,6,6,6,6,1,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2573", - "durability": null, - "name": "Amulet of power", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1732" - }, - { - "shop_price": "1", - "ge_buy_limit": "5000", - "examine": "Used with a thread to make clothes.", - "grand_exchange_price": "5", - "durability": null, - "name": "Needle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1733" - }, - { - "shop_price": "4", - "ge_buy_limit": "10000", - "examine": "Use with a needle to make clothes.", - "grand_exchange_price": "7", - "durability": null, - "name": "Thread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1734" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "For shearing sheep.", - "grand_exchange_price": "76", - "durability": null, - "name": "Shears", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1735" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "76", - "durability": null, - "name": "Shears", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1736" - }, - { - "ge_buy_limit": "5000", - "examine": "I think this came from a sheep.", - "grand_exchange_price": "160", - "durability": null, - "name": "Wool", - "tradeable": "true", - "weight": "0.14", - "archery_ticket_price": "0", - "id": "1737" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "160", - "durability": null, - "name": "Wool", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1738" - }, - { - "ge_buy_limit": "10000", - "examine": "I should take this to the tannery.", - "grand_exchange_price": "140", - "durability": null, - "name": "Cowhide", - "tradeable": "true", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "1739" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "516", - "durability": null, - "name": "Cowhide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1740" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of leather.", - "grand_exchange_price": "638", - "durability": null, - "name": "Leather", - "tradeable": "true", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "1741" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "638", - "durability": null, - "name": "Leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1742" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of hard leather.", - "grand_exchange_price": "640", - "durability": null, - "name": "Hard leather", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "1743" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "640", - "durability": null, - "name": "Hard leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1744" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of prepared green dragon hide.", - "grand_exchange_price": "2237", - "durability": null, - "name": "Green d-leather", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "1745" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2237", - "durability": null, - "name": "Green d-leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1746" - }, - { - "ge_buy_limit": "10000", - "examine": "The scaly rough hide from a Black Dragon.", - "grand_exchange_price": "5802", - "durability": null, - "name": "Black dragonhide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "1747" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "5802", - "durability": null, - "name": "Black dragonhide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1748" - }, - { - "ge_buy_limit": "10000", - "examine": "The scaly rough hide from a red dragon.", - "grand_exchange_price": "3539", - "durability": null, - "name": "Red dragonhide", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "1749" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "3539", - "durability": null, - "name": "Red dragonhide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1750" - }, - { - "ge_buy_limit": "10000", - "examine": "The scaly rough hide from a blue dragon.", - "grand_exchange_price": "2473", - "durability": null, - "name": "Blue dragonhide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "1751" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2473", - "durability": null, - "name": "Blue dragonhide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1752" - }, - { - "ge_buy_limit": "10000", - "examine": "The scaly rough hide from a green dragon.", - "grand_exchange_price": "1000", - "durability": null, - "name": "Green dragonhide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "1753" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1933", - "durability": null, - "name": "Green dragonhide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1754" - }, - { - "shop_price": "14", - "ge_buy_limit": "100", - "examine": "Good for detailed crafting.", - "grand_exchange_price": "20", - "durability": null, - "name": "Chisel", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1755" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Chisel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1756" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A mostly clean apron.", - "grand_exchange_price": "74", - "durability": null, - "name": "Brown apron", - "tradeable": "true", - "weight": "0.45", - "archery_ticket_price": "0", - "id": "1757", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "74", - "durability": null, - "name": "Brown apron", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1758" - }, - { - "ge_buy_limit": "5000", - "examine": "Spun from sheeps' wool.", - "grand_exchange_price": "115", - "durability": null, - "name": "Ball of wool", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1759" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "115", - "durability": null, - "name": "Ball of wool", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1760" - }, - { - "ge_buy_limit": "10000", - "examine": "Clay soft enough to mould.", - "grand_exchange_price": "487", - "durability": null, - "name": "Soft clay", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1761" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "487", - "durability": null, - "name": "Soft clay", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1762" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of red dye.", - "grand_exchange_price": "1374", - "durability": null, - "name": "Red dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1763" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1374", - "durability": null, - "name": "Red dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1764" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of yellow dye.", - "grand_exchange_price": "549", - "durability": null, - "name": "Yellow dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1765" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "549", - "durability": null, - "name": "Yellow dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1766" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of blue dye", - "grand_exchange_price": "642", - "durability": null, - "name": "Blue dye", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1767" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "642", - "durability": null, - "name": "Blue dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1768" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of orange dye.", - "grand_exchange_price": "1258", - "durability": null, - "name": "Orange dye", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1769" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1258", - "durability": null, - "name": "Orange dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1770" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of green dye.", - "grand_exchange_price": "503", - "durability": null, - "name": "Green dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1771" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "503", - "durability": null, - "name": "Green dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1772" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A little bottle of purple dye.", - "grand_exchange_price": "288", - "durability": null, - "name": "Purple dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1773" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "288", - "durability": null, - "name": "Purple dye", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1774" - }, - { - "ge_buy_limit": "10000", - "examine": "Hot glass ready to be blown into useful objects.", - "grand_exchange_price": "924", - "durability": null, - "name": "Molten glass", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1775" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "924", - "durability": null, - "name": "Molten glass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1776" - }, - { - "ge_buy_limit": "10000", - "examine": "I need a bow stave to attach this to.", - "grand_exchange_price": "240", - "durability": null, - "name": "Bow string", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1777" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "223", - "durability": null, - "name": "Bow string", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1778" - }, - { - "shop_price": "73", - "ge_buy_limit": "25000", - "examine": "A plant cultivated for fibres (ground); I should use this with a spinning wheel (item).", - "grand_exchange_price": "73", - "durability": null, - "name": "Flax", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1779" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "73", - "durability": null, - "name": "Flax", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1780" - }, - { - "ge_buy_limit": "10000", - "examine": "One of the ingredients for making glass.", - "grand_exchange_price": "575", - "durability": null, - "name": "Soda ash", - "tradeable": "true", - "weight": "0.09", - "archery_ticket_price": "0", - "id": "1781" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "575", - "durability": null, - "name": "Soda ash", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1782" - }, - { - "ge_buy_limit": "10000", - "examine": "One of the ingredients for making glass.", - "grand_exchange_price": "265", - "durability": null, - "name": "Bucket of sand", - "tradeable": "true", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "1783" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "265", - "durability": null, - "name": "Bucket of sand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1784" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Used to form molten glass into useful items.", - "grand_exchange_price": "195", - "durability": null, - "name": "Glassblowing pipe", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1785" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "195", - "durability": null, - "name": "Glassblowing pipe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1786" - }, - { - "ge_buy_limit": "100", - "examine": "I need to put this in a pottery oven.", - "grand_exchange_price": "53", - "durability": null, - "name": "Unfired pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1787" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "53", - "durability": null, - "name": "Unfired pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1788" - }, - { - "ge_buy_limit": "100", - "examine": "I need to put this in a pottery oven.", - "grand_exchange_price": "96", - "durability": null, - "name": "Unfired pie dish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1789" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "96", - "durability": null, - "name": "Unfired pie dish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1790" - }, - { - "shop_price": "32", - "ge_buy_limit": "100", - "examine": "I need to put this in a pottery oven.", - "grand_exchange_price": "194", - "durability": null, - "name": "Unfired bowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1791" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "194", - "durability": null, - "name": "Unfired bowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1792" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A slightly bluish leaf.", - "grand_exchange_price": "57", - "durability": null, - "name": "Woad leaf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1793" - }, - { - "ge_buy_limit": "100", - "examine": "Useful for crafting items.", - "grand_exchange_price": "249", - "durability": null, - "name": "Bronze wire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1794" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "249", - "durability": null, - "name": "Bronze wire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1795" - }, - { - "examine": "Anna's shiny silver coated necklace.", - "durability": null, - "name": "Silver necklace", - "weight": "1", - "archery_ticket_price": "0", - "id": "1796", - "equipment_slot": "2" - }, - { - "examine": "Anna's shiny silver coated necklace.", - "durability": null, - "name": "Silver necklace", - "weight": "1", - "archery_ticket_price": "0", - "id": "1797", - "equipment_slot": "2" - }, - { - "examine": "Bob's shiny silver coated tea cup.", - "durability": null, - "name": "Silver cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1798" - }, - { - "examine": "Bob's shiny silver coated tea cup.", - "durability": null, - "name": "Silver cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1799" - }, - { - "examine": "Carol's shiny silver coated bottle.", - "durability": null, - "name": "Silver bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1800" - }, - { - "examine": "Carol's shiny silver coated bottle.", - "durability": null, - "name": "Silver bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1801" - }, - { - "examine": "David's shiny silver coated book.", - "durability": null, - "name": "Silver book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1802" - }, - { - "examine": "David's shiny silver coated book.", - "durability": null, - "name": "Silver book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1803" - }, - { - "examine": "Elizabeth's shiny silver coated needle.", - "durability": null, - "name": "Silver needle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1804" - }, - { - "examine": "Elizabeth's shiny silver coated needle.", - "durability": null, - "name": "Silver needle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1805" - }, - { - "examine": "Frank's shiny silver coated pot.", - "durability": null, - "name": "Silver pot", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1806" - }, - { - "examine": "Frank's shiny silver coated pot.", - "durability": null, - "name": "Silver pot", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1807" - }, - { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, - "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1808" - }, - { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, - "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1809" - }, - { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, - "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1810" - }, - { - "examine": "A piece of fly paper. It's sticky.", - "durability": null, - "name": "Flypaper", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1811" - }, - { - "examine": "A pot found at the murder scene, with a sickly odour.", - "durability": null, - "name": "Pungent pot", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1812" - }, - { - "examine": "A flimsy-looking dagger found at the crime scene./A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Criminal's dagger", - "archery_ticket_price": "0", - "id": "1813" - }, - { - "examine": "A flimsy-looking dagger found at the crime scene./A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Criminal's dagger", - "archery_ticket_price": "0", - "id": "1814" - }, - { - "examine": "The fingerprints of the murderer.", - "durability": null, - "name": "Killer's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1815" - }, - { - "examine": "An imprint of Anna's fingerprint.", - "durability": null, - "name": "Anna's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1816" - }, - { - "examine": "An imprint of Bob's fingerprint.", - "durability": null, - "name": "Bob's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1817" - }, - { - "examine": "An imprint of Carol's fingerprint.", - "durability": null, - "name": "Carol's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1818" - }, - { - "examine": "An imprint of David's fingerprint.", - "durability": null, - "name": "David's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1819" - }, - { - "examine": "An imprint of Elizabeth's fingerprint.", - "durability": null, - "name": "Elizabeth's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1820" - }, - { - "examine": "An imprint of Frank's fingerprint.", - "durability": null, - "name": "Frank's print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1821" - }, - { - "examine": "An unidentified fingerprint taken from the murder weapon.", - "durability": null, - "name": "Unknown print", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1822" - }, - { - "shop_price": "30", - "ge_buy_limit": "100", - "examine": "A full waterskin with four portions of water.", - "grand_exchange_price": "184", - "durability": null, - "name": "Waterskin(4)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1823" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "184", - "durability": null, - "name": "Waterskin(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1824" - }, - { - "shop_price": "30", - "examine": "A nearly full waterskin with three portions of water.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(3)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1825" - }, - { - "durability": null, - "name": "Waterskin(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1826" - }, - { - "shop_price": "30", - "examine": "A half empty waterskin with two portions of water.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(2)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1827" - }, - { - "durability": null, - "name": "Waterskin(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1828" - }, - { - "shop_price": "30", - "examine": "A nearly empty waterskin with one portion of water.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(1)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1829" - }, - { - "durability": null, - "name": "Waterskin(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1830" - }, - { - "shop_price": "30", - "ge_buy_limit": "100", - "examine": "A completely empty waterskin - you'll need to fill it up.", - "grand_exchange_price": "93", - "durability": null, - "name": "Waterskin(0)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1831" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "93", - "durability": null, - "name": "Waterskin(0)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1832" - }, - { - "remove_sleeves": "true", - "shop_price": "40", - "ge_buy_limit": "100", - "examine": "A cool, light desert shirt.", - "grand_exchange_price": "93", - "durability": null, - "name": "Desert shirt", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1833", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "93", - "durability": null, - "name": "Desert shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1834" - }, - { - "shop_price": "40", - "ge_buy_limit": "100", - "examine": "A cool, light desert robe.", - "grand_exchange_price": "74", - "durability": null, - "name": "Desert robe", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1835", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "74", - "durability": null, - "name": "Desert robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1836" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Comfortable desert shoes.", - "grand_exchange_price": "257", - "durability": null, - "name": "Desert boots", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1837", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "257", - "durability": null, - "name": "Desert boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1838" - }, - { - "examine": "This key is crudely made. It came from the mining camp Mercenary Captain.", - "durability": null, - "name": "Metal key", - "archery_ticket_price": "0", - "id": "1839" - }, - { - "examine": "A metallic key, usually used by prison guards. (Tourist Trap)", - "durability": null, - "name": "Cell door key", - "archery_ticket_price": "0", - "id": "1840" - }, - { - "examine": "An empty barrel/A Splendid barrel.", - "grand_exchange_price": "206", - "durability": null, - "name": "Barrel", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "1841" - }, - { - "examine": "Inventory: A mining barrel with Ana in it.While in the mine cart: It's Ana... in a barrel... in a mine cart.", - "durability": null, - "name": "Ana in a barrel", - "weight": "32", - "archery_ticket_price": "0", - "id": "1842" - }, - { - "examine": "This key unlocks a very sturdy gate. Ana gave me this key. (Tourist Trap)", - "durability": null, - "name": "Wrought iron key", - "archery_ticket_price": "0", - "id": "1843" - }, - { - "remove_sleeves": "true", - "examine": "A filthy, smelly, flea infested shirt.", - "durability": null, - "name": "Slave shirt", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1844", - "equipment_slot": "4" - }, - { - "examine": "A filthy, smelly, flea infested robe.", - "durability": null, - "name": "Slave robe", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1845", - "equipment_slot": "7" - }, - { - "examine": "A set of filthy, smelly, flea infested desert slave boots.", - "durability": null, - "name": "Slave boots", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "1846", - "equipment_slot": "10" - }, - { - "examine": "A piece of paper with barely legible writing - looks like a recipe!", - "durability": null, - "name": "Scrumpled paper", - "archery_ticket_price": "0", - "id": "1847" - }, - { - "shop_price": "5", - "examine": "Very important information.", - "durability": null, - "name": "Shantay disclaimer", - "archery_ticket_price": "0", - "id": "1848" - }, - { - "examine": "A prototype throwing dart.", - "attack_audios": "2547,0,0,0", - "durability": null, - "name": "Prototype dart", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1849" - }, - { - "examine": "Plans of a technical nature.", - "durability": null, - "name": "Technical plans", - "weight": "1", - "archery_ticket_price": "0", - "id": "1850" - }, - { - "examine": "The most delicious of pineapples.", - "durability": null, - "name": "Tenti pineapple", - "weight": "1", - "archery_ticket_price": "0", - "id": "1851" - }, - { - "examine": "A key to the chest in Captain Siad's room.", - "durability": null, - "name": "Bedabin key", - "archery_ticket_price": "0", - "id": "1852" - }, - { - "examine": "A protoype dart tip - it looks deadly.", - "attack_audios": "2547,0,0,0", - "durability": null, - "name": "Prototype dart tip", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1853" - }, - { - "shop_price": "5", - "examine": "Allows you to pass through the Shantay pass into the Kharid Desert.", - "durability": null, - "name": "Shantay pass", - "archery_ticket_price": "0", - "id": "1854" - }, - { - "examine": "A Tourist's Guide to Ardougne.", - "durability": null, - "name": "Guide book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1856" - }, - { - "examine": "The Rantuki tribe's totem.", - "durability": null, - "name": "Totem", - "weight": "3", - "archery_ticket_price": "0", - "id": "1857" - }, - { - "examine": "It says 'To Lord Handelmort, Handelmort Mansion'.", - "durability": null, - "name": "Address label", - "archery_ticket_price": "0", - "id": "1858" - }, - { - "ge_buy_limit": "1000", - "examine": "I need to cook this first / Freshly cooked ugthanki meat.", - "grand_exchange_price": "486", - "durability": null, - "name": "Raw ugthanki meat", - "tradeable": "true", - "weight": "0.75", - "archery_ticket_price": "0", - "id": "1859" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "486", - "durability": null, - "name": "Raw ugthanki meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1860" - }, - { - "ge_buy_limit": "1000", - "examine": "I need to cook this first / Freshly cooked ugthanki meat.", - "grand_exchange_price": "687", - "durability": null, - "name": "Ugthanki meat", - "tradeable": "true", - "weight": "0.75", - "archery_ticket_price": "0", - "id": "1861" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "687", - "durability": null, - "name": "Ugthanki meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1862" - }, - { - "shop_price": "9", - "ge_buy_limit": "1000", - "examine": "I need to cook this.", - "grand_exchange_price": "90", - "durability": null, - "name": "Pitta dough", - "tradeable": "true", - "weight": "0.14", - "archery_ticket_price": "0", - "id": "1863" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "90", - "durability": null, - "name": "Pitta dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1864" - }, - { - "ge_buy_limit": "1000", - "examine": "Nicely baked pitta bread. Needs more ingredients to make a kebab.", - "grand_exchange_price": "22", - "durability": null, - "name": "Pitta bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1865" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "22", - "durability": null, - "name": "Pitta bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1866" - }, - { - "durability": null, - "name": "Burnt pitta bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1868" - }, - { - "ge_buy_limit": "1000", - "examine": "A mixture of tomatoes in a bowl.", - "grand_exchange_price": "90", - "durability": null, - "name": "Chopped tomato", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1869" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "90", - "durability": null, - "name": "Chopped tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1870" - }, - { - "ge_buy_limit": "1000", - "examine": "A mixture of onions in a bowl.", - "grand_exchange_price": "142", - "durability": null, - "name": "Chopped onion", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1871" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "142", - "durability": null, - "name": "Chopped onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1872" - }, - { - "ge_buy_limit": "1000", - "examine": "Strips of ugthanki meat in a bowl.", - "grand_exchange_price": "348", - "durability": null, - "name": "Chopped ugthanki", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1873" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "348", - "durability": null, - "name": "Chopped ugthanki", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1874" - }, - { - "ge_buy_limit": "1000", - "examine": "A mixture of chopped onions and tomatoes in a bowl", - "grand_exchange_price": "31", - "durability": null, - "name": "Onion & tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1875" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "31", - "durability": null, - "name": "Onion & tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1876" - }, - { - "shop_price": "26", - "ge_buy_limit": "1000", - "examine": "A mixture of chopped onions and ugthanki meat in a bowl.", - "grand_exchange_price": "330", - "durability": null, - "name": "Ugthanki & onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1877" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "330", - "durability": null, - "name": "Ugthanki & onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1878" - }, - { - "ge_buy_limit": "10000", - "examine": "A mixture of chopped tomatoes and ugthanki meat in a bowl.", - "grand_exchange_price": "331", - "durability": null, - "name": "Ugthanki & tomato", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "1879" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "331", - "durability": null, - "name": "Ugthanki & tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1880" - }, - { - "ge_buy_limit": "1000", - "examine": "A mixture of chopped tomatoes, onions and ugthanki meat in a bowl", - "grand_exchange_price": "412", - "durability": null, - "name": "Kebab mix", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1881" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "412", - "durability": null, - "name": "Kebab mix", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1882" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2027", - "durability": null, - "name": "Ugthanki kebab", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1883" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2027", - "durability": null, - "name": "Ugthanki kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1884" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1499", - "durability": null, - "name": "Ugthanki kebab", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1885" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1499", - "durability": null, - "name": "Ugthanki kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1886" - }, - { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "Useful for baking cakes.", - "grand_exchange_price": "20", - "durability": null, - "name": "Cake tin", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1887" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Cake tin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1888" - }, - { - "ge_buy_limit": "1000", - "examine": "Now all I need to do is cook it.", - "grand_exchange_price": "559", - "durability": null, - "name": "Uncooked cake", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1889" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "559", - "durability": null, - "name": "Uncooked cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1890" - }, - { - "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "A plain sponge cake.", - "grand_exchange_price": "155", - "durability": null, - "name": "Cake", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1891" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "155", - "durability": null, - "name": "Cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1892" - }, - { - "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "A plain sponge cake.", - "grand_exchange_price": "39", - "durability": null, - "name": "2/3 cake", "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1893" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "39", - "durability": null, - "name": "2/3 cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1894" - }, - { - "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "A plain sponge cake.", - "grand_exchange_price": "32", - "durability": null, - "name": "Slice of cake", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1895" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "32", - "durability": null, - "name": "Slice of cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1896" - }, - { - "ge_buy_limit": "1000", - "examine": "This looks very tasty.", - "grand_exchange_price": "416", - "durability": null, - "name": "Chocolate cake", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1897" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "416", - "durability": null, - "name": "Chocolate cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1898" - }, - { - "ge_buy_limit": "1000", - "examine": "This looks very tasty.", - "grand_exchange_price": "181", - "durability": null, - "name": "2/3 chocolate cake", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1899" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "181", - "durability": null, - "name": "2/3 chocolate cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1900" - }, - { - "ge_buy_limit": "1000", - "examine": "This looks very tasty.", - "grand_exchange_price": "13", - "durability": null, - "name": "Chocolate slice", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1901" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, - "name": "Chocolate slice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1902" - }, - { - "durability": null, - "name": "Burnt cake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1904" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "190", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1905" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "190", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1906" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "It's got strange bubbles in it.", - "grand_exchange_price": "171", - "durability": null, - "name": "Wizard's mind bomb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1907" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "171", - "durability": null, - "name": "Wizard's mind bomb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1908" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "828", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1909" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "828", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1910" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "487", - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1911" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "487", - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1912" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A pint of thick dark beer.", - "grand_exchange_price": "212", - "durability": null, - "name": "Dwarven stout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1913" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "212", - "durability": null, - "name": "Dwarven stout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1914" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A murky glass full of your average, everyday pirate slosh.", - "grand_exchange_price": "19", - "durability": null, - "name": "Grog", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "1915" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, - "name": "Grog", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1916" - }, - { - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "202", - "durability": null, - "name": "Beer", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1917", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "202", - "durability": null, - "name": "Beer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1918" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "I need to fill this with beer.", - "grand_exchange_price": "10", - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "1919" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10", - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1920" - }, - { - "ge_buy_limit": "100", - "examine": "It's a bowl of water.", - "grand_exchange_price": "97", - "durability": null, - "name": "Bowl of water", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1921" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "97", - "durability": null, - "name": "Bowl of water", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1922" - }, - { - "shop_price": "4", - "ge_buy_limit": "100", - "examine": "Useful for mixing things.", - "grand_exchange_price": "34", - "durability": null, - "name": "Bowl", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1923" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "34", - "durability": null, - "name": "Bowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1924" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "It's an empty bucket.", - "grand_exchange_price": "30", - "durability": null, - "name": "Bucket", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1925" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "30", - "durability": null, - "name": "Bucket", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1926" - }, - { - "shop_price": "12", - "ge_buy_limit": "1000", - "examine": "It's a bucket of milk.", - "grand_exchange_price": "103", - "durability": null, - "name": "Bucket of milk", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "1927" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "103", - "durability": null, - "name": "Bucket of milk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1928" - }, - { - "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?", - "grand_exchange_price": "72", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "1929" + "weight": "0.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "72", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", + "id": "1616", + "name": "Dragonstone", "archery_ticket_price": "0", - "id": "1930" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "This pot is empty.", - "grand_exchange_price": "6", "durability": null, - "name": "Empty pot", - "tradeable": "true", - "weight": "0.45", - "archery_ticket_price": "0", - "id": "1931" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "6", - "durability": null, - "name": "Empty pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1932" - }, - { - "shop_price": "14", - "ge_buy_limit": "1000", - "examine": "There is flour in this pot.", - "grand_exchange_price": "172", - "durability": null, - "name": "Pot of flour", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "1933" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "172", - "durability": null, - "name": "Pot of flour", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1934" - }, - { - "shop_price": "1", - "ge_buy_limit": "10000", - "examine": "This jug is empty.", - "grand_exchange_price": "29", - "durability": null, - "name": "Jug", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1935" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "29", - "durability": null, - "name": "Jug", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1936" - }, - { - "shop_price": "1", - "ge_buy_limit": "10000", - "examine": "It's full of water.", - "grand_exchange_price": "87", - "durability": null, - "name": "Jug of water", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1937" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "87", - "durability": null, - "name": "Jug of water", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1938" - }, - { - "ge_buy_limit": "5000", - "examine": "A foul smelling thick tar like substance.", - "grand_exchange_price": "103", - "durability": null, - "name": "Swamp tar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1939" + "tradeable": "true" }, { - "shop_price": "38", - "ge_buy_limit": "5000", - "examine": "A tar-like substance mixed with flour and warmed.", - "grand_exchange_price": "342", - "durability": null, - "name": "Raw swamp paste", - "tradeable": "true", + "id": "1617", + "name": "Uncut diamond", + "examine": "An uncut diamond.", "archery_ticket_price": "0", - "id": "1940" - }, - { - "shop_price": "38", - "ge_buy_limit": "100", - "examine": "A tar-like substance mixed with flour and warmed.", - "grand_exchange_price": "1", "durability": null, - "name": "Swamp paste", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1941" - }, - { - "shop_price": "11", "ge_buy_limit": "500", - "examine": "This could be used to make a good stew.", - "grand_exchange_price": "91", - "durability": null, - "name": "Potato", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1942" + "tokkul_price": "300", + "tradeable": "true" }, { + "id": "1618", + "name": "Uncut diamond", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "91", - "durability": null, - "name": "Potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1943" + "tradeable": "true" }, { - "shop_price": "1", + "id": "1619", + "name": "Uncut ruby", + "examine": "An uncut ruby.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "A nice fresh egg.", - "grand_exchange_price": "99", - "durability": null, - "name": "Egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1944" + "tokkul_price": "150", + "tradeable": "true" }, { + "id": "1620", + "name": "Uncut ruby", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "99", - "durability": null, - "name": "Egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1945" + "tradeable": "true" }, { - "shop_price": "14", - "examine": "There is flour in this pot.", - "grand_exchange_price": "162", - "durability": null, - "name": "Flour", - "tradeable": "true", - "weight": "1.3", + "id": "1621", + "name": "Uncut emerald", + "examine": "An uncut emerald.", "archery_ticket_price": "0", - "id": "1946" - }, - { - "shop_price": "2", + "durability": null, "ge_buy_limit": "1000", - "examine": "Some wheat heads.", - "grand_exchange_price": "7", - "durability": null, - "name": "Grain", - "tradeable": "true", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "1947" + "shop_price": "50", + "tokkul_price": "75", + "tradeable": "true" }, { + "id": "1622", + "name": "Uncut emerald", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "7", - "durability": null, - "name": "Grain", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1948" + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "What a silly hat.", - "grand_exchange_price": "233", - "durability": null, - "name": "Chef's hat", - "tradeable": "true", - "weight": "0.9", + "id": "1623", + "name": "Uncut sapphire", + "examine": "An uncut sapphire.", "archery_ticket_price": "0", - "id": "1949", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "233", "durability": null, - "name": "Chef's hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1950" - }, - { - "shop_price": "3", "ge_buy_limit": "1000", - "examine": "Very bright red berries.", - "grand_exchange_price": "337", - "durability": null, - "name": "Redberries", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "1951" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "337", - "durability": null, - "name": "Redberries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1952" - }, - { - "ge_buy_limit": "1000", - "examine": "Potentially pastry.", - "grand_exchange_price": "433", - "durability": null, - "name": "Pastry dough", - "tradeable": "true", - "weight": "0.16", - "archery_ticket_price": "0", - "id": "1953" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "433", - "durability": null, - "name": "Pastry dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1954" - }, - { - "shop_price": "36", - "ge_buy_limit": "1000", - "examine": "Keeps the doctor away.", - "grand_exchange_price": "353", - "durability": null, - "name": "Cooking apple", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1955" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "353", - "durability": null, - "name": "Cooking apple", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1956" - }, - { - "shop_price": "12", - "ge_buy_limit": "1000", - "examine": "A strong smelling onion.", - "grand_exchange_price": "8", - "durability": null, - "name": "Onion", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1957" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "8", - "durability": null, - "name": "Onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1958" - }, - { - "ge_buy_limit": "2", - "examine": "Happy Halloween.", - "grand_exchange_price": "273600000", - "durability": null, - "name": "Pumpkin", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1959" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "273600000", - "durability": null, - "name": "Pumpkin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1960" - }, - { - "ge_buy_limit": "2", - "examine": "Happy Easter.", - "grand_exchange_price": "80400000", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1961" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "80400000", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1962" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "263", - "durability": null, - "name": "Banana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1963", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "263", - "durability": null, - "name": "Banana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1964" - }, - { - "ge_buy_limit": "1000", - "examine": "On ground: Cabbage... yuck!In inventory: Yuck, I don't like cabbage.", - "grand_exchange_price": "41", - "durability": null, - "name": "Cabbage", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1965" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "41", - "durability": null, - "name": "Cabbage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1966" - }, - { - "examine": "On ground: Cabbage... yuck!In inventory: Yuck, I don't like cabbage.", - "grand_exchange_price": "55", - "durability": null, - "name": "Cabbage", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1967" - }, - { - "durability": null, - "name": "Cabbage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1968" - }, - { - "shop_price": "1", - "examine": "A home made spinach thing.", - "durability": null, - "name": "Spinach roll", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1969" - }, - { - "shop_price": "1", - "ge_buy_limit": "1000", - "examine": "A meaty kebab.", - "grand_exchange_price": "97", - "durability": null, - "name": "Kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1971" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "97", - "durability": null, - "name": "Kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1972" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Mmmmmmm chocolate.", - "grand_exchange_price": "295", - "durability": null, - "name": "Chocolate bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1973", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "295", - "durability": null, - "name": "Chocolate bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1974" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "It's ground-up chocolate.", - "grand_exchange_price": "370", - "durability": null, - "name": "Chocolate dust", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1975" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "370", - "durability": null, - "name": "Chocolate dust", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1976" - }, - { - "examine": "Milk with chocolate in it.", - "durability": null, - "name": "Chocolatey milk", - "weight": "2", - "archery_ticket_price": "0", - "id": "1977" - }, - { - "shop_price": "10", - "ge_buy_limit": "1000", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "1978" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1979" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "An empty cup.", - "grand_exchange_price": "5", - "durability": null, - "name": "Empty cup", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1980" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5", - "durability": null, - "name": "Empty cup", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1981" - }, - { "shop_price": "25", - "ge_buy_limit": "10000", - "examine": "This would make good ketchup.", - "grand_exchange_price": "159", - "durability": null, - "name": "Tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1982" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "159", - "durability": null, - "name": "Tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1983" - }, - { - "shop_price": "1", - "examine": "Rotten to the core!", - "durability": null, - "name": "Rotten apple", - "archery_ticket_price": "0", - "id": "1984" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "It's got holes in it.", - "grand_exchange_price": "188", - "durability": null, - "name": "Cheese", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1985" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "188", - "durability": null, - "name": "Cheese", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1986" - }, - { - "shop_price": "1", - "ge_buy_limit": "1000", - "examine": "Good grapes for wine making.", - "grand_exchange_price": "1510", - "durability": null, - "name": "Grapes", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1987" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1510", - "durability": null, - "name": "Grapes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1988" - }, - { - "ge_buy_limit": "2", - "examine": "An optimist would say it is half full.", - "grand_exchange_price": "227700000", - "durability": null, - "name": "Half full wine jug", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1989" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "227700000", - "durability": null, - "name": "Half full wine jug", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1990" - }, - { - "examine": "Oh dear, this wine is terrible!", - "durability": null, - "name": "Jug of bad wine", - "weight": "1", - "archery_ticket_price": "0", - "id": "1991" - }, - { - "examine": "Oh dear, this wine is terrible!", - "durability": null, - "name": "Jug of bad wine", - "weight": "1", - "archery_ticket_price": "0", - "id": "1992" - }, - { - "shop_price": "128", - "ge_buy_limit": "1000", - "examine": "It's full of wine.", - "grand_exchange_price": "36", - "durability": null, - "name": "Jug of wine", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "1993" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "36", - "durability": null, - "name": "Jug of wine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1994" - }, - { - "examine": "This wine needs to ferment before it can be drunk.", - "durability": null, - "name": "Unfermented wine", - "weight": "1", - "archery_ticket_price": "0", - "id": "1995" - }, - { - "examine": "This wine needs to ferment before it can be drunk.", - "durability": null, - "name": "Unfermented wine", - "weight": "1", - "archery_ticket_price": "0", - "id": "1996" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "177", - "durability": null, - "name": "Incomplete stew", - "archery_ticket_price": "0", - "id": "1997" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "177", - "durability": null, - "name": "Incomplete stew", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1998" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "210", - "durability": null, - "name": "Incomplete stew", - "archery_ticket_price": "0", - "id": "1999" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "210", - "durability": null, - "name": "Incomplete stew", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2000" - }, - { - "shop_price": "1", - "ge_buy_limit": "200", - "examine": "I need to cook this.", - "grand_exchange_price": "600", - "durability": null, - "name": "Uncooked stew", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2001" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "600", - "durability": null, - "name": "Uncooked stew", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2002" - }, - { - "shop_price": "20", - "ge_buy_limit": "200", - "examine": "It's a meat and potato stew.", - "grand_exchange_price": "1133", - "durability": null, - "name": "Stew", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "2003" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "1133", - "durability": null, - "name": "Stew", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2004" - }, - { - "durability": null, - "name": "Burnt stew", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2006" - }, - { - "shop_price": "230", - "ge_buy_limit": "1000", - "examine": "This could liven up an otherwise bland stew.", - "grand_exchange_price": "277", - "durability": null, - "name": "Spice", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2007" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "277", - "durability": null, - "name": "Spice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2008" - }, - { - "ge_buy_limit": "200", - "examine": "I need to cook this.", - "grand_exchange_price": "1382", - "durability": null, - "name": "Uncooked curry", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2009" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "1382", - "durability": null, - "name": "Uncooked curry", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2010" - }, - { - "ge_buy_limit": "200", - "examine": "It's a spicy hot curry.", - "grand_exchange_price": "1001", - "durability": null, - "name": "Curry", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "2011" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "1001", - "durability": null, - "name": "Curry", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2012" - }, - { - "durability": null, - "name": "Burnt curry", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2014" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "An absolutely clear spirit sold by well-stocked bars.", - "grand_exchange_price": "366", - "durability": null, - "name": "Vodka", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "2015" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "366", - "durability": null, - "name": "Vodka", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2016" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "This Draynor malt is sold by well-stocked bars.", - "grand_exchange_price": "68", - "durability": null, - "name": "Whisky", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2017" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "68", - "durability": null, - "name": "Whisky", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2018" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "A strong spirit that tastes of Juniper; sold at well-stocked bars.", - "grand_exchange_price": "184", - "durability": null, - "name": "Gin", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2019" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "184", - "durability": null, - "name": "Gin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2020" - }, - { - "shop_price": "6", - "ge_buy_limit": "1000", - "examine": "A strong spirit best served in a large glass.", - "grand_exchange_price": "146", - "durability": null, - "name": "Brandy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2021" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "146", - "durability": null, - "name": "Brandy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2022" - }, - { - "shop_price": "5", - "examine": "A book on tree gnome cocktails.", - "durability": null, - "name": "Cocktail guide", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2023" - }, - { - "durability": null, - "name": "Cocktail guide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2024" - }, - { - "shop_price": "2", - "examine": "Used for mixing cocktails.", - "durability": null, - "name": "Cocktail shaker", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2025" - }, - { - "shop_price": "1", - "examine": "For sipping cocktails.", - "durability": null, - "name": "Cocktail glass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2026" - }, - { - "durability": null, - "name": "Cocktail glass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2027" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Blurberry Special.", - "grand_exchange_price": "70", - "durability": null, - "name": "Premade blurb' sp.", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2028" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "70", - "durability": null, - "name": "Premade blurb' sp.", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2029" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Chocolate Saturday.", - "grand_exchange_price": "13", - "durability": null, - "name": "Premade choc s'dy", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2030" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, - "name": "Premade choc s'dy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2031" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Drunk Dragon.", - "grand_exchange_price": "34", - "durability": null, - "name": "Premade dr' dragon", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2032" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "34", - "durability": null, - "name": "Premade dr' dragon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2033" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Fruit Blast.", - "grand_exchange_price": "48", - "durability": null, - "name": "Premade fr' blast", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2034" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "48", - "durability": null, - "name": "Premade fr' blast", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2035" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Pineapple Punch.", - "grand_exchange_price": "40", - "durability": null, - "name": "Premade p' punch", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2036" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "40", - "durability": null, - "name": "Premade p' punch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2037" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A premade Short Green Guy.", - "grand_exchange_price": "33", - "durability": null, - "name": "Premade sgg", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2038" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "33", - "durability": null, - "name": "Premade sgg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2039" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A Premade Wizard Blizzard.", - "grand_exchange_price": "26", - "durability": null, - "name": "Premade wiz blz'd", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2040" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "26", - "durability": null, - "name": "Premade wiz blz'd", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2041" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2042" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2043" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2044" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2045" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2046" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2047" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "A fresh healthy fruit mix.", - "grand_exchange_price": "285", - "durability": null, - "name": "Pineapple punch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2048" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "285", - "durability": null, - "name": "Pineapple punch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2049" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2050" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2051" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2052" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2053" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "This looks like a strange mix.", - "grand_exchange_price": "774", - "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2054" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "774", - "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2055" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2056" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2057" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2058" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2059" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2060" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2061" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2062" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2063" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "Looks good... smells strong.", - "grand_exchange_price": "5468", - "durability": null, - "name": "Blurberry special", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2064" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "5468", - "durability": null, - "name": "Blurberry special", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2065" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2066" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2067" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2068" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2069" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2070" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2071" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2072" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2073" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "A warm creamy alcoholic beverage", - "grand_exchange_price": "272", - "durability": null, - "name": "Choc saturday", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2074" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "272", - "durability": null, - "name": "Choc saturday", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2075" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2076" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2077" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2078" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2079" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "A Short Green Guy... looks good.", - "grand_exchange_price": "756", - "durability": null, - "name": "Short green guy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2080" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "756", - "durability": null, - "name": "Short green guy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2081" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2082" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2083" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "A cool refreshing fruit mix.", - "grand_exchange_price": "1802", - "durability": null, - "name": "Fruit blast", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2084" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1802", - "durability": null, - "name": "Fruit blast", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2085" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2086" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2087" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2088" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2089" - }, - { - "examine": "This cocktail is just missing those little finishing touches.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2090" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2091" - }, - { - "shop_price": "28", - "ge_buy_limit": "1000", - "examine": "A warm creamy alcoholic beverage", - "grand_exchange_price": "506", - "durability": null, - "name": "Drunk dragon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2092" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "506", - "durability": null, - "name": "Drunk dragon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2093" - }, - { - "examine": "I'm not completely sure what this contains.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2094" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2095" - }, - { - "examine": "I'm not completely sure what this contains.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2096" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2097" - }, - { - "examine": "I'm not completely sure what this contains.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2098" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2099" - }, - { - "examine": "I'm not completely sure what this contains.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2100" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2101" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "A common fruit, popularly used in cocktails.", - "grand_exchange_price": "86", - "durability": null, - "name": "Lemon", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2102" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "86", - "durability": null, - "name": "Lemon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2103" - }, - { - "ge_buy_limit": "1000", - "examine": "The not-too-surprising result of using a knife with a lemon!", - "grand_exchange_price": "29", - "durability": null, - "name": "Lemon chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2104" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "29", - "durability": null, - "name": "Lemon chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2105" - }, - { - "ge_buy_limit": "1000", - "examine": "The not-too-surprising result of using a knife with a lemon!", - "grand_exchange_price": "16", - "durability": null, - "name": "Lemon slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2106" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "16", - "durability": null, - "name": "Lemon slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2107" - }, - { - "shop_price": "70", - "ge_buy_limit": "1000", - "examine": "A common fruit.", - "grand_exchange_price": "930", - "durability": null, - "name": "Orange", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2108" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "930", - "durability": null, - "name": "Orange", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2109" - }, - { - "ge_buy_limit": "1000", - "examine": "Fresh chunks of orange.", - "grand_exchange_price": "46", - "durability": null, - "name": "Orange chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2110" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "46", - "durability": null, - "name": "Orange chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2111" - }, - { - "ge_buy_limit": "1000", - "examine": "Fresh orange slices.", - "grand_exchange_price": "163", - "durability": null, - "name": "Orange slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2112" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "163", - "durability": null, - "name": "Orange slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2113" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "It can be cut up into something more manageable with a knife.", - "grand_exchange_price": "45", - "durability": null, - "name": "Pineapple", - "tradeable": "true", - "weight": "0.15", - "archery_ticket_price": "0", - "id": "2114" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "45", - "durability": null, - "name": "Pineapple", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2115" - }, - { - "ge_buy_limit": "1000", - "examine": "Fresh chunks of pineapple.", - "grand_exchange_price": "86", - "durability": null, - "name": "Pineapple chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2116" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "86", - "durability": null, - "name": "Pineapple chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2117" - }, - { - "ge_buy_limit": "1000", - "examine": "The not-too-surprising result of using a knife with a pineapple!", - "grand_exchange_price": "25", - "durability": null, - "name": "Pineapple ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2118" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "25", - "durability": null, - "name": "Pineapple ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2119" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "A common fruit, popularly used in cocktails.", - "grand_exchange_price": "78", - "durability": null, - "name": "Lime", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2120" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "78", - "durability": null, - "name": "Lime", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2121" - }, - { - "ge_buy_limit": "1000", - "examine": "Fresh chunks of lime.", - "grand_exchange_price": "16", - "durability": null, - "name": "Lime chunks", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2122" + "tokkul_price": "37", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "16", - "durability": null, - "name": "Lime chunks", - "tradeable": "true", + "id": "1624", + "name": "Uncut sapphire", "archery_ticket_price": "0", - "id": "2123" - }, - { - "ge_buy_limit": "1000", - "examine": "The not-too-surprising result of using a knife with a lime!", - "grand_exchange_price": "25", "durability": null, - "name": "Lime slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2124" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "25", - "durability": null, - "name": "Lime slices", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2125" - }, - { - "shop_price": "10", - "ge_buy_limit": "10000", - "examine": "Some rather pretty blue berries picked from a dwellberry bush.", - "grand_exchange_price": "144", - "durability": null, - "name": "Dwellberries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2126" + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "144", - "durability": null, - "name": "Dwellberries", - "tradeable": "true", + "id": "1625", + "name": "Uncut opal", + "examine": "An uncut opal.", "archery_ticket_price": "0", - "id": "2127" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "A common ingredient in gnome cuisine.", - "grand_exchange_price": "275", "durability": null, - "name": "Equa leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2128" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "275", - "durability": null, - "name": "Equa leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2129" - }, - { - "shop_price": "2", - "ge_buy_limit": "10000", - "examine": "Fresh cream.", - "grand_exchange_price": "58", - "durability": null, - "name": "Pot of cream", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2130" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "58", - "durability": null, - "name": "Pot of cream", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2131" + "tradeable": "true" }, { - "shop_price": "58", - "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "282", - "durability": null, - "name": "Raw beef", - "tradeable": "true", - "weight": "0.3", + "id": "1626", + "name": "Uncut opal", "archery_ticket_price": "0", - "id": "2132" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "282", "durability": null, - "name": "Raw beef", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2133" - }, - { - "shop_price": "1", "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "85", - "durability": null, - "name": "Raw rat meat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2134" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "85", - "durability": null, - "name": "Raw rat meat", - "tradeable": "true", + "id": "1627", + "name": "Uncut jade", + "examine": "An uncut jade.", "archery_ticket_price": "0", - "id": "2135" - }, - { - "shop_price": "8", - "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "307", "durability": null, - "name": "Raw bear meat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2136" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "307", - "durability": null, - "name": "Raw bear meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2137" + "tradeable": "true" }, { - "shop_price": "60", - "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "386", - "durability": null, - "name": "Raw chicken", - "tradeable": "true", - "weight": "0.15", + "id": "1628", + "name": "Uncut jade", "archery_ticket_price": "0", - "id": "2138" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "386", "durability": null, - "name": "Raw chicken", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2139" - }, - { - "shop_price": "5", "ge_buy_limit": "1000", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "70", - "durability": null, - "name": "Cooked chicken", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2140" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "70", - "durability": null, - "name": "Cooked chicken", - "tradeable": "true", + "id": "1629", + "name": "Uncut red topaz", + "examine": "An uncut red topaz.", "archery_ticket_price": "0", - "id": "2141" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "111", "durability": null, - "name": "Cooked meat", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "2142" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "111", - "durability": null, - "name": "Cooked meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2143" - }, - { - "durability": null, - "name": "Burnt chicken", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2145" - }, - { - "examine": "Oh dear, it's totally burnt!", - "durability": null, - "name": "Burnt meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2146" - }, - { - "durability": null, - "name": "Burnt meat", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2147" - }, - { - "examine": "A very strange eel.", - "durability": null, - "name": "Raw lava eel", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2148" - }, - { - "examine": "Strange, it looks cooler now it's been cooked.", - "durability": null, - "name": "Lava eel", - "weight": "0.15", - "archery_ticket_price": "0", - "id": "2149" - }, - { - "ge_buy_limit": "10000", - "examine": "A slippery little blighter.", - "grand_exchange_price": "750", - "durability": null, - "name": "Swamp toad", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2150" + "weight": "0.2" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "750", - "durability": null, - "name": "Swamp toad", - "tradeable": "true", + "id": "1630", + "name": "Uncut red topaz", "archery_ticket_price": "0", - "id": "2151" - }, - { - "ge_buy_limit": "1000", - "examine": "They're a gnome delicacy apparently.", - "grand_exchange_price": "661", "durability": null, - "name": "Toad's legs", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2152" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "661", - "durability": null, - "name": "Toad's legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2153" - }, - { - "examine": "They're a gnome delicacy apparently.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2154" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2155" - }, - { - "examine": "They're a gnome delicacy apparently.", - "durability": null, - "name": "Spicy toad's legs", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2156" - }, - { - "durability": null, - "name": "Spicy toad's legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2157" - }, - { - "examine": "They're a gnome delicacy apparently.", - "durability": null, - "name": "Seasoned legs", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2158" - }, - { - "durability": null, - "name": "Seasoned legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2159" + "tradeable": "true" }, { - "examine": "They're a gnome delicacy apparently.", - "durability": null, - "name": "Spicy worm", - "tradeable": "true", - "weight": "0.1", + "id": "1631", + "name": "Uncut dragonstone", + "examine": "An uncut dragonstone.", "archery_ticket_price": "0", - "id": "2160" - }, - { "durability": null, - "name": "Spicy worm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2161" - }, - { "ge_buy_limit": "100", - "examine": "They're a gnome delicacy apparently.", - "grand_exchange_price": "24", - "durability": null, - "name": "King worm", + "shop_price": "75000", + "tokkul_price": "1500", "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2162" + "weight": "0.3" }, { + "id": "1632", + "name": "Uncut dragonstone", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "24", - "durability": null, - "name": "King worm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2163" + "tradeable": "true" }, { - "shop_price": "1", - "examine": "A deep tin used to make gnome battas in.", - "durability": null, - "name": "Batta tin", - "tradeable": "true", - "weight": "0.1", + "id": "1633", + "name": "Crushed gem", + "examine": "This gem is crushed and broken.", "archery_ticket_price": "0", - "id": "2164" + "durability": null, + "tradeable": "true" }, { - "shop_price": "10", - "examine": "A shallow tray used to make crunchies in.", - "durability": null, - "name": "Crunchy tray", - "tradeable": "true", - "weight": "0.1", + "id": "1634", + "name": "Crushed gem", "archery_ticket_price": "0", - "id": "2165" + "durability": null, + "tradeable": "true" }, { - "shop_price": "10", - "examine": "A large ovenproof bowl.", - "durability": null, - "name": "Gnomebowl mould", - "tradeable": "true", - "weight": "0.1", + "id": "1635", + "name": "Gold ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2166" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "Aluft Gianne's favourite dishes.", - "durability": null, - "name": "Gianne's cook book", - "tradeable": "true", - "weight": "0.5", + "id": "1636", + "name": "Gold ring", "archery_ticket_price": "0", - "id": "2167" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Gianne's cook book", - "tradeable": "true", + "id": "1637", + "name": "Sapphire ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2168" + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "It's Aluft Gianne's secret mix of spices.", - "grand_exchange_price": "28", - "durability": null, - "name": "Gnome spice", - "tradeable": "true", - "weight": "0.14", + "id": "1638", + "name": "Sapphire ring", "archery_ticket_price": "0", - "id": "2169" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "28", - "durability": null, - "name": "Gnome spice", - "tradeable": "true", + "id": "1639", + "name": "Emerald ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2170" + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "It's made from a secret recipe.", - "grand_exchange_price": "13", - "durability": null, - "name": "Gianne dough", - "tradeable": "true", - "weight": "0.5", + "id": "1640", + "name": "Emerald ring", "archery_ticket_price": "0", - "id": "2171" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, - "name": "Gianne dough", - "tradeable": "true", + "id": "1641", + "name": "Ruby ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2172" + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" }, { - "examine": "This gnome bowl doesn't look very appetizing.", - "durability": null, - "name": "Picture", - "weight": "1", + "id": "1642", + "name": "Ruby ring", "archery_ticket_price": "0", - "id": "2173" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1643", + "name": "Diamond ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2174" + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "shop_price": "3172", + "tradeable": "true" }, { - "durability": null, - "name": "Burnt gnomebowl", - "tradeable": "true", + "id": "1644", + "name": "Diamond ring", "archery_ticket_price": "0", - "id": "2176" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "This gnomebowl is in the early stages of preparation.", - "durability": null, - "name": "Half baked bowl", - "weight": "0.2", + "id": "1645", + "name": "Dragonstone ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2177" + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "shop_price": "7750", + "tradeable": "true" }, { - "examine": "This gnomebowl needs cooking.", - "durability": null, - "name": "Raw gnomebowl", - "tradeable": "true", + "id": "1646", + "name": "Dragonstone ring", "archery_ticket_price": "0", - "id": "2178" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", + "id": "1649", + "name": "Sapphire ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2179" + "durability": null, + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1650", + "name": "Emerald ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2180" + "durability": null, + "tradeable": "true" }, { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", + "id": "1651", + "name": "Ruby ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2181" + "durability": null, + "shop_price": "2025", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1652", + "name": "Diamond ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2182" + "durability": null, + "shop_price": "3172", + "tradeable": "true" }, { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", + "id": "1653", + "name": "Dragonstone ring", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "2183" + "durability": null, + "shop_price": "7750", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1654", + "name": "Gold necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2184" - }, - { + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", "shop_price": "450", - "ge_buy_limit": "1000", - "examine": "Full of creamy, chocolately goodness.", - "grand_exchange_price": "4400", - "durability": null, - "name": "Chocolate bomb", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "2185" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "4400", - "durability": null, - "name": "Chocolate bomb", - "tradeable": "true", + "id": "1655", + "name": "Gold necklace", "archery_ticket_price": "0", - "id": "2186" - }, - { - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "2321", "durability": null, - "name": "Tangled toads' legs", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "2187" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "2321", - "durability": null, - "name": "Tangled toads' legs", - "tradeable": "true", + "id": "1656", + "name": "Sapphire necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2188" - }, - { - "examine": "This unfinished XX needs YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2189" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "945", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1657", + "name": "Sapphire necklace", "archery_ticket_price": "0", - "id": "2190" - }, - { - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "454", "durability": null, - "name": "Worm hole", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2191" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "454", - "durability": null, - "name": "Worm hole", - "tradeable": "true", + "id": "1658", + "name": "Emerald necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2192" - }, - { - "examine": "This unfinished XX needs YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2193" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "1425", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1659", + "name": "Emerald necklace", "archery_ticket_price": "0", - "id": "2194" - }, - { - "ge_buy_limit": "1000", - "examine": "This looks pretty healthy.", - "grand_exchange_price": "481", "durability": null, - "name": "Veg ball", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2195" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "481", - "durability": null, - "name": "Veg ball", - "tradeable": "true", + "id": "1660", + "name": "Ruby necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2196" - }, - { - "examine": "These crunchies don't look very appetising.", "durability": null, - "name": "Odd crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2197" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "870", + "tradeable": "true" }, { - "durability": null, - "name": "Odd crunchies", - "tradeable": "true", + "id": "1661", + "name": "Ruby necklace", "archery_ticket_price": "0", - "id": "2198" - }, - { "durability": null, - "name": "Burnt crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2200" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "This crunchy is in the early stages of preparation.", - "durability": null, - "name": "Half baked crunchy", - "tradeable": "true", + "id": "1662", + "name": "Diamond necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2201" - }, - { - "ge_buy_limit": "1000", - "examine": "These crunchies need cooking.", - "grand_exchange_price": "163", "durability": null, - "name": "Raw crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2202" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" }, { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1663", + "name": "Diamond necklace", "archery_ticket_price": "0", - "id": "2203" - }, - { "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2204" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "80", - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "255", - "durability": null, - "name": "Worm crunchies", - "tradeable": "true", + "id": "1664", + "name": "Dragon necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2205" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "255", "durability": null, - "name": "Worm crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2206" + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1665", + "name": "Dragon necklace", "archery_ticket_price": "0", - "id": "2207" - }, - { "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2208" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "70", - "ge_buy_limit": "1000", - "examine": "Yum... smells good.", - "grand_exchange_price": "816", - "durability": null, - "name": "Chocchip crunchies", - "tradeable": "true", + "id": "1668", + "name": "Sapphire necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2209" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "816", "durability": null, - "name": "Chocchip crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2210" + "shop_price": "945", + "tradeable": "true" }, { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1669", + "name": "Emerald necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2211" - }, - { "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2212" - }, - { - "shop_price": "70", - "ge_buy_limit": "1000", - "examine": "Yum...smells spicy.", - "grand_exchange_price": "101", - "durability": null, - "name": "Spicy crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2213" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "101", - "durability": null, - "name": "Spicy crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2214" - }, - { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2215" + "shop_price": "1425", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2216" - }, - { - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "1228", - "durability": null, - "name": "Toad crunchies", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2217" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1228", - "durability": null, - "name": "Toad crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2218" - }, - { - "ge_buy_limit": "1000", - "examine": "A premade Worm Batta.", - "grand_exchange_price": "130", - "durability": null, - "name": "Premade w'm batta", - "tradeable": "true", - "weight": "0.2", + "id": "1670", + "name": "Ruby necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2219" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "130", "durability": null, - "name": "Premade w'm batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2220" + "shop_price": "870", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Premade Toad Batta.", - "grand_exchange_price": "127", - "durability": null, - "name": "Premade t'd batta", - "tradeable": "true", - "weight": "0.2", + "id": "1671", + "name": "Diamond necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2221" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "127", "durability": null, - "name": "Premade t'd batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2222" + "shop_price": "3307", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Premade Cheese and Tomato Batta.", - "grand_exchange_price": "137", - "durability": null, - "name": "Premade c+t batta", - "tradeable": "true", - "weight": "0.2", + "id": "1672", + "name": "Dragon necklace", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "2223" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "137", "durability": null, - "name": "Premade c+t batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2224" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A premade Fruit Batta.", - "grand_exchange_price": "131", - "durability": null, - "name": "Premade fr't batta", - "tradeable": "true", - "weight": "0.2", + "id": "1673", + "name": "Gold amulet", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2225" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "131", "durability": null, - "name": "Premade fr't batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2226" + "ge_buy_limit": "5000", + "shop_price": "350", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Premade Vegetable Batta.", - "grand_exchange_price": "134", - "durability": null, - "name": "Premade veg batta", - "tradeable": "true", - "weight": "0.2", + "id": "1674", + "name": "Gold amulet", "archery_ticket_price": "0", - "id": "2227" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "134", "durability": null, - "name": "Premade veg batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2228" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A premade Chocolate Bomb.", - "grand_exchange_price": "1227", - "durability": null, - "name": "Premade choc bomb", - "tradeable": "true", - "weight": "0.2", + "id": "1675", + "name": "Sapphire amulet", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2229" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1227", "durability": null, - "name": "Premade choc bomb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2230" + "ge_buy_limit": "5000", + "shop_price": "810", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A premade Tangled Toads Legs.", - "grand_exchange_price": "867", - "durability": null, - "name": "Premade ttl", - "tradeable": "true", - "weight": "0.2", + "id": "1676", + "name": "Sapphire amulet", "archery_ticket_price": "0", - "id": "2231" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "867", "durability": null, - "name": "Premade ttl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2232" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A premade Worm Hole.", - "grand_exchange_price": "237", - "durability": null, - "name": "Premade worm hole", - "tradeable": "true", - "weight": "0.2", + "id": "1677", + "name": "Emerald amulet", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2233" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "237", "durability": null, - "name": "Premade worm hole", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2234" + "ge_buy_limit": "5000", + "shop_price": "1147", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A premade Vegetable Ball.", - "grand_exchange_price": "233", - "durability": null, - "name": "Premade veg ball", - "tradeable": "true", - "weight": "0.2", + "id": "1678", + "name": "Emerald amulet", "archery_ticket_price": "0", - "id": "2235" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "233", "durability": null, - "name": "Premade veg ball", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2236" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Some Premade Worm Crunchies.", - "grand_exchange_price": "76", - "durability": null, - "name": "Premade w'm crun", - "tradeable": "true", - "weight": "0.2", + "id": "1679", + "name": "Ruby amulet", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2237" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "76", + "destroy": "true", "durability": null, - "name": "Premade w'm crun", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2238" + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Some Premade chocchip crunchies.", - "grand_exchange_price": "95", - "durability": null, - "name": "Premade ch' crunch", - "tradeable": "true", - "weight": "0.2", + "id": "1680", + "name": "Ruby amulet", "archery_ticket_price": "0", - "id": "2239" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "95", "durability": null, - "name": "Premade ch' crunch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2240" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Some premade Spicy Crunchies.", - "grand_exchange_price": "103", - "durability": null, - "name": "Premade s'y crunch", - "tradeable": "true", - "weight": "0.2", + "id": "1681", + "name": "Diamond amulet", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2241" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "103", "durability": null, - "name": "Premade s'y crunch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2242" + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" }, { - "shop_price": "80", - "ge_buy_limit": "1000", - "examine": "Some premade Toad Crunchies.", - "grand_exchange_price": "43", - "durability": null, - "name": "Premade t'd crunch", - "tradeable": "true", - "weight": "0.2", + "id": "1682", + "name": "Diamond amulet", "archery_ticket_price": "0", - "id": "2243" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "43", "durability": null, - "name": "Premade t'd crunch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2244" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "This batta doesn't look very appetising.", - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1683", + "name": "Dragonstone ammy", + "examine": "It needs a string so I can wear it.", "archery_ticket_price": "0", - "id": "2245" - }, - { "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2246" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "durability": null, - "name": "Burnt batta", - "tradeable": "true", + "id": "1684", + "name": "Dragonstone ammy", "archery_ticket_price": "0", - "id": "2248" - }, - { - "examine": "This gnome batta is in the early stages of preparation.", "durability": null, - "name": "Half baked batta", - "archery_ticket_price": "0", - "id": "2249" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "1", - "examine": "This gnome batta needs cooking.", - "durability": null, - "name": "Raw batta", + "id": "1687", + "name": "Sapphire amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2250" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2251" + "shop_price": "810", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1688", + "name": "Emerald amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2252" - }, - { - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "140", "durability": null, - "name": "Worm batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2253" + "shop_price": "1147", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "140", - "durability": null, - "name": "Worm batta", - "tradeable": "true", + "id": "1689", + "name": "Ruby amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2254" - }, - { - "shop_price": "78", - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "271", + "destroy": "true", "durability": null, - "name": "Toad batta", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2255" + "shop_price": "2025", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "271", - "durability": null, - "name": "Toad batta", - "tradeable": "true", + "id": "1690", + "name": "Diamond amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2256" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2257" + "shop_price": "3307", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1691", + "name": "Dragonstone ammy", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2258" - }, - { - "ge_buy_limit": "1000", - "examine": "This smells really good.", - "grand_exchange_price": "260", "durability": null, - "name": "Cheese+tom batta", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2259" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "260", - "durability": null, - "name": "Cheese+tom batta", - "tradeable": "true", + "id": "1692", + "name": "Gold amulet", + "examine": "A plain gold amulet.", "archery_ticket_price": "0", - "id": "2260" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2261" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "350", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1693", + "name": "Gold amulet", "archery_ticket_price": "0", - "id": "2262" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2263" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1694", + "name": "Sapphire amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2264" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2265" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "810", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1695", + "name": "Sapphire amulet", "archery_ticket_price": "0", - "id": "2266" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2267" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1696", + "name": "Emerald amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2268" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2269" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "1147", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1697", + "name": "Emerald amulet", "archery_ticket_price": "0", - "id": "2270" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2271" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1698", + "name": "Ruby amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2272" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", + "destroy": "true", "durability": null, - "name": "Unfinished batta", - "archery_ticket_price": "0", - "id": "2273" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" }, { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", + "id": "1699", + "name": "Ruby amulet", "archery_ticket_price": "0", - "id": "2274" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2275" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1700", + "name": "Diamond amulet", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2276" - }, - { - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "303", "durability": null, - "name": "Fruit batta", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2277" + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "303", - "durability": null, - "name": "Fruit batta", - "tradeable": "true", + "id": "1701", + "name": "Diamond amulet", "archery_ticket_price": "0", - "id": "2278" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "2279" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "1702", + "name": "Dragonstone ammy", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "2280" - }, - { - "ge_buy_limit": "1000", - "examine": "Well... It looks healthy.", - "grand_exchange_price": "240", "durability": null, - "name": "Vegetable batta", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2281" + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "240", - "durability": null, - "name": "Vegetable batta", - "tradeable": "true", + "id": "1703", + "name": "Dragonstone ammy", "archery_ticket_price": "0", - "id": "2282" - }, - { - "shop_price": "4", - "ge_buy_limit": "1000", - "examine": "I need to add some tomato next.", - "grand_exchange_price": "418", "durability": null, - "name": "Pizza base", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2283" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "418", - "durability": null, - "name": "Pizza base", - "tradeable": "true", + "id": "1704", + "name": "Amulet of glory", + "examine": "A very powerful dragonstone amulet.", "archery_ticket_price": "0", - "id": "2284" - }, - { - "ge_buy_limit": "1000", - "examine": "I need to add some cheese next.", - "grand_exchange_price": "366", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", "durability": null, - "name": "Incomplete pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2285" + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "366", + "id": "1705", + "name": "Amulet of glory", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Incomplete pizza", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2286" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "This needs cooking.", - "grand_exchange_price": "1141", - "durability": null, - "name": "Uncooked pizza", - "tradeable": "true", + "id": "1706", + "name": "Amulet of glory(1)", + "examine": "A dragonstone amulet with 1 magic charge.", "archery_ticket_price": "0", - "id": "2287" + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1141", + "id": "1707", + "name": "Amulet of glory(1)", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Uncooked pizza", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2288" + "durability": null, + "tradeable": "true" }, { + "id": "1708", + "name": "Amulet of glory(2)", + "examine": "A dragonstone amulet with 2 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "1709", + "name": "Amulet of glory(2)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1710", + "name": "Amulet of glory(3)", + "examine": "A dragonstone amulet with 3 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "1711", + "name": "Amulet of glory(3)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1712", + "name": "Amulet of glory(4)", + "examine": "A dragonstone amulet with 4 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1713", + "name": "Amulet of glory(4)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1714", + "name": "Unstrung symbol", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", "shop_price": "200", - "ge_buy_limit": "1000", - "examine": "A cheese and tomato pizza.", - "grand_exchange_price": "1357", - "durability": null, - "name": "Plain pizza", - "tradeable": "true", - "weight": "0.66", - "archery_ticket_price": "0", - "id": "2289" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1357", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Plain pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2290" - }, - { - "shop_price": "200", - "ge_buy_limit": "1000", - "examine": "Half of this plain pizza has been eaten.", - "grand_exchange_price": "238", - "durability": null, - "name": "1/2 plain pizza", - "tradeable": "true", - "weight": "0.66", - "archery_ticket_price": "0", - "id": "2291" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "238", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "1/2 plain pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2292" - }, - { - "ge_buy_limit": "1000", - "examine": "A pizza with bits of meat on it.", - "grand_exchange_price": "1012", - "durability": null, - "name": "Meat pizza", - "tradeable": "true", - "weight": "0.83", - "archery_ticket_price": "0", - "id": "2293" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1012", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Meat pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2294" - }, - { - "ge_buy_limit": "1000", - "examine": "Half of this meat pizza has been eaten.", - "grand_exchange_price": "339", - "durability": null, - "name": "1/2 meat pizza", - "tradeable": "true", - "weight": "0.83", - "archery_ticket_price": "0", - "id": "2295" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "339", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "1/2 meat pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2296" - }, - { - "ge_buy_limit": "1000", - "examine": "A pizza with anchovies.", - "grand_exchange_price": "1573", - "durability": null, - "name": "Anchovy pizza", - "tradeable": "true", - "weight": "0.83", - "archery_ticket_price": "0", - "id": "2297" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1573", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Anchovy pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2298" - }, - { - "ge_buy_limit": "1000", - "examine": "Half of this anchovy pizza has been eaten.", - "grand_exchange_price": "44", - "durability": null, - "name": "1/2 anchovy pizza", - "tradeable": "true", - "weight": "0.83", - "archery_ticket_price": "0", - "id": "2299" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "44", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "1/2 anchovy pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2300" - }, - { - "ge_buy_limit": "1000", - "examine": "A pizza with bits of pineapple on it.", - "grand_exchange_price": "1469", - "durability": null, - "name": "Pineapple pizza", - "tradeable": "true", - "weight": "0.83", - "archery_ticket_price": "0", - "id": "2301" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1469", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Pineapple pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2302" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "156", - "examine": "Half of this pineapple pizza has been eaten.", - "durability": null, - "name": "1/2 p'apple pizza", - "archery_ticket_price": "0", - "id": "2303" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "156", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "1/2 p'apple pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2304" - }, - { - "examine": "Oh dear!", - "durability": null, - "name": "Burnt pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2306" - }, - { - "ge_buy_limit": "1000", - "examine": "Some uncooked dough.", - "grand_exchange_price": "158", - "durability": null, - "name": "Bread dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2307" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "158", - "durability": null, - "name": "Bread dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2308" - }, - { - "shop_price": "24", - "ge_buy_limit": "1000", - "examine": "Nice crispy bread.", - "grand_exchange_price": "110", - "durability": null, - "name": "Bread", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2309" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "110", - "durability": null, - "name": "Bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2310" - }, - { - "durability": null, - "name": "Burnt bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2312" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Deceptively pie shaped.", - "grand_exchange_price": "174", - "durability": null, - "name": "Pie dish", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2313" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "174", - "durability": null, - "name": "Pie dish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2314" - }, - { - "ge_buy_limit": "1000", - "examine": "I need to find a filling for this pie.", - "grand_exchange_price": "887", - "durability": null, - "name": "Pie shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2315" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "887", - "durability": null, - "name": "Pie shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2316" - }, - { - "ge_buy_limit": "1000", - "examine": "This would be much tastier cooked.", - "grand_exchange_price": "781", - "durability": null, - "name": "Uncooked apple pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2317" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "781", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Uncooked apple pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2318" - }, - { - "ge_buy_limit": "1000", - "examine": "This would be much healthier cooked.", - "grand_exchange_price": "747", - "durability": null, - "name": "Uncooked meat pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2319" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "747", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Uncooked meat pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2320" - }, - { - "ge_buy_limit": "1000", - "examine": "This would be much more appetising cooked.", - "grand_exchange_price": "802", - "durability": null, - "name": "Uncooked berry pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2321" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "802", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Uncooked berry pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2322" - }, - { - "shop_price": "140", - "ge_buy_limit": "1000", - "examine": "Mmm Apple pie.", - "grand_exchange_price": "246", - "durability": null, - "name": "Apple pie", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2323" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "246", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Apple pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2324" - }, - { - "ge_buy_limit": "1000", - "examine": "Looks tasty.", - "grand_exchange_price": "534", - "durability": null, - "name": "Redberry pie", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2325" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "534", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Redberry pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2326" - }, - { - "shop_price": "16", - "ge_buy_limit": "1000", - "grand_exchange_price": "160", - "examine": "Not for vegetarians.", - "durability": null, - "name": "Meat pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2327" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "160", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Meat pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2328" - }, - { - "examine": "I think I left it on the stove too long.", - "durability": null, - "name": "Burnt pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2330" - }, - { - "shop_price": "16", - "ge_buy_limit": "1000", - "grand_exchange_price": "174", - "examine": "Half of it is suitable for vegetarians.", - "durability": null, - "name": "Half a meat pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2331" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "174", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a meat pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2332" - }, - { - "ge_buy_limit": "1000", - "examine": "So tasty I kept some for later.", - "grand_exchange_price": "248", - "durability": null, - "name": "Half a redberry pie", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2333" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "248", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a redberry pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2334" - }, - { - "shop_price": "140", - "ge_buy_limit": "1000", - "examine": "Mmm half an apple pie.", - "grand_exchange_price": "146", - "durability": null, - "name": "Half an apple pie", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2335" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "146", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half an apple pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2336" - }, - { - "ge_buy_limit": "1000", - "examine": "Raw meat from the oomlie bird.", - "grand_exchange_price": "155", - "durability": null, - "name": "Raw oomlie", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2337" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "155", - "durability": null, - "name": "Raw oomlie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2338" - }, - { - "examine": "A thick green palm leaf used by natives to cook meat.", - "durability": null, - "name": "Palm leaf", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2339" - }, - { - "examine": "A thick green palm leaf used by natives to cook meat.", - "durability": null, - "name": "Palm leaf", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2340" - }, - { - "ge_buy_limit": "1000", - "examine": "Oomlie meat in a palm leaf pouch. It just needs to be cooked.", - "grand_exchange_price": "814", - "durability": null, - "name": "Wrapped oomlie", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "2341" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "814", - "durability": null, - "name": "Wrapped oomlie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2342" - }, - { - "ge_buy_limit": "1000", - "examine": "Deliciously cooked oomlie meat in a palm leaf pouch.", - "grand_exchange_price": "1126", - "durability": null, - "name": "Cooked oomlie wrap", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "2343" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1126", - "durability": null, - "name": "Cooked oomlie wrap", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2344" - }, - { - "durability": null, - "name": "Burnt oomlie wrap", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2346" - }, - { - "shop_price": "13", - "ge_buy_limit": "1000", - "examine": "Good for hitting things!", - "grand_exchange_price": "37", - "durability": null, - "name": "Hammer", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "2347" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "37", - "durability": null, - "name": "Hammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2348" - }, - { - "shop_price": "10", - "ge_buy_limit": "10000", - "examine": "It's a bar of bronze.", - "grand_exchange_price": "268", - "durability": null, - "name": "Bronze bar", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "2349" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "268", - "durability": null, - "name": "Bronze bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2350" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a bar of iron.", - "grand_exchange_price": "493", - "durability": null, - "name": "Iron bar", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "2351" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "493", - "durability": null, - "name": "Iron bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2352" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a bar of steel.", - "grand_exchange_price": "1266", - "durability": null, - "name": "Steel bar", - "tradeable": "true", - "weight": "1.81", - "archery_ticket_price": "0", - "id": "2353" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1266", - "durability": null, - "name": "Steel bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2354" + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "It's a bar of silver.", - "grand_exchange_price": "274", - "durability": null, - "name": "Silver bar", - "tradeable": "true", - "weight": "1.81", + "id": "1715", + "name": "Unstrung symbol", "archery_ticket_price": "0", - "id": "2355" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "274", "durability": null, - "name": "Silver bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2356" + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "It's a bar of gold.", - "grand_exchange_price": "107", - "durability": null, - "name": "Gold bar", - "tradeable": "true", - "weight": "1.81", + "id": "1716", + "name": "Unblessed symbol", + "examine": "A symbol of Saradomin.", "archery_ticket_price": "0", - "id": "2357" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "107", "durability": null, - "name": "Gold bar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2358" - }, - { + "equipment_slot": "2", + "ge_buy_limit": "5000", "shop_price": "120", + "tradeable": "true" + }, + { + "id": "1717", + "name": "Unblessed symbol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1718", + "name": "Holy symbol", + "examine": "A blessed holy symbol of Saradomin.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,2,2,2,2,2,3,0,8,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "1719", + "name": "Holy symbol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1720", + "name": "Unstrung emblem", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1721", + "name": "Unstrung emblem", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1722", + "name": "Unpowered symbol", + "examine": "An unholy symbol of Zamorak.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "1723", + "name": "Unpowered symbol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1724", + "name": "Unholy symbol", + "examine": "An unholy symbol of Zamorak.", + "archery_ticket_price": "0", + "bonuses": "2,2,2,2,2,0,0,0,0,0,0,0,8,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "135", + "tradeable": "true" + }, + { + "id": "1725", + "name": "Amulet of strength", + "examine": "An enchanted ruby amulet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,10,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1726", + "name": "Amulet of strength", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1727", + "name": "Amulet of magic", + "examine": "An enchanted sapphire amulet of magic.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,10,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "900", + "tradeable": "true" + }, + { + "id": "1728", + "name": "Amulet of magic", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1729", + "name": "Amulet of defence", + "examine": "An enchanted emerald amulet of protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "1275", + "tradeable": "true" + }, + { + "id": "1730", + "name": "Amulet of defence", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1731", + "name": "Amulet of power", + "examine": "An enchanted diamond amulet of power.", + "archery_ticket_price": "0", + "bonuses": "6,6,6,6,6,6,6,6,6,6,6,6,1,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "3525", + "tradeable": "true" + }, + { + "id": "1732", + "name": "Amulet of power", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1733", + "name": "Needle", + "examine": "Used with a thread to make clothes.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "1734", + "name": "Thread", + "examine": "Use with a needle to make clothes.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", + "shop_price": "4", + "tradeable": "true" + }, + { + "id": "1735", + "name": "Shears", + "examine": "For shearing sheep.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1736", + "name": "Shears", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1737", + "name": "Wool", + "examine": "I think this came from a sheep.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.14" + }, + { + "id": "1738", + "name": "Wool", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1739", + "name": "Cowhide", + "examine": "I should take this to the tannery.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "1740", + "name": "Cowhide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1741", + "name": "Leather", + "examine": "It's a piece of leather.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "2.25" + }, + { + "id": "1742", + "name": "Leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1743", + "name": "Hard leather", + "examine": "It's a piece of hard leather.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "1744", + "name": "Hard leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1745", + "name": "Green d-leather", + "examine": "It's a piece of prepared green dragon hide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "1746", + "name": "Green d-leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1747", + "name": "Black dragonhide", + "examine": "The scaly rough hide from a Black Dragon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "1748", + "name": "Black dragonhide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1749", + "name": "Red dragonhide", + "examine": "The scaly rough hide from a red dragon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3" + }, + { + "id": "1750", + "name": "Red dragonhide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1751", + "name": "Blue dragonhide", + "examine": "The scaly rough hide from a blue dragon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "1752", + "name": "Blue dragonhide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1753", + "name": "Green dragonhide", + "examine": "The scaly rough hide from a green dragon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "1754", + "name": "Green dragonhide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1755", + "name": "Chisel", + "examine": "Good for detailed crafting.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "14", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1756", + "name": "Chisel", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1757", + "name": "Brown apron", + "examine": "A mostly clean apron.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "0.45" + }, + { + "id": "1758", + "name": "Brown apron", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1759", + "name": "Ball of wool", + "examine": "Spun from sheeps' wool.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1760", + "name": "Ball of wool", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1761", + "name": "Soft clay", + "examine": "Clay soft enough to mould.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1762", + "name": "Soft clay", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1763", + "name": "Red dye", + "examine": "A little bottle of red dye.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "1764", + "name": "Red dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1765", + "name": "Yellow dye", + "examine": "A little bottle of yellow dye.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "1766", + "name": "Yellow dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1767", + "name": "Blue dye", + "examine": "A little bottle of blue dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1768", + "name": "Blue dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1769", + "name": "Orange dye", + "examine": "A little bottle of orange dye.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1770", + "name": "Orange dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1771", + "name": "Green dye", + "examine": "A little bottle of green dye.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "1772", + "name": "Green dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1773", + "name": "Purple dye", + "examine": "A little bottle of purple dye.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "1774", + "name": "Purple dye", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1775", + "name": "Molten glass", + "examine": "Hot glass ready to be blown into useful objects.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1776", + "name": "Molten glass", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1777", + "name": "Bow string", + "examine": "I need a bow stave to attach this to.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1778", + "name": "Bow string", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1779", + "name": "Flax", + "examine": "A plant cultivated for fibres (ground); I should use this with a spinning wheel (item).", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "73", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1780", + "name": "Flax", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1781", + "name": "Soda ash", + "examine": "One of the ingredients for making glass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.09" + }, + { + "id": "1782", + "name": "Soda ash", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1783", + "name": "Bucket of sand", + "examine": "One of the ingredients for making glass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "1784", + "name": "Bucket of sand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1785", + "name": "Glassblowing pipe", + "examine": "Used to form molten glass into useful items.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1786", + "name": "Glassblowing pipe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1787", + "name": "Unfired pot", + "examine": "I need to put this in a pottery oven.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1788", + "name": "Unfired pot", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1789", + "name": "Unfired pie dish", + "examine": "I need to put this in a pottery oven.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1790", + "name": "Unfired pie dish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1791", + "name": "Unfired bowl", + "examine": "I need to put this in a pottery oven.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "32", + "tradeable": "true" + }, + { + "id": "1792", + "name": "Unfired bowl", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1793", + "name": "Woad leaf", + "examine": "A slightly bluish leaf.", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "1794", + "name": "Bronze wire", + "examine": "Useful for crafting items.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1795", + "name": "Bronze wire", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1796", + "name": "Silver necklace", + "examine": "Anna's shiny silver coated necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "1" + }, + { + "id": "1797", + "name": "Silver necklace", + "examine": "Anna's shiny silver coated necklace coated with a thin layer of flour.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "1" + }, + { + "id": "1798", + "name": "Silver cup", + "examine": "Bob's shiny silver coated tea cup.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1799", + "name": "Silver cup", + "examine": "Bob's shiny silver coated tea cup coated with a thin layer of flour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1800", + "name": "Silver bottle", + "examine": "Carol's shiny silver coated bottle.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1801", + "name": "Silver bottle", + "examine": "Carol's shiny silver coated bottle coated with a thin layer of flour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1802", + "name": "Silver book", + "examine": "David's shiny silver coated book.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1803", + "name": "Silver book", + "examine": "David's shiny silver coated book coated with a thin layer of flour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1804", + "name": "Silver needle", + "examine": "Elizabeth's shiny silver coated needle.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1805", + "name": "Silver needle", + "examine": "Elizabeth's shiny silver coated needle coated with a thin layer of flour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1806", + "name": "Silver pot", + "examine": "Frank's shiny silver coated pot.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1807", + "name": "Silver pot", + "examine": "Frank's shiny silver coated pot coated with a thin layer of flour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1808", + "name": "Criminal's thread", + "examine": "Some red thread found at the murder scene.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1809", + "name": "Criminal's thread", + "examine": "Some green thread found at the murder scene.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1810", + "name": "Criminal's thread", + "examine": "Some blue thread found at the murder scene.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1811", + "name": "Flypaper", + "examine": "A piece of fly paper. It's sticky.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1812", + "name": "Pungent pot", + "examine": "A pot found at the murder scene, with a sickly odour.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1813", + "name": "Criminal's dagger", + "examine": "A flimsy-looking dagger found at the crime scene.", + "archery_ticket_price": "0", + "attack_audios": "2517,2517,2500,2517", + "durability": null + }, + { + "id": "1814", + "name": "Criminal's dagger", + "examine": "A flimsy looking dagger found at the crime scene coated with a thin layer of flour.", + "archery_ticket_price": "0", + "attack_audios": "2517,2517,2500,2517", + "durability": null + }, + { + "id": "1815", + "name": "Killer's print", + "examine": "The fingerprints of the murderer.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1816", + "name": "Anna's print", + "examine": "An imprint of Anna's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1817", + "name": "Bob's print", + "examine": "An imprint of Bob's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1818", + "name": "Carol's print", + "examine": "An imprint of Carol's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1819", + "name": "David's print", + "examine": "An imprint of David's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1820", + "name": "Elizabeth's print", + "examine": "An imprint of Elizabeth's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1821", + "name": "Frank's print", + "examine": "An imprint of Frank's fingerprint.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1822", + "name": "Unknown print", + "examine": "An unidentified fingerprint taken from the murder weapon.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1823", + "name": "Waterskin(4)", + "examine": "A full waterskin with four portions of water.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1824", + "name": "Waterskin(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1825", + "name": "Waterskin(3)", + "examine": "A nearly full waterskin with three portions of water.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1826", + "name": "Waterskin(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1827", + "name": "Waterskin(2)", + "examine": "A half empty waterskin with two portions of water.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1828", + "name": "Waterskin(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1829", + "name": "Waterskin(1)", + "examine": "A nearly empty waterskin with one portion of water.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1830", + "name": "Waterskin(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1831", + "name": "Waterskin(0)", + "examine": "A completely empty waterskin - you'll need to fill it up.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1832", + "name": "Waterskin(0)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1833", + "name": "Desert shirt", + "examine": "A cool, light desert shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "40", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1834", + "name": "Desert shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1835", + "name": "Desert robe", + "examine": "A cool, light desert robe.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "40", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1836", + "name": "Desert robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1837", + "name": "Desert boots", + "examine": "Comfortable desert shoes.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "1838", + "name": "Desert boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1839", + "name": "Metal key", + "examine": "This key is crudely made. It came from the mining camp Mercenary Captain.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1840", + "name": "Cell door key", + "examine": "A metallic key, usually used by prison guards. (Tourist Trap)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1841", + "name": "Barrel", + "examine": "An empty barrel/A Splendid barrel.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "10" + }, + { + "id": "1842", + "name": "Ana in a barrel", + "examine": "Inventory: A mining barrel with Ana in it.While in the mine cart: It's Ana... in a barrel... in a mine cart.", + "archery_ticket_price": "0", + "durability": null, + "weight": "32" + }, + { + "id": "1843", + "name": "Wrought iron key", + "examine": "This key unlocks a very sturdy gate. Ana gave me this key. (Tourist Trap)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1844", + "name": "Slave shirt", + "examine": "A filthy, smelly, flea infested shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "0.9" + }, + { + "id": "1845", + "name": "Slave robe", + "examine": "A filthy, smelly, flea infested robe.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "0.9" + }, + { + "id": "1846", + "name": "Slave boots", + "examine": "A set of filthy, smelly, flea infested desert slave boots.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "weight": "0.6" + }, + { + "id": "1847", + "name": "Scrumpled paper", + "examine": "A piece of paper with barely legible writing - looks like a recipe!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1848", + "name": "Shantay disclaimer", + "examine": "Very important information.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5" + }, + { + "id": "1849", + "name": "Prototype dart", + "examine": "A prototype throwing dart.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1850", + "name": "Technical plans", + "examine": "Plans of a technical nature.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1851", + "name": "Tenti pineapple", + "examine": "The most delicious of pineapples.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1852", + "name": "Bedabin key", + "examine": "A key to the chest in Captain Siad's room.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1853", + "name": "Prototype dart tip", + "examine": "A protoype dart tip - it looks deadly.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1854", + "name": "Shantay pass", + "examine": "Allows you to pass through the Shantay pass into the Kharid Desert.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5" + }, + { + "id": "1856", + "name": "Guide book", + "examine": "A Tourist's Guide to Ardougne.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1857", + "name": "Totem", + "examine": "The Rantuki tribe's totem.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "1858", + "name": "Address label", + "examine": "It says 'To Lord Handelmort, Handelmort Mansion'.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1859", + "name": "Raw ugthanki meat", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.75" + }, + { + "id": "1860", + "name": "Raw ugthanki meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1861", + "name": "Ugthanki meat", + "examine": "I need to cook this first / Freshly cooked ugthanki meat.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.75" + }, + { + "id": "1862", + "name": "Ugthanki meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1863", + "name": "Pitta dough", + "examine": "I need to cook this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "9", + "tradeable": "true", + "weight": "0.14" + }, + { + "id": "1864", + "name": "Pitta dough", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1865", + "name": "Pitta bread", + "examine": "Nicely baked pitta bread. Needs more ingredients to make a kebab.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1866", + "name": "Pitta bread", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1867", + "name": "Burnt pitta bread", + "examine": "It's all burnt.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1868", + "name": "Burnt pitta bread", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1869", + "name": "Chopped tomato", + "examine": "A mixture of tomatoes in a bowl.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "1870", + "name": "Chopped tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1871", + "name": "Chopped onion", + "examine": "A mixture of onions in a bowl.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1872", + "name": "Chopped onion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1873", + "name": "Chopped ugthanki", + "examine": "Strips of ugthanki meat in a bowl.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1874", + "name": "Chopped ugthanki", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1875", + "name": "Onion & tomato", + "examine": "A mixture of chopped onions and tomatoes in a bowl", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1876", + "name": "Onion & tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1877", + "name": "Ugthanki & onion", + "examine": "A mixture of chopped onions and ugthanki meat in a bowl.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "1878", + "name": "Ugthanki & onion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1879", + "name": "Ugthanki & tomato", + "examine": "A mixture of chopped tomatoes and ugthanki meat in a bowl.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "1880", + "name": "Ugthanki & tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1881", + "name": "Kebab mix", + "examine": "A mixture of chopped tomatoes, onions and ugthanki meat in a bowl", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1882", + "name": "Kebab mix", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1883", + "name": "Ugthanki kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1884", + "name": "Ugthanki kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1885", + "name": "Ugthanki kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1886", + "name": "Ugthanki kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1887", + "name": "Cake tin", + "examine": "Useful for baking cakes.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1888", + "name": "Cake tin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1889", + "name": "Uncooked cake", + "examine": "Now all I need to do is cook it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1890", + "name": "Uncooked cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1891", + "name": "Cake", + "examine": "A plain sponge cake.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1892", + "name": "Cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1893", + "name": "2/3 cake", + "examine": "A plain sponge cake.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1894", + "name": "2/3 cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1895", + "name": "Slice of cake", + "examine": "A plain sponge cake.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1896", + "name": "Slice of cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1897", + "name": "Chocolate cake", + "examine": "This looks very tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1898", + "name": "Chocolate cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1899", + "name": "2/3 chocolate cake", + "examine": "This looks very tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1900", + "name": "2/3 chocolate cake", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1901", + "name": "Chocolate slice", + "examine": "This looks very tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1902", + "name": "Chocolate slice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1904", + "name": "Burnt cake", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1905", + "name": "Asgarnian ale", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "1906", + "name": "Asgarnian ale", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1907", + "name": "Wizard's mind bomb", + "examine": "It's got strange bubbles in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "1908", + "name": "Wizard's mind bomb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1909", + "name": "Greenman's ale", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "1910", + "name": "Greenman's ale", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1911", + "name": "Dragon bitter", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1912", + "name": "Dragon bitter", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1913", + "name": "Dwarven stout", + "examine": "A pint of thick dark beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "1914", + "name": "Dwarven stout", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1915", + "name": "Grog", + "examine": "A murky glass full of your average, everyday pirate slosh.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "2" + }, + { + "id": "1916", + "name": "Grog", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1917", + "name": "Beer", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1918", + "name": "Beer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1919", + "name": "Beer glass", + "examine": "I need to fill this with beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1920", + "name": "Beer glass", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1921", + "name": "Bowl of water", + "examine": "It's a bowl of water.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1922", + "name": "Bowl of water", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1923", + "name": "Bowl", + "examine": "Useful for mixing things.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "4", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1924", + "name": "Bowl", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1925", + "name": "Bucket", + "examine": "It's an empty bucket.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1926", + "name": "Bucket", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1927", + "name": "Bucket of milk", + "examine": "It's a bucket of milk.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "12", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "1928", + "name": "Bucket of milk", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1929", + "name": "Bucket of water", + "examine": "It's a bucket of water.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3" + }, + { + "id": "1930", + "name": "Bucket of water", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1931", + "name": "Empty pot", + "examine": "This pot is empty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.45" + }, + { + "id": "1932", + "name": "Empty pot", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1933", + "name": "Pot of flour", + "examine": "There is flour in this pot.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "14", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "1934", + "name": "Pot of flour", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1935", + "name": "Jug", + "examine": "This jug is empty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "1", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1936", + "name": "Jug", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1937", + "name": "Jug of water", + "examine": "It's full of water.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "1", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1938", + "name": "Jug of water", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1939", + "name": "Swamp tar", + "examine": "A foul smelling thick tar like substance.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1940", + "name": "Raw swamp paste", + "examine": "A tar-like substance mixed with flour and warmed.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "38", + "tradeable": "true" + }, + { + "id": "1941", + "name": "Swamp paste", + "examine": "A tar-like substance mixed with flour and warmed.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "38", + "tradeable": "true" + }, + { + "id": "1942", + "name": "Potato", + "examine": "This could be used to make a good stew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "shop_price": "11", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1943", + "name": "Potato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1944", + "name": "Egg", + "examine": "A nice fresh egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "1945", + "name": "Egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1946", + "name": "Flour", + "examine": "There is flour in this pot.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "14", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "1947", + "name": "Grain", + "examine": "Some wheat heads.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.6" + }, + { + "id": "1948", + "name": "Grain", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1949", + "name": "Chef's hat", + "examine": "What a silly hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "1950", + "name": "Chef's hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1951", + "name": "Redberries", + "examine": "Very bright red berries.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "3", + "tradeable": "true", + "weight": "1" + }, + { + "id": "1952", + "name": "Redberries", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1953", + "name": "Pastry dough", + "examine": "Potentially pastry.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.16" + }, + { + "id": "1954", + "name": "Pastry dough", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1955", + "name": "Cooking apple", + "examine": "Keeps the doctor away.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "36", + "tradeable": "true" + }, + { + "id": "1956", + "name": "Cooking apple", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1957", + "name": "Onion", + "examine": "A strong smelling onion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "12", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1958", + "name": "Onion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1959", + "name": "Pumpkin", + "examine": "Happy Halloween.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1960", + "name": "Pumpkin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1961", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1962", + "name": "Easter egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1963", + "name": "Banana", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "1964", + "name": "Banana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1965", + "name": "Cabbage", + "examine": "Yuck, I don't like cabbage.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1966", + "name": "Cabbage", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1967", + "name": "Cabbage", + "examine": "Yuck, a cabbage from Draynor Manor. I don't like cabbage.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1968", + "name": "Cabbage", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1969", + "name": "Spinach roll", + "examine": "A home made spinach thing.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "0.1" + }, + { + "id": "1971", + "name": "Kebab", + "examine": "A meaty kebab.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "1972", + "name": "Kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1973", + "name": "Chocolate bar", + "examine": "Mmmmmmm chocolate.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "1974", + "name": "Chocolate bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1975", + "name": "Chocolate dust", + "examine": "It's ground-up chocolate.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "1976", + "name": "Chocolate dust", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1977", + "name": "Chocolatey milk", + "examine": "Milk with chocolate in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "1978", + "name": "Cup of tea", + "examine": "A nice cup of tea.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "1979", + "name": "Cup of tea", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1980", + "name": "Empty cup", + "examine": "An empty cup.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "1981", + "name": "Empty cup", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1982", + "name": "Tomato", + "examine": "This would make good ketchup.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "25", + "tradeable": "true" + }, + { + "id": "1983", + "name": "Tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1984", + "name": "Rotten apple", + "examine": "Rotten to the core!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "1985", + "name": "Cheese", + "examine": "It's got holes in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "1986", + "name": "Cheese", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "1987", + "name": "Grapes", + "examine": "Good grapes for wine making.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1988", + "name": "Grapes", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1989", + "name": "Half full wine jug", + "examine": "An optimist would say it is half full.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1990", + "name": "Half full wine jug", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "1991", + "name": "Jug of bad wine", + "examine": "Oh dear, this wine is terrible!", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1992", + "name": "Jug of bad wine", + "examine": "Oh dear, this wine is terrible!", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1993", + "name": "Jug of wine", + "examine": "It's full of wine.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "128", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "1994", + "name": "Jug of wine", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1995", + "name": "Unfermented wine", + "examine": "This wine needs to ferment before it can be drunk.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1996", + "name": "Unfermented wine", + "examine": "This wine needs to ferment before it can be drunk.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1997", + "name": "Incomplete stew", + "examine": "I need to add some meat too.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "1998", + "name": "Incomplete stew", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "1999", + "name": "Incomplete stew", + "examine": "I need to add some potato too.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2000", + "name": "Incomplete stew", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2001", + "name": "Uncooked stew", + "examine": "I need to cook this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "shop_price": "1", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2002", + "name": "Uncooked stew", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2003", + "name": "Stew", + "examine": "It's a meat and potato stew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "shop_price": "20", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "2004", + "name": "Stew", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2006", + "name": "Burnt stew", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2007", + "name": "Spice", + "examine": "This could liven up an otherwise bland stew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "230", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2008", + "name": "Spice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2009", + "name": "Uncooked curry", + "examine": "I need to cook this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2010", + "name": "Uncooked curry", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2011", + "name": "Curry", + "examine": "It's a spicy hot curry.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "2012", + "name": "Curry", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "2013", + "name": "Burnt curry", + "examine": "Eew, it's horribly burnt.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2014", + "name": "Burnt curry", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2015", + "name": "Vodka", + "examine": "An absolutely clear spirit sold by well-stocked bars.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2016", + "name": "Vodka", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2017", + "name": "Whisky", + "examine": "This Draynor malt is sold by well-stocked bars.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2018", + "name": "Whisky", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2019", + "name": "Gin", + "examine": "A strong spirit that tastes of Juniper; sold at well-stocked bars.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2020", + "name": "Gin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2021", + "name": "Brandy", + "examine": "A strong spirit best served in a large glass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "2022", + "name": "Brandy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2023", + "name": "Cocktail guide", + "examine": "A book on tree gnome cocktails.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2024", + "name": "Cocktail guide", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2025", + "name": "Cocktail shaker", + "examine": "Used for mixing cocktails.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2026", + "name": "Cocktail glass", + "examine": "For sipping cocktails.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "2027", + "name": "Cocktail glass", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2028", + "name": "Premade blurb' sp.", + "examine": "A premade Blurberry Special.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2029", + "name": "Premade blurb' sp.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2030", + "name": "Premade choc s'dy", + "examine": "A premade Chocolate Saturday.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2031", + "name": "Premade choc s'dy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2032", + "name": "Premade dr' dragon", + "examine": "A premade Drunk Dragon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2033", + "name": "Premade dr' dragon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2034", + "name": "Premade fr' blast", + "examine": "A premade Fruit Blast.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2035", + "name": "Premade fr' blast", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2036", + "name": "Premade p' punch", + "examine": "A premade Pineapple Punch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2037", + "name": "Premade p' punch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2038", + "name": "Premade sgg", + "examine": "A premade Short Green Guy.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2039", + "name": "Premade sgg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2040", + "name": "Premade wiz blz'd", + "examine": "A Premade Wizard Blizzard.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2041", + "name": "Premade wiz blz'd", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2042", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2043", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2044", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2045", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2046", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2047", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2048", + "name": "Pineapple punch", + "examine": "A fresh healthy fruit mix.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true" + }, + { + "id": "2049", + "name": "Pineapple punch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2050", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2051", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2052", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2053", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2054", + "name": "Wizard blizzard", + "examine": "This looks like a strange mix.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2055", + "name": "Wizard blizzard", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2056", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2057", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2058", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2059", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2060", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2061", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2062", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2063", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2064", + "name": "Blurberry special", + "examine": "Looks good... smells strong.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "2065", + "name": "Blurberry special", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2066", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2067", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2068", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2069", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2070", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2071", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2072", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2073", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2074", + "name": "Choc saturday", + "examine": "A warm creamy alcoholic beverage", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2075", + "name": "Choc saturday", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2076", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2077", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2078", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2079", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2080", + "name": "Short green guy", + "examine": "A Short Green Guy... looks good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true" + }, + { + "id": "2081", + "name": "Short green guy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2082", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2083", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2084", + "name": "Fruit blast", + "examine": "A cool refreshing fruit mix.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true" + }, + { + "id": "2085", + "name": "Fruit blast", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2086", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2087", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2088", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2089", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2090", + "name": "Picture", + "examine": "This cocktail is just missing those little finishing touches.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2091", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2092", + "name": "Drunk dragon", + "examine": "A warm creamy alcoholic beverage", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "28", + "tradeable": "true" + }, + { + "id": "2093", + "name": "Drunk dragon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2094", + "name": "Picture", + "examine": "I'm not completely sure what this contains.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2095", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2096", + "name": "Picture", + "examine": "I'm not completely sure what this contains.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2097", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2098", + "name": "Picture", + "examine": "I'm not completely sure what this contains.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2099", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2100", + "name": "Picture", + "examine": "I'm not completely sure what this contains.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2101", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2102", + "name": "Lemon", + "examine": "A common fruit, popularly used in cocktails.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2103", + "name": "Lemon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2104", + "name": "Lemon chunks", + "examine": "The not-too-surprising result of using a knife with a lemon!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2105", + "name": "Lemon chunks", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2106", + "name": "Lemon slices", + "examine": "The not-too-surprising result of using a knife with a lemon!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2107", + "name": "Lemon slices", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2108", + "name": "Orange", + "examine": "A common fruit.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "70", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2109", + "name": "Orange", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2110", + "name": "Orange chunks", + "examine": "Fresh chunks of orange.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2111", + "name": "Orange chunks", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2112", + "name": "Orange slices", + "examine": "Fresh orange slices.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2113", + "name": "Orange slices", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2114", + "name": "Pineapple", + "examine": "It can be cut up into something more manageable with a knife.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.15" + }, + { + "id": "2115", + "name": "Pineapple", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2116", + "name": "Pineapple chunks", + "examine": "Fresh chunks of pineapple.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2117", + "name": "Pineapple chunks", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2118", + "name": "Pineapple ring", + "examine": "The not-too-surprising result of using a knife with a pineapple!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2119", + "name": "Pineapple ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2120", + "name": "Lime", + "examine": "A common fruit, popularly used in cocktails.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2121", + "name": "Lime", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2122", + "name": "Lime chunks", + "examine": "Fresh chunks of lime.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2123", + "name": "Lime chunks", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2124", + "name": "Lime slices", + "examine": "The not-too-surprising result of using a knife with a lime!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2125", + "name": "Lime slices", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2126", + "name": "Dwellberries", + "examine": "Some rather pretty blue berries picked from a dwellberry bush.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "2127", + "name": "Dwellberries", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2128", + "name": "Equa leaves", + "examine": "A common ingredient in gnome cuisine.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "2129", + "name": "Equa leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2130", + "name": "Pot of cream", + "examine": "Fresh cream.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2131", + "name": "Pot of cream", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2132", + "name": "Raw beef", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "58", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "2133", + "name": "Raw beef", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2134", + "name": "Raw rat meat", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2135", + "name": "Raw rat meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2136", + "name": "Raw bear meat", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "8", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2137", + "name": "Raw bear meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2138", + "name": "Raw chicken", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "60", + "tradeable": "true", + "weight": "0.15" + }, + { + "id": "2139", + "name": "Raw chicken", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2140", + "name": "Cooked chicken", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2141", + "name": "Cooked chicken", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2142", + "name": "Cooked meat", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2143", + "name": "Cooked meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2145", + "name": "Burnt chicken", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2146", + "name": "Burnt meat", + "examine": "Oh dear, it's totally burnt!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2147", + "name": "Burnt meat", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2148", + "name": "Raw lava eel", + "examine": "A very strange eel.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "2149", + "name": "Lava eel", + "examine": "Strange, it looks cooler now it's been cooked.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.15" + }, + { + "id": "2150", + "name": "Swamp toad", + "examine": "A slippery little blighter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2151", + "name": "Swamp toad", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2152", + "name": "Toad's legs", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2153", + "name": "Toad's legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2154", + "name": "Picture", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2155", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2156", + "name": "Spicy toad's legs", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2157", + "name": "Spicy toad's legs", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2158", + "name": "Seasoned legs", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2159", + "name": "Seasoned legs", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2160", + "name": "Spicy worm", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2161", + "name": "Spicy worm", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2162", + "name": "King worm", + "examine": "They're a gnome delicacy apparently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2163", + "name": "King worm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2164", + "name": "Batta tin", + "examine": "A deep tin used to make gnome battas in.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2165", + "name": "Crunchy tray", + "examine": "A shallow tray used to make crunchies in.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2166", + "name": "Gnomebowl mould", + "examine": "A large ovenproof bowl.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2167", + "name": "Gianne's cook book", + "examine": "Aluft Gianne's favourite dishes.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2168", + "name": "Gianne's cook book", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2169", + "name": "Gnome spice", + "examine": "It's Aluft Gianne's secret mix of spices.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.14" + }, + { + "id": "2170", + "name": "Gnome spice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2171", + "name": "Gianne dough", + "examine": "It's made from a secret recipe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2172", + "name": "Gianne dough", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2173", + "name": "Picture", + "examine": "This gnome bowl doesn't look very appetizing.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "2174", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2176", + "name": "Burnt gnomebowl", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2177", + "name": "Half baked bowl", + "examine": "This gnomebowl is in the early stages of preparation.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "2178", + "name": "Raw gnomebowl", + "examine": "This gnomebowl needs cooking.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2179", + "name": "Picture", + "examine": "This unfinished XX needs YY.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2180", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2181", + "name": "Picture", + "examine": "This unfinished XX needs YY.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2182", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2183", + "name": "Picture", + "examine": "This unfinished XX needs YY.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2184", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2185", + "name": "Chocolate bomb", + "examine": "Full of creamy, chocolately goodness.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "450", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "2186", + "name": "Chocolate bomb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2187", + "name": "Tangled toads' legs", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "2188", + "name": "Tangled toads' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2189", + "name": "Picture", + "examine": "This unfinished XX needs YY.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2190", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2191", + "name": "Worm hole", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2192", + "name": "Worm hole", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2193", + "name": "Picture", + "examine": "This unfinished XX needs YY.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2194", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2195", + "name": "Veg ball", + "examine": "This looks pretty healthy.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2196", + "name": "Veg ball", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2197", + "name": "Odd crunchies", + "examine": "These crunchies don't look very appetising.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2198", + "name": "Odd crunchies", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2200", + "name": "Burnt crunchies", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2201", + "name": "Half baked crunchy", + "examine": "This crunchy is in the early stages of preparation.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2202", + "name": "Raw crunchies", + "examine": "These crunchies need cooking.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2203", + "name": "Picture", + "examine": "This XX crunchy needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2204", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2205", + "name": "Worm crunchies", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "80", + "tradeable": "true" + }, + { + "id": "2206", + "name": "Worm crunchies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2207", + "name": "Picture", + "examine": "This XX crunchy needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2208", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2209", + "name": "Chocchip crunchies", + "examine": "Yum... smells good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "70", + "tradeable": "true" + }, + { + "id": "2210", + "name": "Chocchip crunchies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2211", + "name": "Picture", + "examine": "This XX crunchy needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2212", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2213", + "name": "Spicy crunchies", + "examine": "Yum...smells spicy.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "70", + "tradeable": "true" + }, + { + "id": "2214", + "name": "Spicy crunchies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2215", + "name": "Picture", + "examine": "This XX crunchy needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2216", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2217", + "name": "Toad crunchies", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2218", + "name": "Toad crunchies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2219", + "name": "Premade w'm batta", + "examine": "A premade Worm Batta.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2220", + "name": "Premade w'm batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2221", + "name": "Premade t'd batta", + "examine": "A Premade Toad Batta.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2222", + "name": "Premade t'd batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2223", + "name": "Premade c+t batta", + "examine": "A Premade Cheese and Tomato Batta.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2224", + "name": "Premade c+t batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2225", + "name": "Premade fr't batta", + "examine": "A premade Fruit Batta.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2226", + "name": "Premade fr't batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2227", + "name": "Premade veg batta", + "examine": "A Premade Vegetable Batta.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2228", + "name": "Premade veg batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2229", + "name": "Premade choc bomb", + "examine": "A premade Chocolate Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2230", + "name": "Premade choc bomb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2231", + "name": "Premade ttl", + "examine": "A premade Tangled Toads Legs.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2232", + "name": "Premade ttl", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2233", + "name": "Premade worm hole", + "examine": "A premade Worm Hole.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2234", + "name": "Premade worm hole", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2235", + "name": "Premade veg ball", + "examine": "A premade Vegetable Ball.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2236", + "name": "Premade veg ball", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2237", + "name": "Premade w'm crun", + "examine": "Some Premade Worm Crunchies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2238", + "name": "Premade w'm crun", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2239", + "name": "Premade ch' crunch", + "examine": "Some Premade chocchip crunchies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2240", + "name": "Premade ch' crunch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2241", + "name": "Premade s'y crunch", + "examine": "Some premade Spicy Crunchies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2242", + "name": "Premade s'y crunch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2243", + "name": "Premade t'd crunch", + "examine": "Some premade Toad Crunchies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "80", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2244", + "name": "Premade t'd crunch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2245", + "name": "Picture", + "examine": "This batta doesn't look very appetising.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2246", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2248", + "name": "Burnt batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2249", + "name": "Half baked batta", + "examine": "This gnome batta is in the early stages of preparation.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2250", + "name": "Raw batta", + "examine": "This gnome batta needs cooking.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2251", + "name": "Picture", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2252", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2253", + "name": "Worm batta", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2254", + "name": "Worm batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2255", + "name": "Toad batta", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "78", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2256", + "name": "Toad batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2257", + "name": "Picture", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2258", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2259", + "name": "Cheese+tom batta", + "examine": "This smells really good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2260", + "name": "Cheese+tom batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2261", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2262", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2263", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2264", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2265", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2266", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2267", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2268", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2269", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2270", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2271", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2272", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2273", + "name": "Unfinished batta", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2274", + "name": "Unfinished batta", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2275", + "name": "Picture", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2276", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2277", + "name": "Fruit batta", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2278", + "name": "Fruit batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2279", + "name": "Picture", + "examine": "This XX batta needs garnishing with YY.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "2280", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2281", + "name": "Vegetable batta", + "examine": "Well... It looks healthy.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2282", + "name": "Vegetable batta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2283", + "name": "Pizza base", + "examine": "I need to add some tomato next.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "4", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2284", + "name": "Pizza base", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2285", + "name": "Incomplete pizza", + "examine": "I need to add some cheese next.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2286", + "name": "Incomplete pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2287", + "name": "Uncooked pizza", + "examine": "This needs cooking.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2288", + "name": "Uncooked pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2289", + "name": "Plain pizza", + "examine": "A cheese and tomato pizza.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "200", + "tradeable": "true", + "weight": "0.66" + }, + { + "id": "2290", + "name": "Plain pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2291", + "name": "1/2 plain pizza", + "examine": "Half of this plain pizza has been eaten.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "200", + "tradeable": "true", + "weight": "0.66" + }, + { + "id": "2292", + "name": "1/2 plain pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2293", + "name": "Meat pizza", + "examine": "A pizza with bits of meat on it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.83" + }, + { + "id": "2294", + "name": "Meat pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2295", + "name": "1/2 meat pizza", + "examine": "Half of this meat pizza has been eaten.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.83" + }, + { + "id": "2296", + "name": "1/2 meat pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2297", + "name": "Anchovy pizza", + "examine": "A pizza with anchovies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.83" + }, + { + "id": "2298", + "name": "Anchovy pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2299", + "name": "1/2 anchovy pizza", + "examine": "Half of this anchovy pizza has been eaten.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.83" + }, + { + "id": "2300", + "name": "1/2 anchovy pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2301", + "name": "Pineapple pizza", + "examine": "A pizza with bits of pineapple on it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.83" + }, + { + "id": "2302", + "name": "Pineapple pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2303", + "name": "1/2 p'apple pizza", + "examine": "Half of this pineapple pizza has been eaten.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000" + }, + { + "id": "2304", + "name": "1/2 p'apple pizza", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2306", + "name": "Burnt pizza", + "examine": "Oh dear!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2307", + "name": "Bread dough", + "examine": "Some uncooked dough.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2308", + "name": "Bread dough", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2309", + "name": "Bread", + "examine": "Nice crispy bread.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "24", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2310", + "name": "Bread", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2312", + "name": "Burnt bread", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2313", + "name": "Pie dish", + "examine": "Deceptively pie shaped.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "2314", + "name": "Pie dish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2315", + "name": "Pie shell", + "examine": "I need to find a filling for this pie.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2316", + "name": "Pie shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2317", + "name": "Uncooked apple pie", + "examine": "This would be much tastier cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2318", + "name": "Uncooked apple pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2319", + "name": "Uncooked meat pie", + "examine": "This would be much healthier cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2320", + "name": "Uncooked meat pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2321", + "name": "Uncooked berry pie", + "examine": "This would be much more appetising cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2322", + "name": "Uncooked berry pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2323", + "name": "Apple pie", + "examine": "Mmm Apple pie.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "140", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2324", + "name": "Apple pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2325", + "name": "Redberry pie", + "examine": "Looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2326", + "name": "Redberry pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2327", + "name": "Meat pie", + "examine": "Not for vegetarians.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "16", + "tradeable": "true" + }, + { + "id": "2328", + "name": "Meat pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2330", + "name": "Burnt pie", + "examine": "I think I left it on the stove too long.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2331", + "name": "Half a meat pie", + "examine": "Half of it is suitable for vegetarians.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "16", + "tradeable": "true" + }, + { + "id": "2332", + "name": "Half a meat pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2333", + "name": "Half a redberry pie", + "examine": "So tasty I kept some for later.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2334", + "name": "Half a redberry pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2335", + "name": "Half an apple pie", + "examine": "Mmm half an apple pie.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "140", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2336", + "name": "Half an apple pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2337", + "name": "Raw oomlie", + "examine": "Raw meat from the oomlie bird.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2338", + "name": "Raw oomlie", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2339", + "name": "Palm leaf", + "examine": "A thick green palm leaf used by natives to cook meat.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "2340", + "name": "Palm leaf", + "examine": "A thick green palm leaf used by natives to cook meat.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "2341", + "name": "Wrapped oomlie", + "examine": "Oomlie meat in a palm leaf pouch. It just needs to be cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2342", + "name": "Wrapped oomlie", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2343", + "name": "Cooked oomlie wrap", + "examine": "Deliciously cooked oomlie meat in a palm leaf pouch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2344", + "name": "Cooked oomlie wrap", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2346", + "name": "Burnt oomlie wrap", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2347", + "name": "Hammer", + "examine": "Good for hitting things!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "13", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "2348", + "name": "Hammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2349", + "name": "Bronze bar", + "examine": "It's a bar of bronze.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "2350", + "name": "Bronze bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2351", + "name": "Iron bar", + "examine": "It's a bar of iron.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "2352", + "name": "Iron bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2353", + "name": "Steel bar", + "examine": "It's a bar of steel.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" + }, + { + "id": "2354", + "name": "Steel bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2355", + "name": "Silver bar", + "examine": "It's a bar of silver.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" + }, + { + "id": "2356", + "name": "Silver bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2357", + "name": "Gold bar", + "examine": "It's a bar of gold.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" + }, + { + "id": "2358", + "name": "Gold bar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2359", + "name": "Mithril bar", "examine": "It's a bar of mithril.", - "grand_exchange_price": "1920", - "durability": null, - "name": "Mithril bar", - "tradeable": "true", - "weight": "1.6", "archery_ticket_price": "0", - "id": "2359" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "120", + "tradeable": "true", + "weight": "1.6" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1920", - "durability": null, + "id": "2360", "name": "Mithril bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2360" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2361", + "name": "Adamantite bar", "examine": "It's a bar of adamant.", - "grand_exchange_price": "3306", - "durability": null, - "name": "Adamantite bar", - "tradeable": "true", - "weight": "2.04", "archery_ticket_price": "0", - "id": "2361" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "2.04" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "3306", - "durability": null, + "id": "2362", "name": "Adamantite bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2362" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2363", + "name": "Runite bar", "examine": "It's a bar of rune.", - "grand_exchange_price": "13300", - "durability": null, - "name": "Runite bar", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "2363" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "13300", - "durability": null, + "tradeable": "true", + "weight": "2" + }, + { + "id": "2364", "name": "Runite bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2364" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "It's a 'perfect' golden bar.", - "durability": null, + "id": "2365", "name": "'perfect' gold bar", - "weight": "2", + "examine": "It's a 'perfect' golden bar.", "archery_ticket_price": "0", - "id": "2365" + "durability": null, + "weight": "2" }, { - "requirements": "{13,60}", - "ge_buy_limit": "100", + "id": "2366", + "name": "Shield left half", "examine": "The left half of a Dragon Square Shield.", - "grand_exchange_price": "65800", - "durability": null, - "name": "Shield left half", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "2366" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "65800", - "durability": null, - "name": "Shield left half", + "requirements": "{13,60}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2367" + "weight": "2" }, { + "id": "2367", + "name": "Shield left half", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2368", + "name": "Shield right half", + "examine": "The right half of a dragon square shield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{13,60}", "shop_price": "750000", - "ge_buy_limit": "100", - "examine": "The right half of a dragon square shield.", - "grand_exchange_price": "743100", - "durability": null, - "name": "Shield right half", "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "2368" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "743100", - "durability": null, + "id": "2369", "name": "Shield right half", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2369" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "100", - "ge_buy_limit": "5000", + "id": "2370", + "name": "Steel studs", "examine": "A set of studs for leather armour.", - "grand_exchange_price": "792", - "durability": null, - "name": "Steel studs", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "2370" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "792", - "durability": null, + "shop_price": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2371", "name": "Steel studs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2371" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "destroy_message": "This item will likely break if you put it down, and you will have to go looking for another one.", - "examine": "An old statue of an ogre warrior.", - "durability": null, + "id": "2372", "name": "Ogre relic", - "tradeable": "false", - "destroy": "true", - "weight": "3", + "examine": "An old statue of an ogre warrior.", "archery_ticket_price": "0", - "id": "2372" + "destroy": "true", + "destroy_message": "This item will likely break if you put it down, and you will have to go looking for another one.", + "durability": null, + "tradeable": "false", + "weight": "3" }, { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "Part of an ogre relic.", - "durability": null, + "id": "2373", "name": "Relic part 1", - "tradeable": "false", - "destroy": "true", + "examine": "Part of an ogre relic.", "archery_ticket_price": "0", - "id": "2373" + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "Part of an ogre relic.", - "durability": null, + "id": "2374", "name": "Relic part 2", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "2374" - }, - { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", "examine": "Part of an ogre relic.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", "durability": null, + "tradeable": "false" + }, + { + "id": "2375", "name": "Relic part 3", - "tradeable": "false", - "destroy": "true", + "examine": "Part of an ogre relic.", "archery_ticket_price": "0", - "id": "2375" + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "It's a map.", - "durability": null, + "id": "2376", "name": "Skavid map", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "It's a map.", "archery_ticket_price": "0", - "id": "2376" + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "Very tooth-like.", - "durability": null, + "id": "2377", "name": "Ogre tooth", - "tradeable": "false", - "destroy": "true", + "examine": "Very tooth-like.", "archery_ticket_price": "0", - "id": "2377" + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "Formerly the property of the ogre, Toban", - "durability": null, + "id": "2378", "name": "Toban's key", - "tradeable": "false", + "examine": "Formerly the property of the ogre, Toban", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "2378" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Handy if you want to break all your teeth.", - "durability": null, - "name": "Rock cake", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "2379" - }, - { - "destroy_message": "This item will likely break if you put it down here, and you will have to go looking for another.", - "examine": "Eeeeyeeew!", - "durability": null, - "name": "Fingernails", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "2384" - }, - { - "examine": "I can't wear this old thing.", - "durability": null, - "name": "Old robe", - "weight": "1", - "archery_ticket_price": "0", - "id": "2385" - }, - { - "examine": "Looks kind of useless.", - "durability": null, - "name": "Unusual armour", - "weight": "4", - "archery_ticket_price": "0", - "id": "2386" - }, - { - "examine": "Pointy.", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Damaged dagger", - "archery_ticket_price": "0", - "id": "2387" - }, - { - "examine": "Useless as an eye patch.", - "durability": null, - "name": "Tattered eye patch", - "archery_ticket_price": "0", - "id": "2388" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "An empty glass vial.", - "grand_exchange_price": "1", - "durability": null, - "name": "Vial", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2389" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "An empty glass vial.", - "grand_exchange_price": "1", - "durability": null, - "name": "Vial", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2390" - }, - { - "ge_buy_limit": "100", - "examine": "Let's see it fly, now!", - "grand_exchange_price": "350", - "durability": null, - "name": "Ground bat bones", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2391" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "350", - "durability": null, - "name": "Ground bat bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2392" - }, - { "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", - "examine": "It's a stolen bar of gold.", "durability": null, - "name": "Toban's gold", - "tradeable": "false", + "tradeable": "false" + }, + { + "id": "2379", + "name": "Rock cake", + "examine": "Handy if you want to break all your teeth.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "2393" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This is meant to be good for spots.", - "grand_exchange_price": "157", + "id": "2384", + "name": "Fingernails", + "examine": "Eeeeyeeew!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here, and you will have to go looking for another.", "durability": null, - "name": "Potion", + "tradeable": "false" + }, + { + "id": "2385", + "name": "Old robe", + "examine": "I can't wear this old thing.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "2386", + "name": "Unusual armour", + "examine": "Looks kind of useless.", + "archery_ticket_price": "0", + "durability": null, + "weight": "4" + }, + { + "id": "2387", + "name": "Damaged dagger", + "examine": "Pointy.", + "archery_ticket_price": "0", + "attack_audios": "2517,2517,2500,2517", + "durability": null + }, + { + "id": "2388", + "name": "Tattered eye patch", + "examine": "Useless as an eye patch.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2389", + "name": "Vial", + "examine": "An empty glass vial.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "2390", + "name": "Vial", + "examine": "An empty glass vial.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "2391", + "name": "Ground bat bones", + "examine": "Let's see it fly, now!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2394" + "weight": "0.1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A dangerous magical liquid.", - "durability": null, - "name": "Magic ogre potion", + "id": "2392", + "name": "Ground bat bones", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2393", + "name": "Toban's gold", + "examine": "It's a stolen bar of gold.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "This item will likely break if you put it down here and you will have to go looking for another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "2394", + "name": "Potion", + "examine": "This is meant to be good for spots.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { "id": "2395", + "name": "Magic ogre potion", + "examine": "A dangerous magical liquid.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "examine": "A spell is written on this parchment.", - "durability": null, + "id": "2396", "name": "Spell scroll", + "examine": "A spell is written on this parchment.", "archery_ticket_price": "0", - "id": "2396" + "durability": null }, { - "examine": "A tattered old robe.", - "durability": null, + "id": "2397", "name": "Shaman robe", - "weight": "2", + "examine": "A tattered old robe.", "archery_ticket_price": "0", - "id": "2397" + "durability": null, + "weight": "2" }, { - "examine": "Deadly.", - "durability": null, + "id": "2398", "name": "Cave nightshade", + "examine": "Deadly.", "archery_ticket_price": "0", - "id": "2398" + "durability": null }, { - "turn90cw_anim": "821", - "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", - "walk_anim": "819", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "822", - "attack_speed": "5", - "weapon_interface": "6", - "turn180_anim": "820", - "render_anim": "1", - "defence_anim": "388", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "stand_anim": "808", - "name": "Silverlight", - "run_anim": "824", - "archery_ticket_price": "0", "id": "2402", + "name": "Silverlight", + "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0", + "defence_anim": "388", + "durability": null, + "equipment_slot": "3", + "render_anim": "1", + "run_anim": "824", + "stand_anim": "808", "stand_turn_anim": "823", - "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "Scroll containing a powerful enchantment of restoration.", - "durability": null, + "id": "2403", "name": "Hazeel scroll", - "weight": "1", + "examine": "Scroll containing a powerful enchantment of restoration.", "archery_ticket_price": "0", - "id": "2403" + "durability": null, + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Decorative armour; an heirloom of the Carnillean family.", - "durability": null, - "name": "Carnillean armour", - "weight": "9", - "archery_ticket_price": "0", "id": "2405", + "name": "Carnillean armour", + "examine": "Decorative armour; an heirloom of the Carnillean family.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,20,14,8,0,0,5,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "9" }, { - "examine": "A sign of my commitment to Hazeel.", - "durability": null, - "name": "Hazeel's mark", - "archery_ticket_price": "0", "id": "2406", + "name": "Hazeel's mark", + "examine": "A sign of my commitment to Hazeel.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "examine": "A child's ball.", - "durability": null, + "id": "2407", "name": "Ball", + "examine": "A child's ball.", "archery_ticket_price": "0", - "id": "2407" + "durability": null }, { - "examine": "A key to the Witch's house's front door. (Witch's House)", - "durability": null, + "id": "2409", "name": "Door key", + "examine": "A key to the Witch's house's front door. (Witch's House)", "archery_ticket_price": "0", - "id": "2409" + "durability": null }, { - "examine": "A very attractive magnet.", - "durability": null, + "id": "2410", "name": "Magnet", + "examine": "A very attractive magnet.", "archery_ticket_price": "0", - "id": "2410" + "durability": null }, { - "examine": "A cape from the almighty god Saradomin.", - "durability": null, - "name": "Saradomin cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "2412", + "name": "Saradomin cape", + "examine": "A cape from the almighty god Saradomin.", + "archery_ticket_price": "0", "bonuses": "0,0,0,10,0,1,1,2,10,0,5,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "0.4" }, { - "examine": "A cape from the almighty god Guthix.", - "durability": null, - "name": "Guthix cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "2413", + "name": "Guthix cape", + "examine": "A cape from the almighty god Guthix.", + "archery_ticket_price": "0", "bonuses": "0,0,0,10,0,1,1,2,10,0,5,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "0.4" }, { - "examine": "A cape from the almighty god Zamorak.", - "durability": null, - "name": "Zamorak cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "2414", + "name": "Zamorak cape", + "examine": "A cape from the almighty god Zamorak.", + "archery_ticket_price": "0", "bonuses": "0,0,0,10,0,1,1,2,10,0,5,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "80000", - "examine": "A magical staff imbued with the power of Saradomin.", - "durability": null, - "weight": "2.2", - "attack_speed": "4", - "weapon_interface": "1", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "attack_audios": "2555,0,0,0", - "name": "Saradomin staff", - "tradeable": "false", - "archery_ticket_price": "0", "id": "2415", - "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0" - }, - { - "shop_price": "80000", - "examine": "A magical staff imbued with the power of Guthix.", - "durability": null, - "weight": "2", - "attack_speed": "4", - "weapon_interface": "1", - "defence_anim": "420", - "equipment_slot": "3", + "name": "Saradomin staff", + "examine": "A magical staff imbued with the power of Saradomin.", + "archery_ticket_price": "0", "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", - "name": "Guthix staff", + "attack_speed": "4", + "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "shop_price": "80000", "tradeable": "false", - "archery_ticket_price": "0", + "weapon_interface": "1", + "weight": "2.2" + }, + { "id": "2416", - "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0" - }, - { - "shop_price": "80000", - "turn90cw_anim": "1207", - "examine": "A magical staff imbued with the power of Zamorak.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", + "name": "Guthix staff", + "examine": "A magical staff imbued with the power of Guthix.", + "archery_ticket_price": "0", "attack_anims": "419,419,419,419", - "stand_anim": "813", "attack_audios": "2555,0,0,0", + "attack_speed": "4", + "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "shop_price": "80000", "tradeable": "false", - "name": "Zamorak staff", - "run_anim": "1210", - "archery_ticket_price": "0", + "weapon_interface": "1", + "weight": "2" + }, + { "id": "2417", + "name": "Zamorak staff", + "examine": "A magical staff imbued with the power of Zamorak.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "4", + "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "80000", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "examine": "A grey woollen wig.", - "durability": null, - "name": "Wig", + "id": "2418", + "name": "Bronze key", + "examine": "A heavy key made of bronze. (Prince Ali Rescue).", "archery_ticket_price": "0", - "id": "2419" + "durability": null }, { - "examine": "A grey woollen wig.", - "durability": null, + "id": "2419", "name": "Wig", + "examine": "A grey woollen wig.", "archery_ticket_price": "0", - "id": "2421" + "durability": null }, { - "lendable": "true", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "2147483647", - "durability": null, + "id": "2421", + "name": "Wig", + "examine": "A grey woollen wig.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2422", "name": "Blue partyhat", + "examine": "A nice hat from a cracker.", "archery_ticket_price": "0", - "id": "2422" + "durability": null, + "lendable": "true" }, { - "examine": "Imprint of a key in a lump of clay.", - "durability": null, + "id": "2423", "name": "Key print", + "examine": "Imprint of a key in a lump of clay.", "archery_ticket_price": "0", - "id": "2423" + "durability": null }, { - "examine": "A bottle of skin coloured paste.", - "durability": null, + "id": "2424", "name": "Paste", + "examine": "A bottle of skin coloured paste.", "archery_ticket_price": "0", - "id": "2424" + "durability": null }, { - "attack_audios": "2704,0,0,0", - "durability": null, + "id": "2425", "name": "Picture", "archery_ticket_price": "0", - "id": "2425" + "attack_audios": "2704,0,0,0", + "durability": null }, { - "durability": null, + "id": "2427", "name": "Burnt oomlie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2427" + "durability": null, + "tradeable": "true" }, { - "shop_price": "8002", - "ge_buy_limit": "100", + "id": "2428", + "name": "Attack potion(4)", "examine": "4 doses of Attack potion.", - "grand_exchange_price": "275", - "durability": null, - "name": "Attack potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2428" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "8002", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "275", - "durability": null, + "id": "2429", "name": "Attack potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2429" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "139", - "ge_buy_limit": "100", + "id": "2430", + "name": "Restore potion(4)", "examine": "4 doses of restore potion.", - "grand_exchange_price": "101", - "durability": null, - "name": "Restore potion(4)", - "tradeable": "true", - "weight": "0.03", "archery_ticket_price": "0", - "id": "2430" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "139", + "tradeable": "true", + "weight": "0.03" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "101", - "durability": null, + "id": "2431", "name": "Restore potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2431" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "7832", - "ge_buy_limit": "100", + "id": "2432", + "name": "Defence potion(4)", "examine": "4 doses of Defence Potion.", - "grand_exchange_price": "448", - "durability": null, - "name": "Defence potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2432" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "7832", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "448", - "durability": null, + "id": "2433", "name": "Defence potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2433" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2434", + "name": "Prayer potion(4)", "examine": "4 doses of Prayer restore potion.", - "grand_exchange_price": "5687", - "durability": null, - "name": "Prayer potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2434" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "5687", - "durability": null, - "name": "Prayer potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2435" + "tradeable": "true" }, { - "shop_price": "8000", - "ge_buy_limit": "1000", + "id": "2435", + "name": "Prayer potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2436", + "name": "Super attack(4)", "examine": "4 doses of super Attack potion.", - "grand_exchange_price": "898", - "durability": null, - "name": "Super attack(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2436" - }, - { - "shop_price": "8000", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "898", - "durability": null, - "name": "Super attack(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2437" + "shop_price": "8000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2437", + "name": "Super attack(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "8000", + "tradeable": "true" + }, + { + "id": "2438", + "name": "Fishing potion(4)", "examine": "4 doses of Fishing potion.", - "grand_exchange_price": "73", - "durability": null, - "name": "Fishing potion(4)", - "tradeable": "true", - "weight": "0.03", "archery_ticket_price": "0", - "id": "2438" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "73", - "durability": null, - "name": "Fishing potion(4)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2439" + "weight": "0.03" }, { - "shop_price": "8000", - "ge_buy_limit": "1000", + "id": "2439", + "name": "Fishing potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2440", + "name": "Super strength(4)", "examine": "4 doses of super Strength potion.", - "grand_exchange_price": "2278", - "durability": null, - "name": "Super strength(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2440" - }, - { + "durability": null, + "ge_buy_limit": "1000", "shop_price": "8000", - "ge_buy_limit": "1000", - "grand_exchange_price": "2278", - "durability": null, + "tradeable": "true" + }, + { + "id": "2441", "name": "Super strength(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2441" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "8000", + "tradeable": "true" }, { - "shop_price": "7000", - "ge_buy_limit": "1000", + "id": "2442", + "name": "Super defence(4)", "examine": "4 doses of super Defence potion.", - "grand_exchange_price": "153", - "durability": null, - "name": "Super defence(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2442" - }, - { + "durability": null, + "ge_buy_limit": "1000", "shop_price": "7000", - "ge_buy_limit": "1000", - "grand_exchange_price": "153", - "durability": null, + "tradeable": "true" + }, + { + "id": "2443", "name": "Super defence(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2443" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "7000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "2444", + "name": "Ranging potion(4)", "examine": "4 doses of ranging potion.", - "grand_exchange_price": "8317", - "durability": null, - "name": "Ranging potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2444" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "8317", - "durability": null, + "id": "2445", "name": "Ranging potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2445" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "375", - "ge_buy_limit": "1000", + "id": "2446", + "name": "Antipoison(4)", "examine": "4 doses of antipoison potion.", - "grand_exchange_price": "1624", - "durability": null, - "name": "Antipoison(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2446" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "375", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1624", - "durability": null, + "id": "2447", "name": "Antipoison(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2447" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "2448", + "name": "Super antipoison(4)", "examine": "4 doses of super anti poison potion.", - "grand_exchange_price": "1062", - "durability": null, - "name": "Super antipoison(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2448" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1062", - "durability": null, + "id": "2449", "name": "Super antipoison(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2449" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "2450", + "name": "Zamorak brew(4)", "examine": "4 doses of Zamorak brew.", - "grand_exchange_price": "2838", - "durability": null, - "name": "Zamorak brew(4)", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "2450" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "2838", - "durability": null, + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2451", "name": "Zamorak brew(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2451" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2452", + "name": "Antifire potion(4)", "examine": "4 doses of anti-firebreath potion.", - "grand_exchange_price": "4743", - "durability": null, - "name": "Antifire potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2452" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4743", - "durability": null, + "id": "2453", "name": "Antifire potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2453" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2454", + "name": "Antifire potion(3)", "examine": "3 doses of anti-firebreath potion.", - "grand_exchange_price": "3518", - "durability": null, - "name": "Antifire potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2454" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3518", - "durability": null, + "id": "2455", "name": "Antifire potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2455" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2456", + "name": "Antifire potion(2)", "examine": "2 doses of anti-firebreath potion.", - "grand_exchange_price": "2291", - "durability": null, - "name": "Antifire potion(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2456" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2291", - "durability": null, + "id": "2457", "name": "Antifire potion(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2457" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2458", + "name": "Antifire potion(1)", "examine": "1 dose of anti-firebreath potion.", - "grand_exchange_price": "1099", - "durability": null, - "name": "Antifire potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2458" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1099", - "durability": null, + "id": "2459", "name": "Antifire potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2459" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "704", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", "id": "2460", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "704", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2461" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "704", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2461", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2462", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "704", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2463" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "638", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2463", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2464", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "638", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2465" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "629", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2465", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2466", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "629", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2467" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "639", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2467", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2468", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "639", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2469" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "613", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2469", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2470", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "613", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2471" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "711", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2471", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2472", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "711", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2473" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "48700", - "attack_audios": "2566", - "name": "Flowers", - "tradeable": "true", "archery_ticket_price": "0", + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2473", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2474", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "48700", - "durability": null, "name": "Flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2475" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", "examine": "A posy of flowers.", - "durability": null, - "weight": "0.02", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "403", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "381,381,381,381", - "fun_weapon": "true", - "grand_exchange_price": "5975", "attack_audios": "2566", - "name": "Flowers", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" + }, + { + "id": "2475", + "name": "Flowers", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2476", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5975", - "durability": null, "name": "Flowers", - "tradeable": "true", + "examine": "A posy of flowers.", "archery_ticket_price": "0", - "id": "2477" + "attack_anims": "381,381,381,381", + "attack_audios": "2566", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "12", + "weight": "0.02" }, { - "ge_buy_limit": "10000", + "id": "2477", + "name": "Flowers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2481", + "name": "Clean lantadyme", "examine": "A fresh herb.", - "grand_exchange_price": "7274", - "durability": null, - "name": "Clean lantadyme", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2481" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "7274", - "durability": null, - "name": "Clean lantadyme", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2482" + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2482", + "name": "Clean lantadyme", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2483", + "name": "Lantadyme potion(unf)", "examine": "I need another ingredient to finish this lantadyme potion.", - "grand_exchange_price": "6940", - "durability": null, - "name": "Lantadyme potion(unf)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2483" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "6940", - "durability": null, + "tradeable": "true" + }, + { + "id": "2484", "name": "Lantadyme potion(unf)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2484" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{15,67}", - "ge_buy_limit": "10000", + "id": "2485", + "name": "Grimy lantadyme", "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "900", - "durability": null, - "name": "Grimy lantadyme", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2485" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "7210", - "durability": null, - "name": "Grimy lantadyme", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2486" + "requirements": "{15,67}", + "tradeable": "true" }, { - "requirements": "{4,50}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "1705", - "name": "Blue d'hide vamb", - "tradeable": "true", + "id": "2486", + "name": "Grimy lantadyme", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { "id": "2487", - "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1705", - "durability": null, "name": "Blue d'hide vamb", - "tradeable": "true", + "examine": "Made from 100% real dragonhide.", "archery_ticket_price": "0", - "id": "2488" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "5000", - "examine": "Vambraces made from 100% real dragonhide.", + "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,0,0,0,0", "durability": null, - "weight": "0.25", + "equip_audio": "2241", "equipment_slot": "9", + "ge_buy_limit": "5000", "lendable": "true", - "grand_exchange_price": "2046", - "name": "Red d'hide vamb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2489", - "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2046", - "durability": null, - "name": "Red d'hide vamb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2490" - }, - { - "requirements": "{4,70}", - "ge_buy_limit": "5000", - "examine": "Vambraces made from 100% real dragonhide.", - "durability": null, - "weight": "0.25", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "2506", - "name": "Black d'hide vamb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2491", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2506", - "durability": null, - "name": "Black d'hide vamb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2492" - }, - { "requirements": "{4,50}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2488", + "name": "Blue d'hide vamb", + "archery_ticket_price": "0", "durability": null, - "weight": "5.4", - "absorb": "0,5,2", - "equipment_slot": "7", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2489", + "name": "Red d'hide vamb", + "examine": "Vambraces made from 100% real dragonhide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "5000", "lendable": "true", - "grand_exchange_price": "2504", - "name": "Blue d'hide chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2493", - "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2504", - "durability": null, - "name": "Blue d'hide chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2494" - }, - { "requirements": "{4,60}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2490", + "name": "Red d'hide vamb", + "archery_ticket_price": "0", "durability": null, - "weight": "5.4", - "absorb": "0,6,3", - "equipment_slot": "7", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2491", + "name": "Black d'hide vamb", + "examine": "Vambraces made from 100% real dragonhide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "5000", "lendable": "true", - "grand_exchange_price": "3142", - "name": "Red d'hide chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2495", - "bonuses": "0,0,0,-10,14,28,22,30,20,28,25,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3142", - "durability": null, - "name": "Red d'hide chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2496" - }, - { "requirements": "{4,70}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2492", + "name": "Black d'hide vamb", + "archery_ticket_price": "0", "durability": null, - "weight": "5.4", - "absorb": "0,7,3", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2493", + "name": "Blue d'hide chaps", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "7", + "ge_buy_limit": "5000", "lendable": "true", - "grand_exchange_price": "4744", - "name": "Black d'hide chaps", + "requirements": "{4,50}", "tradeable": "true", + "weight": "5.4" + }, + { + "id": "2494", + "name": "Blue d'hide chaps", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2495", + "name": "Red d'hide chaps", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,14,28,22,30,20,28,25,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{4,60}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "2496", + "name": "Red d'hide chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "2497", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "4744", - "durability": null, "name": "Black d'hide chaps", - "tradeable": "true", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,7,3", "archery_ticket_price": "0", - "id": "2498" + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{4,70}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,40}-{4,50}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "6.8", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "5420", - "name": "Blue d'hide body", - "tradeable": "true", + "id": "2498", + "name": "Black d'hide chaps", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "2499", - "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "5420", - "durability": null, "name": "Blue d'hide body", - "tradeable": "true", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", "archery_ticket_price": "0", - "id": "2500" + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "6.8" }, { - "requirements": "{1,40}-{4,60}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "6.8", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "6680", - "name": "Red d'hide body", - "tradeable": "true", + "id": "2500", + "name": "Blue d'hide body", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "2501", - "bonuses": "0,0,0,-15,25,50,42,55,40,50,50,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "6680", - "durability": null, "name": "Red d'hide body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2502" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "5000", "examine": "Made from 100% real dragonhide.", - "durability": null, - "weight": "6.8", "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "8098", - "name": "Black d'hide body", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,25,50,42,55,40,50,50,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{1,40}-{4,60}", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "2502", + "name": "Red d'hide body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "2503", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "8098", - "durability": null, "name": "Black d'hide body", - "tradeable": "true", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", "archery_ticket_price": "0", - "id": "2504" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of prepared blue dragonhide.", - "grand_exchange_price": "2888", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0", "durability": null, - "name": "Blue d-leather", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "2505" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2888", - "durability": null, - "name": "Blue d-leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2506" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of prepared red dragonhide.", - "grand_exchange_price": "4464", - "durability": null, - "name": "Red dragon leather", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "2507" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "4464", - "durability": null, - "name": "Red dragon leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2508" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of prepared black dragonhide.", - "grand_exchange_price": "6664", - "durability": null, - "name": "Black d-leather", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "2509" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "6664", - "durability": null, - "name": "Black d-leather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2510" - }, - { - "shop_price": "4", - "examine": "A number of wooden logs.", - "grand_exchange_price": "124", - "durability": null, - "name": "Logs", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "2511" - }, - { - "lendable": "true", - "examine": "A series of connected metal rings.", - "grand_exchange_price": "2902937", - "rare_item": "true", - "durability": null, - "name": "Dragon chainbody", - "tradeable": "true", - "weight": "6.8", - "archery_ticket_price": "0", - "id": "2513", - "absorb": "4,0,9", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "shop_price": "6", - "examine": "I should try cooking this.", - "grand_exchange_price": "6", - "durability": null, - "name": "Raw shrimps", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2514" - }, - { - "durability": null, - "name": "Raw shrimps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2515" - }, - { - "shop_price": "14", - "examine": "There is flour in this pot.", - "grand_exchange_price": "162", - "durability": null, - "name": "Pot of flour", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "2516" - }, - { - "durability": null, - "name": "Pot of flour", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2517" - }, - { - "shop_price": "1", - "examine": "Pretty smelly.", - "durability": null, - "name": "Rotten tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2518" - }, - { - "durability": null, - "name": "Rotten tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2519" - }, - { - "shop_price": "150", - "examine": "An inexpensive white/black/grey/brown toy horse.", - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2520" - }, - { - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2521" - }, - { - "shop_price": "150", - "examine": "An inexpensive white/black/grey/brown toy horse.", - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2522" - }, - { - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2523" - }, - { - "shop_price": "150", - "examine": "An inexpensive white/black/grey/brown toy horse.", - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2524" - }, - { - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2525" - }, - { - "shop_price": "150", - "examine": "An inexpensive white/black/grey/brown toy horse.", - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2526" - }, - { - "durability": null, - "name": "Toy horsey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2527" - }, - { - "bankable": "false", - "examine": "Wonder what happens if I rub it...", - "durability": null, - "name": "Lamp", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2528" - }, - { - "examine": "A magical sphere that glimmers within.", - "durability": null, - "name": "Orb of light", - "weight": "5", - "archery_ticket_price": "0", - "id": "2529" - }, - { - "shop_price": "140", - "grand_exchange_price": "140", - "durability": null, - "name": "Bones", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2530" - }, - { - "durability": null, - "name": "Bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2531" - }, - { - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with iron heads and oil-soaked cloth. lit: An easy to make, iron-headed fire arrow.", - "grand_exchange_price": "316", - "durability": null, - "name": "Iron fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2532", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with iron heads and oil-soaked cloth. lit: An easy to make, iron-headed fire arrow.", - "grand_exchange_price": "305", - "durability": null, - "name": "Iron fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2533", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "requirements": "{4,5}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with steel heads and oil-soaked cloth. lit: An easy to make, steel-headed fire arrow.", - "grand_exchange_price": "325", - "durability": null, - "name": "Steel fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2534", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "requirements": "{4,5}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with steel heads and oil-soaked cloth. lit: An easy to make, steel-headed fire arrow.", - "grand_exchange_price": "327", - "durability": null, - "name": "Steel fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2535", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with mithril heads and oil-soaked cloth. lit: An easy to make, mithril-headed fire arrow.", - "grand_exchange_price": "9", - "durability": null, - "name": "Mithril fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2536", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with mithril heads and oil-soaked cloth. lit: An easy to make, mithril-headed fire arrow.", - "grand_exchange_price": "63", - "durability": null, - "name": "Mithril fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2537", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with adamant heads and oil-soaked cloth.lit: An easy-to-make, Adamant-headed fire arrow.", - "grand_exchange_price": "90", - "durability": null, - "name": "Adamant fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2538", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with adamant heads and oil-soaked cloth.lit: An easy-to-make, Adamant-headed fire arrow.", - "grand_exchange_price": "127", - "durability": null, - "name": "Adamant fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2539", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "An easy to make, rune-headed fire arrow.", - "grand_exchange_price": "539", - "durability": null, - "name": "Rune fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2540", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "An easy to make, rune-headed fire arrow.", - "grand_exchange_price": "682", - "durability": null, - "name": "Rune fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2541", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { - "ge_buy_limit": "5000", - "examine": "An enchanted ring.", - "grand_exchange_price": "943", - "durability": null, - "name": "Ring of recoil", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2550", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "943", - "durability": null, - "name": "Ring of recoil", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2551" - }, - { - "shop_price": "770", - "ge_buy_limit": "10000", - "examine": "An enchanted ring.", - "grand_exchange_price": "1237", - "durability": null, - "name": "Ring of duelling(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2552", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1237", - "durability": null, - "name": "Ring of duelling(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2553" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2554", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2555" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2556", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2557" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2558", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2559" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2560", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2561" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2562", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2563" - }, - { - "shop_price": "770", - "examine": "An enchanted ring.", - "grand_exchange_price": "8", - "durability": null, - "name": "Ring of duelling(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2564", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of duelling(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2565" - }, - { - "shop_price": "770", - "ge_buy_limit": "10000", - "examine": "An enchanted ring.", - "grand_exchange_price": "594", - "durability": null, - "name": "Ring of duelling(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2566", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "594", - "durability": null, - "name": "Ring of duelling(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2567" - }, - { - "ge_buy_limit": "10000", - "examine": "An enchanted ring.", - "grand_exchange_price": "1103", - "durability": null, - "name": "Ring of forging", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2568", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1103", - "durability": null, - "name": "Ring of forging", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2569" - }, - { - "shop_price": "3000", - "ge_buy_limit": "10000", - "examine": "An enchanted ring.", - "grand_exchange_price": "4540", - "durability": null, - "name": "Ring of life", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2570", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "4540", - "durability": null, - "name": "Ring of life", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2571" - }, - { - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of wealth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2572", - "equipment_slot": "12" - }, - { - "durability": null, - "name": "Ring of wealth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2573" - }, - { - "examine": "Used by navigators to find their position in RuneScape.", - "durability": null, - "name": "Sextant", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2574" - }, - { - "examine": "A fine looking time piece.", - "durability": null, - "name": "Watch", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2575" - }, - { - "examine": "A navigator's chart of RuneScape.", - "durability": null, - "name": "Chart", - "archery_ticket_price": "0", - "id": "2576" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "2", - "examine": "Lightweight boots ideal for rangers.", - "durability": null, - "weight": "0.2", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "13900000", - "name": "Ranger boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2577", - "bonuses": "0,0,0,-10,8,2,3,4,2,0,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "13900000", - "durability": null, - "name": "Ranger boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2578" - }, - { - "requirements": "{6,20}", - "ge_buy_limit": "2", - "examine": "Slightly magical boots.", - "durability": null, - "weight": "1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "747300", - "name": "Wizard boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2579", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "747300", - "durability": null, - "name": "Wizard boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2580" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "2", - "examine": "Endorsed by Robin Hood.", - "durability": null, - "weight": "0.2", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "4400000", - "name": "Robin hood hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": "true", - "id": "2581", - "bonuses": "0,0,0,-10,8,4,6,8,4,4,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "4400000", - "durability": null, - "name": "Robin hood hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2582" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black platebody with trim.", - "durability": null, - "weight": "9.07", - "absorb": "1,0,2", + "equip_audio": "2241", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "74900", - "name": "Black platebody (t)", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{1,40}-{4,70}", "tradeable": "true", + "weight": "6.8" + }, + { + "id": "2504", + "name": "Black d'hide body", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2505", + "name": "Blue d-leather", + "examine": "It's a piece of prepared blue dragonhide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "2506", + "name": "Blue d-leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2507", + "name": "Red dragon leather", + "examine": "It's a piece of prepared red dragonhide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3" + }, + { + "id": "2508", + "name": "Red dragon leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2509", + "name": "Black d-leather", + "examine": "It's a piece of prepared black dragonhide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "2510", + "name": "Black d-leather", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2511", + "name": "Logs", + "examine": "A number of wooden logs.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "4", + "tradeable": "true", + "weight": "2" + }, + { + "id": "2513", + "name": "Dragon chainbody", + "examine": "A series of connected metal rings.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0", + "durability": null, + "lendable": "true", + "rare_item": "true", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "2514", + "name": "Raw shrimps", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "6", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2515", + "name": "Raw shrimps", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2516", + "name": "Pot of flour", + "examine": "There is flour in this pot.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "14", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "2517", + "name": "Pot of flour", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2518", + "name": "Rotten tomato", + "examine": "Pretty smelly.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "2519", + "name": "Rotten tomato", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2520", + "name": "Toy horsey", + "examine": "An inexpensive white/black/grey/brown toy horse.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "2521", + "name": "Toy horsey", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2522", + "name": "Toy horsey", + "examine": "An inexpensive white/black/grey/brown toy horse.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "2523", + "name": "Toy horsey", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2524", + "name": "Toy horsey", + "examine": "An inexpensive white/black/grey/brown toy horse.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "2525", + "name": "Toy horsey", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2526", + "name": "Toy horsey", + "examine": "An inexpensive white/black/grey/brown toy horse.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "2527", + "name": "Toy horsey", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2528", + "name": "Lamp", + "examine": "Wonder what happens if I rub it...", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "weight": "0.1" + }, + { + "id": "2529", + "name": "Orb of light", + "examine": "A magical sphere that glimmers within.", + "archery_ticket_price": "0", + "durability": null, + "weight": "5" + }, + { + "id": "2530", + "name": "Bones", + "examine": "Bones are for burying!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "140", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "2531", + "name": "Bones", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2532", + "name": "Iron fire arrows", + "examine": "unlit: Arrows with iron heads and oil-soaked cloth. lit: An easy to make, iron-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2533", + "name": "Iron fire arrows", + "examine": "unlit: Arrows with iron heads and oil-soaked cloth. lit: An easy to make, iron-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2534", + "name": "Steel fire arrows", + "examine": "unlit: Arrows with steel heads and oil-soaked cloth. lit: An easy to make, steel-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "tradeable": "true" + }, + { + "id": "2535", + "name": "Steel fire arrows", + "examine": "unlit: Arrows with steel heads and oil-soaked cloth. lit: An easy to make, steel-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "tradeable": "true" + }, + { + "id": "2536", + "name": "Mithril fire arrows", + "examine": "unlit: Arrows with mithril heads and oil-soaked cloth. lit: An easy to make, mithril-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "tradeable": "true" + }, + { + "id": "2537", + "name": "Mithril fire arrows", + "examine": "unlit: Arrows with mithril heads and oil-soaked cloth. lit: An easy to make, mithril-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "tradeable": "true" + }, + { + "id": "2538", + "name": "Adamant fire arrows", + "examine": "unlit: Arrows with adamant heads and oil-soaked cloth.lit: An easy-to-make, Adamant-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "tradeable": "true" + }, + { + "id": "2539", + "name": "Adamant fire arrows", + "examine": "unlit: Arrows with adamant heads and oil-soaked cloth.lit: An easy-to-make, Adamant-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "tradeable": "true" + }, + { + "id": "2540", + "name": "Rune fire arrows", + "examine": "An easy to make, rune-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true" + }, + { + "id": "2541", + "name": "Rune fire arrows", + "examine": "An easy to make, rune-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true" + }, + { + "id": "2550", + "name": "Ring of recoil", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2551", + "name": "Ring of recoil", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2552", + "name": "Ring of duelling(8)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10000", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2553", + "name": "Ring of duelling(8)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2554", + "name": "Ring of duelling(7)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2555", + "name": "Ring of duelling(7)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2556", + "name": "Ring of duelling(6)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2557", + "name": "Ring of duelling(6)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2558", + "name": "Ring of duelling(5)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2559", + "name": "Ring of duelling(5)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2560", + "name": "Ring of duelling(4)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2561", + "name": "Ring of duelling(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2562", + "name": "Ring of duelling(3)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2563", + "name": "Ring of duelling(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2564", + "name": "Ring of duelling(2)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2565", + "name": "Ring of duelling(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2566", + "name": "Ring of duelling(1)", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10000", + "shop_price": "770", + "tradeable": "true" + }, + { + "id": "2567", + "name": "Ring of duelling(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2568", + "name": "Ring of forging", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2569", + "name": "Ring of forging", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2570", + "name": "Ring of life", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10000", + "shop_price": "3000", + "tradeable": "true" + }, + { + "id": "2571", + "name": "Ring of life", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2572", + "name": "Ring of wealth", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "tradeable": "true" + }, + { + "id": "2573", + "name": "Ring of wealth", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2574", + "name": "Sextant", + "examine": "Used by navigators to find their position in 2009Scape.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "2575", + "name": "Watch", + "examine": "A fine looking time piece.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "2576", + "name": "Chart", + "examine": "A navigator's chart of 2009Scape.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2577", + "name": "Ranger boots", + "examine": "Lightweight boots ideal for rangers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,2,3,4,2,0,10,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2578", + "name": "Ranger boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2579", + "name": "Wizard boots", + "examine": "Slightly magical boots.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{6,20}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2580", + "name": "Wizard boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2581", + "name": "Robin hood hat", + "examine": "Endorsed by Robin Hood.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,4,6,8,4,4,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": "true", + "lendable": "true", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "2582", + "name": "Robin hood hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2583", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "74900", - "durability": null, "name": "Black platebody (t)", - "tradeable": "true", + "examine": "Black platebody with trim.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "2584" - }, - { + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black platelegs with trim.", - "grand_exchange_price": "39900", - "durability": null, - "name": "Black platelegs (t)", "tradeable": "true", - "weight": "9", + "weight": "9.07" + }, + { + "id": "2584", + "name": "Black platebody (t)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2585", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "39900", - "durability": null, "name": "Black platelegs (t)", - "tradeable": "true", + "examine": "Black platelegs with trim.", "archery_ticket_price": "0", - "id": "2586" + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9" }, { + "id": "2586", + "name": "Black platelegs (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2587", + "name": "Black full helm(t)", + "examine": "Black full helmet with trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "100", - "examine": "Black full helmet with trim.", - "durability": null, - "weight": "2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "82200", - "name": "Black full helm(t)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2587", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "82200", - "durability": null, + "id": "2588", "name": "Black full helm(t)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2588" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "2589", + "name": "Black kiteshield (t)", + "examine": "Black kiteshield with trim.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", "requirements": "{1,10}", "shop_price": "2072", - "ge_buy_limit": "2", - "examine": "Black kiteshield with trim.", - "durability": null, - "weight": "5", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "73000", - "name": "Black kiteshield (t)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2589", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" + "weight": "5" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "73000", - "durability": null, + "id": "2590", "name": "Black kiteshield (t)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2590" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black platebody with gold trim.", - "durability": null, - "weight": "9.07", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "589000", - "name": "Black platebody (g)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "2591", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "589000", - "durability": null, "name": "Black platebody (g)", - "tradeable": "true", + "examine": "Black platebody with gold trim.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "2592" + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9.07" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black platelegs with gold trim.", - "grand_exchange_price": "216700", - "durability": null, - "name": "Black platelegs (g)", - "tradeable": "true", - "weight": "9", + "id": "2592", + "name": "Black platebody (g)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2593", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "216700", - "durability": null, "name": "Black platelegs (g)", - "tradeable": "true", + "examine": "Black platelegs with gold trim.", "archery_ticket_price": "0", - "id": "2594" + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9" }, { - "remove_head": "true", - "requirements": "{1,10}", - "ge_buy_limit": "100", - "examine": "Black full helmet with gold trim.", - "durability": null, - "weight": "2.7", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "364800", - "name": "Black full helm(g)", - "tradeable": "true", + "id": "2594", + "name": "Black platelegs (g)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2595", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "364800", - "durability": null, "name": "Black full helm(g)", - "tradeable": "true", + "examine": "Black full helmet with gold trim.", "archery_ticket_price": "0", - "id": "2596" - }, - { + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_beard": "true", + "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black kiteshield with gold trim.", - "durability": null, - "weight": "5", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "355600", - "name": "Black kiteshield (g)", "tradeable": "true", + "weight": "2.7" + }, + { + "id": "2596", + "name": "Black full helm(g)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2597", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "355600", - "durability": null, "name": "Black kiteshield (g)", - "tradeable": "true", + "examine": "Black kiteshield with gold trim.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "2598" + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant platebody with trim.", - "durability": null, - "weight": "9.07", - "absorb": "2,0,4", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "104000", - "name": "Adam platebody (t)", - "tradeable": "true", + "id": "2598", + "name": "Black kiteshield (g)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2599", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "104000", - "durability": null, "name": "Adam platebody (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2600" - }, - { - "requirements": "{1,30}", - "shop_price": "6464", - "ge_buy_limit": "2", - "examine": "These look heavy", - "durability": null, - "weight": "10", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "23200", - "name": "Adam platelegs (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2601", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "23200", - "durability": null, - "name": "Adam platelegs (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2602" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A large metal shield.", - "durability": null, - "weight": "5.8", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "49100", - "name": "Adam kiteshield (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2603", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "49100", - "durability": null, - "name": "Adam kiteshield (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2604" - }, - { - "remove_head": "true", - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant full helm with trim", - "durability": null, - "weight": "2.7", - "absorb": "1,0,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "85000", - "name": "Adam full helm(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2605", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "85000", - "durability": null, - "name": "Adam full helm(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2606" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant platebody with gold trim.", - "durability": null, - "weight": "9.07", + "examine": "Adamant platebody with trim.", "absorb": "2,0,4", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "359500", - "name": "Adam platebody (g)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2607", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "359500", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", "durability": null, - "name": "Adam platebody (g)", + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2608" + "weight": "9.07" }, { + "id": "2600", + "name": "Adam platebody (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2601", + "name": "Adam platelegs (t)", + "examine": "These look heavy", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", "requirements": "{1,30}", "shop_price": "6464", - "ge_buy_limit": "2", - "examine": "Adamant platelegs with gold trim.", - "durability": null, - "weight": "10", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "94300", - "name": "Adam platelegs (g)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2609", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" + "weight": "10" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "94300", - "durability": null, - "name": "Adam platelegs (g)", - "tradeable": "true", + "id": "2602", + "name": "Adam platelegs (t)", "archery_ticket_price": "0", - "id": "2610" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant kiteshield with gold trim.", - "durability": null, - "weight": "6", + "id": "2603", + "name": "Adam kiteshield (t)", + "examine": "A large metal shield.", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "170100", - "name": "Adam kiteshield (g)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2611", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "170100", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", "durability": null, - "name": "Adam kiteshield (g)", + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2612" + "weight": "5.8" }, { + "id": "2604", + "name": "Adam kiteshield (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2605", + "name": "Adam full helm(t)", + "examine": "Adamant full helm with trim", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant full helmet with gold trim.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "222400", - "name": "Adam full helm(g)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2613", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "222400", - "durability": null, - "name": "Adam full helm(g)", - "tradeable": "true", + "id": "2606", + "name": "Adam full helm(t)", "archery_ticket_price": "0", - "id": "2614" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody with gold trim.", + "id": "2607", + "name": "Adam platebody (g)", + "examine": "Adamant platebody with gold trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", "durability": null, - "weight": "9.07", - "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "2608", + "name": "Adam platebody (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2609", + "name": "Adam platelegs (g)", + "examine": "Adamant platelegs with gold trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "shop_price": "6464", + "tradeable": "true", + "weight": "10" + }, + { + "id": "2610", + "name": "Adam platelegs (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2611", + "name": "Adam kiteshield (g)", + "examine": "Adamant kiteshield with gold trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "2612", + "name": "Adam kiteshield (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2613", + "name": "Adam full helm(g)", + "examine": "Adamant full helmet with gold trim.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "2614", + "name": "Adam full helm(g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2615", + "name": "Rune platebody (g)", + "examine": "Rune platebody with gold trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "426200", - "name": "Rune platebody (g)", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2615", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" + "weight": "9.07" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "426200", + "id": "2616", + "name": "Rune platebody (g)", + "archery_ticket_price": "0", "durability": null, - "name": "Rune platebody (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2616" + "ge_buy_limit": "2", + "tradeable": "true" }, { + "id": "2617", + "name": "Rune platelegs (g)", + "examine": "Rune platelegs with gold trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", "requirements": "{1,40}", "shop_price": "64000", - "ge_buy_limit": "2", - "examine": "Rune platelegs with gold trim.", + "tradeable": "true", + "weight": "9" + }, + { + "id": "2618", + "name": "Rune platelegs (g)", + "archery_ticket_price": "0", "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2619", + "name": "Rune full helm(g)", + "examine": "Rune full helmet with gold trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", "lendable": "true", - "grand_exchange_price": "243400", - "name": "Rune platelegs (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2617", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "243400", - "durability": null, - "name": "Rune platelegs (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2618" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,40}", "shop_price": "35000", - "ge_buy_limit": "100", - "examine": "Rune full helmet with gold trim.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "506800", - "name": "Rune full helm(g)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2619", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "506800", - "durability": null, + "id": "2620", "name": "Rune full helm(g)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2620" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune kiteshield with gold trim", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "392700", - "name": "Rune kiteshield (g)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "2621", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "392700", - "durability": null, "name": "Rune kiteshield (g)", - "tradeable": "true", + "examine": "Rune kiteshield with gold trim", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "2622" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody with trim.", - "durability": null, - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "162700", - "name": "Rune platebody (t)", - "tradeable": "true", + "id": "2622", + "name": "Rune kiteshield (g)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2623", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "162700", - "durability": null, "name": "Rune platebody (t)", - "tradeable": "true", + "examine": "Rune platebody with trim.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "2624" + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9.07" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platelegs with trim!", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "95400", - "name": "Rune platelegs (t)", - "tradeable": "true", + "id": "2624", + "name": "Rune platebody (t)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2625", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "95400", - "durability": null, "name": "Rune platelegs (t)", - "tradeable": "true", + "examine": "Rune platelegs with trim!", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "2626" + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune full helmet with trim.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "216600", - "name": "Rune full helm (t)", - "tradeable": "true", + "id": "2626", + "name": "Rune platelegs (t)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2627", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "216600", - "durability": null, "name": "Rune full helm (t)", - "tradeable": "true", + "examine": "Rune full helmet with trim.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "2628" + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.7" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A large, metal shield with a nice trim.", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "160300", - "name": "Rune kiteshield (t)", - "tradeable": "true", + "id": "2628", + "name": "Rune full helm (t)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2629", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "160300", - "durability": null, "name": "Rune kiteshield (t)", - "tradeable": "true", + "examine": "A large, metal shield with a nice trim.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "2630" + "attack_anims": "", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Your money or your life!", - "grand_exchange_price": "83500", - "durability": null, - "name": "Highwayman mask", - "tradeable": "true", + "id": "2630", + "name": "Rune kiteshield (t)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2631", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "83500", - "durability": null, "name": "Highwayman mask", - "tradeable": "true", + "examine": "Your money or your life!", "archery_ticket_price": "0", - "id": "2632" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "34300", - "durability": null, - "name": "Blue beret", - "tradeable": "true", + "id": "2632", + "name": "Highwayman mask", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2633", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "34300", - "durability": null, "name": "Blue beret", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2634" - }, - { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Parlez-vous francais?(Do you speak French?)", - "grand_exchange_price": "117500", - "durability": null, - "name": "Black beret", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "2635", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "117500", - "durability": null, - "name": "Black beret", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2636" - }, - { - "lendable": "true", - "ge_buy_limit": "2", "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "328000", - "durability": null, - "name": "White beret", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", "hat": true, + "lendable": "true", + "tradeable": "true" + }, + { + "id": "2634", + "name": "Blue beret", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2635", + "name": "Black beret", + "examine": "Parlez-vous francais?(Do you speak French?)", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, + "lendable": "true", + "tradeable": "true" + }, + { + "id": "2636", + "name": "Black beret", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2637", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "328000", - "durability": null, "name": "White beret", - "tradeable": "true", + "examine": "Parlez-vous francais? (Do you speak French?)", "archery_ticket_price": "0", - "id": "2638" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "All for one and one for all!", - "grand_exchange_price": "29100", - "durability": null, - "name": "Tan cavalier", - "tradeable": "true", + "id": "2638", + "name": "White beret", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2639", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "29100", - "durability": null, "name": "Tan cavalier", - "tradeable": "true", + "examine": "All for one and one for all!", "archery_ticket_price": "0", - "id": "2640" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "All for one and one for all!", - "grand_exchange_price": "23600", - "durability": null, - "name": "Dark cavalier", - "tradeable": "true", + "id": "2640", + "name": "Tan cavalier", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2641", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "23600", - "durability": null, "name": "Dark cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2642" - }, - { - "lendable": "true", - "ge_buy_limit": "2", "examine": "All for one and one for all!", - "grand_exchange_price": "384500", - "durability": null, - "name": "Black cavalier", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", "hat": true, + "lendable": "true", + "tradeable": "true" + }, + { + "id": "2642", + "name": "Dark cavalier", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2643", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "384500", - "durability": null, "name": "Black cavalier", - "tradeable": "true", + "examine": "All for one and one for all!", "archery_ticket_price": "0", - "id": "2644" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "A minimalist's hat.", - "grand_exchange_price": "91300", - "durability": null, - "name": "Red headband", - "tradeable": "true", + "id": "2644", + "name": "Black cavalier", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2645", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "91300", - "durability": null, "name": "Red headband", - "tradeable": "true", + "examine": "A minimalist's hat.", "archery_ticket_price": "0", - "id": "2646" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "A minimalist's hat.", - "grand_exchange_price": "32900", - "durability": null, - "name": "Black headband", - "tradeable": "true", + "id": "2646", + "name": "Red headband", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2647", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "32900", - "durability": null, "name": "Black headband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2648" - }, - { - "lendable": "true", - "ge_buy_limit": "2", "examine": "A minimalist's hat.", - "grand_exchange_price": "17800", - "durability": null, - "name": "Brown headband", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "2648", + "name": "Black headband", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2649", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "17800", - "durability": null, "name": "Brown headband", - "tradeable": "true", + "examine": "A minimalist's hat.", "archery_ticket_price": "0", - "id": "2650" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "Shiver me timbers!", - "grand_exchange_price": "82500", - "durability": null, - "name": "Pirate's hat", - "tradeable": "true", + "id": "2650", + "name": "Brown headband", "archery_ticket_price": "0", - "hat": "true", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2651", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "82500", - "durability": null, "name": "Pirate's hat", - "tradeable": "true", + "examine": "Shiver me timbers!", "archery_ticket_price": "0", - "id": "2652" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": "true", + "tradeable": "true" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody in the colours of Zamorak.", - "durability": null, - "weight": "9", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "809000", - "name": "Zamorak platebody", - "tradeable": "true", + "id": "2652", + "name": "Pirate's hat", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2653", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "809000", - "durability": null, "name": "Zamorak platebody", - "tradeable": "true", + "examine": "Rune platebody in the colours of Zamorak.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "2654" + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platelegs in the colours of Zamorak.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "281500", - "name": "Zamorak platelegs", - "tradeable": "true", + "id": "2654", + "name": "Zamorak platebody", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2655", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "281500", - "durability": null, "name": "Zamorak platelegs", - "tradeable": "true", + "examine": "Rune platelegs in the colours of Zamorak.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "2656" + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A full helmet in the colours of Zamorak.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "601500", - "name": "Zamorak full helm", - "tradeable": "true", + "id": "2656", + "name": "Zamorak platelegs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2657", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,1" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "601500", - "durability": null, "name": "Zamorak full helm", - "tradeable": "true", + "examine": "A full helmet in the colours of Zamorak.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "2658" + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,1", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A Rune kiteshield in the colours of Zamorak.", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "499500", - "name": "Zamorak kiteshield", - "tradeable": "true", + "id": "2658", + "name": "Zamorak full helm", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2659", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "499500", - "durability": null, "name": "Zamorak kiteshield", - "tradeable": "true", + "examine": "A Rune kiteshield in the colours of Zamorak.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "2660" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody in the colours of Saradomin.", - "durability": null, - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "1600000", - "name": "Saradomin platebody", - "tradeable": "true", + "id": "2660", + "name": "Zamorak kiteshield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "2661", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1600000", - "durability": null, "name": "Saradomin platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2662" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platelegs in the colours of Saradomin.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "444000", - "name": "Saradomin platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2663", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "444000", - "durability": null, - "name": "Saradomin platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2664" - }, - { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune full helmet in the colours of Saradomin.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "1100000", - "name": "Saradomin full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2665", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1100000", - "durability": null, - "name": "Saradomin full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2666" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune kiteshield in the colours of Saradomin.", - "durability": null, - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "795900", - "name": "Saradomin kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2667", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "795900", - "durability": null, - "name": "Saradomin kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2668" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody in the colours of Guthix.", - "durability": null, - "weight": "9.07", + "examine": "Rune platebody in the colours of Saradomin.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", + "ge_buy_limit": "2", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "307900", - "name": "Guthix platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2669", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "307900", - "durability": null, - "name": "Guthix platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2670" - }, - { "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platelegs in the colours of Guthix.", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "2662", + "name": "Saradomin platebody", + "archery_ticket_price": "0", "durability": null, - "weight": "9", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2663", + "name": "Saradomin platelegs", + "examine": "Rune platelegs in the colours of Saradomin.", "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "169900", - "name": "Guthix platelegs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2671", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "169900", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", "durability": null, - "name": "Guthix platelegs", + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2672" + "weight": "9" }, { + "id": "2664", + "name": "Saradomin platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2665", + "name": "Saradomin full helm", + "examine": "Rune full helmet in the colours of Saradomin.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune full helmet in the colours of Guthix.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "323100", - "name": "Guthix full helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2673", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,1,0,0" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "323100", - "durability": null, - "name": "Guthix full helm", - "tradeable": "true", + "id": "2666", + "name": "Saradomin full helm", "archery_ticket_price": "0", - "id": "2674" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune kiteshield in the colours of Guthix.", - "durability": null, - "weight": "5.4", + "id": "2667", + "name": "Saradomin kiteshield", + "examine": "Rune kiteshield in the colours of Saradomin.", "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "247300", - "name": "Guthix kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2675", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { "ge_buy_limit": "2", - "grand_exchange_price": "247300", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "2668", + "name": "Saradomin kiteshield", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2669", + "name": "Guthix platebody", + "examine": "Rune platebody in the colours of Guthix.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "2670", + "name": "Guthix platebody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2671", + "name": "Guthix platelegs", + "examine": "Rune platelegs in the colours of Guthix.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "2672", + "name": "Guthix platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2673", + "name": "Guthix full helm", + "examine": "A rune full helmet in the colours of Guthix.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,1,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "2674", + "name": "Guthix full helm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2675", "name": "Guthix kiteshield", + "examine": "Rune kiteshield in the colours of Guthix.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2676" + "weight": "5.4" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", + "id": "2676", + "name": "Guthix kiteshield", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2677", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2677" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2678" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2678", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2679" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2679", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2680" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2680", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2681" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2681", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2682" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2682", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2683" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2683", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2684" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2684", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2685" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2685", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2686" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2686", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2687" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2687", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2688" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2688", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2689" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2689", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2690" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2690", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2691" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2691", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2692" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2692", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2693" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2693", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2694" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2694", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2695" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2695", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2696" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2696", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2697" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2697", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2698" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2698", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2699" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2699", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2700" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2700", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2701" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2701", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2702" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2702", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2703" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2703", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2704" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2704", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2705" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2705", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2706" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2706", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2707" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2707", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2708" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2708", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2709" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2709", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2710" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2710", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2711" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2711", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2712" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2712", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2713" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "id": "2713", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "2714", "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2714" - }, - { "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2715" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2716" + "weight": "5" }, { + "id": "2715", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", - "id": "2717" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "2716", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2717", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2718" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2719" + "weight": "5" }, { + "id": "2718", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", - "id": "2720" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "2719", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2720", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2721" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2722" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2723" + "weight": "5" }, { + "id": "2721", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2724" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2722", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2725" + "durability": null, + "shop_price": "5000" }, { + "id": "2723", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2724", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2726" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2725", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2727" + "durability": null, + "shop_price": "5000" }, { + "id": "2726", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2728" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2727", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2729" + "durability": null, + "shop_price": "5000" }, { + "id": "2728", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2730" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2729", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2731" + "durability": null, + "shop_price": "5000" }, { + "id": "2730", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2732" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2731", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2733" + "durability": null, + "shop_price": "5000" }, { + "id": "2732", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2734" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2733", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2735" + "durability": null, + "shop_price": "5000" }, { + "id": "2734", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2736" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2735", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2737" + "durability": null, + "shop_price": "5000" }, { + "id": "2736", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2738" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2737", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2739" + "durability": null, + "shop_price": "5000" }, { + "id": "2738", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2740" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2739", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2741" + "durability": null, + "shop_price": "5000" }, { + "id": "2740", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2742" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2741", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2743" + "durability": null, + "shop_price": "5000" }, { + "id": "2742", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2744" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2743", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2745" + "durability": null, + "shop_price": "5000" }, { + "id": "2744", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2746" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2745", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2747" + "durability": null, + "shop_price": "5000" }, { + "id": "2746", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2748" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2747", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2773" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2774" + "durability": null, + "shop_price": "5000" }, { + "id": "2748", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2775" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2773", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2776" + "durability": null, + "shop_price": "5000" }, { + "id": "2774", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2775", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2777" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2776", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2778" + "durability": null, + "shop_price": "5000" }, { + "id": "2777", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2779" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2778", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2780" + "durability": null, + "shop_price": "5000" }, { + "id": "2779", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2781" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2780", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2782" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2783" + "durability": null, + "shop_price": "5000" }, { + "id": "2781", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2784" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2782", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2785" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2786" + "durability": null, + "shop_price": "5000" }, { + "id": "2783", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2784", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2787" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2785", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2788" + "durability": null, + "shop_price": "5000" }, { + "id": "2786", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2787", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2789" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2788", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2790" + "durability": null, + "shop_price": "5000" }, { + "id": "2789", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2791" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2790", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2792" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2793" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2791", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "2792", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2794" + "durability": null, + "shop_price": "5000" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", + "id": "2793", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "2794", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2795", "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "2795" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2796" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2797" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", "archery_ticket_price": "0", - "id": "2798" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2799" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", "durability": null, + "shop_price": "5" + }, + { + "id": "2796", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2797", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2798", "name": "Puzzle box", + "examine": "I need to solve this.", "archery_ticket_price": "0", - "id": "2800" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2799", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2801" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "id": "2800", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "shop_price": "5" + }, + { + "id": "2801", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2802", "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2802" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2803" - }, - { "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2804" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2803", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2805" + "durability": null, + "shop_price": "5000" }, { + "id": "2804", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2806" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2805", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2807" + "durability": null, + "shop_price": "5000" }, { + "id": "2806", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2808" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2807", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2809" + "durability": null, + "shop_price": "5000" }, { + "id": "2808", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2810" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2809", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2811" + "durability": null, + "shop_price": "5000" }, { + "id": "2810", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2812" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2811", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2813" + "durability": null, + "shop_price": "5000" }, { + "id": "2812", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2814" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2813", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2815" + "durability": null, + "shop_price": "5000" }, { + "id": "2814", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2816" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2815", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2817" + "durability": null, + "shop_price": "5000" }, { + "id": "2816", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2818" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2817", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2819" + "durability": null, + "shop_price": "5000" }, { + "id": "2818", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2820" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2819", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2821" + "durability": null, + "shop_price": "5000" }, { + "id": "2820", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2822" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2821", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2823" + "durability": null, + "shop_price": "5000" }, { + "id": "2822", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2824" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2823", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2825" + "durability": null, + "shop_price": "5000" }, { + "id": "2824", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2826" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2825", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2827" + "durability": null, + "shop_price": "5000" }, { + "id": "2826", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2828" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2827", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2829" + "durability": null, + "shop_price": "5000" }, { + "id": "2828", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "2830" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2829", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2831" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2833" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2830", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "2831", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2835" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2833", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2837" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2835", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2839" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2837", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2841" + "durability": null, + "shop_price": "5000" }, { - "examine": "I need to answer this correctly.", + "id": "2839", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "2841", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2842", "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "2842" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2843" - }, - { "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "2844" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2843", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2845" + "durability": null, + "shop_price": "5000" }, { + "id": "2844", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2845", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "2846", "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "2846" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2847" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2848" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2849" - }, - { "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "2850" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2847", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2851" + "durability": null, + "shop_price": "5000" }, { + "id": "2848", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2849", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2850", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "2852" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2851", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2853" + "durability": null, + "shop_price": "5000" }, { + "id": "2852", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2853", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "2854", "name": "Challenge scroll", + "examine": "I need to answer this correctly.", "archery_ticket_price": "0", - "id": "2854" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "2855", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2855" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2856" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2856", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2857" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", + "id": "2857", + "name": "Clue scroll", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "2858" + "durability": null, + "shop_price": "5000" }, { - "ge_buy_limit": "1000", + "id": "2858", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "2859", + "name": "Wolf bones", "examine": "Bones of a recently slain wolf.", - "grand_exchange_price": "1266", - "durability": null, - "name": "Wolf bones", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "2859" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1266", - "durability": null, + "id": "2860", "name": "Wolf bones", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2860" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "I can make an ogre arrow with these.", - "grand_exchange_price": "5", - "durability": null, + "id": "2861", "name": "Wolfbone arrowtips", - "tradeable": "true", + "examine": "I can make an ogre arrow with these.", "archery_ticket_price": "0", - "id": "2861" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true" }, { - "ge_buy_limit": "25000", + "id": "2862", + "name": "Achey tree logs", "examine": "These logs are longer than normal.", - "grand_exchange_price": "169", - "durability": null, - "name": "Achey tree logs", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "2862" - }, - { + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "169", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "2863", "name": "Achey tree logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2863" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A wooden arrow shaft", - "grand_exchange_price": "47", - "durability": null, + "id": "2864", "name": "Ogre arrow shaft", - "tradeable": "true", + "examine": "A wooden arrow shaft", "archery_ticket_price": "0", - "id": "2864" - }, - { - "ge_buy_limit": "1000", - "examine": "An ogre arrow with four flights attached.", - "grand_exchange_price": "172", "durability": null, - "name": "Flighted ogre arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2865" + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "2865", + "name": "Flighted ogre arrow", + "examine": "An ogre arrow with four flights attached.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "2866", + "name": "Ogre arrow", + "examine": "A large ogre arrow with a bone tip.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", "requirements": "{4,30}", "shop_price": "25", - "ge_buy_limit": "1000", - "examine": "A large ogre arrow with a bone tip.", - "grand_exchange_price": "49", - "durability": null, - "name": "Ogre arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2866", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" + "tradeable": "true" }, { - "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", - "durability": null, - "name": "Ogre bellows", - "archery_ticket_price": "0", "id": "2871", + "name": "Ogre bellows", + "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "3" }, { - "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", - "durability": null, + "id": "2872", "name": "Ogre bellows (3)", + "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", "archery_ticket_price": "0", - "id": "2872" + "durability": null }, { - "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", - "durability": null, + "id": "2873", "name": "Ogre bellows (2)", - "archery_ticket_price": "0", - "id": "2873" - }, - { "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2874", "name": "Ogre bellows (1)", + "examine": "A large pair of ogre bellows. If filled with gas (1-3): A large pair of ogre bellows, it has x load(s) of swamp gas in it.", "archery_ticket_price": "0", - "id": "2874" + "durability": null }, { - "bankable": "false", - "examine": "An inflated toad.", - "durability": null, + "id": "2875", "name": "Bloated toad", - "tradeable": "false", + "examine": "An inflated toad.", + "archery_ticket_price": "0", + "bankable": "false", "destroy": "true", - "weight": "0.7", - "archery_ticket_price": "0", - "id": "2875" + "durability": null, + "tradeable": "false", + "weight": "0.7" }, { - "ge_buy_limit": "10000", + "id": "2876", + "name": "Raw chompy", "examine": "I need to cook this first.", - "grand_exchange_price": "2145", - "durability": null, - "name": "Raw chompy", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "2876" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "2145", - "durability": null, + "tradeable": "true", + "weight": "10" + }, + { + "id": "2877", "name": "Raw chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2877" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "130", - "ge_buy_limit": "1000", + "id": "2878", + "name": "Cooked chompy", "examine": "It might look delicious to an ogre.Roasted chompy bird.", - "grand_exchange_price": "95", - "durability": null, - "name": "Cooked chompy", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "2878" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "95", - "durability": null, + "shop_price": "130", + "tradeable": "true", + "weight": "10" + }, + { + "id": "2879", "name": "Cooked chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2879" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "durability": null, + "id": "2881", "name": "Ruined chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2881" - }, - { - "examine": "It has been deliciously seasoned to taste wonderful for ogres.", "durability": null, - "name": "Seasoned chompy", - "weight": "10", - "archery_ticket_price": "0", - "id": "2882" + "tradeable": "true" }, { + "id": "2882", + "name": "Seasoned chompy", + "examine": "It has been deliciously seasoned to taste wonderful for ogres.", + "archery_ticket_price": "0", + "durability": null, + "weight": "10" + }, + { + "id": "2883", + "name": "Ogre bow", + "examine": "More powerful than a normal bow, useful against large game birds.", + "archery_ticket_price": "0", + "attack_audios": "2700,0,0,0", + "attack_speed": "8", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", "requirements": "{4,30}", "shop_price": "500", - "examine": "More powerful than a normal bow, useful against large game birds.", - "durability": null, - "weight": "1", - "attack_speed": "8", "two_handed": "true", "weapon_interface": "16", - "equipment_slot": "3", - "attack_audios": "2700,0,0,0", - "name": "Ogre bow", - "archery_ticket_price": "0", - "id": "2883", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,0" + "weight": "1" }, { - "examine": "Book of the elemental shield.", - "durability": null, + "id": "2886", "name": "Battered book", + "examine": "Book of the elemental shield.", "archery_ticket_price": "0", - "id": "2886" + "durability": null }, { - "examine": "An old battered key. (Elemental Workshop I)", - "durability": null, + "id": "2887", "name": "Battered key", + "examine": "An old battered key. (Elemental Workshop I)", "archery_ticket_price": "0", - "id": "2887" + "durability": null }, { - "examine": "This is an empty stone bowl. / This is a stone bowl full of lava.", - "durability": null, - "name": "A stone bowl", - "archery_ticket_price": "0", "id": "2888", - "equipment_slot": "3" - }, - { - "examine": "This is an empty stone bowl. / This is a stone bowl full of lava.", - "durability": null, "name": "A stone bowl", + "examine": "This is an empty stone bowl. / This is a stone bowl full of lava.", "archery_ticket_price": "0", - "id": "2889", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "2000", - "ge_buy_limit": "100", - "examine": "A shield made in the Elemental Workshop.", - "grand_exchange_price": "709", - "durability": null, - "name": "Elemental shield", - "tradeable": "true", - "weight": "0.2", + "id": "2889", + "name": "A stone bowl", + "examine": "This is an empty stone bowl. / This is a stone bowl full of lava.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { "id": "2890", - "bonuses": "0,0,0,0,0,0,0,0,6,0,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "709", - "durability": null, "name": "Elemental shield", - "tradeable": "true", + "examine": "A shield made in the Elemental Workshop.", "archery_ticket_price": "0", - "id": "2891" + "bonuses": "0,0,0,0,0,0,0,0,6,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "shop_price": "2000", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "This needs refining.", + "id": "2891", + "name": "Elemental shield", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2892", "name": "Elemental ore", - "weight": "2.2", + "examine": "This needs refining.", "archery_ticket_price": "0", - "id": "2892" + "durability": null, + "weight": "2.2" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Very stylish!", - "grand_exchange_price": "1570", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", "id": "2894", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1570", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Boots", - "tradeable": "true", + "examine": "Very stylish!", "archery_ticket_price": "0", - "id": "2895" + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.3" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Some fine werewolf clothing.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "867", - "name": "Robe top", - "tradeable": "true", + "id": "2895", + "name": "Boots", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2896", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "867", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe top", - "tradeable": "true", + "examine": "Some fine werewolf clothing.", "archery_ticket_price": "0", - "id": "2897" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Made by werewolves for werewolves.", - "grand_exchange_price": "741", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", + "id": "2897", + "name": "Robe top", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2898", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "741", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe bottoms", - "tradeable": "true", + "examine": "Made by werewolves for werewolves.", "archery_ticket_price": "0", - "id": "2899" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "durability": null, - "weight": "0.4", - "equipment_slot": "0", - "grand_exchange_price": "844", - "name": "Hat", - "tradeable": "true", + "id": "2899", + "name": "Robe bottoms", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "hat": "true", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2900", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "844", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Hat", - "tradeable": "true", + "examine": "A silly pointed hat.", "archery_ticket_price": "0", - "id": "2901" + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "10", - "examine": "These gloves from Canifis will keep my hands warm!", - "grand_exchange_price": "1039", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", + "id": "2901", + "name": "Hat", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2902", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1039", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Gloves", - "tradeable": "true", + "examine": "These gloves from Canifis will keep my hands warm!", "archery_ticket_price": "0", - "id": "2903" + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Very stylish!", - "grand_exchange_price": "1359", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", + "id": "2903", + "name": "Gloves", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "2904", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1359", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Boots", - "tradeable": "true", + "examine": "Very stylish!", "archery_ticket_price": "0", - "id": "2905" + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.3" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Some fine werewolf clothing.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "958", - "name": "Robe top", - "tradeable": "true", + "id": "2905", + "name": "Boots", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2906", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "958", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe top", - "tradeable": "true", + "examine": "Some fine werewolf clothing.", "archery_ticket_price": "0", - "id": "2907" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Made by werewolves for werewolves.", - "grand_exchange_price": "474", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", + "id": "2907", + "name": "Robe top", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2908", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "474", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe bottoms", - "tradeable": "true", + "examine": "Made by werewolves for werewolves.", "archery_ticket_price": "0", - "id": "2909" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "durability": null, - "weight": "0.4", - "equipment_slot": "0", - "grand_exchange_price": "1445", - "tradeable": "true", - "name": "Hat", + "id": "2909", + "name": "Robe bottoms", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "hat": "true", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2910", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1445", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Hat", - "tradeable": "true", + "examine": "A silly pointed hat.", "archery_ticket_price": "0", - "id": "2911" + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "10", - "examine": "These gloves from Canifis will keep my hands warm!", - "grand_exchange_price": "1254", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", + "id": "2911", + "name": "Hat", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2912", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1254", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Gloves", - "tradeable": "true", + "examine": "These gloves from Canifis will keep my hands warm!", "archery_ticket_price": "0", - "id": "2913" + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Very stylish!", - "grand_exchange_price": "1054", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", + "id": "2913", + "name": "Gloves", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "2914", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1054", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Boots", - "tradeable": "true", + "examine": "Very stylish!", "archery_ticket_price": "0", - "id": "2915" + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.3" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Some fine werewolf clothing.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "789", - "name": "Robe top", - "tradeable": "true", + "id": "2915", + "name": "Boots", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2916", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "789", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe top", - "tradeable": "true", + "examine": "Some fine werewolf clothing.", "archery_ticket_price": "0", - "id": "2917" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Made by werewolves for werewolves.", - "grand_exchange_price": "603", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", + "id": "2917", + "name": "Robe top", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2918", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "603", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Robe bottoms", - "tradeable": "true", + "examine": "Made by werewolves for werewolves.", "archery_ticket_price": "0", - "id": "2919" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "durability": null, - "weight": "0.4", - "equipment_slot": "0", - "grand_exchange_price": "997", - "name": "Hat", - "tradeable": "true", + "id": "2919", + "name": "Robe bottoms", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "hat": "true", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2920", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "997", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Hat", - "tradeable": "true", + "examine": "A silly pointed hat.", "archery_ticket_price": "0", - "id": "2921" + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "10", - "examine": "These gloves from Canifis will keep my hands warm!", - "grand_exchange_price": "1221", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", + "id": "2921", + "name": "Hat", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2922", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1221", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Gloves", - "tradeable": "true", + "examine": "These gloves from Canifis will keep my hands warm!", "archery_ticket_price": "0", - "id": "2923" + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Very stylish!", - "grand_exchange_price": "737", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", + "id": "2923", + "name": "Gloves", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "2924", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "737", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2925" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Some fine werewolf clothing.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "504", - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2926", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "504", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2927" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Made by werewolves for werewolves.", - "grand_exchange_price": "637", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2928", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "637", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2929" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "durability": null, - "weight": "0.4", - "equipment_slot": "0", - "grand_exchange_price": "925", - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": "true", - "id": "2930", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "925", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2931" - }, - { - "shop_price": "650", - "ge_buy_limit": "10", - "examine": "These gloves from Canifis will keep my hands warm!", - "grand_exchange_price": "1829", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2932", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1829", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2933" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", "examine": "Very stylish!", - "grand_exchange_price": "988", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.3", "archery_ticket_price": "0", - "id": "2934", "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "988", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "2925", "name": "Boots", - "tradeable": "true", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "2935" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "650", - "ge_buy_limit": "100", + "id": "2926", + "name": "Robe top", "examine": "Some fine werewolf clothing.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "925", - "name": "Robe top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2936", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "925", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2937" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Made by werewolves for werewolves.", - "grand_exchange_price": "927", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "2938", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "927", + "id": "2927", + "name": "Robe top", "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2928", "name": "Robe bottoms", - "tradeable": "true", + "examine": "Made by werewolves for werewolves.", "archery_ticket_price": "0", - "id": "2939" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "650", + "id": "2929", + "name": "Robe bottoms", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2930", + "name": "Hat", "examine": "A silly pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", "durability": null, - "weight": "0.4", "equipment_slot": "0", - "grand_exchange_price": "1209", - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": "true", - "id": "2940", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1209", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2941" - }, - { + "hat": "true", "shop_price": "650", - "ge_buy_limit": "10", - "examine": "These gloves from Canifis will keep my hands warm!", - "grand_exchange_price": "634", - "durability": null, - "name": "Gloves", "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2942", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" + "weight": "0.4" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "634", + "id": "2931", + "name": "Hat", "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2932", "name": "Gloves", + "examine": "These gloves from Canifis will keep my hands warm!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2943" + "weight": "0.4" }, { - "examine": "A key made from solid gold", + "id": "2933", + "name": "Gloves", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "2934", + "name": "Boots", + "examine": "Very stylish!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "2935", + "name": "Boots", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2936", + "name": "Robe top", + "examine": "Some fine werewolf clothing.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2937", + "name": "Robe top", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2938", + "name": "Robe bottoms", + "examine": "Made by werewolves for werewolves.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" + }, + { + "id": "2939", + "name": "Robe bottoms", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2940", + "name": "Hat", + "examine": "A silly pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2941", + "name": "Hat", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2942", + "name": "Gloves", + "examine": "These gloves from Canifis will keep my hands warm!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2943", + "name": "Gloves", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "2944", "name": "Golden key", + "examine": "A key made from solid gold", "archery_ticket_price": "0", - "id": "2944" + "durability": null }, { - "examine": "A key made of solid iron.", - "durability": null, + "id": "2945", "name": "Iron key", + "examine": "A key made of solid iron.", "archery_ticket_price": "0", - "id": "2945" + "durability": null }, { - "examine": "A replica tinderbox made of solid gold.", - "durability": null, + "id": "2946", "name": "Golden tinderbox", + "examine": "A replica tinderbox made of solid gold.", "archery_ticket_price": "0", - "id": "2946" + "durability": null }, { - "examine": "A replica candle made of solid gold.", - "durability": null, + "id": "2947", "name": "Golden candle", - "tradeable": "false", - "destroy": "true", + "examine": "A replica candle made of solid gold.", "archery_ticket_price": "0", - "id": "2947" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A replica pot made of solid gold.", - "durability": null, + "id": "2948", "name": "Golden pot", + "examine": "A replica pot made of solid gold.", "archery_ticket_price": "0", - "id": "2948" + "durability": null }, { - "examine": "A replica hammer made of solid gold.", - "durability": null, + "id": "2949", "name": "Golden hammer", - "tradeable": "false", + "examine": "A replica hammer made of solid gold.", + "archery_ticket_price": "0", + "attack_speed": "4", "destroy": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "2949" + "durability": null, + "tradeable": "false", + "weight": "0.9" }, { - "examine": "A replica needle made of solid gold.", - "durability": null, + "id": "2951", "name": "Golden needle", + "examine": "A replica needle made of solid gold.", "archery_ticket_price": "0", - "id": "2951" + "durability": null }, { + "id": "2952", + "name": "Wolfbane", "examine": "A silver dagger that can prevent werewolves changing form.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "400,400,401,400", "attack_audios": "2517,2517,2500,2517", - "name": "Wolfbane", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "2952", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,1,10,5,0,0" - }, - { - "examine": "It's a bucket of water.", - "grand_exchange_price": "74", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,1,10,5,0,0", + "defence_anim": "378", "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "2953" - }, - { - "examine": "It's a bucket of water.", - "grand_exchange_price": "74", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "2954" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "16", - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2955" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16", - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2956" - }, - { - "examine": "An empty druid pouch.", - "durability": null, - "name": "Druid pouch", - "archery_ticket_price": "0", - "id": "2957" - }, - { - "examine": "A druid pouch.", - "durability": null, - "name": "Druid pouch", - "archery_ticket_price": "0", - "id": "2958" - }, - { - "examine": "Erhhh! It stinks.", - "durability": null, - "name": "Rotten food", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2959" - }, - { - "durability": null, - "name": "Rotten food", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2960" - }, - { - "ge_buy_limit": "100", - "examine": "It's a silver sickle.", - "durability": null, - "weight": "1.6", - "weapon_interface": "6", - "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "28", - "name": "Silver sickle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2961", - "bonuses": "0,0,0,0,0,0,1,1,1,1,0,1,0,0,0" + "render_anim": "2584", + "tradeable": "false", + "weapon_interface": "5", + "weight": "0.4" }, { + "id": "2953", + "name": "Bucket of water", + "examine": "It's a bucket of water.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "3" + }, + { + "id": "2954", + "name": "Bucket of water", + "examine": "It's a bucket of water.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "3" + }, + { + "id": "2955", + "name": "Moonlight mead", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "28", - "durability": null, - "name": "Silver sickle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2962" + "shop_price": "5", + "tradeable": "true" }, { - "attack_anims": "390,390,381,390", - "examine": "It's a blessed silver sickle.", - "durability": null, - "name": "Silver sickle(b)", - "weight": "1.5", + "id": "2956", + "name": "Moonlight mead", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "2957", + "name": "Druid pouch", + "examine": "An empty druid pouch.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2958", + "name": "Druid pouch", + "examine": "A druid pouch.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "2959", + "name": "Rotten food", + "examine": "Erhhh! It stinks.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "2960", + "name": "Rotten food", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "2961", + "name": "Silver sickle", + "examine": "It's a silver sickle.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "bonuses": "0,0,0,0,0,0,1,1,1,1,0,1,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "tradeable": "true", "weapon_interface": "6", + "weight": "1.6" + }, + { + "id": "2962", + "name": "Silver sickle", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "2963", + "name": "Silver sickle(b)", + "examine": "It's a blessed silver sickle.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", "bonuses": "0,0,0,0,0,0,1,1,1,1,0,1,5,0,0", "defence_anim": "397", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "6", + "weight": "1.5" }, { - "shop_price": "1", - "examine": "Used for washing your face, amongst other things.", - "durability": null, + "id": "2964", "name": "Washing bowl", - "weight": "0.4", + "examine": "Used for washing your face, amongst other things.", "archery_ticket_price": "0", - "id": "2964" + "durability": null, + "shop_price": "1", + "weight": "0.4" }, { - "examine": "A small mirror, probably used for grooming.", - "durability": null, + "id": "2966", "name": "Mirror", - "weight": "0.2", + "examine": "A small mirror, probably used for grooming.", "archery_ticket_price": "0", - "id": "2966" + "durability": null, + "weight": "0.2" }, { - "examine": "A druidic spell given to you freely by the spirit of Filliman Tarlock.", - "durability": null, + "id": "2968", "name": "Druidic spell", + "examine": "A druidic spell given to you freely by the spirit of Filliman Tarlock.", "archery_ticket_price": "0", - "id": "2968" + "durability": null }, { - "examine": "A used druidic spell given to you freely by the spirit of Filliman Tarlock.", - "durability": null, + "id": "2969", "name": "A used spell", + "examine": "A used druidic spell given to you freely by the spirit of Filliman Tarlock.", "archery_ticket_price": "0", - "id": "2969" + "durability": null }, { - "ge_buy_limit": "10000", + "id": "2970", + "name": "Mort myre fungus", "examine": "A mushroom from the swamps of Mort Myre.", - "grand_exchange_price": "915", - "durability": null, - "name": "Mort myre fungus", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2970" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "915", - "durability": null, - "name": "Mort myre fungus", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2971" + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2971", + "name": "Mort myre fungus", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "2972", + "name": "Mort myre stem", "examine": "A cutting from a budding branch.", - "grand_exchange_price": "338", - "durability": null, - "name": "Mort myre stem", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2972" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "338", - "durability": null, + "id": "2973", "name": "Mort myre stem", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2973" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "2974", + "name": "Mort myre pear", "examine": "A pear picked from a dying bush in Mort Myre.", - "grand_exchange_price": "975", - "durability": null, - "name": "Mort myre pear", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "2974" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "975", - "durability": null, + "id": "2975", "name": "Mort myre pear", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2975" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "10", - "ge_buy_limit": "100", + "id": "2976", + "name": "Sickle mould", "examine": "Used to make sickles.", - "grand_exchange_price": "498", - "durability": null, - "name": "Sickle mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "2976" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "498", - "durability": null, - "name": "Sickle mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2977" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2978", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2979", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2980", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2981", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2982", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2983", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2984", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2985", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2986", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2987", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2988", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2989", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2990", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2991", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2992", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2993", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2994", - "equipment_slot": "0" - }, - { - "examine": "A symbol of your chompy bird hunting prowess.", - "durability": null, - "name": "Chompy bird hat", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "2995", - "equipment_slot": "0" - }, - { - "examine": "I can exchange these for further experience or items.", - "durability": null, - "name": "Agility arena ticket", - "archery_ticket_price": "0", - "id": "2996" - }, - { - "shop_price": "800", - "ge_buy_limit": "10", - "examine": "You should see the shark...", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Pirate's hook", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "2997", - "bonuses": "0,0,0,0,0,1,5,3,0,0,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "4056", - "durability": null, - "name": "Clean toadflax", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2998" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "4056", - "durability": null, - "name": "Clean toadflax", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2999" - }, - { "shop_price": "10", - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "10500", - "durability": null, - "name": "Clean snapdragon", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3000" + "weight": "0.4" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "10500", - "durability": null, - "name": "Clean snapdragon", - "tradeable": "true", + "id": "2977", + "name": "Sickle mould", "archery_ticket_price": "0", - "id": "3001" - }, - { + "durability": null, "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this toadflax potion.", - "grand_exchange_price": "3988", - "durability": null, - "name": "Toadflax potion(unf)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3002" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3988", - "durability": null, - "name": "Toadflax potion(unf)", - "tradeable": "true", + "id": "2978", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3003" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this snapdragon potion.", - "grand_exchange_price": "9588", - "durability": null, - "name": "Snapdragon potion(unf)", - "tradeable": "true", + "id": "2979", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3004" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "9588", - "durability": null, - "name": "Snapdragon potion(unf)", - "tradeable": "true", + "id": "2980", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3005" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "150", - "examine": "Keep out of reach of children and familiars.", - "durability": null, - "name": "Firework", - "tradeable": "true", + "id": "2981", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3006" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "durability": null, - "name": "Firework", - "tradeable": "true", + "id": "2982", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3007" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "145", - "ge_buy_limit": "100", - "examine": "4 doses of energy potion.", - "grand_exchange_price": "33", - "durability": null, - "name": "Energy potion(4)", - "tradeable": "true", + "id": "2983", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3008" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "33", - "durability": null, - "name": "Energy potion(4)", - "tradeable": "true", + "id": "2984", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3009" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "145", - "ge_buy_limit": "100", - "examine": "3 doses of energy potion.", - "grand_exchange_price": "18", - "durability": null, - "name": "Energy potion(3)", - "tradeable": "true", + "id": "2985", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3010" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, - "name": "Energy potion(3)", - "tradeable": "true", + "id": "2986", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3011" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "145", - "ge_buy_limit": "100", - "examine": "2 doses of energy potion.", - "grand_exchange_price": "11", - "durability": null, - "name": "Energy potion(2)", - "tradeable": "true", + "id": "2987", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3012" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "11", - "durability": null, - "name": "Energy potion(2)", - "tradeable": "true", + "id": "2988", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3013" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "145", - "ge_buy_limit": "100", - "examine": "1 dose of energy potion.", - "grand_exchange_price": "25", - "durability": null, - "name": "Energy potion(1)", - "tradeable": "true", + "id": "2989", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3014" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "25", - "durability": null, - "name": "Energy potion(1)", - "tradeable": "true", + "id": "2990", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3015" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "4000", - "ge_buy_limit": "1000", - "examine": "4 doses of super energy potion.", - "grand_exchange_price": "3088", - "durability": null, - "name": "Super energy(4)", - "tradeable": "true", + "id": "2991", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3016" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "shop_price": "4000", - "ge_buy_limit": "1000", - "grand_exchange_price": "3088", - "durability": null, - "name": "Super energy(4)", - "tradeable": "true", + "id": "2992", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3017" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "1000", - "examine": "3 doses of super energy potion.", - "grand_exchange_price": "2485", - "durability": null, - "name": "Super energy(3)", - "tradeable": "true", + "id": "2993", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3018" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "2485", - "durability": null, - "name": "Super energy(3)", - "tradeable": "true", + "id": "2994", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3019" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "1000", - "examine": "2 doses of super energy potion.", - "grand_exchange_price": "1592", - "durability": null, - "name": "Super energy(2)", - "tradeable": "true", + "id": "2995", + "name": "Chompy bird hat", + "examine": "A symbol of your chompy bird hunting prowess.", "archery_ticket_price": "0", - "id": "3020" + "durability": null, + "equipment_slot": "0", + "weight": "0.33" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1592", - "durability": null, - "name": "Super energy(2)", - "tradeable": "true", + "id": "2996", + "name": "Agility arena ticket", + "examine": "I can exchange these for further experience or items.", "archery_ticket_price": "0", - "id": "3021" + "durability": null }, { - "ge_buy_limit": "1000", - "examine": "1 dose of super energy potion.", - "grand_exchange_price": "1432", - "durability": null, - "name": "Super energy(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3022" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1432", - "durability": null, - "name": "Super energy(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3023" - }, - { - "shop_price": "12000", - "ge_buy_limit": "1000", - "examine": "4 doses of super restore potion.", - "grand_exchange_price": "11500", - "durability": null, - "name": "Super restore(4)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "3024" - }, - { - "shop_price": "12000", - "ge_buy_limit": "1000", - "grand_exchange_price": "11500", - "durability": null, - "name": "Super restore(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3025" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of super restore potion.", - "grand_exchange_price": "8690", - "durability": null, - "name": "Super restore(3)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "3026" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "8690", - "durability": null, - "name": "Super restore(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3027" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of super restore potion.", - "grand_exchange_price": "5637", - "durability": null, - "name": "Super restore(2)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "3028" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "5637", - "durability": null, - "name": "Super restore(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3029" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of super restore potion.", - "grand_exchange_price": "2606", - "durability": null, - "name": "Super restore(1)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "3030" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2606", - "durability": null, - "name": "Super restore(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3031" - }, - { - "ge_buy_limit": "100", - "examine": "4 doses of Agility potion.", - "grand_exchange_price": "276", - "durability": null, - "name": "Agility potion(4)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3032" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "276", - "durability": null, - "name": "Agility potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3033" - }, - { - "ge_buy_limit": "100", - "examine": "3 doses of Agility potion.", - "grand_exchange_price": "148", - "durability": null, - "name": "Agility potion(3)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3034" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "148", - "durability": null, - "name": "Agility potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3035" - }, - { - "ge_buy_limit": "100", - "examine": "2 doses of Agility potion.", - "grand_exchange_price": "115", - "durability": null, - "name": "Agility potion(2)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3036" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "115", - "durability": null, - "name": "Agility potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3037" - }, - { - "ge_buy_limit": "100", - "examine": "1 dose of Agility potion.", - "grand_exchange_price": "306", - "durability": null, - "name": "Agility potion(1)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3038" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "306", - "durability": null, - "name": "Agility potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3039" - }, - { - "ge_buy_limit": "100", - "examine": "4 doses of Magic potion.", - "grand_exchange_price": "7214", - "durability": null, - "name": "Magic potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3040" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7214", - "durability": null, - "name": "Magic potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3041" - }, - { - "ge_buy_limit": "100", - "examine": "3 doses of Magic potion.", - "grand_exchange_price": "5285", - "durability": null, - "name": "Magic potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3042" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5285", - "durability": null, - "name": "Magic potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3043" - }, - { - "ge_buy_limit": "100", - "examine": "2 doses of Magic potion.", - "grand_exchange_price": "3697", - "durability": null, - "name": "Magic potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3044" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3697", - "durability": null, - "name": "Magic potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3045" - }, - { - "ge_buy_limit": "100", - "examine": "1 dose of Magic potion.", - "grand_exchange_price": "1826", - "durability": null, - "name": "Magic potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3046" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1826", - "durability": null, - "name": "Magic potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3047" - }, - { - "durability": null, + "id": "2997", "name": "Pirate's hook", - "tradeable": "true", + "examine": "You should see the shark...", "archery_ticket_price": "0", - "id": "3048" + "bonuses": "0,0,0,0,0,1,5,3,0,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "800", + "tradeable": "true", + "weight": "0.3" }, { + "id": "2998", + "name": "Clean toadflax", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "2999", + "name": "Clean toadflax", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3000", + "name": "Clean snapdragon", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "3001", + "name": "Clean snapdragon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3002", + "name": "Toadflax potion(unf)", + "examine": "I need another ingredient to finish this toadflax potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3003", + "name": "Toadflax potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3004", + "name": "Snapdragon potion(unf)", + "examine": "I need another ingredient to finish this snapdragon potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3005", + "name": "Snapdragon potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3006", + "name": "Firework", + "examine": "Keep out of reach of children and familiars.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "3007", + "name": "Firework", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3008", + "name": "Energy potion(4)", + "examine": "4 doses of energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "145", + "tradeable": "true" + }, + { + "id": "3009", + "name": "Energy potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3010", + "name": "Energy potion(3)", + "examine": "3 doses of energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "145", + "tradeable": "true" + }, + { + "id": "3011", + "name": "Energy potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3012", + "name": "Energy potion(2)", + "examine": "2 doses of energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "145", + "tradeable": "true" + }, + { + "id": "3013", + "name": "Energy potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3014", + "name": "Energy potion(1)", + "examine": "1 dose of energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "145", + "tradeable": "true" + }, + { + "id": "3015", + "name": "Energy potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3016", + "name": "Super energy(4)", + "examine": "4 doses of super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "4000", + "tradeable": "true" + }, + { + "id": "3017", + "name": "Super energy(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "4000", + "tradeable": "true" + }, + { + "id": "3018", + "name": "Super energy(3)", + "examine": "3 doses of super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3019", + "name": "Super energy(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3020", + "name": "Super energy(2)", + "examine": "2 doses of super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3021", + "name": "Super energy(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3022", + "name": "Super energy(1)", + "examine": "1 dose of super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3023", + "name": "Super energy(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3024", + "name": "Super restore(4)", + "examine": "4 doses of super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "12000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "3025", + "name": "Super restore(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "12000", + "tradeable": "true" + }, + { + "id": "3026", + "name": "Super restore(3)", + "examine": "3 doses of super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "3027", + "name": "Super restore(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3028", + "name": "Super restore(2)", + "examine": "2 doses of super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "3029", + "name": "Super restore(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3030", + "name": "Super restore(1)", + "examine": "1 dose of super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "3031", + "name": "Super restore(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3032", + "name": "Agility potion(4)", + "examine": "4 doses of Agility potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3033", + "name": "Agility potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3034", + "name": "Agility potion(3)", + "examine": "3 doses of Agility potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3035", + "name": "Agility potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3036", + "name": "Agility potion(2)", + "examine": "2 doses of Agility potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3037", + "name": "Agility potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3038", + "name": "Agility potion(1)", + "examine": "1 dose of Agility potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3039", + "name": "Agility potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3040", + "name": "Magic potion(4)", + "examine": "4 doses of Magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3041", + "name": "Magic potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3042", + "name": "Magic potion(3)", + "examine": "3 doses of Magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3043", + "name": "Magic potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3044", + "name": "Magic potion(2)", + "examine": "2 doses of Magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3045", + "name": "Magic potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3046", + "name": "Magic potion(1)", + "examine": "1 dose of Magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3047", + "name": "Magic potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3048", + "name": "Pirate's hook", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3049", + "name": "Grimy toadflax", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", "requirements": "{15,30}", + "tradeable": "true" + }, + { + "id": "3050", + "name": "Grimy toadflax", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3051", + "name": "Grimy snapdragon", "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "3996", - "durability": null, - "name": "Grimy toadflax", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3049" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "3996", - "durability": null, - "name": "Grimy toadflax", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3050" - }, - { "requirements": "{15,59}", - "ge_buy_limit": "10000", - "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "10500", - "durability": null, - "name": "Grimy snapdragon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3051" + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "10500", - "durability": null, + "id": "3052", "name": "Grimy snapdragon", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3052" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "9985", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "3053", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Lava battlestaff" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "34100", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3054", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "shop_price": "160", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic lava staff" - }, - { - "durability": null, "name": "Lava battlestaff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "3055" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "durability": null, + "id": "3054", "name": "Mystic lava staff", - "tradeable": "true", + "examine": "It's a slightly magical stick.", "archery_ticket_price": "0", - "id": "3056" + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "shop_price": "160", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "examine": "A mime would wear this.", - "durability": null, - "name": "Mime mask", + "id": "3055", + "name": "Lava battlestaff", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3056", + "name": "Mystic lava staff", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3057", + "name": "Mime mask", + "examine": "A mime would wear this.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "0" }, { - "remove_sleeves": "true", - "examine": "A mime would wear these.", - "durability": null, - "name": "Mime top", - "archery_ticket_price": "0", "id": "3058", - "equipment_slot": "4" + "name": "Mime top", + "examine": "A mime would wear these.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "examine": "A mime would wear these.", - "durability": null, - "name": "Mime legs", - "archery_ticket_price": "0", "id": "3059", + "name": "Mime legs", + "examine": "A mime would wear these.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "7" }, { - "examine": "A mime would wear these.", - "durability": null, - "name": "Mime gloves", - "archery_ticket_price": "0", "id": "3060", + "name": "Mime gloves", + "examine": "A mime would wear these.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "9" }, { - "examine": "A mime would wear these.", - "durability": null, - "name": "Mime boots", - "weight": "0.5", - "archery_ticket_price": "0", "id": "3061", - "equipment_slot": "10" + "name": "Mime boots", + "examine": "A mime would wear these.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "weight": "0.5" }, { - "examine": "It seems to be humming...", - "durability": null, + "id": "3062", "name": "Strange box", - "tradeable": "false", + "examine": "It seems to be humming...", + "archery_ticket_price": "0", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3062" + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "requirements": "{4,10}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a black tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "628", - "attack_audios": "2547,0,0,0", - "name": "Black dart", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3093", - "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6" - }, - { - "requirements": "{4,10}", - "ge_buy_limit": "1000", + "name": "Black dart", "examine": "A deadly throwing dart with a black tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "582,582,582,582", - "grand_exchange_price": "740", "attack_audios": "2547,0,0,0", - "name": "Black dart(p)", + "attack_speed": "3", + "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,10}", "tradeable": "true", - "archery_ticket_price": "0", + "weapon_interface": "18" + }, + { "id": "3094", - "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6" + "name": "Black dart(p)", + "examine": "A deadly throwing dart with a black tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,10}", + "tradeable": "true", + "weapon_interface": "18" }, { - "shop_price": "48", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "702", - "name": "Bronze claws", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3095", - "bonuses": "3,4,-4,0,0,1,2,1,0,0,0,5,0,0,0" - }, - { - "shop_price": "50", - "ge_buy_limit": "100", + "name": "Bronze claws", "examine": "A set of fighting claws.", - "durability": null, - "weight": "0.9", + "archery_ticket_price": "0", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", "attack_speed": "4", + "bonuses": "3,4,-4,0,0,1,2,1,0,0,0,5,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", + "shop_price": "48", + "tradeable": "true", "two_handed": "true", "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "13", - "name": "Iron claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3096", - "bonuses": "4,6,-4,0,0,2,3,1,0,0,0,7,0,0,0" + "weight": "0.9" }, { + "id": "3096", + "name": "Iron claws", + "examine": "A set of fighting claws.", + "archery_ticket_price": "0", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "4,6,-4,0,0,2,3,1,0,0,0,7,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", + "shop_price": "50", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "9", + "weight": "0.9" + }, + { + "id": "3097", + "name": "Steel claws", + "examine": "A set of fighting claws.", + "archery_ticket_price": "0", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "8,11,-4,0,0,3,6,2,0,0,0,12,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", "requirements": "{0,5}", "shop_price": "175", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "weight": "0.9", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "20", - "name": "Steel claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3097", - "bonuses": "8,11,-4,0,0,3,6,2,0,0,0,12,0,0,0" + "weight": "0.9" }, { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "weight": "6", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "940", - "name": "Black claws", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3098", - "bonuses": "10,14,-4,0,0,4,7,2,0,0,0,14,0,0,0" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "114", - "name": "Mithril claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3099", - "bonuses": "11,16,-4,0,0,4,8,2,0,0,0,17,0,0,0" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "649", - "name": "Adamant claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3100", - "bonuses": "18,23,-4,0,0,6,12,3,0,0,0,24,0,0,0" - }, - { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "has_special": "true", - "durability": null, - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "lendable": "true", - "grand_exchange_price": "6939", - "name": "Rune claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3101", - "bonuses": "26,38,-4,0,0,10,19,5,0,0,0,39,0,0,0" - }, - { - "examine": "The combination to Burthorpe Castle's equipment room.", - "durability": null, - "name": "Combination", - "archery_ticket_price": "0", - "id": "3102" - }, - { - "examine": "The guard wrote the IOU on the back of some paper.", - "durability": null, - "name": "Iou", - "archery_ticket_price": "0", - "id": "3103" - }, - { - "examine": "This map shows the secret way up to Death Plateau.", - "durability": null, - "name": "Secret way map", - "archery_ticket_price": "0", - "id": "3104" - }, - { - "shop_price": "1900", - "ge_buy_limit": "100", - "examine": "Boots made for general climbing; rocks a speciality.", - "durability": null, - "destroy": "true", - "weight": "0.3", - "equipment_slot": "10", - "grand_exchange_price": "51400", - "name": "Climbing boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3105", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,2,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "51400", - "durability": null, - "name": "Climbing boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3106" - }, - { - "examine": "Climbing boots with spikes.", - "durability": null, - "name": "Spiked boots", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "3107", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,2,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "Place on the stone mechanism in the right order to open the door.", - "durability": null, - "name": "Stone ball", - "archery_ticket_price": "0", - "id": "3109" - }, - { - "examine": "Place on the stone mechanism in the right order to open the door.", - "durability": null, - "name": "Stone ball", - "archery_ticket_price": "0", - "id": "3110" - }, - { - "examine": "Place on the stone mechanism in the right order to open the door.", - "durability": null, - "name": "Stone ball", - "archery_ticket_price": "0", - "id": "3111" - }, - { - "examine": "Place on the stone mechanism in the right order to open the door.", - "durability": null, - "name": "Stone ball", - "archery_ticket_price": "0", - "id": "3112" - }, - { - "examine": "Place on the stone mechanism in the right order to open the door.", - "durability": null, - "name": "Stone ball", - "archery_ticket_price": "0", - "id": "3113" - }, - { - "examine": "Entrance certificate to the Imperial Guard.", - "durability": null, - "name": "Certificate", - "archery_ticket_price": "0", - "id": "3114" - }, - { - "durability": null, - "name": "Bronze claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3115" - }, - { - "durability": null, - "name": "Iron claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3116" - }, - { - "durability": null, - "name": "Steel claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3117" - }, - { - "durability": null, "name": "Black claws", - "tradeable": "true", + "examine": "A set of fighting claws.", "archery_ticket_price": "0", - "id": "3118" + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "10,14,-4,0,0,4,7,2,0,0,0,14,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", + "requirements": "{0,10}", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "9", + "weight": "6" }, { - "durability": null, + "id": "3099", "name": "Mithril claws", - "tradeable": "true", + "examine": "A set of fighting claws.", "archery_ticket_price": "0", - "id": "3119" - }, - { + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "11,16,-4,0,0,4,8,2,0,0,0,17,0,0,0", + "defence_anim": "397", "durability": null, - "name": "Adamant claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3120" - }, - { - "durability": null, - "name": "Rune claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3121" - }, - { - "requirements": "{1,50}-{2,50}", - "ge_buy_limit": "10", - "examine": "A solid stone shield.", - "durability": null, - "weight": "6.8", - "absorb": "5,0,10", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "34800", - "name": "Granite shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3122", - "bonuses": "0,0,0,-12,-8,40,42,38,0,65,50,0,0,0,0" - }, - { - "ge_buy_limit": "10000", - "examine": "Large glistening bones which glow with a pale yellow aura.", - "grand_exchange_price": "1975", - "durability": null, - "name": "Shaikahan bones", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "3123" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1975", - "durability": null, - "name": "Shaikahan bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3124" - }, - { - "ge_buy_limit": "10000", - "examine": "Fairly big bones which smell distinctly of Jogre.", - "grand_exchange_price": "1023", - "durability": null, - "name": "Jogre bones", - "tradeable": "true", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "3125" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1023", - "durability": null, - "name": "Jogre bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3126" - }, - { - "examine": "These blackened Jogre bones have been somehow burnt.", - "durability": null, - "name": "Burnt jogre bones", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "3127" - }, - { - "examine": "See article", - "durability": null, - "name": "Pasty jogre bones", - "weight": "1", - "archery_ticket_price": "0", - "id": "3128" - }, - { - "examine": "See article", - "durability": null, - "name": "Pasty jogre bones", - "weight": "1", - "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.", - "durability": null, - "name": "Marinated j' bones", - "weight": "1", - "archery_ticket_price": "0", - "id": "3130" - }, - { - "examine": "See article", - "durability": null, - "name": "Pasty jogre bones", - "weight": "1", - "archery_ticket_price": "0", - "id": "3131" - }, - { - "examine": "See article", - "durability": null, - "name": "Pasty jogre bones", - "weight": "1", - "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.", - "durability": null, - "name": "Marinated j' bones", - "weight": "1", - "archery_ticket_price": "0", - "id": "3133" - }, - { - "durability": null, - "name": "Granite shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3134" - }, - { - "shop_price": "1", - "examine": "The key to the troll prison.", - "durability": null, - "name": "Cell key 1", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3136" - }, - { - "examine": "The key to the troll prison.", - "durability": null, - "name": "Cell key 2", - "weight": "1", - "archery_ticket_price": "0", - "id": "3137" - }, - { - "ge_buy_limit": "10000", - "examine": "How am I supposed to eat that?!", - "grand_exchange_price": "1834", - "durability": null, - "name": "Potato cactus", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3138" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1834", - "durability": null, - "name": "Potato cactus", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3139" - }, - { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "A series of connected metal rings.", - "rare_item": "true", - "durability": null, - "weight": "6.8", - "absorb": "4,0,9", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "4300000", - "name": "Dragon chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3140", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "4300000", - "durability": null, - "name": "Dragon chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3141" - }, - { - "shop_price": "440", - "ge_buy_limit": "20000", - "examine": "A raw green octopus.", - "grand_exchange_price": "1775", - "durability": null, - "name": "Raw karambwan", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3142" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "1775", - "durability": null, - "name": "Raw karambwan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3143" - }, - { - "shop_price": "460", - "ge_buy_limit": "10000", - "examine": "Cooked octopus.", - "grand_exchange_price": "1455", - "durability": null, - "name": "Cooked karambwan", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3144" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1455", - "durability": null, - "name": "Cooked karambwan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3145" - }, - { - "examine": "Cooked octopus. It looks poorly cooked and quite dangerous.", - "durability": null, - "name": "Poison karambwan", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3146" - }, - { - "shop_price": "460", - "examine": "Cooked octopus.", - "grand_exchange_price": "906", - "durability": null, - "name": "Cooked karambwan", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3147" - }, - { - "durability": null, - "name": "Burnt karambwan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3149" - }, - { - "examine": "Small brightly coloured tropical fish.", - "durability": null, - "name": "Raw karambwanji", - "archery_ticket_price": "0", - "id": "3150" - }, - { - "examine": "Small brightly coloured tropical fish.", - "durability": null, - "name": "Karambwanji", - "weight": "0.99", - "archery_ticket_price": "0", - "id": "3151" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "archery_ticket_price": "0", - "id": "3152" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "archery_ticket_price": "0", - "id": "3153" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "archery_ticket_price": "0", - "id": "3154" - }, - { - "examine": "This paste smells of raw fish./This paste smells of cooked fish.", - "durability": null, - "name": "Karambwanji paste", - "archery_ticket_price": "0", - "id": "3155" - }, - { - "examine": "This paste smells of raw fish./This paste smells of cooked fish.", - "durability": null, - "name": "Karambwanji paste", - "archery_ticket_price": "0", - "id": "3156" - }, - { - "requirements": "{10,65}", - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A wide bodied and thin necked vessel, encrusted with sea salt. / The Karambwan Vessel is loaded with Karambwanji", - "grand_exchange_price": "221", - "durability": null, - "name": "Karambwan vessel", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "3157", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "221", - "durability": null, - "name": "Karambwan vessel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3158" - }, - { - "requirements": "{10,65}", - "shop_price": "5", - "examine": "A wide bodied and thin necked vessel, encrusted with sea salt. / The Karambwan Vessel is loaded with Karambwanji", - "grand_exchange_price": "240", - "durability": null, - "name": "Karambwan vessel", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "3159" - }, - { - "durability": null, - "name": "Karambwan vessel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3160" - }, - { - "examine": "A set of instructions explaining how to construct a Karambwan vessel", - "durability": null, - "name": "Crafting manual", - "archery_ticket_price": "0", - "id": "3161" - }, - { - "ge_buy_limit": "100", - "examine": "You swear you had more than three slices before.", - "grand_exchange_price": "38", - "durability": null, - "name": "Sliced banana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3162" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "38", - "durability": null, - "name": "Sliced banana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3163" - }, - { - "shop_price": "27", - "examine": "A very strong spirit brewed in Karamja.", - "durability": null, - "name": "Karamjan rum", - "archery_ticket_price": "0", - "id": "3164" - }, - { - "shop_price": "27", - "examine": "A very strong spirit brewed in Karamja.", - "durability": null, - "name": "Karamjan rum", - "archery_ticket_price": "0", - "id": "3165" - }, - { - "examine": "It's the body of a dead monkey.", - "durability": null, - "name": "Monkey corpse", - "weight": "1", - "archery_ticket_price": "0", - "id": "3166" - }, - { - "examine": "It's the skin of a (hopefully) dead monkey.", - "durability": null, - "name": "Monkey skin", - "archery_ticket_price": "0", - "id": "3167" - }, - { - "examine": "A 'Seaweed in Monkey Skin' sandwich. Perfect for statue repair.", - "durability": null, - "name": "Seaweed sandwich", - "archery_ticket_price": "0", - "id": "3168" - }, - { - "examine": "A body of a dead monkey, tastefully stuffed with seaweed.", - "durability": null, - "name": "Stuffed monkey", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3169" - }, - { - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned 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", + "equip_audio": "1003", "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "266", - "stand_anim": "813", - "name": "Bronze spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3170", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned 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": "2080,2081,2082,2080", - "grand_exchange_price": "304", - "stand_anim": "813", - "name": "Iron spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3171", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0" - }, - { - "requirements": "{0,5}", - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned 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", - "defence_anim": "2079", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "503", - "stand_anim": "813", - "name": "Steel spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3172", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0" - }, - { + "ge_buy_limit": "100", + "render_anim": "2583", "requirements": "{0,20}", - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned mithril tipped spear.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", + "tradeable": "true", "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": "368", - "stand_anim": "813", - "name": "Mithril spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3173", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0" + "weapon_interface": "9", + "weight": "0.9" }, { + "id": "3100", + "name": "Adamant claws", + "examine": "A set of fighting claws.", + "archery_ticket_price": "0", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "18,23,-4,0,0,6,12,3,0,0,0,24,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", "requirements": "{0,30}", - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned adamant tipped spear.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "5", + "tradeable": "true", "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": "1093", - "stand_anim": "813", - "name": "Adamant spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3174", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0" + "weapon_interface": "9" }, { - "requirements": "{0,40}", - "turn90cw_anim": "1207", - "examine": "\tA Karambwan poisoned 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", - "defence_anim": "2079", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "12089", - "stand_anim": "813", - "name": "Rune spear(kp)", - "run_anim": "1210", + "id": "3101", + "name": "Rune claws", + "examine": "A set of fighting claws.", "archery_ticket_price": "0", - "id": "3175", - "stand_turn_anim": "1209", - "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", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "26,38,-4,0,0,10,19,5,0,0,0,39,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", "has_special": "true", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "5", + "lendable": "true", + "render_anim": "2583", + "requirements": "{0,40}", + "tradeable": "true", "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" + "weapon_interface": "9", + "weight": "0.9" }, { + "id": "3102", + "name": "Combination", + "examine": "The combination to Burthorpe Castle's equipment room.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3103", + "name": "Iou", + "examine": "The guard wrote the IOU on the back of some paper.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3104", + "name": "Secret way map", + "examine": "This map shows the secret way up to Death Plateau.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3105", + "name": "Climbing boots", + "examine": "Boots made for general climbing; rocks a speciality.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,2,0,0,0", + "destroy": "true", "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "1900", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "3106", + "name": "Climbing boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3107", + "name": "Spiked boots", + "examine": "Climbing boots with spikes.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,2,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "weight": "0.3" + }, + { + "id": "3109", + "name": "Stone ball", + "examine": "Place on the stone mechanism in the right order to open the door.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3110", + "name": "Stone ball", + "examine": "Place on the stone mechanism in the right order to open the door.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3111", + "name": "Stone ball", + "examine": "Place on the stone mechanism in the right order to open the door.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3112", + "name": "Stone ball", + "examine": "Place on the stone mechanism in the right order to open the door.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3113", + "name": "Stone ball", + "examine": "Place on the stone mechanism in the right order to open the door.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3114", + "name": "Certificate", + "examine": "Entrance certificate to the Imperial Guard.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3115", + "name": "Bronze claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3116", + "name": "Iron claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3117", + "name": "Steel claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3118", + "name": "Black claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3119", + "name": "Mithril claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3120", + "name": "Adamant claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3121", + "name": "Rune claws", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3122", + "name": "Granite shield", + "examine": "A solid stone shield.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-12,-8,40,42,38,0,65,50,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "lendable": "true", + "requirements": "{1,50}-{2,50}", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "3123", + "name": "Shaikahan bones", + "examine": "Large glistening bones which glow with a pale yellow aura.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "3124", + "name": "Shaikahan bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3125", + "name": "Jogre bones", + "examine": "Fairly big bones which smell distinctly of Jogre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "3126", + "name": "Jogre bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3127", + "name": "Burnt jogre bones", + "examine": "These blackened Jogre bones have been somehow burnt.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3128", + "name": "Pasty jogre bones", + "examine": "Burnt Jogre bones smothered with raw Karambwanji Paste.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3129", + "name": "Pasty jogre bones", + "examine": "Burnt Jogre bones smothered with cooked Karambwanji paste.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3130", + "name": "Marinated j' bones", + "examine": "Burnt Jogre bones marinated in a lovely Karambwanji sauce. Perfect.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3131", + "name": "Pasty jogre bones", + "examine": "Jogre bones smothered with raw Karambwanji paste.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3132", + "name": "Pasty jogre bones", + "examine": "Jogre bones smothered with cooked Karambwanji paste.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3133", + "name": "Marinated j' bones", + "examine": "Jogre Bones marinated in Karambwanji sauce. Not quite right.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false", + "weight": "0.8" + }, + { + "id": "3134", + "name": "Granite shield", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3136", + "name": "Cell key 1", + "examine": "The key to the troll prison.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "shop_price": "1", + "tradeable": "false" + }, + { + "id": "3137", + "name": "Cell key 2", + "examine": "The key to the troll prison.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "3138", + "name": "Potato cactus", + "examine": "How am I supposed to eat that?!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3139", + "name": "Potato cactus", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3140", + "name": "Dragon chainbody", + "examine": "A series of connected metal rings.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "3141", + "name": "Dragon chainbody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "3142", + "name": "Raw karambwan", + "examine": "A raw green octopus.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "shop_price": "440", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3143", + "name": "Raw karambwan", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "3144", + "name": "Cooked karambwan", + "examine": "Cooked octopus.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "460", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3145", + "name": "Cooked karambwan", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3146", + "name": "Poison karambwan", + "examine": "Cooked octopus. It looks poorly cooked and quite dangerous.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "3147", + "name": "Cooked karambwan", + "examine": "Cooked octopus.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "460", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3149", + "name": "Burnt karambwan", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3150", + "name": "Raw karambwanji", + "examine": "Small brightly coloured tropical fish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3151", + "name": "Karambwanji", + "examine": "Small brightly coloured tropical fish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.99" + }, + { + "id": "3152", + "name": "Karambwan paste", + "examine": "Freshly made octopus paste. It smells quite nauseating.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3153", + "name": "Karambwan paste", + "examine": "Freshly made octopus paste. It smells quite nauseating.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3154", + "name": "Karambwan paste", + "examine": "Freshly made octopus paste. It smells quite nauseating.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3155", + "name": "Karambwanji paste", + "examine": "This paste smells of raw fish./This paste smells of cooked fish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3156", + "name": "Karambwanji paste", + "examine": "This paste smells of raw fish./This paste smells of cooked fish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3157", + "name": "Karambwan vessel", + "examine": "A wide bodied and thin necked vessel, encrusted with sea salt. / The Karambwan Vessel is loaded with Karambwanji", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{10,65}", + "shop_price": "5", + "tradeable": "true", + "weight": "3" + }, + { + "id": "3158", + "name": "Karambwan vessel", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3159", + "name": "Karambwan vessel", + "examine": "A wide bodied and thin necked vessel, encrusted with sea salt. / The Karambwan Vessel is loaded with Karambwanji", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{10,65}", + "shop_price": "5", + "tradeable": "true", + "weight": "3" + }, + { + "id": "3160", + "name": "Karambwan vessel", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3161", + "name": "Crafting manual", + "examine": "A set of instructions explaining how to construct a Karambwan vessel", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3162", + "name": "Sliced banana", + "examine": "You swear you had more than three slices before.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3163", + "name": "Sliced banana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3164", + "name": "Karamjan rum", + "examine": "A very strong spirit brewed in Karamja.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "27" + }, + { + "id": "3165", + "name": "Karamjan rum", + "examine": "A very strong spirit brewed in Karamja.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "27" + }, + { + "id": "3166", + "name": "Monkey corpse", + "examine": "It's the body of a dead monkey.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "3167", + "name": "Monkey skin", + "examine": "It's the skin of a (hopefully) dead monkey.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3168", + "name": "Seaweed sandwich", + "examine": "A 'Seaweed in Monkey Skin' sandwich. Perfect for statue repair.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3169", + "name": "Stuffed monkey", + "examine": "A body of a dead monkey, tastefully stuffed with seaweed.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "3170", + "name": "Bronze spear(kp)", + "examine": "A Karambwan poisoned bronze tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "3171", + "name": "Iron spear(kp)", + "examine": "A Karambwan poisoned iron tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "3172", + "name": "Steel spear(kp)", + "examine": "A Karambwan poisoned steel tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "3173", + "name": "Mithril spear(kp)", + "examine": "A Karambwan poisoned mithril tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "3174", + "name": "Adamant spear(kp)", + "examine": "A Karambwan poisoned adamant tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "3175", + "name": "Rune spear(kp)", + "examine": "\tA Karambwan poisoned rune tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "3176", + "name": "Dragon spear(kp)", + "examine": "A Karambwan poisoned dragon tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "requirements": "{0,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "3177", "name": "Picture", "archery_ticket_price": "0", - "id": "3177", + "durability": null, "equipment_slot": "5" }, { - "durability": null, + "id": "3178", "name": "Picture", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3178" - }, - { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", "durability": null, - "name": "Monkey bones", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3179" + "tradeable": "true" }, { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", + "id": "3179", + "name": "Monkey bones", + "examine": "These are smallish monkey bones.", + "archery_ticket_price": "0", "durability": null, - "name": "Monkey bones", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3180" + "weight": "0.5" }, { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", + "id": "3180", + "name": "Monkey bones", + "examine": "These are medium sized monkey bones.", + "archery_ticket_price": "0", "durability": null, - "name": "Monkey bones", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3181" + "weight": "0.5" }, { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", + "id": "3181", + "name": "Monkey bones", + "examine": "These are quite large monkey bones.", + "archery_ticket_price": "0", "durability": null, - "name": "Monkey bones", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3182" + "weight": "0.5" }, { + "id": "3182", + "name": "Monkey bones", + "examine": "These are quite large monkey bones.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3183", + "name": "Monkey bones", + "examine": "These are small monkey bones.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "588", - "durability": null, - "name": "Monkey bones", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3183" + "weight": "0.5" }, { + "id": "3184", + "name": "Monkey bones", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "588", - "durability": null, - "name": "Monkey bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3184" + "tradeable": "true" }, { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", - "durability": null, + "id": "3185", "name": "Monkey bones", - "tradeable": "true", - "weight": "0.5", + "examine": "These are smallish monkey bones. They smell extremely nauseating.", "archery_ticket_price": "0", - "id": "3185" + "durability": null, + "tradeable": "true", + "weight": "0.5" }, { - "examine": "These are small/medium sized monkey bones.", - "grand_exchange_price": "383", - "durability": null, + "id": "3186", "name": "Monkey bones", - "tradeable": "true", - "weight": "0.5", + "examine": "These are small monkey bones. They smell extremely nauseating.", "archery_ticket_price": "0", - "id": "3186" + "durability": null, + "tradeable": "true", + "weight": "0.5" }, { - "shop_price": "140", - "grand_exchange_price": "140", - "durability": null, + "id": "3187", "name": "Bones", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "3187" + "durability": null, + "shop_price": "140", + "tradeable": "true", + "weight": "0.5" }, { - "shop_price": "60", - "ge_buy_limit": "100", + "id": "3188", + "name": "Cleaning cloth", "examine": "A piece of silk which can be used to remove poison from weapons.", - "grand_exchange_price": "286", - "durability": null, - "name": "Cleaning cloth", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "3188" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "60", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "286", - "durability": null, + "id": "3189", "name": "Cleaning cloth", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3189" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "522", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "3190", - "stand_turn_anim": "1209", - "bonuses": "7,8,0,-4,0,-1,1,2,0,0,0,8,0,0,0", - "shop_price": "104", - "durability": null, - "weight": "3", - "weapon_interface": "15", - "render_anim": "28", - "name": "Bronze halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "522", - "durability": null, "name": "Bronze halberd", - "tradeable": "true", + "examine": "A bronze halberd.", "archery_ticket_price": "0", - "id": "3191" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "7,8,0,-4,0,-1,1,2,0,0,0,8,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "104", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An iron halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "124", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3191", + "name": "Bronze halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3192", - "stand_turn_anim": "1209", - "bonuses": "9,12,0,-4,0,-1,1,2,0,0,0,12,0,0,0", - "shop_price": "364", - "durability": null, - "weight": "3.1", - "weapon_interface": "15", - "render_anim": "28", - "name": "Iron halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "124", - "durability": null, "name": "Iron halberd", - "tradeable": "true", + "examine": "An iron halberd.", "archery_ticket_price": "0", - "id": "3193" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "9,12,0,-4,0,-1,1,2,0,0,0,12,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "364", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3.1" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "726", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3193", + "name": "Iron halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3194", - "stand_turn_anim": "1209", - "bonuses": "14,19,0,-4,0,-1,2,3,0,0,0,20,0,0,0", - "requirements": "{0,5}", - "shop_price": "1300", - "durability": null, - "weight": "3", - "weapon_interface": "15", - "render_anim": "28", - "name": "Steel halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "726", - "durability": null, "name": "Steel halberd", - "tradeable": "true", + "examine": "A steel halberd.", "archery_ticket_price": "0", - "id": "3195" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "14,19,0,-4,0,-1,2,3,0,0,0,20,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "shop_price": "1300", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "4344", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3195", + "name": "Steel halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "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}", - "shop_price": "2496", - "durability": null, - "weight": "3", - "weapon_interface": "15", - "render_anim": "28", - "name": "Black halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4344", - "durability": null, "name": "Black halberd", - "tradeable": "true", + "examine": "A black halberd.", "archery_ticket_price": "0", - "id": "3197" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "19,25,0,-4,0,-1,2,3,0,0,0,20,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}-{2,5}", + "run_anim": "1210", + "shop_price": "2496", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "6995", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3197", + "name": "Black halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3198", - "stand_turn_anim": "1209", - "bonuses": "22,28,0,-4,0,-1,2,4,0,0,0,29,0,0,0", - "requirements": "{0,20}-{2,10}", - "shop_price": "3380", - "durability": null, - "weight": "2.7", - "weapon_interface": "15", - "render_anim": "28", - "name": "Mithril halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6995", - "durability": null, "name": "Mithril halberd", - "tradeable": "true", + "examine": "A mithril halberd.", "archery_ticket_price": "0", - "id": "3199" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "22,28,0,-4,0,-1,2,4,0,0,0,29,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}-{2,10}", + "run_anim": "1210", + "shop_price": "3380", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "2.7" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "4690", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3199", + "name": "Mithril halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3200", - "stand_turn_anim": "1209", - "bonuses": "28,41,0,-4,0,-1,3,4,0,0,0,42,0,0,0", - "requirements": "{0,30}-{2,15}", - "shop_price": "8320", - "durability": null, - "weight": "3.6", - "weapon_interface": "15", - "render_anim": "28", - "name": "Adamant halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4690", - "durability": null, "name": "Adamant halberd", - "tradeable": "true", + "examine": "An adamant halberd.", "archery_ticket_price": "0", - "id": "3201" + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "28,41,0,-4,0,-1,3,4,0,0,0,42,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}-{2,15}", + "run_anim": "1210", + "shop_price": "8320", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3.6" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "77100", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3201", + "name": "Adamant halberd", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3202", - "stand_turn_anim": "1209", - "bonuses": "48,67,0,-4,0,-1,4,5,0,0,0,68,0,0,0", - "requirements": "{0,40}-{2,20}", - "shop_price": "128000", - "durability": null, - "weight": "3", - "weapon_interface": "15", - "render_anim": "28", - "lendable": "true", - "name": "Rune halberd" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "77100", - "durability": null, "name": "Rune halberd", - "tradeable": "true", + "examine": "A rune halberd.", "archery_ticket_price": "0", - "id": "3203" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A dragon halberd.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", - "equipment_slot": "3", "attack_anims": "440,440,412,440", - "grand_exchange_price": "201000", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "3204", - "stand_turn_anim": "1209", - "bonuses": "70,95,0,-4,0,-1,4,5,0,0,0,89,0,0,0", - "requirements": "{0,60}-{2,30}", - "shop_price": "325000", + "attack_speed": "7", + "bonuses": "48,67,0,-4,0,-1,4,5,0,0,0,68,0,0,0", + "defence_anim": "430", "durability": null, - "weight": "3.1", - "weapon_interface": "15", - "render_anim": "28", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "lendable": "true", - "name": "Dragon halberd" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "201000", - "durability": null, - "name": "Dragon halberd", + "render_anim": "28", + "requirements": "{0,40}-{2,20}", + "run_anim": "1210", + "shop_price": "128000", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3205" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3" }, { - "examine": "A summons from King Lathas.", + "id": "3203", + "name": "Rune halberd", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3204", + "name": "Dragon halberd", + "examine": "A dragon halberd.", + "archery_ticket_price": "0", + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "70,95,0,-4,0,-1,4,5,0,0,0,89,0,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,60}-{2,30}", + "run_anim": "1210", + "shop_price": "325000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3.1" + }, + { + "id": "3205", + "name": "Dragon halberd", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "3206", "name": "King's message", + "examine": "A summons from King Lathas.", "archery_ticket_price": "0", - "id": "3206" + "durability": null }, { - "examine": "A letter for King Lathas from Lord Iorwerth.", - "durability": null, + "id": "3207", "name": "Iorwerths message", + "examine": "A letter for King Lathas from Lord Iorwerth.", "archery_ticket_price": "0", - "id": "3207" + "durability": null }, { - "examine": "Lord Iorwerth's crystal pendant.", - "durability": null, - "name": "Crystal pendant", - "archery_ticket_price": "0", "id": "3208", + "name": "Crystal pendant", + "examine": "Lord Iorwerth's crystal pendant.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "examine": "A piece of chemical formation.", - "durability": null, + "id": "3209", "name": "Sulphur", - "weight": "0.6", + "examine": "A piece of chemical formation.", "archery_ticket_price": "0", - "id": "3209" + "durability": null, + "weight": "0.6" }, { - "shop_price": "4", - "ge_buy_limit": "10000", + "id": "3211", + "name": "Limestone", "examine": "Some limestone.", - "grand_exchange_price": "238", - "durability": null, - "name": "Limestone", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "3211" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "238", - "durability": null, + "shop_price": "4", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "3212", "name": "Limestone", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3212" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "Some quicklime.", - "durability": null, + "id": "3213", "name": "Quicklime", - "weight": "2.2", + "examine": "Some quicklime.", "archery_ticket_price": "0", - "id": "3213" + "durability": null, + "weight": "2.2" }, { - "examine": "A pot of ground quicklime.", - "durability": null, + "id": "3214", "name": "Pot of quicklime", + "examine": "A pot of ground quicklime.", "archery_ticket_price": "0", - "id": "3214" + "durability": null }, { - "shop_price": "160", - "examine": "A pile of ground sulphur.", - "durability": null, + "id": "3215", "name": "Ground sulphur", - "weight": "0.5", + "examine": "A pile of ground sulphur.", "archery_ticket_price": "0", - "id": "3215" + "durability": null, + "shop_price": "160", + "weight": "0.5" }, { - "ge_buy_limit": "100", + "id": "3216", + "name": "Barrel", "examine": "An empty barrel/A Splendid barrel.", - "grand_exchange_price": "207", - "durability": null, - "name": "Barrel", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "3216" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "207", - "durability": null, - "name": "Barrel", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3217" + "weight": "10" }, { - "examine": "A fused barrel full of fire oil.", + "id": "3217", + "name": "Barrel", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3218", "name": "Barrel bomb", - "weight": "32", - "archery_ticket_price": "0", - "id": "3218" - }, - { "examine": "A fused barrel full of fire oil.", - "durability": null, - "name": "Barrel bomb", - "weight": "32", "archery_ticket_price": "0", - "id": "3219" + "durability": null, + "weight": "32" }, { - "examine": "A barrel full of coal-tar.", + "id": "3219", + "name": "Barrel bomb", + "examine": "A fused barrel full of fire oil.", + "archery_ticket_price": "0", "durability": null, + "weight": "32" + }, + { + "id": "3220", "name": "Barrel of coal-tar", - "weight": "32", + "examine": "A barrel full of coal-tar.", "archery_ticket_price": "0", - "id": "3220" + "durability": null, + "weight": "32" }, { - "examine": "A barrel full of naphtha.", - "durability": null, + "id": "3221", "name": "Barrel of naphtha", - "weight": "32", + "examine": "A barrel full of naphtha.", "archery_ticket_price": "0", - "id": "3221" + "durability": null, + "weight": "32" }, { - "examine": "A barrel full of naphtha and sulphur./A barrel full of naphtha and quicklime.", - "durability": null, + "id": "3222", "name": "Naphtha mix", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3222" - }, - { "examine": "A barrel full of naphtha and sulphur./A barrel full of naphtha and quicklime.", - "durability": null, - "name": "Naphtha mix", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3223" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A strip of cloth.", + "id": "3223", + "name": "Naphtha mix", + "examine": "A barrel full of naphtha and sulphur./A barrel full of naphtha and quicklime.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "3224", "name": "Strip of cloth", + "examine": "A strip of cloth.", "archery_ticket_price": "0", - "id": "3224" + "durability": null }, { - "shop_price": "67", - "ge_buy_limit": "5000", + "id": "3226", + "name": "Raw rabbit", "examine": "Might taste better cooked.", - "grand_exchange_price": "312", - "durability": null, - "name": "Raw rabbit", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "3226" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "312", - "durability": null, + "shop_price": "67", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "3227", "name": "Raw rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3227" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "19", - "ge_buy_limit": "1000", + "id": "3228", + "name": "Cooked rabbit", "examine": "Mmm this looks tasty.", - "grand_exchange_price": "35", - "durability": null, - "name": "Cooked rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3228" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "35", - "durability": null, + "shop_price": "19", + "tradeable": "true" + }, + { + "id": "3229", "name": "Cooked rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3229" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "examine": "A book by Mel Achy.", - "durability": null, + "id": "3230", "name": "Big book of bangs", - "weight": "0.2", + "examine": "A book by Mel Achy.", "archery_ticket_price": "0", - "id": "3230" - }, - { "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3232" + "weight": "0.2" }, { + "id": "3232", + "name": "Picture", + "archery_ticket_price": "0", "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3234" + "tradeable": "true" }, { + "id": "3234", + "name": "Picture", + "archery_ticket_price": "0", "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3236" + "tradeable": "true" }, { + "id": "3236", + "name": "Picture", + "archery_ticket_price": "0", "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3238" + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "3238", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3239", + "name": "Bark", "examine": "Bark from a hollow tree.", - "grand_exchange_price": "23", - "durability": null, - "name": "Bark", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "3239" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "23", - "durability": null, - "name": "Bark", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3240" + "weight": "1" }, { - "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 ", + "id": "3240", + "name": "Bark", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3241", "name": "Man", + "examine": "One of 2009Scape's many citizens.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "3241" + "durability": null }, { - "examine": "He grows crops in the area.", - "durability": null, + "id": "3243", "name": "Farmer", + "examine": "He grows crops in the area.", "archery_ticket_price": "0", - "id": "3243" + "durability": null }, { - "examine": "Rogueish.", - "durability": null, + "id": "3247", "name": "Rogue", + "examine": "Rogueish.", "archery_ticket_price": "0", - "id": "3247" + "durability": null }, { - "examine": "Varies, refer below.", - "durability": null, + "id": "3249", "name": "Guard", + "examine": "Varies, refer below.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "3249" + "durability": null }, { - "examine": "A member of Ardougne's militia.", - "durability": null, + "id": "3251", "name": "Knight of ardougne", + "examine": "A member of Ardougne's militia.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "3251" + "durability": null }, { - "examine": "The strong arm of the law.", - "durability": null, + "id": "3253", "name": "Watchman", + "examine": "The strong arm of the law.", "archery_ticket_price": "0", - "id": "3253" + "durability": null }, { - "examine": "A holy warrior.", - "durability": null, + "id": "3255", "name": "Paladin", + "examine": "A holy warrior.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "3255" + "durability": null }, { - "examine": "Heroic!", - "durability": null, + "id": "3259", "name": "Hero", + "examine": "Heroic!", "archery_ticket_price": "0", "attack_speed": "5", - "id": "3259" + "durability": null }, { - "examine": "A pale, tough looking herb.", - "durability": null, + "id": "3261", "name": "Goutweed", + "examine": "A pale, tough looking herb.", "archery_ticket_price": "0", - "id": "3261" + "durability": null }, { - "examine": "It's tough and spiky.", - "durability": null, + "id": "3262", "name": "Troll thistle", + "examine": "It's tough and spiky.", "archery_ticket_price": "0", - "id": "3262" + "durability": null }, { - "examine": "It'll be easier to grind now.", - "durability": null, + "id": "3263", "name": "Dried thistle", - "tradeable": "false", - "destroy": "true", + "examine": "It'll be easier to grind now.", "archery_ticket_price": "0", - "id": "3263" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "It's ready for mixing.", - "durability": null, + "id": "3264", "name": "Ground thistle", - "tradeable": "false", - "destroy": "true", + "examine": "It's ready for mixing.", "archery_ticket_price": "0", - "id": "3264" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "It's part of Eadgar's plan.", - "durability": null, + "id": "3265", "name": "Troll potion", - "tradeable": "false", - "destroy": "true", + "examine": "It's part of Eadgar's plan.", "archery_ticket_price": "0", - "id": "3265" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "It's rather drunk.", - "durability": null, + "id": "3266", "name": "Drunk parrot", + "examine": "It's rather drunk.", "archery_ticket_price": "0", - "id": "3266" + "durability": null }, { - "examine": "It's dirty and smelly.", - "durability": null, + "id": "3267", "name": "Dirty robe", + "examine": "It's dirty and smelly.", "archery_ticket_price": "0", - "id": "3267" + "durability": null }, { - "examine": "It's good enough to fool a troll.", - "durability": null, + "id": "3268", "name": "Fake man", + "examine": "It's good enough to fool a troll.", "archery_ticket_price": "0", - "id": "3268" + "durability": null }, { - "examine": "The key to the Trollheim storeroom.", - "durability": null, + "id": "3269", "name": "Storeroom key", - "tradeable": "false", + "examine": "The key to the Trollheim storeroom.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "3269" + "durability": null, + "tradeable": "false" }, { - "examine": "Pineapple chunks dipped in a strong liquor.", - "durability": null, + "id": "3270", "name": "Alco-chunks", + "examine": "Pineapple chunks dipped in a strong liquor.", "archery_ticket_price": "0", - "id": "3270" + "durability": null }, { - "ge_buy_limit": "5000", + "id": "3325", + "name": "Vampire dust", "examine": "That used to be a vampyre!", - "grand_exchange_price": "381", - "durability": null, - "name": "Vampire dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3325" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "381", - "durability": null, + "tradeable": "true" + }, + { + "id": "3326", "name": "Vampire dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3326" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "An easy-to-make, marshy-coloured, Mort Myre snail shell helmet.", - "grand_exchange_price": "56", - "durability": null, - "name": "Myre snelm", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "3327", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "56", - "durability": null, "name": "Myre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3328" - }, - { - "remove_head": "true", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Rounded: A red and black snail shell helmet. Pointed: A red and black pointed snail shell helmet.", - "durability": null, - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "291", - "name": "Blood'n'tar snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3329", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "291", - "durability": null, - "name": "Blood'n'tar snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3330" - }, - { - "remove_head": "true", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell helmet.", - "grand_exchange_price": "57", - "durability": null, - "name": "Ochre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3331", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "57", - "durability": null, - "name": "Ochre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3332" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "A moody blue snail shell helmet, mort myre snail shell helm", - "grand_exchange_price": "73", - "durability": null, - "name": "Bruise blue snelm", - "tradeable": "true", - "weight": "1.9", - "archery_ticket_price": "0", - "id": "3333", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "73", - "durability": null, - "name": "Bruise blue snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3334" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "An easy-to-make, orange-and-bark coloured, Mort Myre snail shell helmet.", - "grand_exchange_price": "63", - "durability": null, - "name": "Broken bark snelm", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "3335", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "63", - "durability": null, - "name": "Broken bark snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3336" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", "examine": "An easy-to-make, marshy-coloured, Mort Myre snail shell helmet.", - "grand_exchange_price": "58", - "durability": null, - "name": "Myre snelm", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "3337", "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "58", "durability": null, - "name": "Myre snelm", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3338" + "weight": "2" }, { - "remove_head": "true", - "shop_price": "300", + "id": "3328", + "name": "Myre snelm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3329", + "name": "Blood'n'tar snelm", "examine": "Rounded: A red and black snail shell helmet. Pointed: A red and black pointed snail shell helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", "durability": null, - "weight": "2", "equipment_slot": "0", - "grand_exchange_price": "147", - "name": "Blood'n'tar snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3339", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "147", - "durability": null, - "name": "Blood'n'tar snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3340" - }, - { "remove_head": "true", "shop_price": "300", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3330", + "name": "Blood'n'tar snelm", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3331", + "name": "Ochre snelm", "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell helmet.", - "grand_exchange_price": "54", - "durability": null, - "name": "Ochre snelm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3341", "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "54", "durability": null, - "name": "Ochre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3342" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "A moody blue snail shell helmet, mort myre snail shell helm", - "grand_exchange_price": "248", - "durability": null, - "name": "Bruise blue snelm", - "tradeable": "true", - "weight": "1.9", - "archery_ticket_price": "0", - "id": "3343", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "248", - "durability": null, - "name": "Bruise blue snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3344" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, 'myre'-coloured, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "155", - "durability": null, - "name": "Blamish myre shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3345" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "155", - "durability": null, - "name": "Blamish myre shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3346" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, red, Mort myre snail shell; looks protective.", - "grand_exchange_price": "590", - "durability": null, - "name": "Blamish red shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3347" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "590", - "durability": null, - "name": "Blamish red shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3348" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "134", - "durability": null, - "name": "Blamish ochre shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3349" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "134", - "durability": null, - "name": "Blamish ochre shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3350" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, blue, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "230", - "durability": null, - "name": "Blamish blue shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3351" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "230", - "durability": null, - "name": "Blamish blue shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3352" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, bark-coloured, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "195", - "durability": null, - "name": "Blamish bark shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3353" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "195", - "durability": null, - "name": "Blamish bark shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3354" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, 'myre'-coloured, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "145", - "durability": null, - "name": "Blamish myre shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3355" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "145", - "durability": null, - "name": "Blamish myre shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3356" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, red, Mort myre snail shell; looks protective.", - "grand_exchange_price": "614", - "durability": null, - "name": "Blamish red shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3357" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "614", - "durability": null, - "name": "Blamish red shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3358" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "173", - "durability": null, - "name": "Blamish ochre shell", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "3359" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "173", - "durability": null, - "name": "Blamish ochre shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3360" - }, - { - "ge_buy_limit": "100", - "examine": "An easy-to-make, blue, Mort Myre snail shell; looks protective.", - "grand_exchange_price": "874", - "durability": null, - "name": "Blamish blue shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3361" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "874", - "durability": null, - "name": "Blamish blue shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3362" - }, - { - "shop_price": "74", - "ge_buy_limit": "10000", - "examine": "The thin, slimy corpse of a deceased giant snail.", - "grand_exchange_price": "2047", - "durability": null, - "name": "Thin snail", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "3363" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2047", - "durability": null, - "name": "Thin snail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3364" - }, - { - "ge_buy_limit": "1000", - "examine": "The lean, slimy corspe of a dead snail.", - "grand_exchange_price": "958", - "durability": null, - "name": "Lean snail", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "3365" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "958", - "durability": null, - "name": "Lean snail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3366" - }, - { - "shop_price": "160", - "ge_buy_limit": "10000", - "examine": "The fat, slimy, corpse of a deceased giant snail.", - "grand_exchange_price": "971", - "durability": null, - "name": "Fat snail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3367" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "971", - "durability": null, - "name": "Fat snail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3368" - }, - { - "ge_buy_limit": "10000", - "examine": "A succulently slimy piece of sumptuous snail.", - "grand_exchange_price": "833", - "durability": null, - "name": "Thin snail meat", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "3369" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "833", - "durability": null, - "name": "Thin snail meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3370" - }, - { - "ge_buy_limit": "10000", - "examine": "A succulently slimey slice of sumptuous snail.", - "grand_exchange_price": "251", - "durability": null, - "name": "Lean snail meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3371" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "251", - "durability": null, - "name": "Lean snail meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3372" - }, - { - "ge_buy_limit": "10000", - "examine": "A succulently slimy slice of sumptuous snail.", - "grand_exchange_price": "603", - "durability": null, - "name": "Fat snail meat", - "tradeable": "true", - "weight": "4.5", - "archery_ticket_price": "0", - "id": "3373" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "603", - "durability": null, - "name": "Fat snail meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3374" - }, - { - "durability": null, - "name": "Burnt snail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3376" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "An empty sample bottle.", - "grand_exchange_price": "14", - "durability": null, - "name": "Sample bottle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3377" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "14", - "durability": null, - "name": "Sample bottle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3378" - }, - { - "ge_buy_limit": "1000", - "examine": "A slime covered eel - yuck!", - "grand_exchange_price": "358", - "durability": null, - "name": "Slimy eel", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "3379" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "358", - "durability": null, - "name": "Slimy eel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3380" - }, - { - "ge_buy_limit": "1000", - "examine": "A cooked slimy eel - not delicious, but pretty nutritious.", - "grand_exchange_price": "274", - "durability": null, - "name": "Cooked slimy eel", - "tradeable": "true", - "destroy": "false", - "weight": "2", - "archery_ticket_price": "0", - "id": "3381" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "274", - "durability": null, - "name": "Cooked slimy eel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3382" - }, - { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "A wooden helmet.", - "durability": null, - "weight": "0.9", - "absorb": "3,1,0", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5993", - "name": "Splitbark helm", - "tradeable": "true", + "ge_buy_limit": "100", + "remove_head": "true", + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "3332", + "name": "Ochre snelm", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3333", + "name": "Bruise blue snelm", + "examine": "A moody blue snail shell helmet, mort myre snail shell helm", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "1.9" + }, + { + "id": "3334", + "name": "Bruise blue snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3335", + "name": "Broken bark snelm", + "examine": "An easy-to-make, orange-and-bark coloured, Mort Myre snail shell helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3336", + "name": "Broken bark snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3337", + "name": "Myre snelm", + "examine": "An easy-to-make, marshy-coloured, Mort Myre snail shell helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3338", + "name": "Myre snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3339", + "name": "Blood'n'tar snelm", + "examine": "Rounded: A red and black snail shell helmet. Pointed: A red and black pointed snail shell helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3340", + "name": "Blood'n'tar snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3341", + "name": "Ochre snelm", + "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "3342", + "name": "Ochre snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3343", + "name": "Bruise blue snelm", + "examine": "A moody blue snail shell helmet, mort myre snail shell helm", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "1.9" + }, + { + "id": "3344", + "name": "Bruise blue snelm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3345", + "name": "Blamish myre shell", + "examine": "An easy-to-make, 'myre'-coloured, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3346", + "name": "Blamish myre shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3347", + "name": "Blamish red shell", + "examine": "An easy-to-make, red, Mort myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3348", + "name": "Blamish red shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3349", + "name": "Blamish ochre shell", + "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3350", + "name": "Blamish ochre shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3351", + "name": "Blamish blue shell", + "examine": "An easy-to-make, blue, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3352", + "name": "Blamish blue shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3353", + "name": "Blamish bark shell", + "examine": "An easy-to-make, bark-coloured, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3354", + "name": "Blamish bark shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3355", + "name": "Blamish myre shell", + "examine": "An easy-to-make, 'myre'-coloured, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3356", + "name": "Blamish myre shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3357", + "name": "Blamish red shell", + "examine": "An easy-to-make, red, Mort myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3358", + "name": "Blamish red shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3359", + "name": "Blamish ochre shell", + "examine": "An easy-to-make, muddy-yellow, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3360", + "name": "Blamish ochre shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3361", + "name": "Blamish blue shell", + "examine": "An easy-to-make, blue, Mort Myre snail shell; looks protective.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3362", + "name": "Blamish blue shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3363", + "name": "Thin snail", + "examine": "The thin, slimy corpse of a deceased giant snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "74", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3364", + "name": "Thin snail", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3365", + "name": "Lean snail", + "examine": "The lean, slimy corspe of a dead snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3366", + "name": "Lean snail", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3367", + "name": "Fat snail", + "examine": "The fat, slimy, corpse of a deceased giant snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "160", + "tradeable": "true" + }, + { + "id": "3368", + "name": "Fat snail", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3369", + "name": "Thin snail meat", + "examine": "A succulently slimy piece of sumptuous snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3" + }, + { + "id": "3370", + "name": "Thin snail meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3371", + "name": "Lean snail meat", + "examine": "A succulently slimey slice of sumptuous snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3372", + "name": "Lean snail meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3373", + "name": "Fat snail meat", + "examine": "A succulently slimy slice of sumptuous snail.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "4.5" + }, + { + "id": "3374", + "name": "Fat snail meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3376", + "name": "Burnt snail", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3377", + "name": "Sample bottle", + "examine": "An empty sample bottle.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "3378", + "name": "Sample bottle", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3379", + "name": "Slimy eel", + "examine": "A slime covered eel - yuck!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3380", + "name": "Slimy eel", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3381", + "name": "Cooked slimy eel", + "examine": "A cooked slimy eel - not delicious, but pretty nutritious.", + "archery_ticket_price": "0", + "destroy": "false", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3382", + "name": "Cooked slimy eel", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "3385", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5993", - "durability": null, "name": "Splitbark helm", - "tradeable": "true", + "examine": "A wooden helmet.", + "absorb": "3,1,0", "archery_ticket_price": "0", - "id": "3386" + "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "0.9" }, { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "Provides good protection.", + "id": "3386", + "name": "Splitbark helm", + "archery_ticket_price": "0", "durability": null, - "weight": "4.5", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3387", + "name": "Splitbark body", + "examine": "Provides good protection.", "absorb": "6,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,10,-10,36,26,42,15,0,40,0,0,0,0", + "durability": null, "equipment_slot": "4", + "ge_buy_limit": "100", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "26700", - "name": "Splitbark body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3387", - "bonuses": "0,0,0,10,-10,36,26,42,15,0,40,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26700", - "durability": null, - "name": "Splitbark body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3388" - }, - { "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "These should protect my legs.", - "durability": null, - "weight": "3.6", - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "43700", - "name": "Splitbark legs", "tradeable": "true", + "weight": "4.5" + }, + { + "id": "3388", + "name": "Splitbark body", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3389", - "bonuses": "0,0,0,7,-7,22,20,25,10,0,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "43700", - "durability": null, "name": "Splitbark legs", - "tradeable": "true", + "examine": "These should protect my legs.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "3390" + "bonuses": "0,0,0,7,-7,22,20,25,10,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "3.6" }, { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "These should keep my hands safe.", - "durability": null, - "weight": "0.9", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "2786", - "name": "Splitbark gauntlets", - "tradeable": "true", + "id": "3390", + "name": "Splitbark legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3391", - "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2786", - "durability": null, "name": "Splitbark gauntlets", - "tradeable": "true", + "examine": "These should keep my hands safe.", "archery_ticket_price": "0", - "id": "3392" - }, - { + "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "Wooden foot protection.", - "durability": null, - "weight": "0.9", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "3808", - "name": "Splitbark boots", "tradeable": "true", + "weight": "0.9" + }, + { + "id": "3392", + "name": "Splitbark gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3393", - "bonuses": "0,0,0,2,-1,3,2,4,2,0,9,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3808", - "durability": null, "name": "Splitbark boots", - "tradeable": "true", + "examine": "Wooden foot protection.", "archery_ticket_price": "0", - "id": "3394" - }, - { - "ge_buy_limit": "1000", - "examine": "The remains of a deadly shade.", - "grand_exchange_price": "18", + "bonuses": "0,0,0,2,-1,3,2,4,2,0,9,0,0,0,0", "durability": null, - "name": "Loar remains", - "tradeable": "true", - "weight": "1.35", - "archery_ticket_price": "0", - "id": "3396" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "18", - "durability": null, - "name": "Loar remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3397" - }, - { - "ge_buy_limit": "1000", - "examine": "The remains of a deadly shade.", - "grand_exchange_price": "249", - "durability": null, - "name": "Phrin remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3398" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "249", - "durability": null, - "name": "Phrin remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3399" - }, - { - "ge_buy_limit": "1000", - "examine": "The remains of a deadly shade.", - "grand_exchange_price": "138", - "durability": null, - "name": "Riyl remains", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3400" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "138", - "durability": null, - "name": "Riyl remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3401" - }, - { - "ge_buy_limit": "1000", - "examine": "The remains of a deadly shade.", - "grand_exchange_price": "334", - "durability": null, - "name": "Asyn remains", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3402" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "334", - "durability": null, - "name": "Asyn remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3403" - }, - { - "ge_buy_limit": "1000", - "examine": "The remains of a deadly shade.", - "grand_exchange_price": "5264", - "durability": null, - "name": "Fiyr remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3404" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "5264", - "durability": null, - "name": "Fiyr remains", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3405" - }, - { + "equip_audio": "2237", + "equipment_slot": "10", "ge_buy_limit": "100", - "examine": "I need another ingredient to finish this potion.", - "grand_exchange_price": "137", - "durability": null, - "name": "Ash potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3406" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "137", - "durability": null, - "name": "Ash potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3407" - }, - { - "examine": "4 doses serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (4)", - "archery_ticket_price": "0", - "id": "3408" - }, - { - "examine": "3 doses serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (3)", - "archery_ticket_price": "0", - "id": "3410" - }, - { - "examine": "2 doses serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (2)", - "archery_ticket_price": "0", - "id": "3412" - }, - { - "examine": "1 dose serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (1)", - "archery_ticket_price": "0", - "id": "3414" - }, - { - "shop_price": "208", - "examine": "3 doses permanent serum 208 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 208 (4)", - "archery_ticket_price": "0", - "id": "3416" - }, - { - "shop_price": "208", - "examine": "3 doses permanent serum 208 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 208 (3)", - "archery_ticket_price": "0", - "id": "3417" - }, - { - "shop_price": "208", - "examine": "2 doses permanent serum 208 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 208 (2)", - "archery_ticket_price": "0", - "id": "3418" - }, - { - "shop_price": "208", - "examine": "1 dose permanent serum 208 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 208 (1)", - "archery_ticket_price": "0", - "id": "3419" - }, - { - "shop_price": "21", - "ge_buy_limit": "10000", - "examine": "A well carved limestone brick.", - "grand_exchange_price": "178", - "durability": null, - "name": "Limestone brick", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3420" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "178", - "durability": null, - "name": "Limestone brick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3421" - }, - { - "shop_price": "26", - "ge_buy_limit": "1000", - "examine": "4 doses of olive oil.", - "grand_exchange_price": "678", - "durability": null, - "name": "Olive oil(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3422" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "678", - "durability": null, - "name": "Olive oil(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3423" - }, - { - "shop_price": "26", - "ge_buy_limit": "1000", - "examine": "3 doses of olive oil.", - "grand_exchange_price": "413", - "durability": null, - "name": "Olive oil(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3424" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "413", - "durability": null, - "name": "Olive oil(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3425" - }, - { - "shop_price": "26", - "ge_buy_limit": "1000", - "examine": "2 doses of olive oil.", - "grand_exchange_price": "173", - "durability": null, - "name": "Olive oil(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3426" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "173", - "durability": null, - "name": "Olive oil(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3427" - }, - { - "shop_price": "26", - "ge_buy_limit": "1000", - "examine": "1 dose of olive oil.", - "grand_exchange_price": "110", - "durability": null, - "name": "Olive oil(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3428" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "110", - "durability": null, - "name": "Olive oil(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3429" - }, - { - "ge_buy_limit": "1000", - "examine": "Sacred oil.", - "grand_exchange_price": "3587", - "durability": null, - "name": "Sacred oil(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3430" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3587", - "durability": null, - "name": "Sacred oil(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3431" - }, - { - "ge_buy_limit": "1000", - "examine": "Sacred oil.", - "grand_exchange_price": "2596", - "durability": null, - "name": "Sacred oil(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3432" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2596", - "durability": null, - "name": "Sacred oil(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3433" - }, - { - "ge_buy_limit": "1000", - "examine": "Sacred oil.", - "grand_exchange_price": "1713", - "durability": null, - "name": "Sacred oil(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3434" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1713", - "durability": null, - "name": "Sacred oil(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3435" - }, - { - "ge_buy_limit": "1000", - "examine": "Sacred oil.", - "grand_exchange_price": "1846", - "durability": null, - "name": "Sacred oil(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3436" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1846", - "durability": null, - "name": "Sacred oil(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3437" - }, - { - "ge_buy_limit": "25000", - "examine": "Funeral logs used for burning loars.", - "grand_exchange_price": "853", - "durability": null, - "name": "Pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3438" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "853", - "durability": null, - "name": "Pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3439" - }, - { - "ge_buy_limit": "1000", - "examine": "Oak logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "1122", - "durability": null, - "name": "Oak pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3440" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1122", - "durability": null, - "name": "Oak pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3441" - }, - { - "ge_buy_limit": "1000", - "examine": "Willow logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "1164", - "durability": null, - "name": "Willow pyre logs", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3442" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1164", - "durability": null, - "name": "Willow pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3443" - }, - { - "ge_buy_limit": "1000", - "examine": "Maple logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "3313", - "durability": null, - "name": "Maple pyre logs", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3444" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3313", - "durability": null, - "name": "Maple pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3445" - }, - { - "ge_buy_limit": "1000", - "examine": "Yew logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "4424", - "durability": null, - "name": "Yew pyre logs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3446" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "4424", - "durability": null, - "name": "Yew pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3447" - }, - { - "ge_buy_limit": "1000", - "examine": "Magic logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "5611", - "durability": null, - "name": "Magic pyre logs", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3448" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "5611", - "durability": null, - "name": "Magic pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3449" - }, - { - "examine": "A bronze key with a blood-red painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Bronze key red", - "archery_ticket_price": "0", - "id": "3450" - }, - { - "examine": "A bronze key with a brown painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Bronze key brown", - "archery_ticket_price": "0", - "id": "3451" - }, - { - "examine": "A bronze key with a crimson painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Bronze key crimson", - "archery_ticket_price": "0", - "id": "3452" - }, - { - "examine": "A bronze key with a black painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Bronze key black", - "archery_ticket_price": "0", - "id": "3453" - }, - { - "examine": "A bronze key with a purple painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Bronze key purple", - "archery_ticket_price": "0", - "id": "3454" - }, - { - "examine": "A steel key with a blood-red painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Steel key red", - "archery_ticket_price": "0", - "id": "3455" - }, - { - "examine": "A steel key with a brown painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Steel key brown", - "archery_ticket_price": "0", - "id": "3456" - }, - { - "examine": "A steel key with a crimson painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Steel key crimson", - "archery_ticket_price": "0", - "id": "3457" - }, - { - "examine": "A steel key with a black painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Steel key black", - "archery_ticket_price": "0", - "id": "3458" - }, - { - "examine": "A steel key with a purple painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Steel key purple", - "archery_ticket_price": "0", - "id": "3459" - }, - { - "examine": "A black key with a blood-red painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Black key red", - "archery_ticket_price": "0", - "id": "3460" - }, - { - "examine": "A black key with a brown painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Black key brown", - "archery_ticket_price": "0", - "id": "3461" - }, - { - "examine": "A black key with a crimson painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Black key crimson", - "archery_ticket_price": "0", - "id": "3462" - }, - { - "examine": "A black key with a black painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Black key black", - "archery_ticket_price": "0", - "id": "3463" - }, - { - "examine": "A black key with a purple painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Black key purple", - "archery_ticket_price": "0", - "id": "3464" - }, - { - "examine": "A silver key with a blood-red painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Silver key red", - "archery_ticket_price": "0", - "id": "3465" - }, - { - "examine": "A silver key with a brown painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Silver key brown", - "archery_ticket_price": "0", - "id": "3466" - }, - { - "examine": "A silver key with a crimson painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Silver key crimson", - "archery_ticket_price": "0", - "id": "3467" - }, - { - "examine": "A silver key with a black painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Silver key black", - "archery_ticket_price": "0", - "id": "3468" - }, - { - "examine": "A silver key with a purple painted eyelet. (Shades of Mort'ton)", - "durability": null, - "name": "Silver key purple", - "archery_ticket_price": "0", - "id": "3469" - }, - { - "ge_buy_limit": "1000", - "examine": "Amazingly untouched by time.", - "grand_exchange_price": "956", - "durability": null, - "name": "Fine cloth", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3470" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "956", - "durability": null, - "name": "Fine cloth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3471" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black plateskirt with trim.", - "grand_exchange_price": "3077", - "durability": null, - "name": "Black plateskirt (t)", - "tradeable": "true", - "weight": "8", - "archery_ticket_price": "0", - "id": "3472", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black plateskirt with gold trim.", - "grand_exchange_price": "10600", - "durability": null, - "name": "Black plateskirt (g)", - "tradeable": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "3473", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant plateskirt with trim.", - "durability": null, - "weight": "9", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "4403", - "name": "Adam plateskirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3474", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant plateskirt with gold trim.", - "durability": null, - "weight": "9", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "11000", - "name": "Adam plateskirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3475", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt with gold trim.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", "lendable": "true", - "grand_exchange_price": "42500", - "name": "Rune plateskirt (g)", + "requirements": "{1,40}-{6,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3476", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" + "weight": "0.9" }, { + "id": "3394", + "name": "Splitbark boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3396", + "name": "Loar remains", + "examine": "The remains of a deadly shade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.35" + }, + { + "id": "3397", + "name": "Loar remains", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3398", + "name": "Phrin remains", + "examine": "The remains of a deadly shade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3399", + "name": "Phrin remains", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3400", + "name": "Riyl remains", + "examine": "The remains of a deadly shade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3401", + "name": "Riyl remains", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3402", + "name": "Asyn remains", + "examine": "The remains of a deadly shade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3403", + "name": "Asyn remains", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3404", + "name": "Fiyr remains", + "examine": "The remains of a deadly shade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3405", + "name": "Fiyr remains", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3406", + "name": "Ash potion(unf)", + "examine": "I need another ingredient to finish this potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3407", + "name": "Ash potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3408", + "name": "Serum 207 (4)", + "examine": "4 doses serum 207 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3410", + "name": "Serum 207 (3)", + "examine": "3 doses serum 207 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3412", + "name": "Serum 207 (2)", + "examine": "2 doses serum 207 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3414", + "name": "Serum 207 (1)", + "examine": "1 dose serum 207 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3416", + "name": "Serum 208 (4)", + "examine": "3 doses permanent serum 208 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "208" + }, + { + "id": "3417", + "name": "Serum 208 (3)", + "examine": "3 doses permanent serum 208 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "208" + }, + { + "id": "3418", + "name": "Serum 208 (2)", + "examine": "2 doses permanent serum 208 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "208" + }, + { + "id": "3419", + "name": "Serum 208 (1)", + "examine": "1 dose permanent serum 208 as described in Herbi Flax's diary.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "208" + }, + { + "id": "3420", + "name": "Limestone brick", + "examine": "A well carved limestone brick.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "21", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3421", + "name": "Limestone brick", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3422", + "name": "Olive oil(4)", + "examine": "4 doses of olive oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "3423", + "name": "Olive oil(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3424", + "name": "Olive oil(3)", + "examine": "3 doses of olive oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "3425", + "name": "Olive oil(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3426", + "name": "Olive oil(2)", + "examine": "2 doses of olive oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "3427", + "name": "Olive oil(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3428", + "name": "Olive oil(1)", + "examine": "1 dose of olive oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "26", + "tradeable": "true" + }, + { + "id": "3429", + "name": "Olive oil(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3430", + "name": "Sacred oil(4)", + "examine": "Sacred oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3431", + "name": "Sacred oil(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3432", + "name": "Sacred oil(3)", + "examine": "Sacred oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3433", + "name": "Sacred oil(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3434", + "name": "Sacred oil(2)", + "examine": "Sacred oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3435", + "name": "Sacred oil(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3436", + "name": "Sacred oil(1)", + "examine": "Sacred oil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3437", + "name": "Sacred oil(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3438", + "name": "Pyre logs", + "examine": "Funeral logs used for burning loars.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "3439", + "name": "Pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "3440", + "name": "Oak pyre logs", + "examine": "Oak logs prepared with sacred oil for a funeral pyre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3441", + "name": "Oak pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3442", + "name": "Willow pyre logs", + "examine": "Willow logs prepared with sacred oil for a funeral pyre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3443", + "name": "Willow pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3444", + "name": "Maple pyre logs", + "examine": "Maple logs prepared with sacred oil for a funeral pyre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3445", + "name": "Maple pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3446", + "name": "Yew pyre logs", + "examine": "Yew logs prepared with sacred oil for a funeral pyre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "3447", + "name": "Yew pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3448", + "name": "Magic pyre logs", + "examine": "Magic logs prepared with sacred oil for a funeral pyre.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "3449", + "name": "Magic pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3450", + "name": "Bronze key red", + "examine": "A bronze key with a blood-red painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3451", + "name": "Bronze key brown", + "examine": "A bronze key with a brown painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3452", + "name": "Bronze key crimson", + "examine": "A bronze key with a crimson painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3453", + "name": "Bronze key black", + "examine": "A bronze key with a black painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3454", + "name": "Bronze key purple", + "examine": "A bronze key with a purple painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3455", + "name": "Steel key red", + "examine": "A steel key with a blood-red painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3456", + "name": "Steel key brown", + "examine": "A steel key with a brown painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3457", + "name": "Steel key crimson", + "examine": "A steel key with a crimson painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3458", + "name": "Steel key black", + "examine": "A steel key with a black painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3459", + "name": "Steel key purple", + "examine": "A steel key with a purple painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3460", + "name": "Black key red", + "examine": "A black key with a blood-red painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3461", + "name": "Black key brown", + "examine": "A black key with a brown painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3462", + "name": "Black key crimson", + "examine": "A black key with a crimson painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3463", + "name": "Black key black", + "examine": "A black key with a black painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3464", + "name": "Black key purple", + "examine": "A black key with a purple painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3465", + "name": "Silver key red", + "examine": "A silver key with a blood-red painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3466", + "name": "Silver key brown", + "examine": "A silver key with a brown painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3467", + "name": "Silver key crimson", + "examine": "A silver key with a crimson painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3468", + "name": "Silver key black", + "examine": "A silver key with a black painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3469", + "name": "Silver key purple", + "examine": "A silver key with a purple painted eyelet. (Shades of Mort'ton)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3470", + "name": "Fine cloth", + "examine": "Amazingly untouched by time.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "3471", + "name": "Fine cloth", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3472", + "name": "Black plateskirt (t)", + "examine": "Black plateskirt with trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "8" + }, + { + "id": "3473", + "name": "Black plateskirt (g)", + "examine": "Black plateskirt with gold trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "3474", + "name": "Adam plateskirt (t)", + "examine": "Adamant plateskirt with trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "3475", + "name": "Adam plateskirt (g)", + "examine": "Adamant plateskirt with gold trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "3476", + "name": "Rune plateskirt (g)", + "examine": "Rune plateskirt with gold trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "3477", + "name": "Rune plateskirt (t)", + "examine": "Rune plateskirt with trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", "requirements": "{1,40}", "shop_price": "81280", - "ge_buy_limit": "2", - "examine": "Rune plateskirt with trim.", - "durability": null, - "weight": "8", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "38300", - "name": "Rune plateskirt (t)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3477", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" + "weight": "8" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt in the colours of Zamorak.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "42000", - "name": "Zamorak plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3478", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" + "name": "Zamorak plateskirt", + "examine": "Rune plateskirt in the colours of Zamorak.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt in the colours of Saradomin.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "44000", - "name": "Saradomin plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3479", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt in the colours of Guthix.", - "durability": null, - "weight": "9", + "name": "Saradomin plateskirt", + "examine": "Rune plateskirt in the colours of Saradomin.", "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "39100", - "name": "Guthix plateskirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3480", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody with complete gold trim & plating.", + "id": "3480", + "name": "Guthix plateskirt", + "examine": "Rune plateskirt in the colours of Guthix.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", "durability": null, - "weight": "10", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "3481", + "name": "Gilded platebody", + "examine": "Rune platebody with complete gold trim & plating.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", + "ge_buy_limit": "2", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "2500000", - "name": "Gilded platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3481", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2500000", - "durability": null, - "name": "Gilded platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3482" - }, - { "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platelegs with gold plate.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "2200000", - "name": "Gilded platelegs", "tradeable": "true", + "weight": "10" + }, + { + "id": "3482", + "name": "Gilded platebody", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "3483", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2200000", - "durability": null, "name": "Gilded platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3484" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt with gold plate.", - "durability": null, - "weight": "9", + "examine": "Rune platelegs with gold plate.", "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equip_audio": "2242", "equipment_slot": "7", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "162300", - "name": "Gilded plateskirt", + "requirements": "{1,40}", "tradeable": "true", + "weight": "9" + }, + { + "id": "3484", + "name": "Gilded platelegs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "3485", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune full helmet with gold plate.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "477200", - "name": "Gilded full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3486", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "477200", - "durability": null, - "name": "Gilded full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3487" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune kiteshield with gold plate.", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "515400", - "name": "Gilded kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3488", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "515400", - "durability": null, - "name": "Gilded kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3489" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3490" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3491" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3492" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3493" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3494" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3495" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3496" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3497" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3498" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3499" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3500" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3501" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3502" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3503" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3504" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3505" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3506" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3507" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3508" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3509" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3510" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3511" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3512" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3513" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3514" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3515" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3516" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3517" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3518" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3519" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3520" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3521" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3522" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3523" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3524" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3525" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3526" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3527" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3528" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3529" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3530" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3531" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3532" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3533" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3534" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3535" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3536" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3537" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3538" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3539" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3540" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3541" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3542" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3543" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3544" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3545" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3546" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3547" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3548" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3549" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3550" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3551" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3552" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3553" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3554" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3555" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3556" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3557" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3558" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3559" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3560" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3561" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3562" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3563" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3564" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3565" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3566" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3567" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3568" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3569" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3570" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3571" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3572" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3573" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3574" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3575" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3576" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3577" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "3578" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3579" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3580" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3581" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3582" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3583" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3584" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3585" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3586" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3587" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3588" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3589" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3590" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3591" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3592" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3593" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3594" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3595" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3596" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3597" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3598" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3599" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3600" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3601" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3602" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3603" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3604" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3605" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3607" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3609" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3610" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3611" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3612" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3613" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3614" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3615" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3616" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3617" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3618" - }, - { - "durability": null, - "name": "Black plateskirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3668" - }, - { - "durability": null, - "name": "Black plateskirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3669" - }, - { - "durability": null, - "name": "Adam plateskirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3670" - }, - { - "durability": null, - "name": "Adam plateskirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3671" - }, - { - "durability": null, - "name": "Rune plateskirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3672" - }, - { - "durability": null, - "name": "Rune plateskirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3673" - }, - { - "durability": null, - "name": "Zamorak plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3674" - }, - { - "durability": null, - "name": "Saradomin plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3675" - }, - { - "durability": null, - "name": "Guthix plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3676" - }, - { - "durability": null, "name": "Gilded plateskirt", - "tradeable": "true", + "examine": "Rune plateskirt with gold plate.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "3677" + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" }, { - "shop_price": "13000", - "examine": "An exquisitely shaped tool specially designed for fixing temples.", + "id": "3486", + "name": "Gilded full helm", + "examine": "Rune full helmet with gold plate.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "3487", + "name": "Gilded full helm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "3488", + "name": "Gilded kiteshield", + "examine": "Rune kiteshield with gold plate.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "3489", + "name": "Gilded kiteshield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "3490", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3491", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3492", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3493", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3494", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3495", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3496", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3497", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3498", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3499", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3500", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3501", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3502", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3503", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3504", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3505", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3506", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3507", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3508", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3509", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3510", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3511", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3512", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3513", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3514", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3515", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3516", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3517", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3518", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3519", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3520", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3521", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3522", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3523", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3524", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3525", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3526", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3527", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3528", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3529", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3530", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3531", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3532", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3533", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3534", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3535", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3536", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3537", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3538", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3539", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3540", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3541", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3542", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3543", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3544", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3545", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3546", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3547", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3548", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3549", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3550", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3551", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3552", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3553", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3554", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3555", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3556", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3557", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3558", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3559", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3560", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3561", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3562", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3563", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3564", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3565", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3566", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3567", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3568", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3569", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3570", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3571", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3572", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3573", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3574", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3575", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3576", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3577", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3578", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "3579", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3580", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3581", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3582", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3583", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3584", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3585", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3586", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3587", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3588", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3589", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3590", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3591", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3592", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3593", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3594", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3595", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3596", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3597", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3598", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3599", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3600", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3601", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3602", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3603", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "3604", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3605", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3607", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3609", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3610", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3611", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3612", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3613", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3614", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3615", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3616", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3617", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3618", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3668", + "name": "Black plateskirt (t)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3669", + "name": "Black plateskirt (g)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3670", + "name": "Adam plateskirt (t)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3671", + "name": "Adam plateskirt (g)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3672", + "name": "Rune plateskirt (g)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3673", + "name": "Rune plateskirt (t)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3674", + "name": "Zamorak plateskirt", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3675", + "name": "Saradomin plateskirt", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3676", + "name": "Guthix plateskirt", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3677", + "name": "Gilded plateskirt", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "3678", "name": "Flamtaer hammer", - "weight": "1.3", + "examine": "An exquisitely shaped tool specially designed for fixing temples.", "archery_ticket_price": "0", - "id": "3678" + "durability": null, + "shop_price": "13000", + "weight": "1.3" }, { + "id": "3685", + "name": "Picture", "examine": "A sealed letter of recommendation.", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3685" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "It's almost a musical instrument.", - "durability": null, - "name": "Unstrung lyre", - "weight": "1", - "archery_ticket_price": "0", "id": "3688", - "equipment_slot": "3" - }, - { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", - "durability": null, - "name": "Lyre", - "weight": "1.8", + "name": "Unstrung lyre", + "examine": "It's almost a musical instrument.", "archery_ticket_price": "0", - "id": "3689", - "equipment_slot": "3" - }, - { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", "durability": null, - "name": "Enchanted lyre", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "3690", - "equipment_slot": "3" - }, - { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", - "durability": null, - "name": "Enchanted lyre(1)", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "3691", - "equipment_slot": "3" - }, - { - "examine": "I can use this to make a lyre.", - "durability": null, - "name": "Branch", - "weight": "1", - "archery_ticket_price": "0", - "id": "3692" - }, - { - "shop_price": "1000", - "examine": "I can spin this into golden wool.", - "durability": null, - "name": "Golden fleece", - "weight": "1", - "archery_ticket_price": "0", - "id": "3693" - }, - { - "examine": "I can use this to make a lyre.", - "durability": null, - "name": "Golden wool", - "weight": "1", - "archery_ticket_price": "0", - "id": "3694" - }, - { - "turn90cw_anim": "6662", - "examine": "The lowest maintenance pet you will ever have.", - "walk_anim": "6658", - "durability": null, - "turn90ccw_anim": "6663", - "turn180_anim": "6659", - "render_anim": "792", "equipment_slot": "3", - "stand_anim": "6657", - "name": "Pet rock", - "run_anim": "6660", + "weight": "1" + }, + { + "id": "3689", + "name": "Lyre", + "examine": "A musical intrument that I can magically play.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" + }, + { + "id": "3690", + "name": "Enchanted lyre", + "examine": "This will teleport me to Rellekka when I play it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" + }, + { + "id": "3691", + "name": "Enchanted lyre(1)", + "examine": "This will teleport me to Rellekka when I play it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" + }, + { + "id": "3692", + "name": "Branch", + "examine": "I can use this to make a lyre.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "3693", + "name": "Golden fleece", + "examine": "I can spin this into golden wool.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1000", + "weight": "1" + }, + { + "id": "3694", + "name": "Golden wool", + "examine": "I can use this to make a lyre.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { "id": "3695", - "stand_turn_anim": "6661" - }, - { - "examine": "Talisman to bind the Draugen.", - "durability": null, - "name": "Hunters' talisman", + "name": "Pet rock", + "examine": "The lowest maintenance pet you will ever have.", "archery_ticket_price": "0", - "id": "3696" + "durability": null, + "equipment_slot": "3", + "render_anim": "792", + "run_anim": "6660", + "stand_anim": "6657", + "stand_turn_anim": "6661", + "turn180_anim": "6659", + "turn90ccw_anim": "6663", + "turn90cw_anim": "6662", + "two_handed": "true", + "walk_anim": "6658", + "weight": "1" }, { - "examine": "Talisman to bind the Draugen.", - "durability": null, + "id": "3696", "name": "Hunters' talisman", + "examine": "Talisman to bind the Draugen.", "archery_ticket_price": "0", - "id": "3697" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some flowers from a distant land.", - "durability": null, + "id": "3697", + "name": "Hunters' talisman", + "examine": "Talisman to bind the Draugen.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3698", "name": "Exotic flower", - "tradeable": "false", - "destroy": "true", + "examine": "Some flowers from a distant land.", "archery_ticket_price": "0", - "id": "3698" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "examine": "A hauntingly beautiful love ballad.", - "durability": null, + "id": "3699", "name": "Fremennik ballad", + "examine": "A hauntingly beautiful love ballad.", "archery_ticket_price": "0", - "id": "3699" + "durability": null }, { - "examine": "A pair of sturdy made custom boots.", - "durability": null, + "id": "3700", "name": "Sturdy boots", + "examine": "A pair of sturdy made custom boots.", "archery_ticket_price": "0", - "id": "3700" + "durability": null }, { - "examine": "Could probably be a bit clearer?", - "durability": null, + "id": "3701", "name": "Tracking map", + "examine": "Could probably be a bit clearer?", "archery_ticket_price": "0", - "id": "3701" + "durability": null }, { - "examine": "A finely crafted string for a custom bow", - "durability": null, + "id": "3702", "name": "Custom bow string", + "examine": "A finely crafted string for a custom bow", "archery_ticket_price": "0", - "id": "3702" + "durability": null }, { - "examine": "An extremely odd, non-edible fish.", - "durability": null, + "id": "3703", "name": "Unusual fish", + "examine": "An extremely odd, non-edible fish.", "archery_ticket_price": "0", - "id": "3703" + "durability": null }, { - "examine": "Map showing the best fishing spots out at sea.", - "durability": null, + "id": "3704", "name": "Sea fishing map", + "examine": "Map showing the best fishing spots out at sea.", "archery_ticket_price": "0", - "id": "3704" + "durability": null }, { - "examine": "An estimate of expected local weather conditions.", - "durability": null, + "id": "3705", "name": "Weather forecast", + "examine": "An estimate of expected local weather conditions.", "archery_ticket_price": "0", - "id": "3705" + "durability": null }, { - "examine": "Shows the wearer is worthy of the Champions table.", - "durability": null, + "id": "3706", "name": "Champions token", + "examine": "Shows the wearer is worthy of the Champions table.", "archery_ticket_price": "0", - "id": "3706" + "durability": null }, { - "examine": "Probably the greatest cocktail in the world.", - "durability": null, + "id": "3707", "name": "Legendary cocktail", + "examine": "Probably the greatest cocktail in the world.", "archery_ticket_price": "0", - "id": "3707" + "durability": null }, { - "examine": "A signed statement promising a reduction on sales tax.", - "durability": null, + "id": "3708", "name": "Fiscal statement", + "examine": "A signed statement promising a reduction on sales tax.", "archery_ticket_price": "0", - "id": "3708" + "durability": null }, { - "shop_price": "5000", - "examine": "A legally binding contract promising not to enter the longhall.", - "durability": null, + "id": "3709", "name": "Promissory note", + "examine": "A legally binding contract promising not to enter the longhall.", "archery_ticket_price": "0", - "id": "3709" + "durability": null, + "shop_price": "5000" }, { - "examine": "This employment contract is for a warrior to act as a bodyguard.", - "durability": null, + "id": "3710", "name": "Warriors' contract", + "examine": "This employment contract is for a warrior to act as a bodyguard.", "archery_ticket_price": "0", - "id": "3710" + "durability": null }, { - "shop_price": "325", - "examine": "A lot of beer in a barrel.", - "grand_exchange_price": "1555", - "durability": null, + "id": "3711", "name": "Keg of beer", - "tradeable": "true", - "weight": "20", + "examine": "A lot of beer in a barrel.", "archery_ticket_price": "0", - "id": "3711" + "durability": null, + "shop_price": "325", + "tradeable": "true", + "weight": "20" }, { - "examine": "Suspiciously close to beer, but without the side effects.", - "durability": null, + "id": "3712", "name": "Low alcohol keg", - "weight": "20", + "examine": "Suspiciously close to beer, but without the side effects.", "archery_ticket_price": "0", - "id": "3712" + "durability": null, + "weight": "20" }, { - "examine": "It's some kind of weird little parcel thing.", - "durability": null, + "id": "3713", "name": "Strange object", - "archery_ticket_price": "0", - "id": "3713" - }, - { "examine": "It's some kind of weird little parcel thing.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3714", "name": "Lit strange object", + "examine": "It's some kind of weird little parcel thing.", "archery_ticket_price": "0", - "id": "3714" + "durability": null }, { - "examine": "A red coloured disk, apparently made out of wood.", - "durability": null, + "id": "3715", "name": "Picture", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3715" - }, - { "examine": "A red coloured disk, apparently made out of wood.", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3716" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A very attractive magnet.", + "id": "3716", + "name": "Picture", + "examine": "A red coloured disk, apparently made out of wood.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "3718", "name": "Magnet", + "examine": "A very attractive magnet.", "archery_ticket_price": "0", - "id": "3718" + "durability": null }, { - "examine": "Some blue thread.", - "durability": null, + "id": "3719", "name": "Thread", + "examine": "Some blue thread.", "archery_ticket_price": "0", - "id": "3719" + "durability": null }, { - "examine": "A small pick for cracking small objects.", - "durability": null, + "id": "3720", "name": "Pick", + "examine": "A small pick for cracking small objects.", "archery_ticket_price": "0", - "id": "3720" + "durability": null }, { - "examine": "Might be fun to play with in the bath.", - "durability": null, + "id": "3721", "name": "Ship toy", + "examine": "Might be fun to play with in the bath.", "archery_ticket_price": "0", - "id": "3721" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It's a bucket of salty water.", - "durability": null, + "id": "3722", "name": "Full bucket", + "examine": "It's a bucket of salty water.", "archery_ticket_price": "0", - "id": "3722" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket is eighty percent full. It has a 5 painted on its side.", - "durability": null, + "id": "3723", "name": "4/5ths full bucket", + "examine": "This bucket is eighty percent full. It has a 5 painted on its side.", "archery_ticket_price": "0", - "id": "3723" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket is sixty percent full. It has a 5 painted on its side.", - "durability": null, + "id": "3724", "name": "3/5ths full bucket", + "examine": "This bucket is sixty percent full. It has a 5 painted on its side.", "archery_ticket_price": "0", - "id": "3724" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket is forty percent full. It has a 5 painted on its side.", - "durability": null, + "id": "3725", "name": "2/5ths full bucket", + "examine": "This bucket is forty percent full. It has a 5 painted on its side.", "archery_ticket_price": "0", - "id": "3725" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket is twenty percent full. It has a 5 painted on its side.", - "durability": null, + "id": "3726", "name": "1/5ths full bucket", + "examine": "This bucket is twenty percent full. It has a 5 painted on its side.", "archery_ticket_price": "0", - "id": "3726" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "2", - "examine": "It's an empty bucket.", - "grand_exchange_price": "50", - "durability": null, + "id": "3727", "name": "Empty bucket", - "tradeable": "true", - "weight": "1", + "examine": "It's an empty bucket.", "archery_ticket_price": "0", - "id": "3727" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket of water is frozen solid.", - "durability": null, + "id": "3728", "name": "Frozen bucket", + "examine": "This bucket of water is frozen solid.", "archery_ticket_price": "0", - "id": "3728" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This jug is completely full. It has a 3 painted on its side.", - "durability": null, + "id": "3729", "name": "Full jug", + "examine": "This jug is completely full. It has a 3 painted on its side.", "archery_ticket_price": "0", - "id": "3729" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This jug is two thirds full. It has a 3 painted on its side.", - "durability": null, + "id": "3730", "name": "2/3rds full jug", + "examine": "This jug is two thirds full. It has a 3 painted on its side.", "archery_ticket_price": "0", - "id": "3730" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This jug is one third full. It has a 3 painted on its side.", - "durability": null, + "id": "3731", "name": "1/3rds full jug", + "examine": "This jug is one third full. It has a 3 painted on its side.", "archery_ticket_price": "0", - "id": "3731" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "1", - "examine": "This jug is empty.", - "grand_exchange_price": "164", - "durability": null, + "id": "3732", "name": "Empty jug", - "tradeable": "true", - "weight": "0.4", + "examine": "This jug is empty.", "archery_ticket_price": "0", - "id": "3732" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This jug of water is frozen solid.", "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "3733", "name": "Frozen jug", + "examine": "This jug of water is frozen solid.", "archery_ticket_price": "0", - "id": "3733" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "An unusually shaped vase. You can see something glinting inside.", - "durability": null, + "id": "3734", "name": "Vase", + "examine": "An unusually shaped vase. You can see something glinting inside.", "archery_ticket_price": "0", - "id": "3734" + "durability": null }, { - "examine": "An unusually shaped vase full of water. You can see something glinting inside.", - "durability": null, + "id": "3735", "name": "Vase of water", + "examine": "An unusually shaped vase full of water. You can see something glinting inside.", "archery_ticket_price": "0", - "id": "3735" + "durability": null }, { - "examine": "An unusually shaped vase full of ice. You can see something glinting inside.", - "durability": null, + "id": "3736", "name": "Frozen vase", + "examine": "An unusually shaped vase full of ice. You can see something glinting inside.", "archery_ticket_price": "0", - "id": "3736" + "durability": null }, { - "examine": "This looks like a lid to some kind of container.", - "durability": null, + "id": "3737", "name": "Vase lid", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "This looks like a lid to some kind of container.", "archery_ticket_price": "0", - "id": "3737" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" }, { + "id": "3738", + "name": "Sealed vase", "examine": "The lid is screwed on tightly. ", - "durability": null, - "name": "Sealed vase", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3738" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { + "id": "3739", + "name": "Sealed vase", "examine": "The lid is screwed on tightly. It is full of water.", - "durability": null, - "name": "Sealed vase", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3739" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { + "id": "3740", + "name": "Sealed vase", "examine": "The lid is screwed on tightly. It is very cold. ", - "durability": null, - "name": "Sealed vase", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "3740" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A small, cold key.", - "durability": null, + "id": "3741", "name": "Frozen key", + "examine": "A small, cold key.", "archery_ticket_price": "0", - "id": "3741" + "durability": null }, { - "examine": "The colouring on it seems to be some kind of sticky goop.", - "durability": null, + "id": "3742", "name": "Red herring", - "tradeable": "false", - "destroy": "true", + "examine": "The colouring on it seems to be some kind of sticky goop.", "archery_ticket_price": "0", - "id": "3742" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A red coloured disk, apparently made out of wood.", - "durability": null, + "id": "3743", "name": "Red disk", - "tradeable": "false", - "destroy": "true", + "examine": "A red coloured disk, apparently made out of wood.", "archery_ticket_price": "0", - "id": "3743" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A simple looking disk made of wood.", - "durability": null, + "id": "3744", "name": "Wooden disk", + "examine": "A simple looking disk made of wood.", "archery_ticket_price": "0", - "id": "3744" + "durability": null }, { - "shop_price": "27", - "examine": "The key to leave the Seer's house.", - "durability": null, + "id": "3745", "name": "Seer's key", - "tradeable": "false", + "examine": "The key to leave the Seer's house.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "3745" + "durability": null, + "shop_price": "27", + "tradeable": "false" }, { + "id": "3746", + "name": "Sticky red goop", "examine": "Yup, it's sticky, it's red and it's goop.", - "durability": null, - "name": "Sticky red goop", - "tradeable": "false", "archery_ticket_price": "0", - "id": "3746" + "durability": null, + "tradeable": "false" }, { - "durability": null, + "id": "3747", "name": "Sticky red goop", - "tradeable": "true", + "alchemizable": "true", "archery_ticket_price": "0", - "id": "3747" + "durability": null, + "tradeable": "true" }, { - "remove_head": "true", - "examine": "A sturdy helm worn only by Fremennik clan members.", - "durability": null, - "name": "Fremennik helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "3748", + "name": "Fremennik helm", + "examine": "A sturdy helm worn only by Fremennik clan members.", "absorb": "1,0,2", + "alchemizable": "true", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "2.7" }, { - "remove_head": "true", - "requirements": "{1,45}", - "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helmet is worn by archers.", - "durability": null, - "weight": "2", - "absorb": "0,3,1", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "48900", - "name": "Archer helm", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3749", - "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "48900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Archer helm", - "tradeable": "true", + "examine": "This helmet is worn by archers.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "3750" - }, - { + "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", "remove_head": "true", "requirements": "{1,45}", "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helmet is worn by berserkers.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "60900", - "name": "Berserker helm", "tradeable": "true", + "weight": "2" + }, + { + "id": "3750", + "name": "Archer helm", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3751", - "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "60900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Berserker helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3752" - }, - { - "remove_head": "true", - "requirements": "{1,45}", - "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helm is worn by warriors.", - "durability": null, - "weight": "2.7", + "examine": "This helmet is worn by berserkers.", "absorb": "1,0,3", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "50700", - "name": "Warrior helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3753", - "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "50700", - "examine": "Swap this note at any bank for the equivalent item.", + "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0", "durability": null, - "name": "Warrior helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3754" - }, - { + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", "remove_head": "true", "requirements": "{1,45}", "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helm is worn by farseers.", - "durability": null, - "weight": "2.7", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "47900", - "name": "Farseer helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3755", - "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "47900", + "id": "3752", + "name": "Berserker helm", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Farseer helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3756" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "attack_anims": "390,390,381,390", - "examine": "A sword used only by Fremennik warriors.", - "durability": null, - "name": "Fremennik blade", - "weight": "1.8", + "id": "3753", + "name": "Warrior helm", + "examine": "This helm is worn by warriors.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "attack_speed": "4", - "weapon_interface": "6", + "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,45}", + "shop_price": "78000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "3754", + "name": "Warrior helm", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3755", + "name": "Farseer helm", + "examine": "This helm is worn by farseers.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,45}", + "shop_price": "78000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "3756", + "name": "Farseer helm", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "3757", + "name": "Fremennik blade", + "examine": "A sword used only by Fremennik warriors.", + "alchemizable": "true", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "4", "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0", "defence_anim": "397", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "A shield worn by Fremennik warriors.", - "durability": null, - "name": "Fremennik shield", - "destroy": "true", - "weight": "2.2", - "archery_ticket_price": "0", "id": "3758", + "name": "Fremennik shield", + "examine": "A shield worn by Fremennik warriors.", + "alchemizable": "true", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", - "equipment_slot": "5" + "destroy": "true", + "durability": null, + "equipment_slot": "5", + "weight": "2.2" }, { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "697", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3759", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "697", - "durability": null, "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3760" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "54", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3760", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "3761", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "54", - "durability": null, "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3762" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "581", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3762", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "3763", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "581", - "durability": null, "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3764" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "912", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3764", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "3765", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "912", - "durability": null, "name": "Fremennik cloak", - "tradeable": "true", + "examine": "The latest fashion in Rellekka.", "archery_ticket_price": "0", - "id": "3766" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" }, { - "remove_sleeves": "true", - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "grand_exchange_price": "100", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", + "id": "3766", + "name": "Fremennik cloak", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "3767", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "100", - "durability": null, "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3768" - }, - { - "remove_sleeves": "true", - "shop_price": "325", - "ge_buy_limit": "100", "examine": "The latest in Fremennik fashion.", - "grand_exchange_price": "486", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3769", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "486", "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3770" - }, - { - "remove_sleeves": "true", - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "grand_exchange_price": "242", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3771", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "242", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3772" - }, - { - "remove_sleeves": "true", - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "grand_exchange_price": "500", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3773", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "500", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3774" - }, - { - "remove_sleeves": "true", - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "grand_exchange_price": "450", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3775", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "450", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3776" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "674", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3777", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "674", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3778" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "809", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3779", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "809", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3780" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "632", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3781", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "632", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3782" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "517", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3783", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "517", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3784" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "398", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3785", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "398", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3786" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "283", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3787", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "283", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3788" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "grand_exchange_price": "966", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3789", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "966", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3790" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "Very stylish!", - "grand_exchange_price": "205", - "durability": null, - "name": "Fremennik boots", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "3791", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "205", - "durability": null, - "name": "Fremennik boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3792" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "The latest fashion of Rellekka.", - "durability": null, - "weight": "0.9", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "1191", - "name": "Fremennik robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3793", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1191", - "durability": null, - "name": "Fremennik robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3794" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "The latest fashion of Rellekka.", - "grand_exchange_price": "663", - "durability": null, - "name": "Fremennik skirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "3795", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "663", - "durability": null, - "name": "Fremennik skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3796" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", - "grand_exchange_price": "2380", - "durability": null, - "name": "Fremennik hat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3797", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2380", - "durability": null, - "name": "Fremennik hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3798" - }, - { - "ge_buy_limit": "10", - "shop_price": "650", - "examine": "These will keep my hands warm!", - "grand_exchange_price": "172", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "3799", - "bonuses": "0,0,0,0,0,0,1,2,0,0,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "172", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3800" - }, - { "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3768", + "name": "Fremennik shirt", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3769", + "name": "Fremennik shirt", + "examine": "The latest in Fremennik fashion.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3770", + "name": "Fremennik shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3771", + "name": "Fremennik shirt", + "examine": "The latest in Fremennik fashion.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3772", + "name": "Fremennik shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3773", + "name": "Fremennik shirt", + "examine": "The latest in Fremennik fashion.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3774", + "name": "Fremennik shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3775", + "name": "Fremennik shirt", + "examine": "The latest in Fremennik fashion.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3776", + "name": "Fremennik shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3777", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3778", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3779", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3780", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3781", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3782", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3783", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3784", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3785", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3786", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3787", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3788", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3789", + "name": "Fremennik cloak", + "examine": "The latest fashion in Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "1000", + "shop_price": "325", + "tradeable": "true" + }, + { + "id": "3790", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3791", + "name": "Fremennik boots", + "examine": "Very stylish!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "3792", + "name": "Fremennik boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3793", + "name": "Fremennik robe", + "examine": "The latest fashion of Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "650", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "3794", + "name": "Fremennik robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3795", + "name": "Fremennik skirt", + "examine": "The latest fashion of Rellekka.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "3796", + "name": "Fremennik skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3797", + "name": "Fremennik hat", + "examine": "A silly pointed hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" + }, + { + "id": "3798", + "name": "Fremennik hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "3799", + "name": "Gloves", + "examine": "These will keep my hands warm!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "shop_price": "650", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "3800", + "name": "Gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "3801", + "name": "Keg of beer", "examine": "A lot of beer in a barrel. Beer in a barrel - a specialty of the Fremennik Province.", - "grand_exchange_price": "1817", - "durability": null, - "name": "Keg of beer", - "tradeable": "true", - "weight": "20", "archery_ticket_price": "0", - "id": "3801" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "325", + "tradeable": "true", + "weight": "20" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1817", - "durability": null, + "id": "3802", "name": "Keg of beer", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3802" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "3803", + "name": "Beer", "examine": "A glass of frothy ale.", - "grand_exchange_price": "393", - "durability": null, - "name": "Beer", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "3803" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "393", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "3804", "name": "Beer", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3804" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "26", + "id": "3805", + "name": "Tankard", "examine": "A big cup for a big thirst.", - "durability": null, - "name": "Tankard", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3805" + "durability": null, + "shop_price": "26", + "tradeable": "true" }, { - "durability": null, + "id": "3806", "name": "Tankard", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3806" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "1400000", - "durability": null, + "id": "3827", "name": "Saradomin page 1", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3827" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "686000", - "durability": null, + "id": "3828", "name": "Saradomin page 2", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3828" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "522800", - "durability": null, + "id": "3829", "name": "Saradomin page 3", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3829" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "629500", - "durability": null, + "id": "3830", "name": "Saradomin page 4", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3830" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "7100000", - "durability": null, + "id": "3831", "name": "Zamorak page 1", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3831" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "5400000", - "durability": null, + "id": "3832", "name": "Zamorak page 2", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3832" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "4700000", - "durability": null, + "id": "3833", "name": "Zamorak page 3", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3833" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "5900000", - "durability": null, + "id": "3834", "name": "Zamorak page 4", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3834" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "1200000", - "durability": null, + "id": "3835", "name": "Guthix page 1", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3835" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "997100", - "durability": null, + "id": "3836", "name": "Guthix page 2", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3836" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "953100", - "durability": null, + "id": "3837", "name": "Guthix page 3", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3837" - }, - { - "ge_buy_limit": "2", "examine": "This seems to have been torn from a book...", - "grand_exchange_price": "1000000", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "3838", "name": "Guthix page 4", - "tradeable": "true", + "examine": "This seems to have been torn from a book...", "archery_ticket_price": "0", - "id": "3838" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "shop_price": "5000", - "examine": "An incomplete book of Saradomin.", - "durability": null, - "name": "Damaged book", - "weight": "1", - "archery_ticket_price": "0", "id": "3839", + "name": "Damaged book", + "examine": "An incomplete book of Saradomin.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "shop_price": "5000", + "weight": "1" }, { - "shop_price": "5000", - "examine": "The Holy book of Saradomin.", - "durability": null, - "name": "Holy book", - "weight": "1", - "archery_ticket_price": "0", "id": "3840", + "name": "Holy book", + "examine": "The Holy book of Saradomin.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,8,8,8,8,8,8,0,5,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "shop_price": "5000", + "weight": "1" }, { - "shop_price": "5000", - "examine": "An incomplete book of Zamorak.", - "durability": null, - "name": "Damaged book", - "weight": "1", - "archery_ticket_price": "0", "id": "3841", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "5" - }, - { - "examine": "The unholy book of Zamorak.", - "durability": null, - "name": "Unholy book", - "weight": "1", - "archery_ticket_price": "0", - "id": "3842", - "bonuses": "8,8,8,8,8,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "5" - }, - { - "shop_price": "5000", - "examine": "An incomplete book of Guthix.", - "durability": null, "name": "Damaged book", - "weight": "1", + "examine": "An incomplete book of Zamorak.", "archery_ticket_price": "0", - "id": "3843", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "shop_price": "5000", + "weight": "1" }, { - "examine": "The holy book of Guthix.", - "durability": null, - "name": "Book of balance", - "weight": "1", + "id": "3842", + "name": "Unholy book", + "examine": "The unholy book of Zamorak.", "archery_ticket_price": "0", + "bonuses": "8,8,8,8,8,0,0,0,0,0,0,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "weight": "1" + }, + { + "id": "3843", + "name": "Damaged book", + "examine": "An incomplete book of Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "shop_price": "5000", + "weight": "1" + }, + { "id": "3844", + "name": "Book of balance", + "examine": "The holy book of Guthix.", + "archery_ticket_price": "0", "bonuses": "4,4,4,4,4,4,4,4,4,4,4,0,5,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "weight": "1" }, { - "examine": "Looks like some kind of manual.", - "durability": null, + "id": "3847", "name": "Manual", - "tradeable": "false", - "destroy": "true", - "weight": "0.5", + "examine": "Looks like some kind of manual.", "archery_ticket_price": "0", - "id": "3847" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "0.5" }, { - "examine": "A key to the front door of the lighthouse. (Horror from the Deep)", - "durability": null, + "id": "3848", "name": "Lighthouse key", + "examine": "A key to the front door of the lighthouse. (Horror from the Deep)", "archery_ticket_price": "0", - "id": "3848" + "durability": null }, { - "examine": "Looks old and rusty...", - "durability": null, + "id": "3849", "name": "Rusty casket", - "tradeable": "false", + "examine": "Looks old and rusty...", + "archery_ticket_price": "0", "destroy": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "3849" + "durability": null, + "tradeable": "false", + "weight": "5" }, { - "durability": null, + "id": "3852", "name": "Unholy symbol", - "tradeable": "true", "archery_ticket_price": "0", - "id": "3852" + "durability": null, + "tradeable": "true" }, { - "shop_price": "490", - "ge_buy_limit": "10000", - "examine": "An enchanted necklace.", - "grand_exchange_price": "852", - "durability": null, - "name": "Games necklace(8)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3853", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "852", - "durability": null, "name": "Games necklace(8)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3854" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "10000", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(7)", - "tradeable": "true", + "id": "3854", + "name": "Games necklace(8)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { "id": "3855", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(7)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3856" + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(6)", - "tradeable": "true", + "id": "3856", + "name": "Games necklace(7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3857", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(6)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3858" + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(5)", - "tradeable": "true", + "id": "3858", + "name": "Games necklace(6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3859", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(5)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3860" + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(4)", - "tradeable": "true", + "id": "3860", + "name": "Games necklace(5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3861", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(4)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3862" + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(3)", - "tradeable": "true", + "id": "3862", + "name": "Games necklace(4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3863", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(3)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3864" + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" }, { - "shop_price": "490", - "examine": "An enchanted necklace.", - "grand_exchange_price": "8", - "durability": null, - "name": "Games necklace(2)", - "tradeable": "true", + "id": "3864", + "name": "Games necklace(3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3865", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Games necklace(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3866" - }, - { - "shop_price": "490", - "ge_buy_limit": "10000", "examine": "An enchanted necklace.", - "grand_exchange_price": "2093", - "durability": null, - "name": "Games necklace(1)", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "shop_price": "490", + "tradeable": "true" + }, + { + "id": "3866", + "name": "Games necklace(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "3867", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2093", - "durability": null, "name": "Games necklace(1)", - "tradeable": "true", + "examine": "An enchanted necklace.", "archery_ticket_price": "0", - "id": "3868" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "10000", + "shop_price": "490", + "tradeable": "true" }, { + "id": "3868", + "name": "Games necklace(1)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "3893", "name": "Stool", "archery_ticket_price": "0", - "id": "3893", + "durability": null, "equipment_slot": "3" }, { - "examine": "It's not very good.", - "durability": null, + "id": "3894", "name": "Awful anthem", - "tradeable": "false", - "destroy": "true", + "examine": "It's not very good.", "archery_ticket_price": "0", - "id": "3894" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Much better.", - "durability": null, + "id": "3895", "name": "Good anthem", - "tradeable": "false", - "destroy": "true", + "examine": "Much better.", "archery_ticket_price": "0", - "id": "3895" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Just needs the King's signature.", - "durability": null, + "id": "3896", "name": "Treaty", + "examine": "Just needs the King's signature.", "archery_ticket_price": "0", - "id": "3896" + "durability": null }, { - "examine": "For making a giant pen.", - "durability": null, + "id": "3897", "name": "Giant nib", + "examine": "For making a giant pen.", "archery_ticket_price": "0", - "id": "3897" + "durability": null }, { - "examine": "The king should be able to use this.", - "durability": null, + "id": "3898", "name": "Giant pen", + "examine": "The king should be able to use this.", "archery_ticket_price": "0", - "id": "3898" + "durability": null }, { - "shop_price": "15", - "examine": "Not as good as a pet frog.", - "durability": null, - "name": "Iron sickle", - "weight": "1.6", - "archery_ticket_price": "0", "id": "3899", - "weapon_interface": "6", + "name": "Iron sickle", + "examine": "Not as good as a pet frog.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,1,1,1,1,0,1,0,0,0", - "equipment_slot": "3" - }, - { - "examine": "Managing Thine Kingdom for Noobes by A. Ghrim.", "durability": null, - "name": "Ghrim's book", - "weight": "1", - "archery_ticket_price": "0", - "id": "3901" - }, - { - "examine": "A pile of gout tubers suitable for use in mountainous terrain.", - "durability": null, - "name": "Hardy gout tuber", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "4001" - }, - { - "examine": "It looks like some kind of control panel.", - "durability": null, - "name": "Spare controls", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "4002" - }, - { - "examine": "It's the official Gnome Royal Seal, signed by King Narode Shareen.", - "durability": null, - "name": "Gnome royal seal", - "archery_ticket_price": "0", - "id": "4004" - }, - { - "shop_price": "160", - "examine": "Unreadable orders handwritten by King Narnode Shareen.", - "durability": null, - "name": "Narnode's orders", - "archery_ticket_price": "0", - "id": "4005" - }, - { - "shop_price": "10", - "examine": "Magical monkey talking dentures! What more can we say? Ook!", - "durability": null, - "name": "Monkey dentures", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "4006" - }, - { - "examine": "A gold bar with a talkative monkey spirit.", - "durability": null, - "name": "Enchanted bar", - "weight": "2", - "archery_ticket_price": "0", - "id": "4007" - }, - { - "shop_price": "3", - "examine": "It's... the eye of a gnome! Now what on earth could one do with this?", - "durability": null, - "name": "Eye of gnome", - "weight": "1", - "archery_ticket_price": "0", - "id": "4008" - }, - { - "shop_price": "3", - "examine": "It's... the eye of a gnome! Now what on earth could one do with this?", - "durability": null, - "name": "Eye of gnome", - "weight": "1", - "archery_ticket_price": "0", - "id": "4009" - }, - { - "shop_price": "3", - "examine": "They are Monkey Nuts. Yummy.", - "durability": null, - "name": "Monkey nuts", - "archery_ticket_price": "0", - "id": "4012" - }, - { - "shop_price": "50", - "examine": "It's a monkey bar. It looks highly nutritious.", - "durability": null, - "name": "Monkey bar", - "archery_ticket_price": "0", - "id": "4014" - }, - { - "shop_price": "300", - "examine": "It's a bowl full of mushy banana", - "durability": null, - "name": "Banana stew", - "archery_ticket_price": "0", - "id": "4016" - }, - { - "examine": "It's an amulet mould shaped like a monkey head.", - "durability": null, - "name": "M'amulet mould", - "weight": "1", - "archery_ticket_price": "0", - "id": "4020" - }, - { - "examine": "It's an Amulet of Monkey Speak. It makes vague chattering noises.", - "durability": null, - "name": "M'speak amulet", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4021", - "equipment_slot": "2" - }, - { - "examine": "It's an unstrung Amulet of Monkey Speak. It makes vague chattering noises.", - "durability": null, - "name": "M'speak amulet", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4022" - }, - { - "shop_price": "1000", - "examine": "A magic talisman in shape of a monkey head.", - "durability": null, - "name": "Monkey talisman", - "weight": "1.25", - "archery_ticket_price": "0", - "id": "4023" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4024", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4025", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4026", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4027", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4028", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4029", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4030", - "equipment_slot": "3" - }, - { - "examine": "A magical talisman in the shape of a Karamjan monkey head.", - "durability": null, - "name": "Monkey greegree", - "archery_ticket_price": "0", - "id": "4031", - "equipment_slot": "3" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Dummy", - "archery_ticket_price": "0", - "id": "4032" - }, - { - "examine": "It's a very ancient skull from some kind of ape.", - "durability": null, - "name": "Monkey skull", - "archery_ticket_price": "0", - "id": "4034" - }, - { - "examine": "It is the official sigil of the 10th squad of the Royal Guard.", - "durability": null, - "name": "10th squad sigil", - "archery_ticket_price": "0", - "id": "4035", - "equipment_slot": "2" - }, - { - "turn90cw_anim": "1424", - "examine": "The Holy Team Standard", - "walk_anim": "1422", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", "equipment_slot": "3", - "stand_anim": "1421", - "name": "Saradomin banner", - "tradeable": "false", - "run_anim": "1427", - "archery_ticket_price": "0", - "id": "4037", - "stand_turn_anim": "1426" - }, - { - "durability": null, - "name": "Saradomin banner", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "4038" - }, - { - "turn90cw_anim": "1424", - "examine": "The Zamorak team's flag.", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Zamorak banner", - "tradeable": "false", - "run_anim": "1427", - "archery_ticket_price": "0", - "id": "4039", - "stand_turn_anim": "1426" - }, - { - "durability": null, - "name": "Zamorak banner", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "4040" - }, - { - "examine": "The colours of Saradomin/Zamorak.", - "durability": null, - "name": "Hooded cloak", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4041", - "equipment_slot": "1" - }, - { - "examine": "The colours of Saradomin/Zamorak.", - "durability": null, - "name": "Hooded cloak", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4042", - "equipment_slot": "1" - }, - { - "durability": null, - "name": "Rock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4044" - }, - { - "examine": "I could use this to destroy things...", - "durability": null, - "name": "Explosive potion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4045" - }, - { - "durability": null, - "name": "Explosive potion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4046" - }, - { - "examine": "A coil of rope.", - "durability": null, - "name": "Climbing rope", - "archery_ticket_price": "0", - "id": "4047" - }, - { - "durability": null, - "name": "Climbing rope", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4048" - }, - { - "examine": "A box of bandages for healing.", - "durability": null, - "name": "Bandages", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4049" - }, - { - "durability": null, - "name": "Bandages", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4050" - }, - { - "examine": "Good for repairing a broken cannon.", - "durability": null, - "name": "Toolkit", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "4051" - }, - { - "durability": null, - "name": "Toolkit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4052" - }, - { - "examine": "Handy for hindering the enemy team's movement.", - "durability": null, - "name": "Barricade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4053" - }, - { - "durability": null, - "name": "Barricade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4054" - }, - { - "shop_price": "5", - "examine": "It's a manual for Castle Wars", - "durability": null, - "name": "Castlewars manual", - "weight": "1", - "archery_ticket_price": "0", - "id": "4055" - }, - { - "examine": "I can exchange these for further items.", - "durability": null, - "name": "Castle wars ticket", - "archery_ticket_price": "0", - "id": "4067" - }, - { - "requirements": "{0,5}", - "shop_price": "50", - "turn90cw_anim": "821", - "examine": "A very decorative sword.", - "walk_anim": "819", - "durability": null, - "weight": "1", - "turn90ccw_anim": "822", + "shop_price": "15", "weapon_interface": "6", - "turn180_anim": "820", - "render_anim": "1", + "weight": "1.6" + }, + { + "id": "3901", + "name": "Ghrim's book", + "examine": "Managing Thine Kingdom for Noobes by A. Ghrim.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4001", + "name": "Hardy gout tuber", + "examine": "A pile of gout tubers suitable for use in mountainous terrain.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "4002", + "name": "Spare controls", + "examine": "It looks like some kind of control panel.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "4004", + "name": "Gnome royal seal", + "examine": "It's the official Gnome Royal Seal, signed by King Narode Shareen.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4005", + "name": "Narnode's orders", + "examine": "Unreadable orders handwritten by King Narnode Shareen.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160" + }, + { + "id": "4006", + "name": "Monkey dentures", + "examine": "Magical monkey talking dentures! What more can we say? Ook!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "weight": "0.2" + }, + { + "id": "4007", + "name": "Enchanted bar", + "examine": "A gold bar with a talkative monkey spirit.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "4008", + "name": "Eye of gnome", + "examine": "It's... the eye of a gnome! Now what on earth could one do with this?", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3", + "weight": "1" + }, + { + "id": "4009", + "name": "Eye of gnome", + "examine": "It's... the eye of a gnome! Now what on earth could one do with this?", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3", + "weight": "1" + }, + { + "id": "4012", + "name": "Monkey nuts", + "examine": "They are Monkey Nuts. Yummy.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3" + }, + { + "id": "4014", + "name": "Monkey bar", + "examine": "It's a monkey bar. It looks highly nutritious.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "50" + }, + { + "id": "4016", + "name": "Banana stew", + "examine": "It's a bowl full of mushy banana", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "300" + }, + { + "id": "4020", + "name": "M'amulet mould", + "examine": "It's an amulet mould shaped like a monkey head.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4021", + "name": "M'speak amulet", + "examine": "It's an Amulet of Monkey Speak. It makes vague chattering noises.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "1.3" + }, + { + "id": "4022", + "name": "M'speak amulet", + "examine": "It's an unstrung Amulet of Monkey Speak. It makes vague chattering noises.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "4023", + "name": "Monkey talisman", + "examine": "A magic talisman in shape of a monkey head.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1000", + "weight": "1.25" + }, + { + "id": "4024", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4025", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4026", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4027", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4028", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4029", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4030", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4031", + "name": "Monkey greegree", + "examine": "A magical talisman in the shape of a Karamjan monkey head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4032", + "name": "Dummy", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4034", + "name": "Monkey skull", + "examine": "It's a very ancient skull from some kind of ape.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4035", + "name": "10th squad sigil", + "examine": "It is the official sigil of the 10th squad of the Royal Guard.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2" + }, + { + "id": "4037", + "name": "Saradomin banner", + "examine": "The Holy Team Standard", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "3", - "stand_anim": "808", - "name": "Decorative sword", - "run_anim": "824", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "tradeable": "false", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "4038", + "name": "Saradomin banner", "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "4039", + "name": "Zamorak banner", + "examine": "The Zamorak team's flag.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "tradeable": "false", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" + }, + { + "id": "4040", + "name": "Zamorak banner", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "4041", + "name": "Hooded cloak", + "examine": "The colours of Saradomin/Zamorak.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1" + }, + { + "id": "4042", + "name": "Hooded cloak", + "examine": "The colours of Saradomin/Zamorak.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1" + }, + { + "id": "4044", + "name": "Rock", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4045", + "name": "Explosive potion", + "examine": "I could use this to destroy things...", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4046", + "name": "Explosive potion", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4047", + "name": "Climbing rope", + "examine": "A coil of rope.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4048", + "name": "Climbing rope", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4049", + "name": "Bandages", + "examine": "A box of bandages for healing.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4050", + "name": "Bandages", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4051", + "name": "Toolkit", + "examine": "Good for repairing a broken cannon.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "4052", + "name": "Toolkit", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4053", + "name": "Barricade", + "examine": "Handy for hindering the enemy team's movement.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4054", + "name": "Barricade", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4055", + "name": "Castlewars manual", + "examine": "It's a manual for Castle Wars", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5", + "weight": "1" + }, + { + "id": "4067", + "name": "Castle wars ticket", + "examine": "I can exchange these for further items.", + "archery_ticket_price": "0", + "durability": null + }, + { "id": "4068", + "name": "Decorative sword", + "examine": "A very decorative sword.", + "archery_ticket_price": "0", + "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,0,0,0", + "castle_wars_ticket_price": "5", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1", + "requirements": "{0,5}", + "run_anim": "824", + "shop_price": "50", + "stand_anim": "808", "stand_turn_anim": "823", - "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" }, { - "remove_sleeves": "true", - "requirements": "{1,5}", - "durability": null, - "name": "Decorative armour", - "archery_ticket_price": "0", "id": "4069", - "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,5}", - "durability": null, "name": "Decorative armour", "archery_ticket_price": "0", - "id": "4070", - "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", + "castle_wars_ticket_price": "8", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,5}" }, { + "id": "4070", + "name": "Decorative armour", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0", + "castle_wars_ticket_price": "6", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,5}" + }, + { + "id": "4071", + "name": "Decorative helm", + "examine": "A very decorative helm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,0", + "castle_wars_ticket_price": "4", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,5}", "shop_price": "40", - "examine": "A very decorative helm.", - "durability": null, - "name": "Decorative helm", - "weight": "1", - "archery_ticket_price": "0", - "id": "4071", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,0", - "equipment_slot": "0" + "weight": "1" }, { - "requirements": "{1,5}", - "shop_price": "60", - "examine": "A very decorative shield.", - "durability": null, - "name": "Decorative shield", - "archery_ticket_price": "0", "id": "4072", + "name": "Decorative shield", + "examine": "A very decorative shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "castle_wars_ticket_price": "6", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "60" }, { + "id": "4073", + "name": "Damp tinderbox", "examine": "Not so useful for lighting a fire.", - "durability": null, - "name": "Damp tinderbox", "archery_ticket_price": "0", - "id": "4073" + "durability": null }, { - "durability": null, + "id": "4074", "name": "Damp tinderbox", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4074" + "durability": null, + "tradeable": "true" }, { - "examine": "A bizarre fungus. It glows with a pale blue light.", - "durability": null, + "id": "4075", "name": "Glowing fungus", + "examine": "A bizarre fungus. It glows with a pale blue light.", "archery_ticket_price": "0", - "id": "4075" + "durability": null }, { - "examine": "A key I found in the lower levels of the Morytanian mines. (Haunted Mine) / As the 'Innocent-looking key': A shiny key sitting quietly on a crate.", - "durability": null, + "id": "4077", "name": "Crystal-mine key", + "examine": "A key I found in the lower levels of the Morytanian mines. (Haunted Mine) / As the 'Innocent-looking key': A shiny key sitting quietly on a crate.", "archery_ticket_price": "0", - "id": "4077" + "durability": null }, { - "examine": "I stole this from a Saradominist I met South of Mort'ton.", - "durability": null, + "id": "4078", "name": "Zealot's key", + "examine": "I stole this from a Saradominist I met South of Mort'ton.", "archery_ticket_price": "0", - "id": "4078" + "durability": null }, { - "examine": "A gift from Santa.", - "durability": null, + "id": "4079", "name": "Yo-yo", + "examine": "A gift from Santa.", "archery_ticket_price": "0", - "id": "4079" + "durability": null }, { - "examine": "Increases the wearer's strength and accuracy by 15% when fighting the undead.", - "durability": null, - "name": "Salve amulet", - "weight": "0.6", - "archery_ticket_price": "0", "id": "4081", - "bonuses": "0,0,0,0,0,3,3,3,0,0,3,0,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "An unstrung crystal imbued with the power of Saradomin.", - "durability": null, - "name": "Salve shard", + "name": "Salve amulet", + "examine": "Increases the wearer's strength and accuracy by 15% when fighting the undead.", "archery_ticket_price": "0", - "id": "4082" + "bonuses": "0,0,0,0,0,3,3,3,0,0,3,0,3,0,0", + "durability": null, + "equipment_slot": "2", + "weight": "0.6" }, { - "examine": "Unwaxed: It needs waxing before I can use it. Waxed: A waxed sled.", - "durability": null, + "id": "4082", + "name": "Salve shard", + "examine": "An unstrung crystal imbued with the power of Saradomin.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4083", "name": "Sled", + "examine": "Unwaxed: It needs waxing before I can use it. Waxed: A waxed sled.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4083", + "durability": null, "equipment_slot": "3" }, { - "turn90cw_anim": "1468", - "examine": "Unwaxed: It needs waxing before I can use it. Waxed: A waxed sled.", - "walk_anim": "1468", - "durability": null, - "turn90ccw_anim": "1468", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "1468", - "render_anim": "1119", - "equipment_slot": "3", - "stand_anim": "1461", - "name": "Sled", - "run_anim": "1468", - "archery_ticket_price": "0", "id": "4084", - "stand_turn_anim": "1468" + "name": "Sled", + "examine": "Unwaxed: It needs waxing before I can use it. Waxed: A waxed sled.", + "archery_ticket_price": "0", + "attack_speed": "4", + "durability": null, + "equipment_slot": "3", + "render_anim": "1119", + "run_anim": "1468", + "stand_anim": "1461", + "stand_turn_anim": "1468", + "turn180_anim": "1468", + "turn90ccw_anim": "1468", + "turn90cw_anim": "1468", + "two_handed": "true", + "walk_anim": "1468" }, { - "examine": "I can use this to wax my sled.", - "durability": null, - "name": "Wax", - "archery_ticket_price": "0", "id": "4085", + "name": "Wax", + "examine": "I can use this to wax my sled.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "5" }, { - "examine": "These pale flowers have a pungent odour.", - "durability": null, + "id": "4086", "name": "Trollweiss", + "examine": "These pale flowers have a pungent odour.", "archery_ticket_price": "0", "attack_speed": "5", - "id": "4086", - "weapon_interface": "12", "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "12" }, { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "Looks pretty heavy.", - "durability": null, - "weight": "9", - "absorb": "3,0,6", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "190500", - "name": "Dragon platelegs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4087", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "190500", - "durability": null, "name": "Dragon platelegs", - "tradeable": "true", + "examine": "Looks pretty heavy.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "4088" + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "9" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "Blue: A magical hat. ", - "durability": null, - "weight": "0.4", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "13700", - "name": "Mystic hat", - "tradeable": "true", + "id": "4088", + "name": "Dragon platelegs", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4089", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "13700", - "durability": null, "name": "Mystic hat", - "tradeable": "true", + "examine": "Blue: A magical hat. ", + "absorb": "3,1,0", "archery_ticket_price": "0", - "id": "4090" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": true, + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "15000", + "tradeable": "true", + "weight": "0.4" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "120000", - "ge_buy_limit": "100", - "examine": "The upper half of a magical robe.", - "durability": null, - "weight": "2.7", - "absorb": "3,1,0", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "72100", - "name": "Mystic robe top", - "tradeable": "true", + "id": "4090", + "name": "Mystic hat", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4091", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "72100", - "durability": null, "name": "Mystic robe top", - "tradeable": "true", + "examine": "The upper half of a magical robe.", + "absorb": "3,1,0", "archery_ticket_price": "0", - "id": "4092" + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "120000", + "tradeable": "true", + "weight": "2.7" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "80000", - "ge_buy_limit": "100", - "examine": "The lower half of a magical robe.", - "durability": null, - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "47700", - "name": "Mystic robe bottom", - "tradeable": "true", + "id": "4092", + "name": "Mystic robe top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4093", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "47700", - "durability": null, "name": "Mystic robe bottom", - "tradeable": "true", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "4094" + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "80000", + "tradeable": "true" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical gloves.", - "durability": null, - "weight": "0.4", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "5913", - "name": "Mystic gloves", - "tradeable": "true", + "id": "4094", + "name": "Mystic robe bottom", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4095", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5913", - "durability": null, "name": "Mystic gloves", - "tradeable": "true", + "examine": "Magical gloves.", "archery_ticket_price": "0", - "id": "4096" - }, - { + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{1,20}-{6,40}", "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical boots.", - "durability": null, - "weight": "0.4", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "6303", - "name": "Mystic boots", "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4096", + "name": "Mystic gloves", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4097", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6303", - "durability": null, "name": "Mystic boots", - "tradeable": "true", + "examine": "Magical boots.", "archery_ticket_price": "0", - "id": "4098" + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "10000", + "tradeable": "true", + "weight": "0.4" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "Black: A magical hat. ", - "durability": null, - "weight": "0.4", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "16700", - "name": "Mystic hat", - "tradeable": "true", + "id": "4098", + "name": "Mystic boots", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4099", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16700", - "durability": null, "name": "Mystic hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4100" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "120000", - "ge_buy_limit": "100", - "examine": "The upper half of a magical robe.", - "durability": null, - "weight": "2.7", + "examine": "Black: A magical hat. ", "absorb": "3,1,0", - "equipment_slot": "4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": true, "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "75100", - "name": "Mystic robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4101", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "75100", - "durability": null, - "name": "Mystic robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4102" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "80000", - "ge_buy_limit": "100", - "examine": "The lower half of a magical robe.", - "durability": null, - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "50700", - "name": "Mystic robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4103", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "50700", - "durability": null, - "name": "Mystic robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4104" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Dark magical gloves.", - "durability": null, - "weight": "0.4", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "8913", - "name": "Mystic gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4105", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8913", - "durability": null, - "name": "Mystic gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4106" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical boots.", - "durability": null, - "weight": "0.4", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "9303", - "name": "Mystic boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4107", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9303", - "durability": null, - "name": "Mystic boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4108" - }, - { "requirements": "{1,20}-{6,40}", "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "White: A magical hat. ", - "durability": null, - "weight": "0.4", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "16700", - "name": "Mystic hat", "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "4109", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "16700", - "durability": null, + "id": "4100", "name": "Mystic hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4110" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "120000", - "ge_buy_limit": "100", + "id": "4101", + "name": "Mystic robe top", "examine": "The upper half of a magical robe.", - "durability": null, - "weight": "2.7", "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "durability": null, "equipment_slot": "4", + "ge_buy_limit": "100", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "75100", - "name": "Mystic robe top", + "requirements": "{1,20}-{6,40}", + "shop_price": "120000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4111", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "75100", + "id": "4102", + "name": "Mystic robe top", + "archery_ticket_price": "0", "durability": null, - "name": "Mystic robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4112" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "4103", + "name": "Mystic robe bottom", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", "requirements": "{1,20}-{6,40}", "shop_price": "80000", - "ge_buy_limit": "100", - "examine": "The lower half of a magical robe.", - "durability": null, - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "50700", - "name": "Mystic robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4113", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "50700", - "durability": null, + "id": "4104", "name": "Mystic robe bottom", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4114" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,20}-{6,40}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Bright magical gloves.", + "id": "4105", + "name": "Mystic gloves", + "examine": "Dark magical gloves.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", "durability": null, - "weight": "0.4", "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "8913", - "name": "Mystic gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4115", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "8913", - "durability": null, - "name": "Mystic gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4116" - }, - { + "lendable": "true", "requirements": "{1,20}-{6,40}", "shop_price": "10000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4106", + "name": "Mystic gloves", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4107", + "name": "Mystic boots", "examine": "Magical boots.", - "durability": null, - "weight": "0.4", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "9303", - "name": "Mystic boots", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "10000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4108", + "name": "Mystic boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4109", + "name": "Mystic hat", + "examine": "White: A magical hat. ", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": true, + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "15000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4110", + "name": "Mystic hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4111", + "name": "Mystic robe top", + "examine": "The upper half of a magical robe.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "120000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "4112", + "name": "Mystic robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4113", + "name": "Mystic robe bottom", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "80000", + "tradeable": "true" + }, + { + "id": "4114", + "name": "Mystic robe bottom", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4115", + "name": "Mystic gloves", + "examine": "Bright magical gloves.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "10000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4116", + "name": "Mystic gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4117", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9303", - "durability": null, "name": "Mystic boots", - "tradeable": "true", + "examine": "Magical boots.", "archery_ticket_price": "0", - "id": "4118" - }, - { - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "359", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", "durability": null, - "name": "Bronze boots", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4119", - "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "359", - "durability": null, - "name": "Bronze boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4120" - }, - { - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "17000", - "durability": null, - "name": "Iron boots", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4121", - "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17000", - "durability": null, - "name": "Iron boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4122" - }, - { - "requirements": "{1,5}", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "258", - "durability": null, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "258", - "durability": null, - "name": "Steel boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4124" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "317", - "durability": null, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "317", - "durability": null, - "name": "Black boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4126" - }, - { - "requirements": "{1,20}", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "580", - "durability": null, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "580", - "durability": null, - "name": "Mithril boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4128" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "grand_exchange_price": "1336", - "durability": null, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1336", - "durability": null, - "name": "Adamant boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4130" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "durability": null, - "weight": "1.3", "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "7474", - "name": "Rune boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4131", - "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "7474", - "durability": null, - "name": "Rune boots", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "10000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4132" + "weight": "0.4" }, { + "id": "4118", + "name": "Mystic boots", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4119", + "name": "Bronze boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4120", + "name": "Bronze boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4121", + "name": "Iron boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4122", + "name": "Iron boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4123", + "name": "Steel boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,5,6,7,0,0,5,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "requirements": "{1,5}", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4124", + "name": "Steel boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4125", + "name": "Black boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4126", + "name": "Black boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4127", + "name": "Mithril boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "requirements": "{1,20}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4128", + "name": "Mithril boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4129", + "name": "Adamant boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4130", + "name": "Adamant boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4131", + "name": "Rune boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "4132", + "name": "Rune boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4133", "name": "Crawling hand", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4133" + "durability": null }, { - "examine": "A spiky crawling critter. (level 23) Not very tasty-looking. (level 138)", - "durability": null, + "id": "4134", "name": "Cave crawler", + "examine": "A spiky crawling critter. (level 23) Not very tasty-looking. (level 138)", "archery_ticket_price": "0", "attack_speed": "3", - "id": "4134" + "durability": null }, { - "examine": "A tortured screaming soul.", - "durability": null, + "id": "4135", "name": "Banshee", + "examine": "A tortured screaming soul.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4135" + "durability": null }, { - "examine": "The slime of evil.", - "durability": null, + "id": "4136", "name": "Rockslug", + "examine": "The slime of evil.", "archery_ticket_price": "0", - "id": "4136" + "durability": null }, { - "examine": "The winged reptile.", - "durability": null, + "id": "4137", "name": "Cockatrice", + "examine": "The winged reptile.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4137" + "durability": null }, { - "examine": "A small fire demon.", - "durability": null, + "id": "4138", "name": "Pyrefiend", + "examine": "A small fire demon.", "archery_ticket_price": "0", - "id": "4138" + "durability": null }, { - "examine": "The eyes of evil.", - "durability": null, + "id": "4139", "name": "Basilisk", + "examine": "The eyes of evil.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4139" + "durability": null }, { - "examine": "An evil magic user.", - "durability": null, + "id": "4140", "name": "Infernal mage", + "examine": "An evil magic user.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4140" + "durability": null }, { - "examine": "The tongue of evil.", - "durability": null, + "id": "4141", "name": "Bloodveld", + "examine": "The tongue of evil.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4141" + "durability": null }, { - "examine": "• Needs cream.....• Wibbly.• Wobbly...• There's always room for jelly.• Doesn't look so tough...• Looks scared to see me.", - "durability": null, + "id": "4142", "name": "Jelly", + "examine": "• Needs cream.....• Wibbly.• Wobbly...• There's always room for jelly.• Doesn't look so tough...• Looks scared to see me.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4142" + "durability": null }, { - "durability": null, + "id": "4143", "name": "Turoth", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4143" + "durability": null }, { - "examine": "A very smelly ghost.", - "durability": null, + "id": "4144", "name": "Abberant spectre", + "examine": "A very smelly ghost.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4144" + "durability": null }, { - "examine": "The vacuumed face of evil.", - "durability": null, + "id": "4145", "name": "Dust devil", + "examine": "The vacuumed face of evil.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4145" + "durability": null }, { - "durability": null, + "id": "4146", "name": "Kurask", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4146" + "durability": null }, { - "examine": "Flies like a rock.", - "durability": null, + "id": "4147", "name": "Gargoyle", + "examine": "Flies like a rock.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4147" + "durability": null }, { - "durability": null, + "id": "4148", "name": "Nechryael", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4148" + "durability": null }, { - "examine": "A denizen of the Abyss!", - "durability": null, + "id": "4149", "name": "Abyssal demon", + "examine": "A denizen of the Abyss!", "archery_ticket_price": "0", "attack_speed": "4", - "id": "4149" + "durability": null }, { - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrows", - "archery_ticket_price": "0", "id": "4150", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,28,0,0" + "name": "Broad arrows", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,28,0,0", + "durability": null, + "shop_price": "90" }, { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A weapon from the Abyss.", - "walk_anim": "1660", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "1659", - "equipment_slot": "3", - "attack_anims": "1658,1658,1658,1658", - "grand_exchange_price": "413500", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "1661", - "archery_ticket_price": "0", "id": "4151", - "stand_turn_anim": "823", - "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0", - "requirements": "{0,70}", - "durability": null, - "weight": "0.45", - "weapon_interface": "11", - "equip_audio": "2249", - "render_anim": "620", - "lendable": "true", - "attack_audios": "2720,0,0,0", - "name": "Abyssal whip" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "413500", - "durability": null, "name": "Abyssal whip", - "tradeable": "true", + "examine": "A weapon from the Abyss.", "archery_ticket_price": "0", - "id": "4152" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1663", - "examine": "Simplicity is the best weapon.", - "walk_anim": "1663", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "1663", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1663", - "defence_anim": "1666", + "attack_anims": "1658,1658,1658,1658", + "attack_audios": "2720,0,0,0", + "attack_speed": "4", + "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0", + "defence_anim": "1659", + "durability": null, + "equip_audio": "2249", "equipment_slot": "3", - "attack_anims": "1665,1665,1665,1665", - "grand_exchange_price": "29900", - "stand_anim": "1662", - "tradeable": "true", - "run_anim": "1664", - "archery_ticket_price": "0", - "id": "4153", - "stand_turn_anim": "823", - "bonuses": "0,0,81,0,0,0,0,0,0,0,0,79,0,0,0", - "requirements": "{0,50}-{2,50}", - "durability": null, - "weight": "4.5", - "weapon_interface": "10", - "render_anim": "27", - "lendable": "true", - "attack_audios": "2714,0,0,0", - "name": "Granite maul" - }, - { "ge_buy_limit": "10", - "grand_exchange_price": "29900", - "durability": null, - "name": "Granite maul", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "620", + "requirements": "{0,70}", + "run_anim": "1661", + "stand_anim": "808", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4154" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1660", + "weapon_interface": "11", + "weight": "0.45" }, { - "shop_price": "1", - "examine": "I can contact the Slayer Masters with this.", + "id": "4152", + "name": "Abyssal whip", + "archery_ticket_price": "0", "durability": null, - "name": "Enchanted gem", - "archery_ticket_price": "0", - "id": "4155" + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "4153", + "name": "Granite maul", + "examine": "Simplicity is the best weapon.", + "archery_ticket_price": "0", + "attack_anims": "1665,1665,1665,1665", + "attack_audios": "2714,0,0,0", + "attack_speed": "7", + "bonuses": "0,0,81,0,0,0,0,0,0,0,0,79,0,0,0", + "defence_anim": "1666", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "27", + "requirements": "{0,50}-{2,50}", + "run_anim": "1664", + "stand_anim": "1662", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1663", + "turn90ccw_anim": "1663", + "turn90cw_anim": "1663", + "two_handed": "true", + "walk_anim": "1663", + "weapon_interface": "10", + "weight": "4.5" + }, + { + "id": "4154", + "name": "Granite maul", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4155", + "name": "Enchanted gem", + "examine": "I can contact the Slayer Masters with this.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "4156", + "name": "Mirror shield", + "examine": "I can just about see things in this shield's reflection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,5,5,10,5,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{1,20}-{18,25}", "shop_price": "5000", - "ge_buy_limit": "100", - "examine": "I can just about see things in this shield's reflection.", - "durability": null, - "weight": "2.2", - "equipment_slot": "5", - "grand_exchange_price": "3318", - "name": "Mirror shield", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4156", - "bonuses": "0,0,0,0,0,10,15,5,5,10,5,0,0,0,0" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3318", - "durability": null, + "id": "4157", "name": "Mirror shield", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4157" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,55}-{18,50}", - "shop_price": "31000", - "turn90cw_anim": "1207", - "examine": "A spear with a leaf-shaped point.", - "walk_anim": "1205", - "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", - "stand_anim": "813", - "name": "Leaf-bladed spear", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4158", + "name": "Leaf-bladed spear", + "examine": "A spear with a leaf-shaped point.", + "alchemizable": "true", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "47,42,36,0,0,1,1,0,0,0,0,50,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,55}-{18,50}", + "run_anim": "1210", + "shop_price": "31000", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "47,42,36,0,0,1,1,0,0,0,0,50,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" }, { - "requirements": "{0,55}-{18,50}", - "shop_price": "31000", - "examine": "A spear with a leaf-shaped point.", - "durability": null, + "id": "4159", "name": "Leaf-bladed spear", - "weight": "2", + "examine": "A spear with a leaf-shaped point.", "archery_ticket_price": "0", "attack_speed": "5", - "id": "4159", - "equipment_slot": "3" + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "requirements": "{0,55}-{18,50}", + "shop_price": "31000", + "weight": "2" }, { - "requirements": "{18,55}-{4,50}", - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrow", - "archery_ticket_price": "0", "id": "4160", + "name": "Broad arrow", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{18,55}-{4,50}", + "shop_price": "90" }, { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "A bag of salt.", - "grand_exchange_price": "1", - "durability": null, + "id": "4161", "name": "Bag of salt", - "tradeable": "true", + "examine": "A bag of salt.", "archery_ticket_price": "0", - "id": "4161" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true" }, { - "shop_price": "500", - "ge_buy_limit": "100", - "examine": "I can even smash stone with this.", - "grand_exchange_price": "140", - "durability": null, - "name": "Rock hammer", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", "id": "4162", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "140", - "durability": null, "name": "Rock hammer", - "tradeable": "true", + "examine": "I can even smash stone with this.", "archery_ticket_price": "0", - "id": "4163" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "500", + "tradeable": "true", + "weight": "2.2" }, { + "id": "4163", + "name": "Rock hammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4164", + "name": "Face mask", + "examine": "Stops me breathing in too much dust.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "requirements": "{18,10}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "Stops me breathing in too much dust.", - "grand_exchange_price": "615", - "durability": null, - "name": "Face mask", "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4164", - "equipment_slot": "0" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "615", - "durability": null, + "id": "4165", "name": "Face mask", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4165" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "4166", + "name": "Earmuffs", + "examine": "These will protect my ears from loud noise.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "requirements": "{18,15}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "These will protect my ears from loud noise.", - "grand_exchange_price": "484", - "durability": null, - "name": "Earmuffs", "tradeable": "true", - "weight": "2.6", - "archery_ticket_price": "0", - "id": "4166", - "equipment_slot": "0" + "weight": "2.6" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "484", - "durability": null, + "id": "4167", "name": "Earmuffs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4167" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "4168", + "name": "Nose peg", + "examine": "Protects me from any bad smells.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "requirements": "{18,60}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "Protects me from any bad smells.", - "grand_exchange_price": "523", - "durability": null, - "name": "Nose peg", "tradeable": "true", - "weight": "8", - "archery_ticket_price": "0", - "id": "4168", - "equipment_slot": "0" + "weight": "8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "523", - "durability": null, + "id": "4169", "name": "Nose peg", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4169" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An old and magical staff.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "12100", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4170", - "stand_turn_anim": "1209", - "bonuses": "7,-1,25,12,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{18,55}-{6,50}", - "shop_price": "21000", - "durability": null, - "weight": "1.8", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Slayer's staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12100", - "durability": null, "name": "Slayer's staff", - "tradeable": "true", + "examine": "An old and magical staff.", "archery_ticket_price": "0", - "id": "4171" - }, - { - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrows", - "archery_ticket_price": "0", - "id": "4172", - "equipment_slot": "13" - }, - { - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrows", - "archery_ticket_price": "0", - "id": "4173", - "equipment_slot": "13" - }, - { - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrows", - "archery_ticket_price": "0", - "id": "4174", - "equipment_slot": "13" - }, - { - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", - "durability": null, - "name": "Broad arrows", - "archery_ticket_price": "0", - "id": "4175", - "equipment_slot": "13" - }, - { - "examine": "A weapon from the Abyss.", - "rare_item": "true", - "durability": null, - "weight": "0.45", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", "attack_speed": "4", - "render_anim": "620", - "lendable": "true", - "grand_exchange_price": "388392", - "name": "Abyssal whip", + "bonuses": "7,-1,25,12,0,2,3,1,10,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{18,55}-{6,50}", + "run_anim": "1210", + "shop_price": "21000", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4178", - "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1.8" }, { - "examine": "A big, bad troll.", + "id": "4171", + "name": "Slayer's staff", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4172", + "name": "Broad arrows", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "13", + "shop_price": "90" + }, + { + "id": "4173", + "name": "Broad arrows", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "13", + "shop_price": "90" + }, + { + "id": "4174", + "name": "Broad arrows", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "13", + "shop_price": "90" + }, + { + "id": "4175", + "name": "Broad arrows", + "examine": "Arrows with a wider than normal tip.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "13", + "shop_price": "90" + }, + { + "id": "4178", + "name": "Abyssal whip", + "examine": "A weapon from the Abyss.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0", + "durability": null, + "lendable": "true", + "rare_item": "true", + "render_anim": "620", + "tradeable": "true", + "weight": "0.45" + }, + { + "id": "4179", "name": "Stick", + "examine": "A big, bad troll.", "archery_ticket_price": "0", "attack_speed": "6", - "id": "4179" + "durability": null }, { - "lendable": "true", - "examine": "Looks pretty heavy.", - "grand_exchange_price": "165900", - "durability": null, - "name": "Dragon platelegs", - "tradeable": "true", - "weight": "9", - "archery_ticket_price": "0", "id": "4180", + "name": "Dragon platelegs", + "examine": "Looks pretty heavy.", "absorb": "3,0,6", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "durability": null, + "lendable": "true", + "tradeable": "true", + "weight": "9" }, { - "durability": null, + "id": "4181", "name": "Mouth grip", "archery_ticket_price": "0", - "id": "4181", + "durability": null, "equipment_slot": "3" }, { - "examine": "A pale, tough looking herb.", - "durability": null, + "id": "4182", "name": "Goutweed", + "examine": "A pale, tough looking herb.", "archery_ticket_price": "0", - "id": "4182" + "durability": null }, { - "examine": "A six-pointed marble and obsidian amulet", - "durability": null, + "id": "4183", "name": "Star amulet", + "examine": "A six-pointed marble and obsidian amulet", "archery_ticket_price": "0", - "id": "4183" + "durability": null }, { - "examine": "Upon close examination, this seems to be a key.", - "durability": null, + "id": "4184", "name": "Cavern key", + "examine": "Upon close examination, this seems to be a key.", "archery_ticket_price": "0", - "id": "4184" + "durability": null }, { - "examine": "Upon close examination, this seems to be a key.", - "durability": null, + "id": "4185", "name": "Tower key", - "archery_ticket_price": "0", - "id": "4185" - }, - { "examine": "Upon close examination, this seems to be a key.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4186", "name": "Shed key", + "examine": "Upon close examination, this seems to be a key.", "archery_ticket_price": "0", - "id": "4186" + "durability": null }, { - "examine": "Triangular in shape, made from marble, and as large as your hand.", - "durability": null, + "id": "4187", "name": "Marble amulet", + "examine": "Triangular in shape, made from marble, and as large as your hand.", "archery_ticket_price": "0", - "id": "4187" + "durability": null }, { - "examine": "Triangular in shape, made from obsidian, and as large as your hand.", - "durability": null, + "id": "4188", "name": "Obsidian amulet", + "examine": "Triangular in shape, made from obsidian, and as large as your hand.", "archery_ticket_price": "0", - "id": "4188" + "durability": null }, { - "examine": "A length of garden cane.", - "durability": null, + "id": "4189", "name": "Garden cane", - "weight": "0.1", + "examine": "A length of garden cane.", "archery_ticket_price": "0", - "id": "4189" + "durability": null, + "weight": "0.1" }, { - "examine": "See article", - "durability": null, + "id": "4190", "name": "Garden brush", - "weight": "1", + "examine": "A typical garden brush.", "archery_ticket_price": "0", - "id": "4190" + "durability": null, + "weight": "1" }, { - "examine": "See article", - "durability": null, + "id": "4191", "name": "Extended brush", - "weight": "1", + "examine": "A typical garden brush, with a cane tied to it.", "archery_ticket_price": "0", - "id": "4191" + "durability": null, + "weight": "1" }, { - "examine": "See article", - "durability": null, + "id": "4192", "name": "Extended brush", - "weight": "1", + "examine": "A typical garden brush, with two canes tied to it.", "archery_ticket_price": "0", - "id": "4192" + "durability": null, + "weight": "1" }, { - "examine": "See article", - "durability": null, - "name": "Extended brush", - "weight": "1", - "archery_ticket_price": "0", "id": "4193", - "equipment_slot": "3" + "name": "Extended brush", + "examine": "A typical garden brush, with three canes tied to it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1" }, { - "examine": "A pair of limp, dead arms.", - "durability": null, + "id": "4195", "name": "Arms", - "weight": "2", + "examine": "A pair of limp, dead arms.", + "alchemizable": "true", "archery_ticket_price": "0", - "id": "4195" + "durability": null, + "weight": "2" }, { - "examine": "A pair of lifeless, rotting legs.", - "durability": null, + "id": "4196", "name": "Legs", + "examine": "A pair of lifeless, rotting legs.", + "alchemizable": "true", "archery_ticket_price": "0", - "id": "4196" + "durability": null }, { - "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", - "durability": null, + "id": "4197", "name": "Decapitated head", - "weight": "3", - "archery_ticket_price": "0", - "id": "4197" - }, - { "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", - "durability": null, - "name": "Decapitated head", - "weight": "3", + "alchemizable": "true", "archery_ticket_price": "0", - "id": "4198" + "durability": null, + "weight": "3" }, { - "shop_price": "50", - "examine": "A pickled brain, submerged inside a jar of vinegar.", + "id": "4198", + "name": "Decapitated head", + "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", + "alchemizable": "true", + "archery_ticket_price": "0", "durability": null, + "weight": "3" + }, + { + "id": "4199", "name": "Pickled brain", + "examine": "A pickled brain, submerged inside a jar of vinegar.", + "alchemizable": "true", "archery_ticket_price": "0", - "id": "4199" + "durability": null, + "shop_price": "50" }, { - "examine": "A mould for making silver lightning conductors.", - "durability": null, + "id": "4200", "name": "Conductor mould", + "examine": "A mould for making silver lightning conductors.", "archery_ticket_price": "0", - "id": "4200" + "durability": null }, { - "examine": "A silver lightning conductor.", - "durability": null, + "id": "4201", "name": "Conductor", - "weight": "4", + "examine": "A silver lightning conductor.", "archery_ticket_price": "0", - "id": "4201" + "durability": null, + "weight": "4" }, { - "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.", - "durability": null, - "name": "Ring of charos", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "4202", - "equipment_slot": "12" - }, - { - "examine": "This consecration seed looks grey and dead.", - "durability": null, - "name": "Consecration seed", + "name": "Ring of charos", + "examine": "The Ring of charos", "archery_ticket_price": "0", - "id": "4205" - }, - { - "examine": "This consecration seed looks grey and dead.", + "destroy": "true", + "destroy_message": "Another can be received from pickpocketing Dr. Fenkenstrain in the tower.", "durability": null, - "name": "Consecration seed", - "archery_ticket_price": "0", - "id": "4206" + "equipment_slot": "12", + "tradeable": "false" }, { - "ge_buy_limit": "10", + "id": "4205", + "name": "Consecration seed", + "examine": "This consecration seed looks grey and dead.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4206", + "name": "Consecration seed", + "examine": "This consecration seed looks grey and dead.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4207", + "name": "Crystal seed", "examine": "This crystal seed looks grey and dead.", - "grand_exchange_price": "92600", - "durability": null, - "name": "Crystal seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4207" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "92600", - "durability": null, - "name": "Crystal seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4208" + "tradeable": "true" }, { - "examine": "A book on Cadarn clan history.", + "id": "4208", + "name": "Crystal seed", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4209", "name": "Cadarn lineage", + "examine": "A book on Cadarn clan history.", "archery_ticket_price": "0", - "id": "4209" + "durability": null }, { - "requirements": "{4,70}-{16,50}", - "shop_price": "900000", - "ge_buy_limit": "10", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "16", - "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", - "name": "New crystal bow", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4212", - "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "905300", - "durability": null, "name": "New crystal bow", - "tradeable": "true", + "examine": "A nice sturdy magical bow.", "archery_ticket_price": "0", - "id": "4213" - }, - { + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow full", + "weight": "1.8" + }, + { + "id": "4213", + "name": "New crystal bow", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4214", - "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" - }, - { + "name": "Crystal bow full", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 9/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4215", - "bonuses": "0,0,0,0,96,0,0,0,0,0,0,0,0,0,68" - }, - { + "name": "Crystal bow 9/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,96,0,0,0,0,0,0,0,0,0,68", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 8/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4216", - "bonuses": "0,0,0,0,92,0,0,0,0,0,0,0,0,0,66" - }, - { + "name": "Crystal bow 8/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,92,0,0,0,0,0,0,0,0,0,66", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 7/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4217", - "bonuses": "0,0,0,0,88,0,0,0,0,0,0,0,0,0,64" - }, - { + "name": "Crystal bow 7/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,88,0,0,0,0,0,0,0,0,0,64", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 6/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4218", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,62" - }, - { + "name": "Crystal bow 6/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,62", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 5/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4219", - "bonuses": "0,0,0,0,80,0,0,0,0,0,0,0,0,0,60" - }, - { + "name": "Crystal bow 5/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,80,0,0,0,0,0,0,0,0,0,60", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 4/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4220", - "bonuses": "0,0,0,0,76,0,0,0,0,0,0,0,0,0,58" - }, - { + "name": "Crystal bow 4/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,76,0,0,0,0,0,0,0,0,0,58", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 3/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4221", - "bonuses": "0,0,0,0,72,0,0,0,0,0,0,0,0,0,56" - }, - { + "name": "Crystal bow 3/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,72,0,0,0,0,0,0,0,0,0,56", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 2/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4222", - "bonuses": "0,0,0,0,68,0,0,0,0,0,0,0,0,0,54" - }, - { + "name": "Crystal bow 2/10", + "examine": "A nice sturdy magical bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,68,0,0,0,0,0,0,0,0,0,54", + "defence_anim": "424", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", "requirements": "{4,70}-{16,50}", "shop_price": "900000", - "examine": "A nice sturdy magical bow.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", "two_handed": "true", "weapon_interface": "16", - "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", - "name": "Crystal bow 1/10", - "archery_ticket_price": "0", + "weight": "1.8" + }, + { "id": "4223", - "bonuses": "0,0,0,0,64,0,0,0,0,0,0,0,0,0,52" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "ge_buy_limit": "10", - "examine": "A nice sturdy crystal shield.", - "durability": null, - "weight": "2", - "absorb": "6,0,12", - "equipment_slot": "5", - "grand_exchange_price": "756900", - "name": "New crystal shield", - "tradeable": "true", + "name": "Crystal bow 1/10", + "examine": "A nice sturdy magical bow.", "archery_ticket_price": "0", - "id": "4224", - "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", + "attack_anims": "426,426,426,426", + "attack_audios": "1352,1352,1352,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,64,0,0,0,0,0,0,0,0,0,52", + "defence_anim": "424", "durability": null, - "name": "Crystal shield full", - "weight": "2", - "archery_ticket_price": "0", - "id": "4225", - "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 9/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 8/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 7/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 6/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 5/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 4/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 3/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 2/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "requirements": "{1,70}-{16,50}", - "shop_price": "750000", - "examine": "A nice sturdy crystal shield.", - "grand_exchange_price": "749305", - "durability": null, - "name": "Crystal shield 1/10", - "weight": "2", - "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", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "New crystal shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4235" - }, - { - "requirements": "{4,5}", - "examine": "This bow has been signed by Robin, Master Bowman.", - "durability": null, - "weight": "2", - "attack_speed": "6", + "equip_audio": "2238", + "equipment_slot": "3", + "render_anim": "2588", + "requirements": "{4,70}-{16,50}", + "shop_price": "900000", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "equipment_slot": "3", - "name": "Signed oak bow", + "weight": "1.8" + }, + { + "id": "4224", + "name": "New crystal shield", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "4225", + "name": "Crystal shield full", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4226", + "name": "Crystal shield 9/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,49,52,51,0,78,68,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4227", + "name": "Crystal shield 8/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,47,50,49,0,76,66,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4228", + "name": "Crystal shield 7/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,45,48,47,0,74,65,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4229", + "name": "Crystal shield 6/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,43,46,45,0,72,63,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4230", + "name": "Crystal shield 5/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,41,44,43,0,70,61,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4231", + "name": "Crystal shield 4/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,39,42,41,0,68,59,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4232", + "name": "Crystal shield 3/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,37,40,39,0,66,58,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4233", + "name": "Crystal shield 2/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,35,38,37,0,64,56,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4234", + "name": "Crystal shield 1/10", + "examine": "A nice sturdy crystal shield.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,33,36,35,0,62,54,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,70}-{16,50}", + "shop_price": "750000", + "weight": "2" + }, + { + "id": "4235", + "name": "New crystal shield", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "4236", - "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0" + "name": "Signed oak bow", + "examine": "This bow has been signed by Robin, Master Bowman.", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,14,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "2588", + "requirements": "{4,5}", + "two_handed": "true", + "weapon_interface": "16", + "weight": "2" }, { - "examine": "It's a bowl of water, with some nettles in it.", - "durability": null, + "id": "4237", "name": "Nettle-water", - "weight": "0.9", + "examine": "It's a bowl of water, with some nettles in it.", "archery_ticket_price": "0", - "id": "4237" + "durability": null, + "weight": "0.9" }, { - "examine": "It's a bowl of (milky) nettle tea.", - "durability": null, + "id": "4239", "name": "Nettle tea", - "weight": "0.9", + "examine": "It's a bowl of nettle tea.", "archery_ticket_price": "0", - "id": "4239" + "durability": null, + "weight": "0.9" }, { - "examine": "It's a bowl of (milky) nettle tea.", - "durability": null, + "id": "4240", "name": "Nettle tea", - "weight": "0.9", + "examine": "It's a bowl of milky nettle tea.", "archery_ticket_price": "0", - "id": "4240" + "durability": null, + "weight": "0.9" }, { - "examine": "(In inventory) A handful of nettles (In ground) I better not get stung/I wouldn't like to get stung/I wish I could sting other people/Dock leaves at the ready/These may hurt/nettles sting my leggies(Draynor Village nettles)", - "durability": null, + "id": "4241", "name": "Nettles", + "examine": "A handful of nettles.", "archery_ticket_price": "0", - "id": "4241" + "durability": null }, { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, + "id": "4242", "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", + "examine": "A nice cup of nettle tea.", "archery_ticket_price": "0", - "id": "4242" - }, - { + "durability": null, "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4243" + "tradeable": "false", + "weight": "0.1" }, { - "examine": "A porcelain cup.", + "id": "4243", + "name": "Cup of tea", + "examine": "A milky cup of nettle tea.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "4244", "name": "Porcelain cup", + "examine": "A porcelain cup.", "archery_ticket_price": "0", - "id": "4244" + "durability": null }, { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, + "id": "4245", "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", + "examine": "Some nettle tea in a porcelain cup.", "archery_ticket_price": "0", - "id": "4245" - }, - { + "durability": null, "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4246" + "tradeable": "false", + "weight": "0.1" }, { - "examine": "The Robes of Necrovarus.", + "id": "4246", + "name": "Cup of tea", + "examine": "Some milky nettle tea in a porcelain cup.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "4247", "name": "Mystical robes", - "weight": "2.3", + "examine": "The Robes of Necrovarus.", "archery_ticket_price": "0", - "id": "4247" + "durability": null, + "weight": "2.3" }, { - "examine": "The Book of Haricanto.", - "durability": null, + "id": "4248", "name": "Book of haricanto", - "weight": "2", + "examine": "The Book of Haricanto.", "archery_ticket_price": "0", - "id": "4248" + "durability": null, + "weight": "2" }, { - "shop_price": "32", - "examine": "A translation manual.", - "durability": null, + "id": "4249", "name": "Translation manual", - "weight": "1", + "examine": "A translation manual.", "archery_ticket_price": "0", - "id": "4249" + "durability": null, + "shop_price": "32", + "weight": "1" }, { - "examine": "The amulet of ghostspeak glows green from the crone's enchantment.", - "durability": null, - "name": "Ghostspeak amulet", - "archery_ticket_price": "0", "id": "4250", + "name": "Ghostspeak amulet", + "examine": "The amulet of ghostspeak glows green from the crone's enchantment.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "destroy_message": "The Ectophial will smash if you drop it. Velorina can replace it.", - "examine": "The Ectophial.", - "durability": null, + "id": "4251", "name": "Ectophial", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "4251" - }, - { - "destroy_message": "The Ectophial will smash if you drop it. Velorina can replace it.", "examine": "The Ectophial.", + "archery_ticket_price": "0", + "destroy_message": "The Ectophial will smash if you drop it. Velorina can replace it.", "durability": null, + "tradeable": "false" + }, + { + "id": "4252", "name": "Ectophial", - "tradeable": "false", + "examine": "The Ectophial.", "archery_ticket_price": "0", - "id": "4252" + "destroy_message": "The Ectophial will smash if you drop it. Velorina can replace it.", + "durability": null, + "tradeable": "false" }, { - "examine": "A small wooden ship. / A small wooden ship with a silk flag.", - "durability": null, + "id": "4253", "name": "Model ship", - "archery_ticket_price": "0", - "id": "4253" - }, - { "examine": "A small wooden ship. / A small wooden ship with a silk flag.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4254", "name": "Model ship", + "examine": "A small wooden ship. / A small wooden ship with a silk flag.", "archery_ticket_price": "0", - "id": "4254" + "durability": null }, { - "examine": "A pot of crushed bones.", - "durability": null, + "id": "4255", "name": "Bonemeal", - "weight": "1", - "archery_ticket_price": "0", - "id": "4255" - }, - { "examine": "A pot of crushed bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4256" + "durability": null, + "weight": "1" }, { + "id": "4256", + "name": "Bonemeal", + "examine": "A pot of crushed small ninja monkey bones.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4257", + "name": "Bonemeal", "examine": "A pot of crushed wolf bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4257" + "durability": null, + "weight": "1" }, { + "id": "4258", + "name": "Bonemeal", "examine": "A pot of crushed burnt bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4258" + "durability": null, + "weight": "1" }, { + "id": "4259", + "name": "Bonemeal", + "examine": "A pot of crushed burnt jogre bones.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4260", + "name": "Bonemeal", "examine": "A pot of crushed monkey bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4259" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed monkey bones.", - "durability": null, + "id": "4261", "name": "Bonemeal", - "weight": "1", - "archery_ticket_price": "0", - "id": "4260" - }, - { "examine": "A pot of crushed bat bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4261" + "durability": null, + "weight": "1" }, { + "id": "4262", + "name": "Bonemeal", "examine": "A pot of crushed big bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4262" + "durability": null, + "weight": "1" }, { + "id": "4263", + "name": "Bonemeal", "examine": "A pot of crushed jogre bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4263" + "durability": null, + "weight": "1" }, { + "id": "4264", + "name": "Bonemeal", "examine": "A pot of crushed zogre bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4264" + "durability": null, + "weight": "1" }, { + "id": "4265", + "name": "Bonemeal", "examine": "A pot of crushed shaikahan bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4265" + "durability": null, + "weight": "1" }, { + "id": "4266", + "name": "Bonemeal", "examine": "A pot of crushed baby dragon bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4266" + "durability": null, + "weight": "1" }, { + "id": "4267", + "name": "Bonemeal", "examine": "A pot of crushed wyvern bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4267" + "durability": null, + "weight": "1" }, { + "id": "4268", + "name": "Bonemeal", "examine": "A pot of crushed dragon bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4268" + "durability": null, + "weight": "1" }, { + "id": "4269", + "name": "Bonemeal", "examine": "A pot of crushed [type of bones].", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4269" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4270", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed medium ninja monkey bones.", "archery_ticket_price": "0", - "id": "4270" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4271", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed Dagannoth-king bones.", "archery_ticket_price": "0", - "id": "4271" + "durability": null, + "weight": "1" }, { - "shop_price": "100", - "examine": "A key fashioned from a shard of bone. (Shilo Village)", - "durability": null, + "id": "4272", "name": "Bone key", + "examine": "A key fashioned from a shard of bone. (Shilo Village)", "archery_ticket_price": "0", - "id": "4272" + "durability": null, + "shop_price": "100" }, { - "examine": "A section from some kind of map.", - "durability": null, + "id": "4274", "name": "Map scrap", - "archery_ticket_price": "0", - "id": "4274" - }, - { "examine": "A section from some kind of map.", - "durability": null, - "name": "Map scrap", "archery_ticket_price": "0", - "id": "4275" + "durability": null }, { + "id": "4275", + "name": "Map scrap", "examine": "A section from some kind of map.", - "durability": null, - "name": "Map scrap", "archery_ticket_price": "0", - "id": "4276" + "durability": null }, { - "examine": "A complete treasure map.", - "durability": null, + "id": "4276", + "name": "Map scrap", + "examine": "A section from some kind of map.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4277", "name": "Treasure map", + "examine": "A complete treasure map.", "archery_ticket_price": "0", - "id": "4277" + "durability": null }, { - "examine": "A token with ectoplasm on it.", - "durability": null, + "id": "4278", "name": "Ecto-token", - "tradeable": "false", + "examine": "A token with ectoplasm on it.", "archery_ticket_price": "0", - "id": "4278" + "durability": null, + "tradeable": "false" }, { - "examine": "A scroll of paper containing signatures.", - "durability": null, + "id": "4283", "name": "Petition form", + "examine": "A scroll of paper containing signatures.", "archery_ticket_price": "0", - "id": "4283" + "durability": null }, { - "examine": "Before Ectofuntus: It's a bedsheet.After Ectofuntus: It's an ectoplasm-covered bedsheet.", - "durability": null, - "name": "Bedsheet", - "tradeable": "false", - "weight": "1", - "archery_ticket_price": "0", "id": "4284", - "equipment_slot": "0" - }, - { - "examine": "Before Ectofuntus: It's a bedsheet.After Ectofuntus: It's an ectoplasm-covered bedsheet.", - "durability": null, "name": "Bedsheet", - "tradeable": "false", - "weight": "1", + "examine": "Before Ectofuntus: It's a bedsheet.After Ectofuntus: It's an ectoplasm-covered bedsheet.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "1" + }, + { "id": "4285", - "equipment_slot": "0" + "name": "Bedsheet", + "examine": "Before Ectofuntus: It's a bedsheet.After Ectofuntus: It's an ectoplasm-covered bedsheet.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "1" }, { - "examine": "It's a bucket of ectoplasm.", - "durability": null, + "id": "4286", "name": "Bucket of slime", - "weight": "3", + "examine": "It's a bucket of ectoplasm.", "archery_ticket_price": "0", - "id": "4286" + "durability": null, + "weight": "3" }, { + "id": "4287", + "name": "Raw beef", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", "shop_price": "58", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "4288", + "name": "Raw beef", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4289", + "name": "Raw chicken", "examine": "I need to cook this first.", - "grand_exchange_price": "440", - "durability": null, - "name": "Raw beef", - "tradeable": "true", - "weight": "0.3", "archery_ticket_price": "0", - "id": "4287" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "440", - "durability": null, - "name": "Raw beef", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4288" - }, - { "shop_price": "60", - "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "263", - "durability": null, + "tradeable": "true", + "weight": "0.15" + }, + { + "id": "4290", "name": "Raw chicken", - "tradeable": "true", - "weight": "0.15", "archery_ticket_price": "0", - "id": "4289" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "263", - "durability": null, - "name": "Raw chicken", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4290" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "59", - "durability": null, + "id": "4291", "name": "Cooked chicken", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4291" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "59", - "durability": null, - "name": "Cooked chicken", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4292" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", "examine": "Mmm this looks tasty.", - "grand_exchange_price": "74", - "durability": null, - "name": "Cooked meat", - "tradeable": "true", - "weight": "0.25", "archery_ticket_price": "0", - "id": "4293" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "74", - "durability": null, - "name": "Cooked meat", + "shop_price": "5", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4294" + "weight": "0.1" }, { - "examine": "A member of the 'Humans Against Monsters' group.", + "id": "4292", + "name": "Cooked chicken", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4293", + "name": "Cooked meat", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "4294", + "name": "Cooked meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4295", "name": "Female h.a.m.", - "archery_ticket_price": "0", - "id": "4295" - }, - { "examine": "A member of the 'Humans Against Monsters' group.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4297", "name": "Male h.a.m.", + "examine": "A member of the 'Humans Against Monsters' group.", "archery_ticket_price": "0", - "id": "4297" + "durability": null }, { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", - "grand_exchange_price": "19", - "durability": null, - "name": "Ham shirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", "id": "4298", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, "name": "Ham shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4299" - }, - { - "ge_buy_limit": "100", "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", - "grand_exchange_price": "11", - "durability": null, - "name": "Ham robe", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "4300", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "11", + "id": "4299", + "name": "Ham shirt", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4300", "name": "Ham robe", - "tradeable": "true", + "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", "archery_ticket_price": "0", - "id": "4301" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.9" }, { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Light-weight head protection and eye shield.", - "grand_exchange_price": "400", - "durability": null, - "name": "Ham hood", - "tradeable": "true", - "weight": "0.4", + "id": "4301", + "name": "Ham robe", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4302", - "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "400", - "durability": null, "name": "Ham hood", - "tradeable": "true", + "examine": "Light-weight head protection and eye shield.", "archery_ticket_price": "0", - "id": "4303" + "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "examine": "A HAM cape.", - "grand_exchange_price": "25", - "durability": null, - "name": "Ham cloak", - "tradeable": "true", - "weight": "0.4", + "id": "4303", + "name": "Ham hood", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4304", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25", - "durability": null, "name": "Ham cloak", - "tradeable": "true", + "examine": "A HAM cape.", "archery_ticket_price": "0", - "id": "4305" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "examine": "A badge for the HAM cult.", - "grand_exchange_price": "63", - "durability": null, - "name": "H.a.m logo", - "tradeable": "true", + "id": "4305", + "name": "Ham cloak", "archery_ticket_price": "0", - "weight": "0.5", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4306", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "63", - "durability": null, "name": "H.a.m logo", - "tradeable": "true", + "examine": "A badge for the HAM cult.", "archery_ticket_price": "0", - "id": "4307" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "10", - "examine": "Gloves worn by the Humans Against Monsters group.", - "grand_exchange_price": "59", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "weight": "0.4", + "id": "4307", + "name": "H.a.m logo", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4308", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "59", - "durability": null, "name": "Gloves", - "tradeable": "true", + "examine": "Gloves worn by the Humans Against Monsters group.", "archery_ticket_price": "0", - "id": "4309" + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "examine": "Boots worn by the Humans Against Monsters group.", - "grand_exchange_price": "323", - "durability": null, - "name": "Boots", - "tradeable": "true", - "weight": "0.4", + "id": "4309", + "name": "Gloves", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4310", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "323", - "durability": null, "name": "Boots", - "tradeable": "true", + "examine": "Boots worn by the Humans Against Monsters group.", "archery_ticket_price": "0", - "id": "4311" + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "", + "equipment_slot": "10", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "A book on elven crystal.", + "id": "4311", + "name": "Boots", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4313", "name": "Crystal of seren", - "tradeable": "true", + "examine": "A book on elven crystal.", "archery_ticket_price": "0", - "id": "4313" + "durability": null, + "tradeable": "true" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "2342", - "durability": null, - "name": "Team-1 cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", "id": "4315", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2342", - "durability": null, "name": "Team-1 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4316" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1145", - "durability": null, - "name": "Team-2 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4316", + "name": "Team-1 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4317", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1145", - "durability": null, "name": "Team-2 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4318" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "141", - "durability": null, - "name": "Team-3 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4318", + "name": "Team-2 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4319", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "141", - "durability": null, "name": "Team-3 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4320" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "973", - "durability": null, - "name": "Team-4 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4320", + "name": "Team-3 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4321", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "973", - "durability": null, "name": "Team-4 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4322" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1294", - "durability": null, - "name": "Team-5 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4322", + "name": "Team-4 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4323", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1294", - "durability": null, "name": "Team-5 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4324" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "124", - "durability": null, - "name": "Team-6 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4324", + "name": "Team-5 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4325", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "124", - "durability": null, "name": "Team-6 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4326" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1264", - "durability": null, - "name": "Team-7 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4326", + "name": "Team-6 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4327", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1264", - "durability": null, "name": "Team-7 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4328" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1824", - "durability": null, - "name": "Team-8 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4328", + "name": "Team-7 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4329", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1824", - "durability": null, "name": "Team-8 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4330" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "841", - "durability": null, - "name": "Team-9 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4330", + "name": "Team-8 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4331", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "841", - "durability": null, "name": "Team-9 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4332" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1127", - "durability": null, - "name": "Team-10 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4332", + "name": "Team-9 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4333", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1127", - "durability": null, "name": "Team-10 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4334" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "2011", - "durability": null, - "name": "Team-11 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4334", + "name": "Team-10 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4335", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2011", - "durability": null, "name": "Team-11 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4336" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1591", - "durability": null, - "name": "Team-12 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4336", + "name": "Team-11 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4337", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1591", - "durability": null, "name": "Team-12 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4338" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "308", - "durability": null, - "name": "Team-13 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4338", + "name": "Team-12 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4339", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "308", - "durability": null, "name": "Team-13 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4340" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1504", - "durability": null, - "name": "Team-14 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4340", + "name": "Team-13 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4341", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1504", - "durability": null, "name": "Team-14 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4342" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1462", - "durability": null, - "name": "Team-15 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4342", + "name": "Team-14 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4343", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1462", - "durability": null, "name": "Team-15 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4344" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "313", - "durability": null, - "name": "Team-16 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4344", + "name": "Team-15 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4345", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "313", - "durability": null, "name": "Team-16 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4346" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1231", - "durability": null, - "name": "Team-17 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4346", + "name": "Team-16 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4347", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1231", - "durability": null, "name": "Team-17 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4348" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1678", - "durability": null, - "name": "Team-18 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4348", + "name": "Team-17 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4349", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1678", - "durability": null, "name": "Team-18 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4350" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1041", - "durability": null, - "name": "Team-19 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4350", + "name": "Team-18 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4351", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1041", - "durability": null, "name": "Team-19 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4352" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "703", - "durability": null, - "name": "Team-20 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4352", + "name": "Team-19 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4353", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "703", - "durability": null, "name": "Team-20 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4354" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1292", - "durability": null, - "name": "Team-21 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4354", + "name": "Team-20 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4355", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1292", - "durability": null, "name": "Team-21 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4356" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1368", - "durability": null, - "name": "Team-22 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4356", + "name": "Team-21 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4357", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1368", - "durability": null, "name": "Team-22 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4358" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "280", - "durability": null, - "name": "Team-23 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4358", + "name": "Team-22 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4359", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "280", - "durability": null, "name": "Team-23 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4360" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1512", - "durability": null, - "name": "Team-24 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4360", + "name": "Team-23 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4361", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1512", - "durability": null, "name": "Team-24 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4362" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1395", - "durability": null, - "name": "Team-25 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4362", + "name": "Team-24 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4363", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1395", - "durability": null, "name": "Team-25 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4364" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "191", - "durability": null, - "name": "Team-26 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4364", + "name": "Team-25 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4365", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "191", - "durability": null, "name": "Team-26 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4366" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1420", - "durability": null, - "name": "Team-27 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4366", + "name": "Team-26 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4367", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1420", - "durability": null, "name": "Team-27 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4368" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1920", - "durability": null, - "name": "Team-28 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4368", + "name": "Team-27 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4369", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1920", - "durability": null, "name": "Team-28 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4370" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1175", - "durability": null, - "name": "Team-29 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4370", + "name": "Team-28 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4371", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1175", - "durability": null, "name": "Team-29 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4372" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "552", - "durability": null, - "name": "Team-30 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4372", + "name": "Team-29 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4373", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "552", - "durability": null, "name": "Team-30 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4374" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1385", - "durability": null, - "name": "Team-31 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4374", + "name": "Team-30 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4375", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1385", - "durability": null, "name": "Team-31 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4376" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1168", - "durability": null, - "name": "Team-32 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4376", + "name": "Team-31 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4377", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1168", - "durability": null, "name": "Team-32 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4378" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "211", - "durability": null, - "name": "Team-33 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4378", + "name": "Team-32 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4379", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "211", - "durability": null, "name": "Team-33 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4380" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1128", - "durability": null, - "name": "Team-34 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4380", + "name": "Team-33 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4381", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1128", - "durability": null, "name": "Team-34 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4382" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1140", - "durability": null, - "name": "Team-35 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4382", + "name": "Team-34 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4383", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1140", - "durability": null, "name": "Team-35 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4384" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "84", - "durability": null, - "name": "Team-36 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4384", + "name": "Team-35 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4385", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "84", - "durability": null, "name": "Team-36 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4386" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1010", - "durability": null, - "name": "Team-37 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4386", + "name": "Team-36 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4387", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1010", - "durability": null, "name": "Team-37 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4388" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1833", - "durability": null, - "name": "Team-38 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4388", + "name": "Team-37 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4389", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1833", - "durability": null, "name": "Team-38 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4390" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "914", - "durability": null, - "name": "Team-39 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4390", + "name": "Team-38 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4391", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "914", - "durability": null, "name": "Team-39 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4392" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "582", - "durability": null, - "name": "Team-40 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4392", + "name": "Team-39 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4393", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "582", - "durability": null, "name": "Team-40 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4394" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1471", - "durability": null, - "name": "Team-41 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4394", + "name": "Team-40 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4395", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1471", - "durability": null, "name": "Team-41 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4396" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1072", - "durability": null, - "name": "Team-42 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4396", + "name": "Team-41 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4397", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1072", - "durability": null, "name": "Team-42 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4398" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "298", - "durability": null, - "name": "Team-43 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4398", + "name": "Team-42 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4399", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "298", - "durability": null, "name": "Team-43 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4400" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1268", - "durability": null, - "name": "Team-44 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4400", + "name": "Team-43 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4401", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1268", - "durability": null, "name": "Team-44 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4402" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1371", - "durability": null, - "name": "Team-45 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4402", + "name": "Team-44 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4403", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1371", - "durability": null, "name": "Team-45 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4404" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "140", - "durability": null, - "name": "Team-46 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4404", + "name": "Team-45 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4405", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "140", - "durability": null, "name": "Team-46 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4406" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1233", - "durability": null, - "name": "Team-47 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4406", + "name": "Team-46 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4407", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1233", - "durability": null, "name": "Team-47 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4408" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1836", - "durability": null, - "name": "Team-48 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4408", + "name": "Team-47 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4409", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1836", - "durability": null, "name": "Team-48 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4410" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "1185", - "durability": null, - "name": "Team-49 cape", - "tradeable": "true", - "weight": "0.4", + "id": "4410", + "name": "Team-48 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4411", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1185", - "durability": null, "name": "Team-49 cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4412" - }, - { - "shop_price": "50", - "ge_buy_limit": "100", "examine": "Ooohhh look at the pretty colours...", - "grand_exchange_price": "708", - "durability": null, - "name": "Team-50 cape", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "4413", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "708", + "id": "4412", + "name": "Team-49 cape", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4413", "name": "Team-50 cape", - "tradeable": "true", + "examine": "Ooohhh look at the pretty colours...", "archery_ticket_price": "0", - "id": "4414" + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "A jungle forester's blunt axe.", + "id": "4414", + "name": "Team-50 cape", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4415", "name": "Blunt axe", - "weight": "1.3", + "examine": "A jungle forester's blunt axe.", "archery_ticket_price": "0", - "id": "4415" + "durability": null, + "weight": "1.3" }, { - "examine": "A strong medicinal brew for heavy chests.", - "durability": null, + "id": "4416", "name": "Herbal tincture", - "weight": "0.1", + "examine": "A strong medicinal brew for heavy chests.", "archery_ticket_price": "0", - "id": "4416" + "durability": null, + "weight": "0.1" }, { - "ge_buy_limit": "100", - "examine": "A cup of Guthix rest.", - "grand_exchange_price": "223", - "durability": null, + "id": "4417", "name": "Guthix rest(4)", - "tradeable": "true", + "examine": "A cup of Guthix rest.", "archery_ticket_price": "0", - "id": "4417" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "223", - "durability": null, + "id": "4418", "name": "Guthix rest(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4418" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A cup of Guthix rest.", - "grand_exchange_price": "134", - "durability": null, + "id": "4419", "name": "Guthix rest(3)", - "tradeable": "true", + "examine": "A cup of Guthix rest.", "archery_ticket_price": "0", - "id": "4419" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "134", - "durability": null, + "id": "4420", "name": "Guthix rest(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4420" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A cup of Guthix rest.", - "grand_exchange_price": "83", - "durability": null, + "id": "4421", "name": "Guthix rest(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4421" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Guthix rest(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4422" - }, - { - "ge_buy_limit": "100", "examine": "A cup of Guthix rest.", - "grand_exchange_price": "44", - "durability": null, - "name": "Guthix rest(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4423" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "44", - "durability": null, - "name": "Guthix rest(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4424" + "tradeable": "true" }, { - "examine": "A half-filled feather mattress.", + "id": "4422", + "name": "Guthix rest(2)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4423", + "name": "Guthix rest(1)", + "examine": "A cup of Guthix rest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4424", + "name": "Guthix rest(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4425", "name": "Stodgy mattress", - "weight": "10", + "examine": "A half-filled feather mattress.", "archery_ticket_price": "0", - "id": "4425" + "durability": null, + "weight": "10" }, { - "examine": "A comfy-looking feather mattress.", - "durability": null, + "id": "4426", "name": "Comfy mattress", - "weight": "15", + "examine": "A comfy-looking feather mattress.", "archery_ticket_price": "0", - "id": "4426" + "durability": null, + "weight": "15" }, { - "examine": "Looks like a bunch of rust to me.", - "durability": null, + "id": "4427", "name": "Iron oxide", - "weight": "1", + "examine": "Looks like a bunch of rust to me.", "archery_ticket_price": "0", - "id": "4427" + "durability": null, + "weight": "1" }, { - "examine": "An animate rock spell is written on this parchment.", - "durability": null, + "id": "4428", "name": "Animate rock scroll", + "examine": "An animate rock spell is written on this parchment.", "archery_ticket_price": "0", - "id": "4428" + "durability": null }, { - "examine": "The weathervane directions should now work.", - "durability": null, + "id": "4430", "name": "Directionals", + "examine": "The weathervane directions should now work.", "archery_ticket_price": "0", - "id": "4430" + "durability": null }, { - "examine": "A fixed weathervane ornament.", - "durability": null, + "id": "4432", "name": "Ornament", + "examine": "A fixed weathervane ornament.", "archery_ticket_price": "0", - "id": "4432" + "durability": null }, { - "examine": "A fixed weathervane rotating pillar.", - "durability": null, + "id": "4434", "name": "Weathervane pillar", + "examine": "A fixed weathervane rotating pillar.", "archery_ticket_price": "0", - "id": "4434" + "durability": null }, { - "examine": "Clear skies ahead, with some chance of showers, thunderstorms, ice and hail.", - "durability": null, + "id": "4435", "name": "Weather report", + "examine": "Clear skies ahead, with some chance of showers, thunderstorms, ice and hail.", "archery_ticket_price": "0", - "id": "4435" + "durability": null }, { - "ge_buy_limit": "100", + "id": "4436", + "name": "Airtight pot", "examine": "This is pretty well sealed.", - "grand_exchange_price": "245", - "durability": null, - "name": "Airtight pot", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "4436" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "245", - "durability": null, + "id": "4437", "name": "Airtight pot", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4437" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4438", + "name": "Unfired pot lid", "examine": "This needs firing, then it should fit on a normal-sized pot.", - "grand_exchange_price": "16", - "durability": null, - "name": "Unfired pot lid", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4438" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "16", - "durability": null, + "id": "4439", "name": "Unfired pot lid", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4439" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4440", + "name": "Pot lid", "examine": "This should fit on a normal-sized pot.", - "grand_exchange_price": "5", - "durability": null, - "name": "Pot lid", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4440" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "5", - "durability": null, - "name": "Pot lid", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4441" + "tradeable": "true" }, { - "examine": "An airtight pot with something inside, most likely breathing salts.", + "id": "4441", + "name": "Pot lid", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4442", "name": "Breathing salts", + "examine": "An airtight pot with something inside, most likely breathing salts.", "archery_ticket_price": "0", - "id": "4442" + "durability": null }, { - "examine": "A large cage for transporting chickens.", - "durability": null, + "id": "4443", "name": "Chicken cage", - "weight": "1", + "examine": "A large cage for transporting chickens.", "archery_ticket_price": "0", - "id": "4443" + "durability": null, + "weight": "1" }, { - "examine": "A jungle forester's super sharp axe.", - "durability": null, + "id": "4444", "name": "Sharpened axe", - "weight": "1.3", + "examine": "A jungle forester's super sharp axe.", "archery_ticket_price": "0", - "id": "4444" + "durability": null, + "weight": "1.3" }, { - "examine": "Some mahogany logs which have been professionally cured.", - "durability": null, + "id": "4445", "name": "Red mahogany log", - "weight": "1.3", + "examine": "Some mahogany logs which have been professionally cured.", "archery_ticket_price": "0", - "id": "4445" + "durability": null, + "weight": "1.3" }, { - "destroy_message": "You can get another key ring from Yanni Salika in Shilo Village.", - "shop_price": "500", - "examine": "I can store my keys here.", - "durability": null, + "id": "4446", "name": "Steel key ring", - "tradeable": "false", - "destroy": "true", + "examine": "I can store my keys here.", "archery_ticket_price": "0", - "id": "4446" + "destroy": "true", + "destroy_message": "You can get another key ring from Yanni Salika in Shilo Village.", + "durability": null, + "shop_price": "500", + "tradeable": "false" }, { - "examine": "I wonder what happens if I rub it.", - "durability": null, + "id": "4447", "name": "Antique lamp", - "tradeable": "false", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4447" + "durability": null, + "tradeable": "false" }, { - "ge_buy_limit": "100", + "id": "4456", + "name": "Bowl of hot water", "examine": "It's a bowl of hot water.", - "grand_exchange_price": "111", - "durability": null, - "name": "Bowl of hot water", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "4456" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "111", - "durability": null, + "id": "4457", "name": "Bowl of hot water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4457" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4458", + "name": "Cup of water", "examine": "A cup of water.", - "grand_exchange_price": "36", - "durability": null, - "name": "Cup of water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4458" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "36", - "durability": null, + "id": "4459", "name": "Cup of water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4459" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4460", + "name": "Cup of hot water", "examine": "It's hot!", - "grand_exchange_price": "102", - "durability": null, - "name": "Cup of hot water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4460" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "102", - "durability": null, - "name": "Cup of hot water", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4461" + "tradeable": "true" }, { + "id": "4461", + "name": "Cup of hot water", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4462", + "name": "Ruined herb tea", "examine": "A ruined herb tea.", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "4463", "name": "Ruined herb tea", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4462" - }, - { "durability": null, - "name": "Ruined herb tea", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4463" + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4464", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "13", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4464" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, + "id": "4465", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4465" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4466", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "13", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4466" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, + "id": "4467", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4467" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4468", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "19", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4468" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, + "id": "4469", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4469" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4470", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "19", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4470" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, + "id": "4471", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4471" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4472", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "17", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4472" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "17", - "durability": null, + "id": "4473", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4473" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4474", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "18", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4474" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, + "id": "4475", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4475" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4476", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "18", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4476" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, + "id": "4477", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4477" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4478", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "46", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4478" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "46", - "durability": null, + "id": "4479", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4479" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4480", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "41", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4480" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "41", - "durability": null, + "id": "4481", "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4481" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4482", + "name": "Herb tea mix", "examine": "See article", - "grand_exchange_price": "43", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4482" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "43", - "durability": null, - "name": "Herb tea mix", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4483" + "tradeable": "true" }, { - "shop_price": "160", - "examine": "The strange characters supposedly grant Svidi safe passage into Rellekka.", + "id": "4483", + "name": "Herb tea mix", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4484", "name": "Safety guarantee", + "examine": "The strange characters supposedly grant Svidi safe passage into Rellekka.", "archery_ticket_price": "0", - "id": "4484" + "durability": null, + "shop_price": "160" }, { - "examine": "This fruit is known as White Pearl. Should taste good.", - "durability": null, + "id": "4485", "name": "White pearl", - "weight": "1", + "examine": "This fruit is known as White Pearl. Should taste good.", "archery_ticket_price": "0", - "id": "4485" + "durability": null, + "weight": "1" }, { - "examine": "You can grow this seed even in cold mountain ranges!", - "durability": null, + "id": "4486", "name": "White pearl seed", + "examine": "You can grow this seed even in cold mountain ranges!", "archery_ticket_price": "0", - "id": "4486" + "durability": null }, { - "examine": "It's a piece of the Ancient Rock of the mountain people. It's still just a stone.", - "durability": null, + "id": "4487", "name": "Half a rock", - "weight": "1", + "examine": "It's a piece of the Ancient Rock of the mountain people. It's still just a stone.", "archery_ticket_price": "0", - "id": "4487" + "durability": null, + "weight": "1" }, { - "examine": "The corpse of a woman who died long ago.", - "durability": null, + "id": "4488", "name": "Corpse of woman", - "weight": "1", + "examine": "The corpse of a woman who died long ago.", "archery_ticket_price": "0", - "id": "4488" + "durability": null, + "weight": "1" }, { - "examine": "This used to belong to Asleif, daughter of the mountain camp chieftain.", - "durability": null, + "id": "4489", "name": "Asleif's necklace", + "examine": "This used to belong to Asleif, daughter of the mountain camp chieftain.", "archery_ticket_price": "0", - "id": "4489" + "durability": null }, { - "examine": "Marginally better than egg on your face.", - "durability": null, + "id": "4490", "name": "Mud", + "examine": "Marginally better than egg on your face.", "archery_ticket_price": "0", - "id": "4490" + "durability": null }, { - "examine": "A muddy rock.", - "durability": null, + "id": "4492", "name": "Muddy rock", - "weight": "1", + "examine": "A muddy rock.", "archery_ticket_price": "0", - "id": "4492" + "durability": null, + "weight": "1" }, { - "examine": "It's just a long stick, really.", - "durability": null, + "id": "4494", "name": "Pole", - "archery_ticket_price": "0", - "id": "4494" - }, - { "examine": "It's just a long stick, really.", - "durability": null, - "name": "Pole", "archery_ticket_price": "0", - "id": "4495" + "durability": null }, { - "examine": "Splintered into pieces, it has become completely useless to you.", - "durability": null, + "id": "4495", + "name": "Pole", + "examine": "It's just a long stick, really.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4496", "name": "Broken pole", + "examine": "Splintered into pieces, it has become completely useless to you.", "archery_ticket_price": "0", - "id": "4496" + "durability": null }, { - "shop_price": "18", - "examine": "A coil of rope.", - "grand_exchange_price": "101", - "durability": null, - "name": "Rope", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", "id": "4498", - "equipment_slot": "3" - }, - { - "durability": null, "name": "Rope", + "examine": "A coil of rope.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "shop_price": "18", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4499" + "weight": "1.3" }, { - "examine": "It's just a long stick, really.", - "durability": null, - "name": "Pole", + "id": "4499", + "name": "Rope", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "4500", - "equipment_slot": "3" - }, - { - "examine": "It's just a long stick, really.", - "durability": null, "name": "Pole", + "examine": "It's just a long stick, really.", "archery_ticket_price": "0", - "id": "4501", + "durability": null, "equipment_slot": "3" }, { + "id": "4501", + "name": "Pole", + "examine": "It's just a long stick, really.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "4502", + "name": "Bearhead", + "examine": "Quite ferocious looking.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-3,12,14,10,7,9,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Hamal, the Chieftain of the Mountain Camp, can replace this.", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "shop_price": "3000", - "examine": "Quite ferocious looking.", - "durability": null, - "destroy": "true", - "weight": "0.4", - "equipment_slot": "0", - "destroy_message": "Hamal, the Chieftain of the Mountain Camp, can replace this.", - "name": "Bearhead", "tradeable": "false", - "archery_ticket_price": "0", - "id": "4502", - "bonuses": "0,0,0,-3,-3,12,14,10,7,9,0,0,0,0,0" + "weight": "0.4" }, { - "requirements": "{0,20}", - "shop_price": "50", - "turn90cw_anim": "821", - "examine": "A very decorative sword.", - "walk_anim": "819", - "durability": null, - "weight": "1", - "turn90ccw_anim": "822", - "weapon_interface": "6", - "turn180_anim": "820", - "render_anim": "1", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "stand_anim": "808", - "name": "Decorative sword", - "run_anim": "824", - "archery_ticket_price": "0", "id": "4503", - "stand_turn_anim": "823", - "bonuses": "15,20,-2,0,0,0,3,2,0,0,0,22,0,0,0" - }, - { - "remove_sleeves": "true", - "requirements": "{1,20}", - "durability": null, - "name": "Decorative armour", - "archery_ticket_price": "0", - "id": "4504", - "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,20}", - "durability": null, - "name": "Decorative armour", - "archery_ticket_price": "0", - "id": "4505", - "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "requirements": "{1,20}", - "shop_price": "40", - "examine": "A very decorative helm.", - "durability": null, - "name": "Decorative helm", - "weight": "1", - "archery_ticket_price": "0", - "id": "4506", - "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,0", - "equipment_slot": "0" - }, - { - "requirements": "{1,20}", - "shop_price": "60", - "examine": "A very decorative shield.", - "durability": null, - "name": "Decorative shield", - "archery_ticket_price": "0", - "id": "4507", - "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,0,0,0", - "equipment_slot": "5" - }, - { - "requirements": "{0,30}", - "shop_price": "50", - "turn90cw_anim": "821", - "examine": "A very decorative sword.", - "walk_anim": "819", - "durability": null, - "weight": "1", - "turn90ccw_anim": "822", - "weapon_interface": "6", - "turn180_anim": "820", - "render_anim": "1", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "stand_anim": "808", "name": "Decorative sword", + "examine": "A very decorative sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "bonuses": "15,20,-2,0,0,0,3,2,0,0,0,22,0,0,0", + "castle_wars_ticket_price": "50", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1", + "requirements": "{0,20}", "run_anim": "824", - "archery_ticket_price": "0", - "id": "4508", + "shop_price": "50", + "stand_anim": "808", "stand_turn_anim": "823", - "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" }, { + "id": "4504", + "name": "Decorative armour", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,0,0,0", + "castle_wars_ticket_price": "80", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", - "requirements": "{1,30}", - "durability": null, + "requirements": "{1,20}" + }, + { + "id": "4505", "name": "Decorative armour", "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0", + "castle_wars_ticket_price": "60", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}" + }, + { + "id": "4506", + "name": "Decorative helm", + "examine": "A very decorative helm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,0", + "castle_wars_ticket_price": "40", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}", + "shop_price": "40", + "weight": "1" + }, + { + "id": "4507", + "name": "Decorative shield", + "examine": "A very decorative shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,0,0,0", + "castle_wars_ticket_price": "60", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,20}", + "shop_price": "60" + }, + { + "id": "4508", + "name": "Decorative sword", + "examine": "A very decorative sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0", + "castle_wars_ticket_price": "500", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1", + "requirements": "{0,30}", + "run_anim": "824", + "shop_price": "50", + "stand_anim": "808", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" + }, + { "id": "4509", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,30}", - "durability": null, "name": "Decorative armour", "archery_ticket_price": "0", - "id": "4510", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", - "equipment_slot": "7" + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", + "castle_wars_ticket_price": "800", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,30}" }, { + "id": "4510", + "name": "Decorative armour", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "castle_wars_ticket_price": "600", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,30}" + }, + { + "id": "4511", + "name": "Decorative helm", + "examine": "A very decorative helm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0", + "castle_wars_ticket_price": "400", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,30}", "shop_price": "40", - "examine": "A very decorative helm.", - "durability": null, - "name": "Decorative helm", - "weight": "1", - "archery_ticket_price": "0", - "id": "4511", - "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0", - "equipment_slot": "0" + "weight": "1" }, { - "requirements": "{1,30}", - "shop_price": "60", - "examine": "A very decorative shield.", - "durability": null, - "name": "Decorative shield", - "archery_ticket_price": "0", "id": "4512", + "name": "Decorative shield", + "examine": "A very decorative shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", - "equipment_slot": "5" + "castle_wars_ticket_price": "600", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,30}", + "shop_price": "60" }, { - "remove_head": "true", - "durability": null, - "name": "Castlewars hood", - "archery_ticket_price": "0", "id": "4513", - "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, - "name": "Castlewars cloak", - "archery_ticket_price": "0", - "id": "4514", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "remove_head": "true", - "durability": null, "name": "Castlewars hood", "archery_ticket_price": "0", - "id": "4515", "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", - "equipment_slot": "0" + "castle_wars_ticket_price": "10", + "durability": null, + "equipment_slot": "0", + "remove_head": "true" }, { - "durability": null, + "id": "4514", "name": "Castlewars cloak", "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", + "durability": null, "equipment_slot": "1" }, { - "ge_buy_limit": "1000", - "examine": "This could feed a family of gnomes for a week!", - "grand_exchange_price": "970", - "durability": null, - "name": "Giant frog legs", - "tradeable": "true", - "weight": "0.4", + "id": "4515", + "name": "Castlewars hood", "archery_ticket_price": "0", - "id": "4517" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "970", + "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", + "castle_wars_ticket_price": "10", "durability": null, - "name": "Giant frog legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4518" - }, - { - "requirements": "{12,11}", - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", - "grand_exchange_price": "1575", - "durability": null, - "name": "Oil lamp", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "4522" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1575", - "durability": null, - "name": "Oil lamp", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4523" - }, - { - "shop_price": "10", - "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", - "grand_exchange_price": "2", - "durability": null, - "name": "Oil lamp", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "4524" - }, - { - "requirements": "{12,11}", - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", - "grand_exchange_price": "2", - "durability": null, - "name": "Oil lamp", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "4525" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, - "name": "Oil lamp", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4526" - }, - { - "requirements": "{4,11}", - "ge_buy_limit": "100", - "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", - "grand_exchange_price": "39", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4527" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "39", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4528" - }, - { - "requirements": "{4,11}", - "ge_buy_limit": "100", - "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", - "grand_exchange_price": "431", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4529" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "431", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4530" - }, - { - "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", - "grand_exchange_price": "1", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4531" - }, - { - "requirements": "{4,11}", - "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", - "grand_exchange_price": "1", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4532" - }, - { - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4533" - }, - { - "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", - "grand_exchange_price": "1", - "durability": null, - "name": "Candle lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4534" - }, - { - "requirements": "{11,26}", - "ge_buy_limit": "100", - "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", - "grand_exchange_price": "12", - "durability": null, - "name": "Oil lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4535" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12", - "durability": null, - "name": "Oil lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4536" - }, - { - "requirements": "{11,26}", - "ge_buy_limit": "100", - "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", - "grand_exchange_price": "218", - "durability": null, - "name": "Oil lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4537" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "218", - "durability": null, - "name": "Oil lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4538" - }, - { - "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", - "grand_exchange_price": "198", - "durability": null, - "name": "Oil lantern", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4539" - }, - { - "shop_price": "43", - "ge_buy_limit": "100", - "examine": "Add the glass to complete.", - "grand_exchange_price": "8", - "durability": null, - "name": "Oil lantern frame", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4540" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, - "name": "Oil lantern frame", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4541" - }, - { - "shop_price": "88", - "ge_buy_limit": "100", - "examine": "A roughly circular disc of glass.", - "grand_exchange_price": "1", - "durability": null, - "name": "Lantern lens", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4542" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Lantern lens", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4543" - }, - { - "shop_price": "400", - "ge_buy_limit": "100", - "examine": "You need to add a lens before you can use it.", - "grand_exchange_price": "88", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4544" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "88", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4545" - }, - { - "requirements": "{11,49}", - "shop_price": "400", - "ge_buy_limit": "100", - "examine": "You need to add lamp oil before you can use it.", - "grand_exchange_price": "158", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4546" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "158", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4547" - }, - { - "requirements": "{11,49}", - "shop_price": "400", - "ge_buy_limit": "100", - "examine": "A sturdy steel lantern.", - "grand_exchange_price": "1345", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4548" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1345", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4549" - }, - { - "shop_price": "400", - "examine": "A sturdy steel lantern casting a bright beam.", - "grand_exchange_price": "1235", - "durability": null, - "name": "Bullseye lantern", - "tradeable": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "4550" - }, - { - "remove_head": "true", - "requirements": "{1,5}-{18,35}", - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "You don't want to wear it inside-out.", - "durability": null, - "weight": "2.2", - "remove_beard": "true", "equipment_slot": "0", - "grand_exchange_price": "774", - "name": "Spiny helmet", - "tradeable": "true", + "remove_head": "true" + }, + { + "id": "4516", + "name": "Castlewars cloak", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "castle_wars_ticket_price": "10", + "durability": null, + "equipment_slot": "1" + }, + { + "id": "4517", + "name": "Giant frog legs", + "examine": "This could feed a family of gnomes for a week!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4518", + "name": "Giant frog legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4522", + "name": "Oil lamp", + "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{12,11}", + "shop_price": "10", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4523", + "name": "Oil lamp", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4524", + "name": "Oil lamp", + "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4525", + "name": "Oil lamp", + "examine": "Empty: An oil lamp with no oil in it.Filled: Not the genie sort.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{12,11}", + "shop_price": "10", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4526", + "name": "Oil lamp", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4527", + "name": "Candle lantern", + "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{4,11}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4528", + "name": "Candle lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4529", + "name": "Candle lantern", + "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{4,11}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4530", + "name": "Candle lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4531", + "name": "Candle lantern", + "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4532", + "name": "Candle lantern", + "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,11}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4533", + "name": "Candle lantern", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4534", + "name": "Candle lantern", + "examine": "Lit: A flickering candle in a glass cage.Unlit: a candle in a glass cage.Without candle: Put a candle in to complete it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4535", + "name": "Oil lantern", + "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{11,26}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4536", + "name": "Oil lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4537", + "name": "Oil lantern", + "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{11,26}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4538", + "name": "Oil lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4539", + "name": "Oil lantern", + "examine": "Unfuelled: Put oil in to complete it. Unlit: An unlit oil lantern. Lit: It lights your way through the dark places of the earth.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4540", + "name": "Oil lantern frame", + "examine": "Add the glass to complete.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "43", + "tradeable": "true" + }, + { + "id": "4541", + "name": "Oil lantern frame", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4542", + "name": "Lantern lens", + "examine": "A roughly circular disc of glass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "88", + "tradeable": "true" + }, + { + "id": "4543", + "name": "Lantern lens", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4544", + "name": "Bullseye lantern", + "examine": "You need to add a lens before you can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "400", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4545", + "name": "Bullseye lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4546", + "name": "Bullseye lantern", + "examine": "You need to add lamp oil before you can use it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{11,49}", + "shop_price": "400", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4547", + "name": "Bullseye lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4548", + "name": "Bullseye lantern", + "examine": "A sturdy steel lantern.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{11,49}", + "shop_price": "400", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4549", + "name": "Bullseye lantern", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4550", + "name": "Bullseye lantern", + "examine": "A sturdy steel lantern casting a bright beam.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "400", + "tradeable": "false", + "weight": "1" + }, + { "id": "4551", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "774", - "durability": null, "name": "Spiny helmet", - "tradeable": "true", + "examine": "You don't want to wear it inside-out.", "archery_ticket_price": "0", - "id": "4552" - }, - { - "examine": "Remember to brush after eating!", + "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", "durability": null, - "name": "Blue sweets", - "archery_ticket_price": "0", - "id": "4558" - }, - { - "examine": "Remember to brush after eating!", - "durability": null, - "name": "Deep blue sweets", - "archery_ticket_price": "0", - "id": "4559" - }, - { - "examine": "Remember to brush after eating!", - "durability": null, - "name": "White sweets", - "archery_ticket_price": "0", - "id": "4560" - }, - { - "examine": "Remember to brush after eating!", - "grand_exchange_price": "10037", - "durability": null, - "name": "Purple sweets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4561" - }, - { - "examine": "Remember to brush after eating!", - "durability": null, - "name": "Red sweets", - "archery_ticket_price": "0", - "id": "4562" - }, - { - "examine": "Remember to brush after eating!", - "durability": null, - "name": "Green sweets", - "archery_ticket_price": "0", - "id": "4563" - }, - { - "examine": "Remember to brush after eating!", - "durability": null, - "name": "Pink sweets", - "archery_ticket_price": "0", - "id": "4564" - }, - { - "bankable": "true", - "turn90cw_anim": "821", - "examine": "Hand them out to spread the Easter happiness.", - "walk_anim": "1836", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "822", - "weapon_interface": "12", - "turn180_anim": "820", - "render_anim": "594", - "equipment_slot": "3", - "fun_weapon": "true", - "stand_anim": "1837", - "name": "Basket of eggs", - "tradeable": "false", - "run_anim": "1836", - "archery_ticket_price": "0", - "id": "4565", - "stand_turn_anim": "823" - }, - { - "examine": "Perhaps not the most powerful weapon in RuneScape.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "12", - "defence_anim": "1834", - "equipment_slot": "3", - "attack_anims": "1833,1833,1833,1833", - "fun_weapon": "true", - "name": "Rubber chicken", - "archery_ticket_price": "0", - "id": "4566", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "remove_head": "true", - "examine": "Made of gold and white gold.", - "durability": null, - "name": "Gold helmet", - "weight": "31.7", - "archery_ticket_price": "0", - "id": "4567", - "bonuses": "0,0,0,0,0,0,10,20,0,10,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "examine": "This book is almost falling apart, you'll have to handle it quite carefully.", - "durability": null, - "name": "Dwarven lore", - "archery_ticket_price": "0", - "id": "4568" - }, - { - "examine": "A missing page from Rolad's book! It seems to be the first one.", - "durability": null, - "name": "Book page 1", - "archery_ticket_price": "0", - "id": "4569" - }, - { - "examine": "A missing page from Rolad's book! It seems to be the second one.", - "durability": null, - "name": "Book page 2", - "archery_ticket_price": "0", - "id": "4570" - }, - { - "examine": "A missing page from Rolad's book! It seems to be the third one.", - "durability": null, - "name": "Book page 3", - "archery_ticket_price": "0", - "id": "4571" - }, - { - "examine": "A collection of missing pages from Rolad's book!", - "durability": null, - "name": "Pages", - "archery_ticket_price": "0", - "id": "4572" - }, - { - "examine": "A collection of missing pages from Rolad's book!", - "durability": null, - "name": "Pages", - "archery_ticket_price": "0", - "id": "4573" - }, - { - "examine": "These are the base schematics of a dwarven multicannon.", - "durability": null, - "name": "Base schematics", - "archery_ticket_price": "0", - "id": "4574" - }, - { - "requirements": "{0,10}", + "equipment_slot": "0", "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black 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", - "grand_exchange_price": "536", - "stand_anim": "813", - "tradeable": "true", - "name": "Black spear", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4580", - "stand_turn_anim": "1209", - "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "536", - "durability": null, - "name": "Black spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4581" - }, - { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black 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", - "grand_exchange_price": "2808", - "stand_anim": "813", - "tradeable": "true", - "name": "Black spear(p)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4582", - "stand_turn_anim": "1209", - "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2808", - "durability": null, - "name": "Black spear(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4583" - }, - { - "requirements": "{0,10}", - "turn90cw_anim": "1207", - "examine": "A Karambwan poisoned black 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", - "grand_exchange_price": "309", - "stand_anim": "813", - "name": "Black spear(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4584", - "stand_turn_anim": "1209", - "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0" - }, - { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "This looks pretty heavy.", - "durability": null, - "weight": "9", - "absorb": "3,0,6", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "161000", - "name": "Dragon plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4585", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "161000", - "durability": null, - "name": "Dragon plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4586" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "65400", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "4587", - "stand_turn_anim": "823", - "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0", - "requirements": "{0,60}", - "shop_price": "100000", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "lendable": "true", - "attack_audios": "2500,0,2517,0", - "name": "Dragon scimitar" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "65400", - "durability": null, - "name": "Dragon scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4588" - }, - { - "examine": "Keys to the Mayor's house. (The Feud)", - "durability": null, - "name": "Keys", - "archery_ticket_price": "0", - "id": "4589" - }, - { - "examine": "The Mayor of Pollnivneach's wife's jewels.", - "durability": null, - "name": "Jewels", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "4590" - }, - { - "shop_price": "1", - "examine": "Wear it on your head.", - "durability": null, - "name": "Karidian headpiece", - "archery_ticket_price": "0", - "id": "4591" - }, - { - "shop_price": "1", - "examine": "Makes me itch.", - "durability": null, - "name": "Fake beard", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4593" - }, - { - "shop_price": "160", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Note", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4597" - }, - { - "shop_price": "160", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Note", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4598" - }, - { - "shop_price": "1", - "examine": "A handy little club made out of oak.", - "durability": null, - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "10", - "defence_anim": "425", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "name": "Oak-blackjack", - "archery_ticket_price": "0", - "id": "4599", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,2,0,0,0" - }, - { - "shop_price": "600", - "ge_buy_limit": "100", - "examine": "A handy little club made out of willow.", - "durability": null, - "weight": "1", - "attack_speed": "4", - "weapon_interface": "10", - "defence_anim": "425", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "321", - "name": "Willow-blackjack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4600", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,8,0,0,0" - }, - { - "examine": "Dung of the Camelus Horribleus variety.", - "durability": null, - "name": "Ugthanki dung", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "4601" - }, - { - "examine": "Dung of the Camelus Horribleus variety.", - "durability": null, - "name": "Ugthanki dung", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "4602" - }, - { - "shop_price": "2", - "examine": "A receipt for one 'Camelus Horribleus", - "durability": null, - "name": "Receipt", - "archery_ticket_price": "0", - "id": "4603" - }, - { - "examine": "A red viscous liquid in a vial.", - "durability": null, - "name": "Hag's poison", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4604" - }, - { - "examine": "Makes a hissing sound.", - "durability": null, - "name": "Snake charm", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4605", - "equipment_slot": "3" - }, - { - "examine": "This is used to hold snakes.", - "durability": null, - "name": "Snake basket", - "archery_ticket_price": "0", - "id": "4606" - }, - { - "examine": "This basket contains a snake.", - "durability": null, - "name": "Snake basket full", - "weight": "1", - "archery_ticket_price": "0", - "id": "4607" - }, - { - "shop_price": "5", - "ge_buy_limit": "1000", - "examine": "A meaty and very hot kebab.", - "grand_exchange_price": "403", - "durability": null, - "name": "Super kebab", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "4608" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "403", - "durability": null, - "name": "Super kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4609" - }, - { - "examine": "The bottle feels warm.", - "durability": null, - "name": "Red hot sauce", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "4610" - }, - { - "remove_head": "true", - "examine": "A disguise suitable for the desert.", - "durability": null, - "name": "Desert disguise", - "archery_ticket_price": "0", - "id": "4611", "remove_beard": "true", - "equipment_slot": "0" - }, - { - "durability": null, - "name": "Willow-blackjack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4612" - }, - { - "shop_price": "75", - "examine": "It has a picture of a dragon on it.", - "durability": null, - "name": "Spinning plate", - "archery_ticket_price": "0", - "id": "4613" - }, - { - "examine": "Alas...", - "durability": null, - "name": "Broken plate", - "archery_ticket_price": "0", - "id": "4614" - }, - { - "examine": "An archaeologist's notes.", - "durability": null, - "name": "Varmen's notes", - "archery_ticket_price": "0", - "id": "4616" - }, - { - "examine": "The museum curator's key (The Golem).", - "durability": null, - "name": "Display cabinet key", - "archery_ticket_price": "0", - "id": "4617" - }, - { - "examine": "It's pretty, but you wish you knew what it was.", - "durability": null, - "name": "Strange implement", - "archery_ticket_price": "0", - "id": "4619" - }, - { - "examine": "It looks horrible.On ground: Not good for eating.", - "durability": null, - "name": "Black mushroom", - "weight": "1", - "archery_ticket_price": "0", - "id": "4620" - }, - { - "examine": "A long feather patterned like a flame.", - "durability": null, - "name": "Phoenix feather", - "archery_ticket_price": "0", - "id": "4621" - }, - { - "examine": "Black ink made out of mushrooms.", - "durability": null, - "name": "Black mushroom ink", - "weight": "1", - "archery_ticket_price": "0", - "id": "4622" - }, - { - "examine": "A phoenix feather dipped in ink.", - "durability": null, - "name": "Phoenix quill pen", - "archery_ticket_price": "0", - "id": "4623" - }, - { - "examine": "It reads 'YOUR TASK IS DONE'.", - "durability": null, - "name": "Golem program", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "4624" - }, - { + "remove_head": "true", + "requirements": "{1,5}", "shop_price": "650", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "4552", + "name": "Spiny helmet", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4558", + "name": "Blue sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4559", + "name": "Deep blue sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4560", + "name": "White sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4561", + "name": "Purple sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4562", + "name": "Red sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4563", + "name": "Green sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4564", + "name": "Pink sweets", + "examine": "Remember to brush after eating!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4565", + "name": "Basket of eggs", + "examine": "Hand them out to spread the Easter happiness.", + "archery_ticket_price": "0", + "bankable": "true", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "fun_weapon": "true", + "render_anim": "594", + "run_anim": "1836", + "stand_anim": "1837", + "stand_turn_anim": "823", + "tradeable": "false", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1836", + "weapon_interface": "12" + }, + { + "id": "4566", + "name": "Rubber chicken", + "examine": "Perhaps not the most powerful weapon in 2009Scape.", + "archery_ticket_price": "0", + "attack_anims": "1833,1833,1833,1833", + "attack_audios": "2257,2257,2257", + "attack_speed": "4", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "defence_anim": "1834", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "3", + "fun_weapon": "true", + "weapon_interface": "12", + "weight": "0.4" + }, + { + "id": "4567", + "name": "Gold helmet", + "examine": "Made of gold and white gold.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,10,20,0,10,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "31.7" + }, + { + "id": "4568", + "name": "Dwarven lore", + "examine": "This book is almost falling apart, you'll have to handle it quite carefully.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4569", + "name": "Book page 1", + "examine": "A missing page from Rolad's book! It seems to be the first one.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4570", + "name": "Book page 2", + "examine": "A missing page from Rolad's book! It seems to be the second one.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4571", + "name": "Book page 3", + "examine": "A missing page from Rolad's book! It seems to be the third one.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4572", + "name": "Pages", + "examine": "A collection of missing pages from Rolad's book!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4573", + "name": "Pages", + "examine": "A collection of missing pages from Rolad's book!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4574", + "name": "Base schematics", + "examine": "These are the base schematics of a dwarven multicannon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4580", + "name": "Black spear", + "examine": "A black tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "4581", + "name": "Black spear", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4582", + "name": "Black spear(p)", + "examine": "A black tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "4583", + "name": "Black spear(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4584", + "name": "Black spear(kp)", + "examine": "A Karambwan poisoned black tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,10}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "4585", + "name": "Dragon plateskirt", + "examine": "This looks pretty heavy.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "4586", + "name": "Dragon plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4587", + "name": "Dragon scimitar", + "examine": "A vicious, curved sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "1", + "requirements": "{0,60}", + "run_anim": "824", + "shop_price": "100000", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "4588", + "name": "Dragon scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4589", + "name": "Keys", + "examine": "Keys to the Mayor's house. (The Feud)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4590", + "name": "Jewels", + "examine": "The Mayor of Pollnivneach's wife's jewels.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "4591", + "name": "Karidian headpiece", + "examine": "Wear it on your head.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "4593", + "name": "Fake beard", + "examine": "Makes me itch.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4597", + "name": "Note", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "tradeable": "true" + }, + { + "id": "4598", + "name": "Note", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "tradeable": "true" + }, + { + "id": "4599", + "name": "Oak-blackjack", + "examine": "A handy little club made out of oak.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,2,0,0,0", + "defence_anim": "425", + "durability": null, + "equipment_slot": "3", + "shop_price": "1", + "weapon_interface": "10", + "weight": "1.8" + }, + { + "id": "4600", + "name": "Willow-blackjack", + "examine": "A handy little club made out of willow.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,8,0,0,0", + "defence_anim": "425", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "600", + "tradeable": "true", + "weapon_interface": "10", + "weight": "1" + }, + { + "id": "4601", + "name": "Ugthanki dung", + "examine": "Dung of the Camelus Horribleus variety.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.4" + }, + { + "id": "4602", + "name": "Ugthanki dung", + "examine": "Dung of the Camelus Horribleus variety.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.4" + }, + { + "id": "4603", + "name": "Receipt", + "examine": "A receipt for one 'Camelus Horribleus", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2" + }, + { + "id": "4604", + "name": "Hag's poison", + "examine": "A red viscous liquid in a vial.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "4605", + "name": "Snake charm", + "examine": "Makes a hissing sound.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "0.1" + }, + { + "id": "4606", + "name": "Snake basket", + "examine": "This is used to hold snakes.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4607", + "name": "Snake basket full", + "examine": "This basket contains a snake.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4608", + "name": "Super kebab", + "examine": "A meaty and very hot kebab.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "5", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "4609", + "name": "Super kebab", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4610", + "name": "Red hot sauce", + "examine": "The bottle feels warm.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "4611", + "name": "Desert disguise", + "examine": "A disguise suitable for the desert.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true" + }, + { + "id": "4612", + "name": "Willow-blackjack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "4613", + "name": "Spinning plate", + "examine": "It has a picture of a dragon on it.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "75" + }, + { + "id": "4614", + "name": "Broken plate", + "examine": "Alas...", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4616", + "name": "Varmen's notes", + "examine": "An archaeologist's notes.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4617", + "name": "Display cabinet key", + "examine": "The museum curator's key (The Golem).", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4619", + "name": "Strange implement", + "examine": "It's pretty, but you wish you knew what it was.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4620", + "name": "Black mushroom", + "examine": "It looks horrible.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4621", + "name": "Phoenix feather", + "examine": "A long feather patterned like a flame.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4622", + "name": "Black mushroom ink", + "examine": "Black ink made out of mushrooms.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "4623", + "name": "Phoenix quill pen", + "examine": "A phoenix feather dipped in ink.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4624", + "name": "Golem program", + "examine": "It reads 'YOUR TASK IS DONE'.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "4627", + "name": "Bandit's brew", "examine": "A cheeky little lager from the Bandit Camp.", - "grand_exchange_price": "1133", - "durability": null, - "name": "Bandit's brew", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4627" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1133", - "durability": null, + "shop_price": "650", + "tradeable": "true" + }, + { + "id": "4628", "name": "Bandit's brew", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4628" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "A copy of the engravings found on a mysterious stone tablet.", - "durability": null, + "id": "4654", "name": "Etchings", + "examine": "A copy of the engravings found on a mysterious stone tablet.", "archery_ticket_price": "0", - "id": "4654" + "durability": null }, { - "examine": "A rough translation made from archaeological etchings.", - "durability": null, + "id": "4655", "name": "Translation", - "weight": "1", + "examine": "A rough translation made from archaeological etchings.", "archery_ticket_price": "0", - "id": "4655" + "durability": null, + "weight": "1" }, { - "examine": "This key is unusally warm to the touch.", - "durability": null, + "id": "4656", "name": "Warm key", + "examine": "This key is unusally warm to the touch.", "archery_ticket_price": "0", - "id": "4656" + "durability": null }, { - "examine": "A ring that allows you to see things that are normally invisible.", - "durability": null, - "name": "Ring of visibility", - "archery_ticket_price": "0", "id": "4657", + "name": "Ring of visibility", + "examine": "A ring that allows you to see things that are normally invisible.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "12" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "4658", "name": "Silver pot", - "tradeable": "false", + "examine": "A silver pot made by Ruantun.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4658" + "durability": null, + "tradeable": "false" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", - "durability": null, + "id": "4659", "name": "Blessed pot", - "weight": "2", + "examine": "A silver pot made by Ruantun and blessed on Entrana.", "archery_ticket_price": "0", - "id": "4659" + "durability": null, + "weight": "2" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "4660", "name": "Silver pot", - "tradeable": "false", + "examine": "A silver pot made by Ruantun filled with your blood.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4660" + "durability": null, + "tradeable": "false" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", - "durability": null, + "id": "4661", "name": "Blessed pot", - "weight": "2", + "examine": "A blessed silver pot made by Ruantun filled with your blood.", "archery_ticket_price": "0", - "id": "4661" + "durability": null, + "weight": "2" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "4662", "name": "Silver pot", - "tradeable": "false", + "examine": "A silver pot made by Ruantun filled with blood and garlic.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4662" + "durability": null, + "tradeable": "false" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", - "durability": null, + "id": "4663", "name": "Blessed pot", - "weight": "2", + "examine": "A blessed silver pot filled with blood and garlic.", "archery_ticket_price": "0", - "id": "4663" + "durability": null, + "weight": "2" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "4664", "name": "Silver pot", - "tradeable": "false", + "examine": "A silver pot made by Ruantun filled with blood and spices.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4664" + "durability": null, + "tradeable": "false" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", - "durability": null, + "id": "4665", "name": "Blessed pot", - "weight": "2", + "examine": "A blessed silver pot filled with blood and spices.", "archery_ticket_price": "0", - "id": "4665" + "durability": null, + "weight": "2" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "4666", "name": "Silver pot", - "tradeable": "false", + "examine": "A silver pot made by Ruantun filled with blood, garlic and spices.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "4666" + "durability": null, + "tradeable": "false" }, { - "examine": "A silver pot made by Ruantun and blessed on Entrana.", - "durability": null, + "id": "4667", "name": "Blessed pot", - "weight": "2", + "examine": "A blessed silver pot filled with blood, garlic and spices.", "archery_ticket_price": "0", - "id": "4667" + "durability": null, + "weight": "2" }, { - "examine": "Finely ground garlic powder.", - "durability": null, + "id": "4668", "name": "Garlic powder", + "examine": "Finely ground garlic powder.", "archery_ticket_price": "0", - "id": "4668" + "durability": null }, { - "examine": "The Diamond of Blood.", - "durability": null, + "id": "4670", "name": "Blood diamond", + "examine": "The Diamond of Blood.", "archery_ticket_price": "0", - "id": "4670" + "durability": null }, { - "examine": "The Diamond of Ice.", - "durability": null, + "id": "4671", "name": "Ice diamond", + "examine": "The Diamond of Ice.", "archery_ticket_price": "0", - "id": "4671" + "durability": null }, { - "examine": "The Diamond of Smoke.", - "durability": null, + "id": "4672", "name": "Smoke diamond", - "weight": "1", + "examine": "The Diamond of Smoke.", "archery_ticket_price": "0", - "id": "4672" + "durability": null, + "weight": "1" }, { - "examine": "The Diamond of Shadow.", - "durability": null, + "id": "4673", "name": "Shadow diamond", - "weight": "1", + "examine": "The Diamond of Shadow.", "archery_ticket_price": "0", - "id": "4673" + "durability": null, + "weight": "1" }, { - "examine": "An old and strangely shaped metal cross.", - "durability": null, + "id": "4674", "name": "Gilded cross", + "examine": "An old and strangely shaped metal cross.", "archery_ticket_price": "0", - "id": "4674" + "durability": null }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A magical staff of ancient origin...", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "59300", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4675", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,15,0,2,3,1,15,0,0,50,-1,0,0", - "requirements": "{0,50}-{6,50}", - "shop_price": "80000", - "durability": null, - "weight": "2.25", - "weapon_interface": "1", - "render_anim": "28", - "lendable": "true", - "attack_audios": "2555,0,0,0", - "name": "Ancient staff" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "59300", - "durability": null, "name": "Ancient staff", - "tradeable": "true", + "examine": "A magical staff of ancient origin...", "archery_ticket_price": "0", - "id": "4676" - }, - { - "examine": "It's an amulet of cat speak. It makes vague purring noises.", - "durability": null, - "name": "Catspeak amulet", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4677", - "equipment_slot": "2" - }, - { - "examine": "Varies. See Types", - "durability": null, - "name": "Canopic jar", - "weight": "4", - "archery_ticket_price": "0", - "id": "4678" - }, - { - "examine": "Varies. See Types", - "durability": null, - "name": "Canopic jar", - "weight": "4", - "archery_ticket_price": "0", - "id": "4679" - }, - { - "examine": "Varies. See Types", - "durability": null, - "name": "Canopic jar", - "weight": "4", - "archery_ticket_price": "0", - "id": "4680" - }, - { - "examine": "Varies. See Types", - "durability": null, - "name": "Canopic jar", - "weight": "4", - "archery_ticket_price": "0", - "id": "4681" - }, - { - "shop_price": "300", - "examine": "A blessed holy symbol of Saradomin.", - "grand_exchange_price": "112", - "durability": null, - "name": "Holy symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4682", - "bonuses": "0,0,0,0,0,2,2,2,2,2,3,0,0,8,0" - }, - { - "shop_price": "135", - "examine": "An unholy symbol of Zamorak.", - "grand_exchange_price": "52", - "durability": null, - "name": "Unholy symbol", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4683", - "bonuses": "2,2,2,2,2,0,0,0,0,0,0,0,0,8,0" - }, - { - "shop_price": "30", - "examine": "One sheet of mummy wrap.", - "durability": null, - "name": "Linen", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "4684" - }, - { - "examine": "Little book of embalming by Bod E. Wrapper.", - "durability": null, - "name": "Embalming manual", - "archery_ticket_price": "0", - "id": "4686" - }, - { - "examine": "It's a bucket of sap.", - "durability": null, - "name": "Bucket of sap", - "weight": "2", - "archery_ticket_price": "0", - "id": "4687" - }, - { - "examine": "A little heap of salt.", - "durability": null, - "name": "Pile of salt", - "archery_ticket_price": "0", - "id": "4689" - }, - { - "examine": "Miniature golden statue of a sphinx.", - "durability": null, - "name": "Sphinx's token", - "archery_ticket_price": "0", - "id": "4691" - }, - { - "shop_price": "130000", - "examine": "A very delicate sheet of gold.", - "grand_exchange_price": "132580", - "durability": null, - "name": "Gold leaf", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4692" - }, - { - "examine": "It's a bucket of salty water.", - "durability": null, - "name": "Full bucket", - "archery_ticket_price": "0", - "id": "4693" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined Water and Fire Rune.", - "grand_exchange_price": "48", - "durability": null, - "name": "Steam rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4694" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined air and water rune.", - "grand_exchange_price": "249", - "durability": null, - "name": "Mist rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4695" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined Air and Earth Rune.", - "grand_exchange_price": "20", - "durability": null, - "name": "Dust rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4696" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined Air and Fire Rune.", - "grand_exchange_price": "148", - "durability": null, - "name": "Smoke rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4697" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined Earth and Water rune.", - "grand_exchange_price": "657", - "durability": null, - "name": "Mud rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4698" - }, - { - "shop_price": "11151", - "ge_buy_limit": "25000", - "examine": "A combined earth and fire rune.", - "grand_exchange_price": "49", - "durability": null, - "name": "Lava rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4699" - }, - { - "examine": "You need to add lamp oil before you can use it.", - "durability": null, - "name": "Sapphire lantern", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4700" - }, - { - "examine": "A bullseye lantern with a sapphire for a lens.", - "durability": null, - "name": "Sapphire lantern", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4701" - }, - { - "examine": "A lantern casting a bright blue beam.", - "durability": null, - "name": "Sapphire lantern", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4702" - }, - { - "shop_price": "975000", - "examine": "A magic stone to make high-level furniture.", - "grand_exchange_price": "977755", - "durability": null, - "name": "Magic stone", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "4703" - }, - { - "turn90cw_anim": "821", - "examine": "A magic stone bowl for catching the tears of Guthix.", - "walk_anim": "819", - "durability": null, - "destroy": "true", - "weight": "1.8", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "1", - "equipment_slot": "3", - "stand_anim": "808", - "name": "Stone bowl", - "tradeable": "false", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "4704", - "stand_turn_anim": "823" - }, - { - "examine": "This book must be really old!", - "durability": null, - "name": "Crumbling tome", - "archery_ticket_price": "0", - "id": "4707" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", - "examine": "Ahrim the Blighted's leather hood.", - "durability": null, - "rare_item": "true", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "94300", - "name": "Ahrim's hood", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4708", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "94300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's hood", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4709" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", "attack_anims": "419,419,419,419", - "grand_exchange_price": "69500", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4710", - "stand_turn_anim": "1209", - "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", - "requirements": "{0,70}-{6,70}", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "69500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4711" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", - "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "rare_item": "true", - "weight": "5", - "absorb": "10,5,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1000000", - "name": "Ahrim's robetop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4712", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1000000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's robetop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4713" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", - "examine": "Ahrim the Blighted's armoured robe skirt.", - "durability": null, - "rare_item": "true", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1700000", - "name": "Ahrim's robeskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4714", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1700000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's robeskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4715" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Dharok the Wretched's helm.", - "durability": "1", - "rare_item": "true", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1300000", - "name": "Dharok's helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4716", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1300000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4717" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "rare_item": "true", - "turn90ccw_anim": "822", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "820", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2067,2067,2066,2067", - "grand_exchange_price": "516900", - "stand_anim": "2065", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "4718", - "stand_turn_anim": "823", - "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", - "render_anim": "134", - "name": "Dharok's greataxe" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "516900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's greataxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4719" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "rare_item": "true", - "weight": "9.9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "511400", - "name": "Dharok's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4720", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "511400", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4721" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Dharok the Wretched's plate leg armour.", - "durability": null, - "rare_item": "true", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "748800", - "name": "Dharok's platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4722", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "748800", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4723" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Guthan the Infested's helm", - "durability": null, - "rare_item": "true", - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "396300", - "name": "Guthan's helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4724", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "396300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4725" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "588400", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4726", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "name": "Guthan's warspear" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "588400", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's warspear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4727" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "rare_item": "true", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "316800", - "name": "Guthan's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4728", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "316800", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4729" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Guthan the Infested's chainskirt.", - "durability": null, - "rare_item": "true", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "315600", - "name": "Guthan's chainskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4730", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "315600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's chainskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4731" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", - "examine": "Karil the Tainted's coif.", - "durability": null, - "rare_item": "true", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "38900", - "name": "Karil's coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4732", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "38900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4733" - }, - { - "ge_buy_limit": "1", - "turn90cw_anim": "2076", - "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "rare_item": "true", - "turn90ccw_anim": "2076", "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "2076", - "defence_anim": "424", + "bonuses": "10,-1,40,15,0,2,3,1,15,0,0,50,-1,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", "equipment_slot": "3", - "attack_anims": "2075,2075,2075,2075", - "grand_exchange_price": "188000", - "stand_anim": "2074", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,50}-{6,50}", + "run_anim": "1210", + "shop_price": "80000", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "run_anim": "2077", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.25" + }, + { + "id": "4676", + "name": "Ancient staff", "archery_ticket_price": "0", - "id": "4734", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4677", + "name": "Catspeak amulet", + "examine": "It's an amulet of cat speak. It makes vague purring noises.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "1.3" + }, + { + "id": "4678", + "name": "Canopic jar", + "examine": "Varies. See Types", + "archery_ticket_price": "0", + "durability": null, + "weight": "4" + }, + { + "id": "4679", + "name": "Canopic jar", + "examine": "Varies. See Types", + "archery_ticket_price": "0", + "durability": null, + "weight": "4" + }, + { + "id": "4680", + "name": "Canopic jar", + "examine": "Varies. See Types", + "archery_ticket_price": "0", + "durability": null, + "weight": "4" + }, + { + "id": "4681", + "name": "Canopic jar", + "examine": "Varies. See Types", + "archery_ticket_price": "0", + "durability": null, + "weight": "4" + }, + { + "id": "4682", + "name": "Holy symbol", + "examine": "A blessed holy symbol of Saradomin.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,2,2,2,2,2,3,0,0,8,0", + "durability": null, + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "4683", + "name": "Unholy symbol", + "examine": "An unholy symbol of Zamorak.", + "archery_ticket_price": "0", + "bonuses": "2,2,2,2,2,0,0,0,0,0,0,0,0,8,0", + "durability": null, + "shop_price": "135", + "tradeable": "true" + }, + { + "id": "4684", + "name": "Linen", + "examine": "One sheet of mummy wrap.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "30", + "weight": "0.9" + }, + { + "id": "4686", + "name": "Embalming manual", + "examine": "Little book of embalming by Bod E. Wrapper.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4687", + "name": "Bucket of sap", + "examine": "It's a bucket of sap.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "4689", + "name": "Pile of salt", + "examine": "A little heap of salt.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4691", + "name": "Sphinx's token", + "examine": "Miniature golden statue of a sphinx.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4692", + "name": "Gold leaf", + "examine": "A very delicate sheet of gold.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "130000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4693", + "name": "Full bucket", + "examine": "It's a bucket of salty water.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4694", + "name": "Steam rune", + "examine": "A combined Water and Fire Rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4695", + "name": "Mist rune", + "examine": "A combined air and water rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4696", + "name": "Dust rune", + "examine": "A combined Air and Earth Rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4697", + "name": "Smoke rune", + "examine": "A combined Air and Fire Rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4698", + "name": "Mud rune", + "examine": "A combined Earth and Water rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4699", + "name": "Lava rune", + "examine": "A combined earth and fire rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "11151", + "tradeable": "true" + }, + { + "id": "4700", + "name": "Sapphire lantern", + "examine": "You need to add lamp oil before you can use it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "4701", + "name": "Sapphire lantern", + "examine": "A bullseye lantern with a sapphire for a lens.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "4702", + "name": "Sapphire lantern", + "examine": "A lantern casting a bright blue beam.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "4703", + "name": "Magic stone", + "examine": "Doesn't look very special.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "false", + "weight": "2.267" + }, + { + "id": "4704", + "name": "Stone bowl", + "examine": "A magic stone bowl for catching the tears of Guthix.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "render_anim": "1", + "run_anim": "824", + "stand_anim": "808", "stand_turn_anim": "823", - "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", - "render_anim": "372", - "attack_audios": "2700,0,0,0", - "name": "Karil's crossbow" + "tradeable": "false", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weight": "1.8" }, { - "ge_buy_limit": "1", - "grand_exchange_price": "188000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's crossbow", - "tradeable": "true", + "id": "4707", + "name": "Crumbling tome", + "examine": "This book must be really old!", "archery_ticket_price": "0", - "id": "4735" + "durability": null }, { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "10000", - "examine": "Karil the Tainted's leather body armour.", + "id": "4708", + "name": "Ahrim's hood", + "examine": "Ahrim the Blighted's leather hood.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", "rare_item": "true", - "weight": "6", - "absorb": "0,10,5", + "remove_head": "true", + "requirements": "{1,70}-{6,70}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4709", + "name": "Ahrim's hood", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4710", + "name": "Ahrim's staff", + "examine": "Ahrim the Blighted's quarterstaff.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "4711", + "name": "Ahrim's staff", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4712", + "name": "Ahrim's robetop", + "examine": "Ahrim the Blighted's armoured robe top.", + "absorb": "10,5,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", + "durability": null, "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1800000", - "name": "Karil's leathertop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4736", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1800000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's leathertop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4737" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "10000", - "examine": "Karil the Tainted's leather skirt.", - "durability": null, + "ge_buy_limit": "10", "rare_item": "true", - "weight": "2", - "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "217000", - "name": "Karil's leatherskirt", + "remove_sleeves": "true", + "requirements": "{1,70}-{6,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4738", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" + "weight": "5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "217000", + "id": "4713", + "name": "Ahrim's robetop", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's leatherskirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4739" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "4714", + "name": "Ahrim's robeskirt", + "examine": "Ahrim the Blighted's armoured robe skirt.", + "absorb": "7,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,70}-{6,70}", + "tradeable": "true", + "weight": "12" + }, + { + "id": "4715", + "name": "Ahrim's robeskirt", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4716", + "name": "Dharok's helm", + "examine": "Dharok the Wretched's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", + "durability": "1", + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_head": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "4717", + "name": "Dharok's helm", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4718", + "name": "Dharok's greataxe", + "examine": "Dharok the Wretched's greataxe.", + "archery_ticket_price": "0", + "attack_anims": "2067,2067,2066,2067", + "attack_speed": "7", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "134", + "requirements": "{0,70}-{2,70}", + "run_anim": "824", + "stand_anim": "2065", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" + }, + { + "id": "4719", + "name": "Dharok's greataxe", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4720", + "name": "Dharok's platebody", + "examine": "Dharok the Wretched's platebody armour.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9.9" + }, + { + "id": "4721", + "name": "Dharok's platebody", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4722", + "name": "Dharok's platelegs", + "examine": "Dharok the Wretched's plate leg armour.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "10" + }, + { + "id": "4723", + "name": "Dharok's platelegs", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4724", + "name": "Guthan's helm", + "examine": "Guthan the Infested's helm", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "4725", + "name": "Guthan's helm", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4726", + "name": "Guthan's warspear", + "examine": "Guthan the Infested's warspear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "4727", + "name": "Guthan's warspear", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4728", + "name": "Guthan's platebody", + "examine": "Guthan the Infested's platebody armour.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "4729", + "name": "Guthan's platebody", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4730", + "name": "Guthan's chainskirt", + "examine": "Guthan the Infested's chainskirt.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "8" + }, + { + "id": "4731", + "name": "Guthan's chainskirt", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4732", + "name": "Karil's coif", + "examine": "Karil the Tainted's coif.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "1", + "rare_item": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true" + }, + { + "id": "4733", + "name": "Karil's coif", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "4734", + "name": "Karil's crossbow", + "examine": "Karil the Tainted's repeating crossbow.", + "archery_ticket_price": "0", + "attack_anims": "2075,2075,2075,2075", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1", + "rare_item": "true", + "render_anim": "372", + "requirements": "{4,70}", + "run_anim": "2077", + "stand_anim": "2074", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" + }, + { + "id": "4735", + "name": "Karil's crossbow", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "4736", + "name": "Karil's leathertop", + "examine": "Karil the Tainted's leather body armour.", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "10000", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "4737", + "name": "Karil's leathertop", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4738", + "name": "Karil's leatherskirt", + "examine": "Karil the Tainted's leather skirt.", + "absorb": "0,7,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "ge_buy_limit": "10000", + "rare_item": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "4739", + "name": "Karil's leatherskirt", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4740", + "name": "Bolt rack", + "examine": "Must need a special type of crossbow to use this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,55", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,70}", "shop_price": "420", - "ge_buy_limit": "10000", - "examine": "Must need a special type of crossbow to use this.", - "grand_exchange_price": "238", - "durability": null, - "name": "Bolt rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4740", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,55", - "equipment_slot": "13" + "tradeable": "true" }, { + "id": "4745", + "name": "Torag's helm", + "examine": "Torag the Corrupted's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "rare_item": "true", - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "139000", - "name": "Torag's helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4745", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" + "weight": "5" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "139000", + "id": "4746", + "name": "Torag's helm", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4746" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{0,70}-{2,70}", - "ge_buy_limit": "1000", + "id": "4747", + "name": "Torag's hammers", "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "rare_item": "true", - "weight": "3", + "archery_ticket_price": "0", + "attack_anims": "2068,2068,2068,2068", + "attack_audios": "1332,0,0,0", "attack_speed": "5", + "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "rare_item": "true", + "requirements": "{0,70}-{2,70}", + "tradeable": "true", "two_handed": "true", "weapon_interface": "10", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2068,2068,2068,2068", - "grand_exchange_price": "111600", - "attack_audios": "1332,0,0,0", - "name": "Torag's hammers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4747", - "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0" + "weight": "3" }, { + "id": "4748", + "name": "Torag's hammers", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "111600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's hammers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4748" + "tradeable": "true" }, { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "rare_item": "true", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "346200", - "name": "Torag's platebody", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4749", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "346200", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Torag's platebody", - "tradeable": "true", + "examine": "Torag the Corrupted's platebody armour.", + "absorb": "5,0,10", "archery_ticket_price": "0", - "id": "4750" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Torag the Corrupted's plate leg armour.", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", "rare_item": "true", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "427200", - "name": "Torag's platelegs", + "remove_sleeves": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4751", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "weight": "9" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "427200", + "id": "4750", + "name": "Torag's platebody", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's platelegs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4752" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "4751", + "name": "Torag's platelegs", + "examine": "Torag the Corrupted's plate leg armour.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "4752", + "name": "Torag's platelegs", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4753", + "name": "Verac's helm", + "examine": "Verac the Defiled's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", + "rare_item": "true", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Verac the Defiled's helm.", - "durability": null, - "rare_item": "true", - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "687200", - "name": "Verac's helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4753", - "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0" + "weight": "1" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "687200", + "id": "4754", + "name": "Verac's helm", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Verac's helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4754" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "ge_buy_limit": "10", - "turn90cw_anim": "2060", - "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "rare_item": "true", - "turn90ccw_anim": "2060", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "2060", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2062,2062,2062,2062", - "grand_exchange_price": "135500", - "stand_anim": "1832", - "tradeable": "true", - "run_anim": "1831", - "archery_ticket_price": "0", "id": "4755", - "stand_turn_anim": "823", - "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", - "render_anim": "1426", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "135500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Verac's flail", - "tradeable": "true", + "examine": "Verac the Defiled's flail.", "archery_ticket_price": "0", - "id": "4756" + "attack_anims": "2062,2062,2062,2062", + "attack_audios": "1323,0,0,0", + "attack_speed": "5", + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "1426", + "requirements": "{0,70}", + "run_anim": "1831", + "stand_anim": "1832", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" }, { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Verac the Defiled's brassard.", - "durability": null, - "rare_item": "true", - "weight": "5", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "270000", - "name": "Verac's brassard", - "tradeable": "true", + "id": "4756", + "name": "Verac's flail", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4757", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "270000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Verac's brassard", - "tradeable": "true", + "examine": "Verac the Defiled's brassard.", + "absorb": "5,0,10", "archery_ticket_price": "0", - "id": "4758" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", - "examine": "Verac the Defiled's plate skirt.", + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "10", "rare_item": "true", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "411400", - "name": "Verac's plateskirt", + "remove_sleeves": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4759", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" + "weight": "5" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "411400", + "id": "4758", + "name": "Verac's brassard", "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4759", "name": "Verac's plateskirt", - "tradeable": "true", + "examine": "Verac the Defiled's plate skirt.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "4760" + "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt bronze arrow...ouch", - "grand_exchange_price": "39", - "durability": null, - "name": "Bronze brutal", - "tradeable": "true", + "id": "4760", + "name": "Verac's plateskirt", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4773", + "name": "Bronze brutal", + "examine": "Blunt bronze arrow...ouch", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,11", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt iron arrow...ouch", - "grand_exchange_price": "14", - "durability": null, - "name": "Iron brutal", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4778", + "name": "Iron brutal", + "examine": "Blunt iron arrow...ouch", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,13", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt steel arrow...ouch", - "grand_exchange_price": "20", - "durability": null, - "name": "Steel brutal", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4783", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,19", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt black arrow...ouch", - "grand_exchange_price": "63", - "durability": null, - "name": "Black brutal", - "tradeable": "true", + "name": "Steel brutal", + "examine": "Blunt steel arrow...ouch", "archery_ticket_price": "0", - "id": "4788", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,19", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true" }, { + "id": "4788", + "name": "Black brutal", + "examine": "Blunt black arrow...ouch", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true" + }, + { + "id": "4793", + "name": "Mithril brutal", + "examine": "Blunt mithril arrow...ouch", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,34", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", "requirements": "{4,30}", "shop_price": "20", - "ge_buy_limit": "1000", - "examine": "Blunt mithril arrow...ouch", - "grand_exchange_price": "80", - "durability": null, - "name": "Mithril brutal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4793", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,34", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt adamant arrow...ouch", - "grand_exchange_price": "154", - "durability": null, - "name": "Adamant brutal", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4798", + "name": "Adamant brutal", + "examine": "Blunt adamant arrow...ouch", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,45", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "shop_price": "180", + "durability": null, + "equipment_slot": "13", "ge_buy_limit": "1000", - "examine": "Blunt rune arrow...ouch", - "grand_exchange_price": "188", - "durability": null, - "name": "Rune brutal", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{4,30}", + "tradeable": "true" + }, + { "id": "4803", + "name": "Rune brutal", + "examine": "Blunt rune arrow...ouch", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "examine": "A very black prism.", "durability": null, - "name": "Black prism", - "archery_ticket_price": "0", - "id": "4808" - }, - { - "examine": "A half torn necromantic page.", - "durability": null, - "name": "Torn page", - "archery_ticket_price": "0", - "id": "4809" - }, - { - "examine": "It's about to fall apart.", - "durability": null, - "name": "Ruined backpack", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "4810" - }, - { - "examine": "A white ceramic mug with a dragon insignia.", - "durability": null, - "name": "Dragon inn tankard", - "archery_ticket_price": "0", - "id": "4811" - }, - { - "ge_buy_limit": "10000", - "examine": "A pile of Zombie Ogre bones.", - "grand_exchange_price": "1167", - "durability": null, - "name": "Zogre bones", - "tradeable": "true", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "4812" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1167", - "durability": null, - "name": "Zogre bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4813" - }, - { - "examine": "A classic realist charcoal portrait of Sithik.", - "durability": null, - "name": "Sithik portrait", - "archery_ticket_price": "0", - "id": "4814" - }, - { - "examine": "A classic realist charcoal portrait of Sithik.", - "durability": null, - "name": "Sithik portrait", - "archery_ticket_price": "0", - "id": "4815" - }, - { - "examine": "A signed classic realist charcoal portrait of Sithik.", - "durability": null, - "name": "Signed portrait", - "archery_ticket_price": "0", - "id": "4816" - }, - { - "examine": "A book explaining the art of portraiture.", - "durability": null, - "name": "Book of portraiture", - "weight": "1", - "archery_ticket_price": "0", - "id": "4817", - "equipment_slot": "5" - }, - { - "examine": "An ancient ogre artefact-resembling a large heavy helm.", - "durability": null, - "name": "Ogre artefact", - "archery_ticket_price": "0", - "id": "4818" - }, - { - "shop_price": "4", - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "16", - "durability": null, - "name": "Bronze nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4819" - }, - { - "shop_price": "33", - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "16", - "durability": null, - "name": "Iron nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4820" - }, - { - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "13", - "durability": null, - "name": "Black nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4821" - }, - { - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "35", - "durability": null, - "name": "Mithril nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4822" - }, - { - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "87", - "durability": null, - "name": "Adamantite nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4823" - }, - { - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "114", - "durability": null, - "name": "Rune nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4824" - }, - { - "ge_buy_limit": "100", - "examine": "An unstrung composite ogre bow.", - "grand_exchange_price": "447", - "durability": null, - "name": "Unstrung comp bow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4825" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "447", - "durability": null, - "name": "Unstrung comp bow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4826" - }, - { + "equipment_slot": "13", + "ge_buy_limit": "1000", "requirements": "{4,30}", "shop_price": "180", - "ge_buy_limit": "100", - "examine": "A composite ogre bow.", + "tradeable": "true" + }, + { + "id": "4808", + "name": "Black prism", + "examine": "A very black prism.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4809", + "name": "Torn page", + "examine": "A half torn necromantic page.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4810", + "name": "Ruined backpack", + "examine": "It's about to fall apart.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, - "weight": "1.8", + "tradeable": "false" + }, + { + "id": "4811", + "name": "Dragon inn tankard", + "examine": "A white ceramic mug with a dragon insignia.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4812", + "name": "Zogre bones", + "examine": "A pile of Zombie Ogre bones.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "4813", + "name": "Zogre bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4814", + "name": "Sithik portrait", + "examine": "A classic realist charcoal portrait of Sithik.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4815", + "name": "Sithik portrait", + "examine": "A classic realist charcoal portrait of Sithik.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4816", + "name": "Signed portrait", + "examine": "A signed classic realist charcoal portrait of Sithik.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4817", + "name": "Book of portraiture", + "examine": "A book explaining the art of portraiture.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "1" + }, + { + "id": "4818", + "name": "Ogre artefact", + "examine": "An ancient ogre artefact-resembling a large heavy helm.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "4819", + "name": "Bronze nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "4", + "tradeable": "true" + }, + { + "id": "4820", + "name": "Iron nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "33", + "tradeable": "true" + }, + { + "id": "4821", + "name": "Black nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4822", + "name": "Mithril nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4823", + "name": "Adamantite nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4824", + "name": "Rune nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4825", + "name": "Unstrung comp bow", + "examine": "An unstrung composite ogre bow.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4826", + "name": "Unstrung comp bow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4827", + "name": "Comp ogre bow", + "examine": "A composite ogre bow.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "2700,0,0,0", "attack_speed": "5", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,30}", + "shop_price": "180", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "225", - "attack_audios": "2700,0,0,0", - "name": "Comp ogre bow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4827", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,0" + "weight": "1.8" }, { + "id": "4828", + "name": "Comp ogre bow", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "225", - "durability": null, - "name": "Comp ogre bow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4828" + "tradeable": "true" }, { - "examine": "A book of H.A.M. affiliation", - "durability": null, - "name": "Book of 'h.a.m", - "archery_ticket_price": "0", "id": "4829", + "name": "Book of 'h.a.m", + "examine": "A book of H.A.M. affiliation", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "5" }, { - "ge_buy_limit": "10000", - "examine": "Ancient ogre bones from the ogre burial tomb.", - "grand_exchange_price": "5931", - "durability": null, + "id": "4830", "name": "Fayrg bones", - "tradeable": "true", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "4830" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "5931", - "durability": null, - "name": "Fayrg bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4831" - }, - { - "ge_buy_limit": "10000", "examine": "Ancient ogre bones from the ogre burial tomb.", - "grand_exchange_price": "7093", - "durability": null, - "name": "Raurg bones", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "4832" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "7093", - "durability": null, - "name": "Raurg bones", - "tradeable": "true", + "id": "4831", + "name": "Fayrg bones", "archery_ticket_price": "0", - "id": "4833" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { + "id": "4832", + "name": "Raurg bones", + "examine": "Ancient ogre bones from the ogre burial tomb.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "4833", + "name": "Raurg bones", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4834", + "name": "Ourg bones", "examine": "Ancient ogre bones from the burial tomb.", - "grand_exchange_price": "16300", - "durability": null, - "name": "Ourg bones", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "4834" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "16300", - "durability": null, - "name": "Ourg bones", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4835" + "weight": "0.8" }, { - "examine": "Some strange liquid given to you by Zavistic Rarve.", + "id": "4835", + "name": "Ourg bones", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "4836", "name": "Strange potion", + "examine": "Some strange liquid given to you by Zavistic Rarve.", "archery_ticket_price": "0", - "id": "4836" + "durability": null }, { - "examine": "A book of necromantic spells.", - "durability": null, - "name": "Necromancy book", - "archery_ticket_price": "0", "id": "4837", + "name": "Necromancy book", + "examine": "A book of necromantic spells.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "5" }, { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, + "id": "4838", "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", + "examine": "A nice cup of tea.", "archery_ticket_price": "0", - "id": "4838" + "durability": null, + "shop_price": "10", + "tradeable": "true", + "weight": "0.1" }, { - "examine": "A key to some sort of special tomb area.", - "durability": null, + "id": "4839", "name": "Ogre gate key", + "examine": "A key to some sort of special tomb area.", "archery_ticket_price": "0", - "id": "4839" + "durability": null }, { - "ge_buy_limit": "100", + "id": "4840", + "name": "Rogue's purse potion(unf)", "examine": "I need another ingredient to finish this rogue's purse potion.", - "grand_exchange_price": "2194", - "durability": null, - "name": "Rogue's purse potion(unf)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4840" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "2194", - "durability": null, - "name": "Rogue's purse potion(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4841" + "tradeable": "true" }, { - "shop_price": "200", - "ge_buy_limit": "1000", + "id": "4841", + "name": "Rogue's purse potion(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4842", + "name": "Relicym's balm(4)", "examine": "4 doses of Relicym's balm, which helps cure disease.", - "grand_exchange_price": "1430", - "durability": null, - "name": "Relicym's balm(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4842" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "1430", - "durability": null, - "name": "Relicym's balm(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4843" - }, - { "shop_price": "200", + "tradeable": "true" + }, + { + "id": "4843", + "name": "Relicym's balm(4)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4844", + "name": "Relicym's balm(3)", "examine": "3 doses of Relicym's balm, which helps cure disease.", - "grand_exchange_price": "654", - "durability": null, - "name": "Relicym's balm(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4844" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "654", - "durability": null, - "name": "Relicym's balm(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4845" - }, - { "shop_price": "200", + "tradeable": "true" + }, + { + "id": "4845", + "name": "Relicym's balm(3)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4846", + "name": "Relicym's balm(2)", "examine": "2 doses of Relicym's balm, which helps cure disease.", - "grand_exchange_price": "325", - "durability": null, - "name": "Relicym's balm(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4846" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "325", - "durability": null, - "name": "Relicym's balm(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4847" - }, - { "shop_price": "200", + "tradeable": "true" + }, + { + "id": "4847", + "name": "Relicym's balm(2)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4848", + "name": "Relicym's balm(1)", "examine": "1 dose of Relicym's balm, which helps cure disease.", - "grand_exchange_price": "204", - "durability": null, - "name": "Relicym's balm(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4848" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "204", - "durability": null, - "name": "Relicym's balm(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4849" + "shop_price": "200", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "4849", + "name": "Relicym's balm(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4850", + "name": "Ogre coffin key", "examine": "A key which opens a coffin!", - "grand_exchange_price": "1963", - "durability": null, - "name": "Ogre coffin key", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4850" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1963", - "durability": null, + "tradeable": "true" + }, + { + "id": "4851", "name": "Ogre coffin key", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4851" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "4852", + "name": "Bonemeal", "examine": "A pot of crushed fayrg bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4852" + "durability": null, + "weight": "1" }, { + "id": "4853", + "name": "Bonemeal", "examine": "A pot of crushed raurg bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4853" + "durability": null, + "weight": "1" }, { + "id": "4854", + "name": "Bonemeal", "examine": "A pot of crushed ourg bones.", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4854" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4855", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed gorilla monkey bones.", "archery_ticket_price": "0", - "id": "4855" + "durability": null, + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "grand_exchange_price": "77678", - "durability": null, - "name": "Ahrim's hood 100", - "weight": "0.9", - "archery_ticket_price": "0", "id": "4856", + "name": "Ahrim's hood 100", + "examine": "Ahrim the Blighted's leather hood.", "absorb": "5,2,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "grand_exchange_price": "77678", - "durability": null, - "name": "Ahrim's hood 75", - "weight": "0.9", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4857", + "name": "Ahrim's hood 75", + "examine": "Ahrim the Blighted's leather hood.", "absorb": "5,2,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "grand_exchange_price": "77678", - "durability": null, - "name": "Ahrim's hood 50", - "weight": "0.9", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4858", + "name": "Ahrim's hood 50", + "examine": "Ahrim the Blighted's leather hood.", "absorb": "5,2,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "grand_exchange_price": "77678", - "durability": null, - "name": "Ahrim's hood 25", - "weight": "0.9", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4859", - "absorb": "5,2,0", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", + "name": "Ahrim's hood 25", "examine": "Ahrim the Blighted's leather hood.", - "durability": null, - "weight": "0.9", "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", + "durability": null, "equipment_slot": "0", - "grand_exchange_price": "51700", - "name": "Ahrim's hood 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4860" + "remove_head": "true", + "requirements": "{1,70}-{6,70}", + "weight": "0.9" }, { + "id": "4860", + "name": "Ahrim's hood 0", + "examine": "Ahrim the Blighted's leather hood.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "ge_buy_limit": "10", - "grand_exchange_price": "51700", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's hood 0", + "remove_head": "true", + "requirements": "{1,70}-{6,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4861" + "weight": "0.9" }, { - "requirements": "{0,70}-{6,70}", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 100", - "run_anim": "1210", + "id": "4861", + "name": "Ahrim's hood 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4862", + "name": "Ahrim's staff 100", + "examine": "Ahrim the Blighted's quarterstaff.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "requirements": "{0,70}-{6,70}", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 75", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4863", + "name": "Ahrim's staff 75", + "examine": "Ahrim the Blighted's quarterstaff.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "requirements": "{0,70}-{6,70}", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 50", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4864", - "stand_turn_anim": "1209", - "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0" - }, - { - "requirements": "{0,70}-{6,70}", - "turn90cw_anim": "1207", + "name": "Ahrim's staff 50", "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 25", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4865", - "stand_turn_anim": "1209", - "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0" - }, - { - "requirements": "{0,70}-{6,70}", - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", "attack_speed": "6", + "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "50600", - "stand_anim": "813", + "weight": "2" + }, + { + "id": "4865", + "name": "Ahrim's staff 25", + "examine": "Ahrim the Blighted's quarterstaff.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 0", - "tradeable": "true", + "attack_speed": "6", + "bonuses": "12,-1,65,15,0,3,5,2,15,0,0,68,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", "run_anim": "1210", - "archery_ticket_price": "0", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { "id": "4866", - "stand_turn_anim": "1209" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "50600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Ahrim's staff 0", - "tradeable": "true", + "examine": "Ahrim the Blighted's quarterstaff.", "archery_ticket_price": "0", - "id": "4867" + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,70}-{6,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "requirements": "{1,70}-{6,70}", - "remove_sleeves": "true", - "examine": "Ahrim the Blighted's armoured robe top.", - "grand_exchange_price": "1458307", - "durability": null, - "name": "Ahrim's top 100", - "weight": "5", + "id": "4867", + "name": "Ahrim's staff 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4868", + "name": "Ahrim's top 100", + "examine": "Ahrim the Blighted's armoured robe top.", "absorb": "10,5,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{6,70}", + "weight": "5" }, { - "requirements": "{1,70}-{6,70}", - "remove_sleeves": "true", - "examine": "Ahrim the Blighted's armoured robe top.", - "grand_exchange_price": "1458307", - "durability": null, - "name": "Ahrim's top 75", - "weight": "5", - "archery_ticket_price": "0", "id": "4869", + "name": "Ahrim's top 75", + "examine": "Ahrim the Blighted's armoured robe top.", "absorb": "10,5,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{6,70}", + "weight": "5" }, { - "requirements": "{1,70}-{6,70}", - "remove_sleeves": "true", - "examine": "Ahrim the Blighted's armoured robe top.", - "grand_exchange_price": "1458307", - "durability": null, - "name": "Ahrim's top 50", - "weight": "5", - "archery_ticket_price": "0", "id": "4870", + "name": "Ahrim's top 50", + "examine": "Ahrim the Blighted's armoured robe top.", "absorb": "10,5,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{6,70}", + "weight": "5" }, { - "requirements": "{1,70}-{6,70}", - "remove_sleeves": "true", - "examine": "Ahrim the Blighted's armoured robe top.", - "grand_exchange_price": "1458307", - "durability": null, - "name": "Ahrim's top 25", - "weight": "5", - "archery_ticket_price": "0", "id": "4871", - "absorb": "10,5,0", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", + "name": "Ahrim's top 25", "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "weight": "5", "absorb": "10,5,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "1100000", - "name": "Ahrim's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4872" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1100000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4873" - }, - { "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's armoured robe skirt.", - "grand_exchange_price": "1561591", - "durability": null, - "name": "Ahrim's skirt 100", - "weight": "12", + "weight": "5" + }, + { + "id": "4872", + "name": "Ahrim's top 0", + "examine": "Ahrim the Blighted's armoured robe top.", + "absorb": "10,5,0", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,70}-{6,70}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "4873", + "name": "Ahrim's top 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4874", + "name": "Ahrim's skirt 100", + "examine": "Ahrim the Blighted's armoured robe skirt.", "absorb": "7,3,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "requirements": "{1,70}-{6,70}", + "weight": "12" }, { - "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's armoured robe skirt.", - "grand_exchange_price": "1561591", - "durability": null, - "name": "Ahrim's skirt 75", - "weight": "12", - "archery_ticket_price": "0", "id": "4875", + "name": "Ahrim's skirt 75", + "examine": "Ahrim the Blighted's armoured robe skirt.", "absorb": "7,3,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "requirements": "{1,70}-{6,70}", + "weight": "12" }, { - "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's armoured robe skirt.", - "grand_exchange_price": "1561591", - "durability": null, - "name": "Ahrim's skirt 50", - "weight": "12", - "archery_ticket_price": "0", "id": "4876", + "name": "Ahrim's skirt 50", + "examine": "Ahrim the Blighted's armoured robe skirt.", "absorb": "7,3,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "requirements": "{1,70}-{6,70}", + "weight": "12" }, { - "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's armoured robe skirt.", - "grand_exchange_price": "1561591", - "durability": null, - "name": "Ahrim's skirt 25", - "weight": "12", - "archery_ticket_price": "0", "id": "4877", - "absorb": "7,3,0", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", + "name": "Ahrim's skirt 25", "examine": "Ahrim the Blighted's armoured robe skirt.", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Ahrim's skirt 0", - "tradeable": "true", - "weight": "12", - "archery_ticket_price": "0", - "id": "4878", "absorb": "7,3,0", - "equipment_slot": "7" + "archery_ticket_price": "0", + "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,70}-{6,70}", + "weight": "12" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "1500000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4878", "name": "Ahrim's skirt 0", - "tradeable": "true", + "examine": "Ahrim the Blighted's armoured robe skirt.", + "absorb": "7,3,0", "archery_ticket_price": "0", - "id": "4879" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}-{6,70}", + "tradeable": "true", + "weight": "12" }, { - "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", + "id": "4879", + "name": "Ahrim's skirt 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4880", + "name": "Dharok's helm 100", + "examine": "Dharok the Wretched's helm.", "absorb": "2,0,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equip_audio": "2240", + "equipment_slot": "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", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4881", + "name": "Dharok's helm 75", + "examine": "Dharok the Wretched's helm.", "absorb": "2,0,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equip_audio": "2240", + "equipment_slot": "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", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4882", + "name": "Dharok's helm 50", + "examine": "Dharok the Wretched's helm.", "absorb": "2,0,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equip_audio": "2240", + "equipment_slot": "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", - "archery_ticket_price": "0", + "weight": "1" + }, + { "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" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's helm 25", "examine": "Dharok the Wretched's helm.", - "durability": null, - "weight": "1", "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "1300000", - "name": "Dharok's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4884" + "remove_head": "true", + "requirements": "{1,70}", + "weight": "1" }, { + "id": "4884", + "name": "Dharok's helm 0", + "examine": "Dharok the Wretched's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "ge_buy_limit": "10", - "grand_exchange_price": "1300000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's helm 0", + "remove_head": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4885" + "weight": "1" }, { - "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", + "id": "4885", + "name": "Dharok's helm 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4886", + "name": "Dharok's axe 100", + "examine": "Dharok the Wretched's greataxe.", + "archery_ticket_price": "0", + "attack_anims": "2067,2067,2066,2067", + "attack_audios": "1320,0,0,0", + "attack_speed": "7", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "render_anim": "134", + "requirements": "{0,70}-{2,70}", + "run_anim": "824", + "stand_anim": "2065", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "turn180_anim": "2064", + "turn90ccw_anim": "2064", + "turn90cw_anim": "2064", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" }, { - "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", + "name": "Dharok's axe 75", + "examine": "Dharok the Wretched's greataxe.", + "archery_ticket_price": "0", + "attack_anims": "2067,2067,2066,2067", + "attack_audios": "1320,0,0,0", + "attack_speed": "7", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "render_anim": "134", + "requirements": "{0,70}-{2,70}", + "run_anim": "824", + "stand_anim": "2065", "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0" + "turn180_anim": "2064", + "turn90ccw_anim": "2064", + "turn90cw_anim": "2064", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" }, { - "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" - }, - { - "requirements": "{0,70}-{2,70}", - "turn90cw_anim": "2064", + "name": "Dharok's axe 50", "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", + "archery_ticket_price": "0", "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" - }, - { - "requirements": "{0,70}-{2,70}", - "ge_buy_limit": "10", - "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", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", "render_anim": "134", - "equipment_slot": "3", - "grand_exchange_price": "442600", - "stand_anim": "2065", - "attack_audios": "1320,0,0,0", - "tradeable": "true", - "name": "Dharok's axe 0", + "requirements": "{0,70}-{2,70}", "run_anim": "824", + "stand_anim": "2065", + "stand_turn_anim": "823", + "turn180_anim": "2064", + "turn90ccw_anim": "2064", + "turn90cw_anim": "2064", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" + }, + { + "id": "4889", + "name": "Dharok's axe 25", + "examine": "Dharok the Wretched's greataxe.", "archery_ticket_price": "0", + "attack_anims": "2067,2067,2066,2067", + "attack_audios": "1320,0,0,0", + "attack_speed": "7", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "render_anim": "134", + "requirements": "{0,70}-{2,70}", + "run_anim": "824", + "stand_anim": "2065", + "stand_turn_anim": "823", + "turn180_anim": "2064", + "turn90ccw_anim": "2064", + "turn90cw_anim": "2064", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" + }, + { "id": "4890", - "stand_turn_anim": "823" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "442600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Dharok's axe 0", - "tradeable": "true", + "examine": "Dharok the Wretched's greataxe.", "archery_ticket_price": "0", - "id": "4891" + "attack_audios": "1320,0,0,0", + "attack_speed": "7", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "134", + "requirements": "{0,70}-{2,70}", + "run_anim": "824", + "stand_anim": "2065", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "2064", + "turn90ccw_anim": "2064", + "turn90cw_anim": "2064", + "two_handed": "true", + "walk_anim": "2064", + "weapon_interface": "2", + "weight": "13" }, { - "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", + "id": "4891", + "name": "Dharok's axe 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4892", + "name": "Dharok's body 100", + "examine": "Dharok the Wretched's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9.9" }, { - "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", - "archery_ticket_price": "0", "id": "4893", + "name": "Dharok's body 75", + "examine": "Dharok the Wretched's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9.9" }, { - "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", - "archery_ticket_price": "0", "id": "4894", + "name": "Dharok's body 50", + "examine": "Dharok the Wretched's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9.9" }, { - "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", - "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" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's body 25", "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "weight": "9.9", "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "438200", - "name": "Dharok's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4896" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "438200", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dharok's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4897" - }, - { "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "897451", - "durability": null, - "name": "Dharok's legs 100", - "weight": "10", + "weight": "9.9" + }, + { + "id": "4896", + "name": "Dharok's body 0", + "examine": "Dharok the Wretched's platebody armour.", + "absorb": "5,0,10", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9.9" + }, + { + "id": "4897", + "name": "Dharok's body 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4898", + "name": "Dharok's legs 100", + "examine": "Dharok the Wretched's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "897451", - "durability": null, - "name": "Dharok's legs 75", - "weight": "10", - "archery_ticket_price": "0", "id": "4899", + "name": "Dharok's legs 75", + "examine": "Dharok the Wretched's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "897451", - "durability": null, - "name": "Dharok's legs 50", - "weight": "10", - "archery_ticket_price": "0", "id": "4900", + "name": "Dharok's legs 50", + "examine": "Dharok the Wretched's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "897451", - "durability": null, - "name": "Dharok's legs 25", - "weight": "10", - "archery_ticket_price": "0", "id": "4901", - "absorb": "3,0,7", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's legs 25", "examine": "Dharok the Wretched's plate leg armour.", - "grand_exchange_price": "676000", - "durability": null, - "name": "Dharok's legs 0", - "tradeable": "true", - "weight": "10", + "absorb": "3,0,7", "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" + }, + { "id": "4902", - "absorb": "3,0,7", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "676000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Dharok's legs 0", - "tradeable": "true", + "examine": "Dharok the Wretched's plate leg armour.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "4903" + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "10" }, { - "remove_head": "true", - "requirements": "{1,70}", - "examine": "Guthan the Infested's helm", - "durability": null, - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "232455", - "name": "Guthan's helm 100", + "id": "4903", + "name": "Dharok's legs 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4904", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Guthan's helm 100", + "examine": "Guthan the Infested's helm", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm", - "durability": null, - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "232455", - "name": "Guthan's helm 75", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "4905", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Guthan's helm 75", + "examine": "Guthan the Infested's helm", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm", - "durability": null, - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "232455", - "name": "Guthan's helm 50", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "4906", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Guthan's helm 50", + "examine": "Guthan the Infested's helm", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm", - "durability": null, - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "232455", - "name": "Guthan's helm 25", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "4907", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Guthan's helm 25", "examine": "Guthan the Infested's helm", - "durability": null, - "weight": "2", "absorb": "2,0,5", - "remove_beard": "true", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "368200", - "name": "Guthan's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4908" + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", + "weight": "2" }, { + "id": "4908", + "name": "Guthan's helm 0", + "examine": "Guthan the Infested's helm", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "ge_buy_limit": "10", - "grand_exchange_price": "368200", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's helm 0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4909" + "weight": "2" }, { - "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", + "id": "4909", + "name": "Guthan's helm 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4910", + "name": "Guthan's spear 100", + "examine": "Guthan the Infested's warspear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_audios": "1316,0,0,0", + "attack_speed": "5", + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { - "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", + "name": "Guthan's spear 75", + "examine": "Guthan the Infested's warspear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_audios": "1316,0,0,0", + "attack_speed": "5", + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { - "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" - }, - { - "requirements": "{0,70}", - "turn90cw_anim": "1207", + "name": "Guthan's spear 50", "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", + "archery_ticket_price": "0", "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" - }, - { - "requirements": "{0,70}", - "ge_buy_limit": "10", - "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", + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "379500", - "stand_anim": "813", - "attack_audios": "1316,0,0,0", - "tradeable": "true", - "name": "Guthan's spear 0", + "requirements": "{0,70}", "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "4913", + "name": "Guthan's spear 25", + "examine": "Guthan the Infested's warspear.", "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_audios": "1316,0,0,0", + "attack_speed": "5", + "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { "id": "4914", - "stand_turn_anim": "1209" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "379500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Guthan's spear 0", - "tradeable": "true", + "examine": "Guthan the Infested's warspear.", "archery_ticket_price": "0", - "id": "4915" + "attack_audios": "1316,0,0,0", + "attack_speed": "5", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" }, { - "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", + "id": "4915", + "name": "Guthan's spear 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4916", + "name": "Guthan's body 100", + "examine": "Guthan the Infested's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "archery_ticket_price": "0", "id": "4917", + "name": "Guthan's body 75", + "examine": "Guthan the Infested's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "archery_ticket_price": "0", "id": "4918", + "name": "Guthan's body 50", + "examine": "Guthan the Infested's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "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" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Guthan's body 25", "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "weight": "9", "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "245900", - "name": "Guthan's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4920" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "245900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Guthan's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4921" - }, - { "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "224030", - "durability": null, - "name": "Guthan's skirt 100", - "weight": "8", + "weight": "9" + }, + { + "id": "4920", + "name": "Guthan's body 0", + "examine": "Guthan the Infested's platebody armour.", + "absorb": "5,0,10", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "4921", + "name": "Guthan's body 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4922", + "name": "Guthan's skirt 100", + "examine": "Guthan the Infested's chainskirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "224030", - "durability": null, - "name": "Guthan's skirt 75", - "weight": "8", - "archery_ticket_price": "0", "id": "4923", + "name": "Guthan's skirt 75", + "examine": "Guthan the Infested's chainskirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "224030", - "durability": null, - "name": "Guthan's skirt 50", - "weight": "8", - "archery_ticket_price": "0", "id": "4924", + "name": "Guthan's skirt 50", + "examine": "Guthan the Infested's chainskirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "224030", - "durability": null, - "name": "Guthan's skirt 25", - "weight": "8", - "archery_ticket_price": "0", "id": "4925", - "absorb": "3,0,7", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Guthan's skirt 25", "examine": "Guthan the Infested's chainskirt.", - "grand_exchange_price": "240900", - "durability": null, - "name": "Guthan's skirt 0", - "tradeable": "true", - "weight": "8", + "absorb": "3,0,7", "archery_ticket_price": "0", + "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" + }, + { "id": "4926", - "absorb": "3,0,7", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "240900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Guthan's skirt 0", - "tradeable": "true", + "examine": "Guthan the Infested's chainskirt.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "4927" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "8" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", - "durability": null, - "name": "Karil's coif 100", + "id": "4927", + "name": "Guthan's skirt 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4928", + "name": "Karil's coif 100", + "examine": "Karil the Tainted's coif.", "absorb": "0,5,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", - "durability": null, - "name": "Karil's coif 75", - "archery_ticket_price": "0", "id": "4929", + "name": "Karil's coif 75", + "examine": "Karil the Tainted's coif.", "absorb": "0,5,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", - "durability": null, - "name": "Karil's coif 50", - "archery_ticket_price": "0", "id": "4930", + "name": "Karil's coif 50", + "examine": "Karil the Tainted's coif.", "absorb": "0,5,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "grand_exchange_price": "7520", - "durability": null, - "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" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", + "name": "Karil's coif 25", "examine": "Karil the Tainted's coif.", - "durability": null, "absorb": "0,5,2", - "remove_beard": "true", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", + "durability": null, + "equip_audio": "2238", "equipment_slot": "0", - "grand_exchange_price": "7557", - "name": "Karil's coif 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4932" + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { + "id": "4932", + "name": "Karil's coif 0", + "examine": "Karil the Tainted's coif.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "ge_buy_limit": "1", - "grand_exchange_price": "7557", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's coif 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4933" + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true" }, { - "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", + "id": "4933", + "name": "Karil's coif 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { "id": "4934", + "name": "Karil's x-bow 100", + "examine": "Karil the Tainted's repeating crossbow.", + "archery_ticket_price": "0", + "attack_anims": "2075,2075,2075,2075", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "372", + "requirements": "{4,70}", + "run_anim": "2077", + "stand_anim": "2074", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" }, { - "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", + "name": "Karil's x-bow 75", + "examine": "Karil the Tainted's repeating crossbow.", + "archery_ticket_price": "0", + "attack_anims": "2075,2075,2075,2075", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "372", + "requirements": "{4,70}", + "run_anim": "2077", + "stand_anim": "2074", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0" + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" }, { - "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" - }, - { - "requirements": "{4,70}", - "turn90cw_anim": "2076", + "name": "Karil's x-bow 50", "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", + "archery_ticket_price": "0", "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" - }, - { - "requirements": "{4,70}", - "ge_buy_limit": "10", - "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", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", "render_anim": "372", - "equipment_slot": "3", - "grand_exchange_price": "106300", - "stand_anim": "2074", - "name": "Karil's x-bow 0", - "tradeable": "true", + "requirements": "{4,70}", "run_anim": "2077", + "stand_anim": "2074", + "stand_turn_anim": "823", + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" + }, + { + "id": "4937", + "name": "Karil's x-bow 25", + "examine": "Karil the Tainted's repeating crossbow.", "archery_ticket_price": "0", + "attack_anims": "2075,2075,2075,2075", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "372", + "requirements": "{4,70}", + "run_anim": "2077", + "stand_anim": "2074", + "stand_turn_anim": "823", + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" + }, + { "id": "4938", - "stand_turn_anim": "823" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "106300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Karil's x-bow 0", - "tradeable": "true", + "examine": "Karil the Tainted's repeating crossbow.", "archery_ticket_price": "0", - "id": "4939" + "attack_speed": "4", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "372", + "requirements": "{4,70}", + "run_anim": "2077", + "stand_anim": "2074", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "2076", + "turn90ccw_anim": "2076", + "turn90cw_anim": "2076", + "two_handed": "true", + "walk_anim": "2076", + "weapon_interface": "17", + "weight": "2" }, { - "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", + "id": "4939", + "name": "Karil's x-bow 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "", + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4940", + "name": "Karil's top 100", + "examine": "Karil the Tainted's leather body armour.", "absorb": "0,10,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "weight": "6" }, { - "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", - "archery_ticket_price": "0", "id": "4941", + "name": "Karil's top 75", + "examine": "Karil the Tainted's leather body armour.", "absorb": "0,10,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "weight": "6" }, { - "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", - "archery_ticket_price": "0", "id": "4942", + "name": "Karil's top 50", + "examine": "Karil the Tainted's leather body armour.", "absorb": "0,10,5", + "archery_ticket_price": "0", "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "weight": "6" }, { - "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", - "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" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", + "name": "Karil's top 25", "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "weight": "6", "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "1500000", - "name": "Karil's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4944" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "1500000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4945" - }, - { "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "85135", - "durability": null, - "name": "Karil's skirt 100", - "weight": "2", + "weight": "6" + }, + { + "id": "4944", + "name": "Karil's top 0", + "examine": "Karil the Tainted's leather body armour.", + "absorb": "0,10,5", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "1", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "4945", + "name": "Karil's top 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { "id": "4946", + "name": "Karil's skirt 100", + "examine": "Karil the Tainted's leather skirt.", "absorb": "0,7,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "requirements": "{1,70}-{4,70}", + "weight": "2" }, { - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "85135", - "durability": null, - "name": "Karil's skirt 75", - "weight": "2", - "archery_ticket_price": "0", "id": "4947", + "name": "Karil's skirt 75", + "examine": "Karil the Tainted's leather skirt.", "absorb": "0,7,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "requirements": "{1,70}-{4,70}", + "weight": "2" }, { - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "85135", - "durability": null, - "name": "Karil's skirt 50", - "weight": "2", - "archery_ticket_price": "0", "id": "4948", + "name": "Karil's skirt 50", + "examine": "Karil the Tainted's leather skirt.", "absorb": "0,7,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "requirements": "{1,70}-{4,70}", + "weight": "2" }, { - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "85135", - "durability": null, - "name": "Karil's skirt 25", - "weight": "2", - "archery_ticket_price": "0", "id": "4949", - "absorb": "0,7,3", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", + "name": "Karil's skirt 25", "examine": "Karil the Tainted's leather skirt.", - "grand_exchange_price": "153100", - "durability": null, - "name": "Karil's skirt 0", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "4950", "absorb": "0,7,3", - "equipment_slot": "7" + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "requirements": "{1,70}-{4,70}", + "weight": "2" }, { - "ge_buy_limit": "1", - "grand_exchange_price": "153100", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4950", "name": "Karil's skirt 0", - "tradeable": "true", + "examine": "Karil the Tainted's leather skirt.", + "absorb": "0,7,3", "archery_ticket_price": "0", - "id": "4951" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", + "ge_buy_limit": "1", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "2" }, { - "remove_head": "true", - "requirements": "{1,70}", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "71798", - "name": "Torag's helm 100", + "id": "4951", + "name": "Karil's skirt 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { "id": "4952", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Torag's helm 100", + "examine": "Torag the Corrupted's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "71798", - "name": "Torag's helm 75", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "4953", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Torag's helm 75", + "examine": "Torag the Corrupted's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "71798", - "name": "Torag's helm 50", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "4954", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { + "name": "Torag's helm 50", + "examine": "Torag the Corrupted's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "71798", - "name": "Torag's helm 25", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "4955", - "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Torag's helm 25", "examine": "Torag the Corrupted's helm.", - "durability": null, - "weight": "5", "absorb": "2,0,5", - "remove_beard": "true", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "78200", - "name": "Torag's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4956" + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", + "weight": "5" }, { + "id": "4956", + "name": "Torag's helm 0", + "examine": "Torag the Corrupted's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "ge_buy_limit": "10", - "grand_exchange_price": "78200", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's helm 0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4957" + "weight": "5" }, { - "requirements": "{0,70}-{2,70}", - "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "weight": "3", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2068,2068,2068,2068", - "grand_exchange_price": "95800", - "attack_audios": "1332,0,0,0", - "name": "Torag's hammer 100", + "id": "4957", + "name": "Torag's helm 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4958", - "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0" - }, - { - "requirements": "{0,70}-{2,70}", + "name": "Torag's hammer 100", "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "weight": "3", + "archery_ticket_price": "0", + "attack_anims": "2068,2068,2068,2068", + "attack_audios": "1332,0,0,0", "attack_speed": "5", + "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,70}-{2,70}", "two_handed": "true", "weapon_interface": "10", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2068,2068,2068,2068", - "grand_exchange_price": "95800", - "attack_audios": "1332,0,0,0", - "name": "Torag's hammer 75", - "archery_ticket_price": "0", + "weight": "3" + }, + { "id": "4959", - "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0" - }, - { - "requirements": "{0,70}-{2,70}", + "name": "Torag's hammer 75", "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "weight": "3", + "archery_ticket_price": "0", + "attack_anims": "2068,2068,2068,2068", + "attack_audios": "1332,0,0,0", "attack_speed": "5", + "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,70}-{2,70}", "two_handed": "true", "weapon_interface": "10", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2068,2068,2068,2068", - "grand_exchange_price": "95800", - "attack_audios": "1332,0,0,0", - "name": "Torag's hammer 50", - "archery_ticket_price": "0", + "weight": "3" + }, + { "id": "4960", - "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0" - }, - { - "requirements": "{0,70}-{2,70}", + "name": "Torag's hammer 50", "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "weight": "3", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "2068,2068,2068,2068", - "grand_exchange_price": "95800", "attack_audios": "1332,0,0,0", - "name": "Torag's hammer 25", - "archery_ticket_price": "0", - "id": "4961", - "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0" - }, - { - "requirements": "{0,70}-{2,70}", - "ge_buy_limit": "10", - "examine": "Torag the Corrupted's twin hammers.", - "durability": null, - "weight": "3", "attack_speed": "5", + "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,70}-{2,70}", "two_handed": "true", "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "95600", + "weight": "3" + }, + { + "id": "4961", + "name": "Torag's hammer 25", + "examine": "Torag the Corrupted's twin hammers.", + "archery_ticket_price": "0", + "attack_anims": "2068,2068,2068,2068", "attack_audios": "1332,0,0,0", - "name": "Torag's hammer 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4962" + "attack_speed": "5", + "bonuses": "-4,-4,85,-4,0,0,0,0,0,0,0,72,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,70}-{2,70}", + "two_handed": "true", + "weapon_interface": "10", + "weight": "3" }, { + "id": "4962", + "name": "Torag's hammer 0", + "examine": "Torag the Corrupted's twin hammers.", + "archery_ticket_price": "0", + "attack_audios": "1332,0,0,0", + "attack_speed": "5", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", "ge_buy_limit": "10", - "grand_exchange_price": "95600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's hammer 0", + "requirements": "{0,70}-{2,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4963" + "two_handed": "true", + "weapon_interface": "10", + "weight": "3" }, { - "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", + "id": "4963", + "name": "Torag's hammer 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4964", + "name": "Torag's body 100", + "examine": "Torag the Corrupted's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "archery_ticket_price": "0", "id": "4965", + "name": "Torag's body 75", + "examine": "Torag the Corrupted's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "archery_ticket_price": "0", "id": "4966", + "name": "Torag's body 50", + "examine": "Torag the Corrupted's platebody armour.", "absorb": "5,0,10", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "9" }, { - "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", - "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" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Torag's body 25", "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "weight": "9", "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "271600", - "name": "Torag's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4968" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "271600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's body 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4969" - }, - { "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "384813", - "durability": null, - "name": "Torag's legs 100", - "weight": "9", + "weight": "9" + }, + { + "id": "4968", + "name": "Torag's body 0", + "examine": "Torag the Corrupted's platebody armour.", + "absorb": "5,0,10", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "4969", + "name": "Torag's body 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4970", + "name": "Torag's legs 100", + "examine": "Torag the Corrupted's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "384813", - "durability": null, - "name": "Torag's legs 75", - "weight": "9", - "archery_ticket_price": "0", "id": "4971", + "name": "Torag's legs 75", + "examine": "Torag the Corrupted's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "384813", - "durability": null, - "name": "Torag's legs 50", - "weight": "9", - "archery_ticket_price": "0", "id": "4972", + "name": "Torag's legs 50", + "examine": "Torag the Corrupted's plate leg armour.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "384813", - "durability": null, - "name": "Torag's legs 25", - "weight": "9", - "archery_ticket_price": "0", "id": "4973", - "absorb": "3,0,7", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Torag's legs 25", "examine": "Torag the Corrupted's plate leg armour.", - "grand_exchange_price": "371300", - "durability": null, - "name": "Torag's legs 0", - "tradeable": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "4974", "absorb": "3,0,7", - "equipment_slot": "7" + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "371300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4974", "name": "Torag's legs 0", - "tradeable": "true", + "examine": "Torag the Corrupted's plate leg armour.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "4975" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" }, { - "remove_head": "true", - "requirements": "{1,70}", - "examine": "Verac the Defiled's helm.", - "durability": null, - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "459537", - "name": "Verac's helm 100", + "id": "4975", + "name": "Torag's legs 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4976", - "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0" - }, - { + "name": "Verac's helm 100", + "examine": "Verac the Defiled's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Verac the Defiled's helm.", - "durability": null, - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "459537", - "name": "Verac's helm 75", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4977", - "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0" - }, - { + "name": "Verac's helm 75", + "examine": "Verac the Defiled's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Verac the Defiled's helm.", - "durability": null, - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "459537", - "name": "Verac's helm 50", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4978", - "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0" - }, - { + "name": "Verac's helm 50", + "examine": "Verac the Defiled's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "examine": "Verac the Defiled's helm.", - "durability": null, - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "459537", - "name": "Verac's helm 25", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4979", - "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0" - }, - { + "name": "Verac's helm 25", + "examine": "Verac the Defiled's helm.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,55,58,54,0,56,15,0,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,70}", - "ge_buy_limit": "10", + "weight": "1" + }, + { + "id": "4980", + "name": "Verac's helm 0", "examine": "Verac the Defiled's helm.", - "durability": null, - "weight": "1", "absorb": "2,0,5", - "remove_beard": "true", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "648900", - "name": "Verac's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4980" - }, - { "ge_buy_limit": "10", - "grand_exchange_price": "648900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Verac's helm 0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4981" + "weight": "1" }, { - "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", + "id": "4981", + "name": "Verac's helm 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4982", + "name": "Verac's flail 100", + "examine": "Verac the Defiled's flail.", + "archery_ticket_price": "0", + "attack_anims": "2062,2062,2062,2062", + "attack_audios": "1323,0,0,0", + "attack_speed": "5", + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "render_anim": "1426", + "requirements": "{0,70}", + "run_anim": "1831", + "stand_anim": "1832", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" }, { - "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", + "name": "Verac's flail 75", + "examine": "Verac the Defiled's flail.", + "archery_ticket_price": "0", + "attack_anims": "2062,2062,2062,2062", + "attack_audios": "1323,0,0,0", + "attack_speed": "5", + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "render_anim": "1426", + "requirements": "{0,70}", + "run_anim": "1831", + "stand_anim": "1832", "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0" + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" }, { - "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" - }, - { - "requirements": "{0,70}", - "turn90cw_anim": "2060", + "name": "Verac's flail 50", "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", + "archery_ticket_price": "0", "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" - }, - { - "requirements": "{0,70}", - "ge_buy_limit": "10", - "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", - "render_anim": "1426", + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2246", "equipment_slot": "3", - "grand_exchange_price": "95500", - "stand_anim": "1832", - "attack_audios": "1323,0,0,0", - "tradeable": "true", - "name": "Verac's flail 0", + "render_anim": "1426", + "requirements": "{0,70}", "run_anim": "1831", + "stand_anim": "1832", + "stand_turn_anim": "823", + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" + }, + { + "id": "4985", + "name": "Verac's flail 25", + "examine": "Verac the Defiled's flail.", "archery_ticket_price": "0", + "attack_anims": "2062,2062,2062,2062", + "attack_audios": "1323,0,0,0", + "attack_speed": "5", + "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "render_anim": "1426", + "requirements": "{0,70}", + "run_anim": "1831", + "stand_anim": "1832", + "stand_turn_anim": "823", + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" + }, + { "id": "4986", - "stand_turn_anim": "823" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "95500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Verac's flail 0", - "tradeable": "true", + "examine": "Verac the Defiled's flail.", "archery_ticket_price": "0", - "id": "4987" - }, - { - "requirements": "{1,70}", - "remove_sleeves": "true", - "examine": "Verac the Defiled's brassard.", - "grand_exchange_price": "177963", + "attack_audios": "1323,0,0,0", + "attack_speed": "5", "durability": null, - "name": "Verac's top 100", - "weight": "5", - "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" - }, - { - "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", - "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" - }, - { - "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", - "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" - }, - { - "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", - "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" - }, - { - "requirements": "{1,70}", + "equip_audio": "2246", + "equipment_slot": "3", "ge_buy_limit": "10", - "examine": "Verac the Defiled's brassard.", + "render_anim": "1426", + "requirements": "{0,70}", + "run_anim": "1831", + "stand_anim": "1832", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "2060", + "turn90ccw_anim": "2060", + "turn90cw_anim": "2060", + "two_handed": "true", + "walk_anim": "1830", + "weapon_interface": "8", + "weight": "2" + }, + { + "id": "4987", + "name": "Verac's flail 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, - "weight": "5", + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "4988", + "name": "Verac's top 100", + "examine": "Verac the Defiled's brassard.", "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "174000", - "name": "Verac's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4992" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "174000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Verac's top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4993" - }, - { "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "297540", - "durability": null, - "name": "Verac's skirt 100", - "weight": "5", + "weight": "5" + }, + { + "id": "4989", + "name": "Verac's top 75", + "examine": "Verac the Defiled's brassard.", + "absorb": "5,0,10", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "5" + }, + { + "id": "4990", + "name": "Verac's top 50", + "examine": "Verac the Defiled's brassard.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "5" + }, + { + "id": "4991", + "name": "Verac's top 25", + "examine": "Verac the Defiled's brassard.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,70}", + "weight": "5" + }, + { + "id": "4992", + "name": "Verac's top 0", + "examine": "Verac the Defiled's brassard.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "4993", + "name": "Verac's top 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "4994", + "name": "Verac's skirt 100", + "examine": "Verac the Defiled's plate skirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "297540", - "durability": null, - "name": "Verac's skirt 75", - "weight": "5", - "archery_ticket_price": "0", "id": "4995", + "name": "Verac's skirt 75", + "examine": "Verac the Defiled's plate skirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "297540", - "durability": null, - "name": "Verac's skirt 50", - "weight": "5", - "archery_ticket_price": "0", "id": "4996", + "name": "Verac's skirt 50", + "examine": "Verac the Defiled's plate skirt.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "297540", - "durability": null, - "name": "Verac's skirt 25", - "weight": "5", - "archery_ticket_price": "0", "id": "4997", - "absorb": "3,0,7", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", - "equipment_slot": "7" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Verac's skirt 25", "examine": "Verac the Defiled's plate skirt.", - "grand_exchange_price": "321600", - "durability": null, - "name": "Verac's skirt 0", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "4998", "absorb": "3,0,7", - "equipment_slot": "7" + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "321600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4998", "name": "Verac's skirt 0", - "tradeable": "true", + "examine": "Verac the Defiled's plate skirt.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "4999" + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "5" }, { - "ge_buy_limit": "1000", + "id": "4999", + "name": "Verac's skirt 0", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "5001", + "name": "Raw cave eel", "examine": "It's incredibly slimy.", - "grand_exchange_price": "373", - "durability": null, - "name": "Raw cave eel", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "5001" - }, - { + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5003", + "name": "Cave eel", "examine": "It's a bit slimy.", - "grand_exchange_price": "164", - "durability": null, - "name": "Cave eel", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "5003" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "examine": "That's disgusting!", - "durability": null, + "id": "5004", "name": "Frog spawn", + "examine": "That's disgusting!", "archery_ticket_price": "0", - "id": "5004" + "durability": null }, { - "durability": null, + "id": "5005", "name": "Raw cave eel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5005" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "5006", "name": "Burnt cave eel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5006" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "5007", "name": "Cave eel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5007" + "durability": null, + "tradeable": "true" }, { - "examine": "A book about the ancient goblin tribes.", - "durability": null, + "id": "5009", "name": "Goblin symbol book", - "weight": "1", + "examine": "A book about the ancient goblin tribes.", "archery_ticket_price": "0", - "id": "5009" + "durability": null, + "weight": "1" }, { - "examine": "You found the Lumbridge silverware in the HAM cave.", - "durability": null, + "id": "5011", "name": "Silverware", + "examine": "You found the Lumbridge silverware in the HAM cave.", "archery_ticket_price": "0", - "id": "5011" + "durability": null }, { - "examine": "A peace treaty between Lumbridge and the Cave Goblins.", - "durability": null, + "id": "5012", "name": "Peace treaty", + "examine": "A peace treaty between Lumbridge and the Cave Goblins.", "archery_ticket_price": "0", - "id": "5012" + "durability": null }, { - "shop_price": "900", - "examine": "A helmet with a lamp on it.", - "grand_exchange_price": "380", - "durability": null, - "name": "Mining helmet", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "5013", + "name": "Mining helmet", + "examine": "A helmet with a lamp on it.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "shop_price": "900", + "tradeable": "true", + "weight": "1" }, { + "id": "5014", + "name": "Mining helmet", + "examine": "A helmet with an unlit lamp on it.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "requirements": "{11,65}", "shop_price": "900", - "ge_buy_limit": "100", - "examine": "A helmet with an unlit lamp on it.", - "durability": null, - "weight": "1", - "equipment_slot": "0", - "grand_exchange_price": "342", - "name": "Mining helmet", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5014", - "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "342", - "durability": null, + "id": "5015", "name": "Mining helmet", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5015" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "shop_price": "600", - "turn90cw_anim": "1207", - "examine": "Basic but brutal!", - "walk_anim": "1205", - "durability": null, - "weight": "1.3", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "two_handed": "false", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "219", - "stand_anim": "813", - "tradeable": "true", - "name": "Bone spear", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "5016", - "stand_turn_anim": "1209", - "bonuses": "11,11,11,0,0,1,1,0,0,0,0,13,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "219", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Bone spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5017" - }, - { - "shop_price": "600", - "ge_buy_limit": "100", "examine": "Basic but brutal!", - "durability": null, - "weight": "0.9", + "archery_ticket_price": "0", "attack_speed": "6", - "weapon_interface": "10", + "bonuses": "11,11,11,0,0,1,1,0,0,0,0,13,0,0,0", + "durability": null, + "equip_audio": "2247", "equipment_slot": "3", - "grand_exchange_price": "384", - "name": "Bone club", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "600", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5018", - "bonuses": "-4,-4,16,-4,0,0,0,0,0,0,0,15,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "384", + "id": "5017", + "name": "Bone spear", "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5018", "name": "Bone club", - "tradeable": "true", + "examine": "Basic but brutal!", "archery_ticket_price": "0", - "id": "5019" - }, - { - "shop_price": "150", - "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", + "attack_anims": "401,401,400,401", + "attack_speed": "6", + "bonuses": "-4,-4,16,-4,0,0,0,0,0,0,0,15,0,0,0", + "defence_anim": "403", "durability": null, - "name": "Minecart ticket", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5020" - }, - { - "shop_price": "150", - "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", - "durability": null, - "name": "Minecart ticket", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5021" - }, - { - "shop_price": "150", - "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", - "durability": null, - "name": "Minecart ticket", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5022" - }, - { - "shop_price": "150", - "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", - "durability": null, - "name": "Minecart ticket", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5023" - }, - { - "remove_sleeves": "true", - "shop_price": "812", + "equipment_slot": "3", "ge_buy_limit": "100", - "examine": "Far too small to wear.", - "grand_exchange_price": "710", - "durability": null, - "name": "Woven top", + "shop_price": "600", "tradeable": "true", - "weight": "0.1", + "weapon_interface": "10", + "weight": "0.9" + }, + { + "id": "5019", + "name": "Bone club", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5020", + "name": "Minecart ticket", + "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "weight": "0.1" + }, + { + "id": "5021", + "name": "Minecart ticket", + "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "weight": "0.1" + }, + { + "id": "5022", + "name": "Minecart ticket", + "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "weight": "0.1" + }, + { + "id": "5023", + "name": "Minecart ticket", + "examine": "A ticket to take you from the dwarven mines under Ice Mountain to Keldagrim.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "150", + "weight": "0.1" + }, + { "id": "5024", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "710", - "durability": null, "name": "Woven top", - "tradeable": "true", + "examine": "Far too small to wear.", "archery_ticket_price": "0", - "id": "5025" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", "shop_price": "812", - "ge_buy_limit": "100", - "examine": "Yellow top, too small for me.", - "grand_exchange_price": "908", - "durability": null, - "name": "Woven top", "tradeable": "true", - "weight": "0.1", + "weight": "0.1" + }, + { + "id": "5025", + "name": "Woven top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5026", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "908", - "durability": null, "name": "Woven top", - "tradeable": "true", + "examine": "Yellow top, too small for me.", "archery_ticket_price": "0", - "id": "5027" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", "shop_price": "812", - "ge_buy_limit": "100", - "examine": "Blue top, very tiny.", - "grand_exchange_price": "1357", - "durability": null, - "name": "Woven top", "tradeable": "true", - "weight": "0.1", + "weight": "0.1" + }, + { + "id": "5027", + "name": "Woven top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5028", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1357", - "durability": null, "name": "Woven top", - "tradeable": "true", + "examine": "Blue top, very tiny.", "archery_ticket_price": "0", - "id": "5029" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "812", + "tradeable": "true", + "weight": "0.1" }, { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "867", - "examine": "Tiny!", - "durability": null, - "name": "Shirt", - "tradeable": "true", + "id": "5029", + "name": "Woven top", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5030", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "867", - "durability": null, "name": "Shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5031" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "1023", "examine": "Tiny!", - "durability": null, - "name": "Shirt", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5031", + "name": "Shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5032", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1023", - "durability": null, "name": "Shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5033" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "1282", "examine": "Tiny!", - "durability": null, - "name": "Shirt", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5033", + "name": "Shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5034", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1282", - "durability": null, "name": "Shirt", - "tradeable": "true", + "examine": "Tiny!", "archery_ticket_price": "0", - "id": "5035" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "746", - "examine": "A pair of long dwarven trousers... long for dwarves, of course.", - "durability": null, - "name": "Trousers", - "tradeable": "true", + "id": "5035", + "name": "Shirt", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5036", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "746", - "durability": null, "name": "Trousers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5037" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "378", "examine": "A pair of long dwarven trousers... long for dwarves, of course.", - "durability": null, - "name": "Trousers", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5037", + "name": "Trousers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5038", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "378", - "durability": null, "name": "Trousers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5039" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "442", "examine": "A pair of long dwarven trousers... long for dwarves, of course.", - "durability": null, - "name": "Trousers", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5039", + "name": "Trousers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5040", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "442", - "durability": null, "name": "Trousers", - "tradeable": "true", + "examine": "A pair of long dwarven trousers... long for dwarves, of course.", "archery_ticket_price": "0", - "id": "5041" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1191", - "examine": "These look great, on dwarves!", - "durability": null, - "name": "Shorts", - "tradeable": "true", + "id": "5041", + "name": "Trousers", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5042", - "equipment_slot": "7" + "name": "Shorts", + "examine": "These look great, on dwarves!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1191", - "durability": null, + "id": "5043", "name": "Shorts", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5043" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1229", - "examine": "Yellow shorts. Far too small for you.", - "durability": null, - "name": "Shorts", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", "id": "5044", - "equipment_slot": "7" + "name": "Shorts", + "examine": "Yellow shorts. Far too small for you.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1229", - "durability": null, + "id": "5045", "name": "Shorts", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5045" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2582", - "examine": "Blue shorts, these would look great on dwarves!", - "durability": null, - "name": "Shorts", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", "id": "5046", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2582", - "durability": null, "name": "Shorts", - "tradeable": "true", + "examine": "Blue shorts, these would look great on dwarves!", "archery_ticket_price": "0", - "id": "5047" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "681", - "examine": "A brown skirt. Size small!", - "durability": null, - "name": "Skirt", - "tradeable": "true", + "id": "5047", + "name": "Shorts", "archery_ticket_price": "0", - "weight": "0.1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5048", - "equipment_slot": "7" + "name": "Skirt", + "examine": "A brown skirt. Size small!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "681", - "durability": null, + "id": "5049", "name": "Skirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5049" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1151", - "examine": "A blue skirt.", - "durability": null, - "name": "Skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", "id": "5050", - "equipment_slot": "7" + "name": "Skirt", + "examine": "A blue skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1151", - "durability": null, + "id": "5051", "name": "Skirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5051" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "803", - "examine": "Lilac skirt.", - "durability": null, - "name": "Skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", "id": "5052", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "803", - "durability": null, "name": "Skirt", - "tradeable": "true", + "examine": "Lilac skirt.", "archery_ticket_price": "0", - "id": "5053" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { + "id": "5053", + "name": "Skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5054", + "name": "Dwarf", "examine": "A short angry guy.", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "5055", "name": "Dwarf", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5054" + "durability": null, + "tradeable": "true" }, { - "durability": null, - "name": "Dwarf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5055" - }, - { - "examine": "This looks very rusty and worn;", - "durability": null, + "id": "5056", "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", "archery_ticket_price": "0", - "id": "5056" + "durability": null }, { - "examine": "This looks very rusty and worn;", - "durability": null, + "id": "5057", "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", "archery_ticket_price": "0", - "id": "5057" + "durability": null }, { - "examine": "This looks very rusty and worn;", - "durability": null, + "id": "5058", "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", "archery_ticket_price": "0", - "id": "5058" + "durability": null }, { - "examine": "This looks very rusty and worn;", - "durability": null, + "id": "5059", "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", "archery_ticket_price": "0", - "id": "5059" + "durability": null }, { - "examine": "This looks very rusty and worn;", - "durability": null, - "name": "Dwarven battleaxe", - "archery_ticket_price": "0", "id": "5060", - "equipment_slot": "3" - }, - { - "examine": "This looks very rusty and worn;", - "durability": null, "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", "archery_ticket_price": "0", - "id": "5061", + "durability": null, + "equip_audio": "2232", "equipment_slot": "3" }, { - "examine": "One of a pair I assume.", + "id": "5061", + "name": "Dwarven battleaxe", + "examine": "This looks very rusty and worn;", + "archery_ticket_price": "0", "durability": null, + "equip_audio": "2232", + "equipment_slot": "3" + }, + { + "id": "5062", "name": "Left boot", - "weight": "0.5", + "examine": "One of a pair I assume.", "archery_ticket_price": "0", - "id": "5062" + "durability": null, + "weight": "0.5" }, { - "examine": "A good looking boot, for the right foot. Literally.", - "durability": null, + "id": "5063", "name": "Right boot", - "weight": "0.5", + "examine": "A good looking boot, for the right foot. Literally.", "archery_ticket_price": "0", - "id": "5063" + "durability": null, + "weight": "0.5" }, { - "examine": "A lovely pair of boots.", - "durability": null, + "id": "5064", "name": "Exquisite boots", - "tradeable": "false", + "examine": "A lovely pair of boots.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5064" + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "An old library book. It bears the title 'Scholars to Dwarven Costumes'.", - "durability": null, + "id": "5065", "name": "Book on costumes", + "examine": "An old library book. It bears the title 'Scholars to Dwarven Costumes'.", "archery_ticket_price": "0", - "id": "5065" + "durability": null }, { - "examine": "These notes are from a meeting of the Keldagrim Consortium.", - "durability": null, + "id": "5066", "name": "Meeting notes", + "examine": "These notes are from a meeting of the Keldagrim Consortium.", "archery_ticket_price": "0", - "id": "5066" + "durability": null }, { - "examine": "Clothes for the sculptor's model.", - "durability": null, + "id": "5067", "name": "Exquisite clothes", + "examine": "Clothes for the sculptor's model.", "archery_ticket_price": "0", - "id": "5067" + "durability": null }, { - "examine": "A master at farming.", - "durability": null, + "id": "5068", "name": "Master farmer", + "examine": "A master at farming.", "archery_ticket_price": "0", - "id": "5068" + "durability": null }, { - "examine": "Full: ", - "durability": null, + "id": "5070", "name": "Bird's nest", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "5070" - }, - { "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5071" + "durability": null, + "tradeable": "false" }, { + "id": "5071", + "name": "Bird's nest", "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5072" + "durability": null, + "tradeable": "false" }, { + "id": "5072", + "name": "Bird's nest", "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5073" + "durability": null, + "tradeable": "false" }, { + "id": "5073", + "name": "Bird's nest", "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5074" + "durability": null, + "tradeable": "false" }, { + "id": "5074", + "name": "Bird's nest", "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5075" + "durability": null, + "tradeable": "false" }, { + "id": "5075", + "name": "Bird's nest", + "examine": "Full: ", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "5076", + "name": "Bird's egg", "examine": "A red bird's egg.", - "durability": null, - "name": "Bird's egg", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5076" + "durability": null, + "tradeable": "false" }, { + "id": "5077", + "name": "Bird's egg", "examine": "A blue bird's egg.", - "durability": null, - "name": "Bird's egg", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5077" + "durability": null, + "tradeable": "false" }, { + "id": "5078", + "name": "Bird's egg", "examine": "A green bird's egg.", - "durability": null, - "name": "Bird's egg", - "tradeable": "false", "archery_ticket_price": "0", - "id": "5078" + "durability": null, + "tradeable": "false" }, { + "id": "5096", + "name": "Marigold seed", + "examine": "A marigold seed - plant in flower patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{2,19}", "shop_price": "84", - "ge_buy_limit": "100", - "examine": "A marigold seed - plant in flower patch.", - "grand_exchange_price": "8", - "durability": null, - "name": "Marigold seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5096" + "tradeable": "true" }, { + "id": "5097", + "name": "Rosemary seed", + "examine": "A rosemary seed - plant in flower patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{11,19}", "shop_price": "98", - "ge_buy_limit": "100", - "examine": "A rosemary seed - plant in flower patch.", - "grand_exchange_price": "3", - "durability": null, - "name": "Rosemary seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5097" + "tradeable": "true" }, { + "id": "5098", + "name": "Nasturtium seed", + "examine": "A nasturtium seed - plant in flower patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{24,19}", "shop_price": "14", - "ge_buy_limit": "100", - "examine": "A nasturtium seed - plant in flower patch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Nasturtium seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5098" + "tradeable": "true" }, { - "requirements": "{19,25}", - "ge_buy_limit": "100", - "examine": "A woad seed - plant in flower patch.", - "grand_exchange_price": "1", - "durability": null, + "id": "5099", "name": "Woad seed", - "tradeable": "true", + "examine": "A woad seed - plant in flower patch.", "archery_ticket_price": "0", - "id": "5099" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,25}", + "tradeable": "true" }, { + "id": "5100", + "name": "Limpwurt seed", + "examine": "A limpwurt seed - plant in flower patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,26}", "shop_price": "70", - "ge_buy_limit": "100", - "examine": "A limpwurt seed - plant in flower patch.", - "grand_exchange_price": "273", - "durability": null, - "name": "Limpwurt seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5100" + "tradeable": "true" }, { - "requirements": "{10,19}", - "ge_buy_limit": "100", - "examine": "A redberry seed - Plant in a bush patch.", - "grand_exchange_price": "1", - "durability": null, + "id": "5101", "name": "Redberry seed", - "tradeable": "true", + "examine": "A redberry seed - Plant in a bush patch.", "archery_ticket_price": "0", - "id": "5101" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{10,19}", + "tradeable": "true" }, { - "requirements": "{22,19}", - "ge_buy_limit": "100", - "examine": "A cadavaberry seed - Plant in a bush patch.", - "grand_exchange_price": "1", - "durability": null, + "id": "5102", "name": "Cadavaberry seed", - "tradeable": "true", + "examine": "A cadavaberry seed - Plant in a bush patch.", "archery_ticket_price": "0", - "id": "5102" - }, - { - "requirements": "{19,36}", - "ge_buy_limit": "100", - "examine": "A dwellberry seed - Plant in a bush patch.", - "grand_exchange_price": "1", "durability": null, - "name": "Dwellberry seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5103" + "ge_buy_limit": "100", + "requirements": "{22,19}", + "tradeable": "true" }, { + "id": "5103", + "name": "Dwellberry seed", + "examine": "A dwellberry seed - Plant in a bush patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,36}", + "tradeable": "true" + }, + { + "id": "5104", + "name": "Jangerberry seed", + "examine": "A jangerberry bush seed - plant in a bush patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,48}", "shop_price": "22", - "ge_buy_limit": "100", - "examine": "A jangerberry bush seed - plant in a bush patch.", - "grand_exchange_price": "2", - "durability": null, - "name": "Jangerberry seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5104" + "tradeable": "true" }, { - "requirements": "{19,59}", - "ge_buy_limit": "100", - "examine": "A whiteberry bush seed - plant in a bush patch.", - "grand_exchange_price": "8", - "durability": null, + "id": "5105", "name": "Whiteberry seed", - "tradeable": "true", + "examine": "A whiteberry bush seed - plant in a bush patch.", "archery_ticket_price": "0", - "id": "5105" - }, - { - "requirements": "{19,70}", - "ge_buy_limit": "100", - "examine": "A poison ivy bush seed - plant in a bush patch.", - "grand_exchange_price": "38", "durability": null, - "name": "Poison ivy seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5106" + "ge_buy_limit": "100", + "requirements": "{19,59}", + "tradeable": "true" }, { + "id": "5106", + "name": "Poison ivy seed", + "examine": "A poison ivy bush seed - plant in a bush patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,70}", + "tradeable": "true" + }, + { + "id": "5280", + "name": "Cactus seed", + "examine": "A Cactus seed - plant in a cactus patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,55}", "shop_price": "29", - "ge_buy_limit": "100", - "examine": "A Cactus seed - plant in a cactus patch.", - "grand_exchange_price": "10", - "durability": null, - "name": "Cactus seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5280" + "tradeable": "true" }, { - "requirements": "{19,63}", - "ge_buy_limit": "100", - "examine": "Also known as Deadly Nightshade - plant in a belladonna patch.", - "grand_exchange_price": "24", - "durability": null, + "id": "5281", "name": "Belladonna seed", - "tradeable": "true", + "examine": "Also known as Deadly Nightshade - plant in a belladonna patch.", "archery_ticket_price": "0", - "id": "5281" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,63}", + "tradeable": "true" }, { - "requirements": "{19,53}", - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, + "id": "5282", "name": "Mushroom spore", "archery_ticket_price": "0", - "id": "5282" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,53}" }, { - "requirements": "{19,27}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "4", - "durability": null, + "id": "5283", "name": "Apple tree seed", - "tradeable": "true", + "examine": "Plant this in a plantpot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5283" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,27}", + "tradeable": "true" }, { - "requirements": "{19,33}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "5", - "durability": null, + "id": "5284", "name": "Banana tree seed", - "tradeable": "true", + "examine": "Plant this in a plantpot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5284" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,33}", + "tradeable": "true" }, { - "requirements": "{19,39}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "30", - "durability": null, + "id": "5285", "name": "Orange tree seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5285" - }, - { - "requirements": "{19,42}", - "ge_buy_limit": "100", - "examine": "Plant this in a plant pot of soil to grow a sapling.", - "grand_exchange_price": "265", - "durability": null, - "name": "Curry tree seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5286" - }, - { - "requirements": "{19,51}", - "ge_buy_limit": "100", "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "13700", - "durability": null, - "name": "Pineapple seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5287" - }, - { - "requirements": "{19,57}", + "durability": null, "ge_buy_limit": "100", - "examine": "Plant this in a plant pot of soil to grow a sapling.", - "grand_exchange_price": "41200", - "durability": null, - "name": "Papaya tree seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5288" + "requirements": "{19,39}", + "tradeable": "true" }, { + "id": "5286", + "name": "Curry tree seed", + "examine": "Plant this in a plant pot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,42}", + "tradeable": "true" + }, + { + "id": "5287", + "name": "Pineapple seed", + "examine": "Plant this in a plantpot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,51}", + "tradeable": "true" + }, + { + "id": "5288", + "name": "Papaya tree seed", + "examine": "Plant this in a plant pot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,57}", + "tradeable": "true" + }, + { + "id": "5289", + "name": "Palm tree seed", + "examine": "Plant in a plantpot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,68}", "shop_price": "35000", - "ge_buy_limit": "100", - "examine": "Plant in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "105100", - "durability": null, - "name": "Palm tree seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5289" + "tradeable": "true" }, { + "id": "5290", + "name": "Calquat tree seed", + "examine": "Plant in a plantpot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,72}", "shop_price": "35000", - "ge_buy_limit": "100", - "examine": "Plant in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "32000", - "durability": null, - "name": "Calquat tree seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5290" + "tradeable": "true" }, { - "requirements": "{9,19}", - "ge_buy_limit": "100", - "examine": "A guam seed - plant in a herb patch.", - "grand_exchange_price": "5", - "durability": null, + "id": "5291", "name": "Guam seed", - "tradeable": "true", + "examine": "A guam seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5291" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{9,19}", + "tradeable": "true" }, { - "requirements": "{14,19}", - "ge_buy_limit": "100", - "examine": "A marrentill seed - plant in a herb patch.", - "grand_exchange_price": "5", - "durability": null, + "id": "5292", "name": "Marrentill seed", - "tradeable": "true", + "examine": "A marrentill seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5292" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{14,19}", + "tradeable": "true" }, { - "requirements": "{19,19}", - "ge_buy_limit": "100", - "examine": "A tarromin seed - plant in a herb patch.", - "grand_exchange_price": "1", - "durability": null, + "id": "5293", "name": "Tarromin seed", - "tradeable": "true", + "examine": "A tarromin seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5293" - }, - { - "requirements": "{19,26}", - "ge_buy_limit": "100", - "examine": "A harralander seed - plant in a herb patch.", - "grand_exchange_price": "4", "durability": null, - "name": "Harralander seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5294" + "ge_buy_limit": "100", + "requirements": "{19,19}", + "tradeable": "true" }, { + "id": "5294", + "name": "Harralander seed", + "examine": "A harralander seed - plant in a herb patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,26}", + "tradeable": "true" + }, + { + "id": "5295", + "name": "Ranarr seed", + "examine": "A Ranarr seed - plant in a herb patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,32}", "shop_price": "4000", - "ge_buy_limit": "100", - "examine": "A Ranarr seed - plant in a herb patch.", - "grand_exchange_price": "9489", - "durability": null, - "name": "Ranarr seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5295" + "tradeable": "true" }, { - "requirements": "{19,38}", - "ge_buy_limit": "100", - "examine": "A toadflax seed - plant in a herb patch.", - "grand_exchange_price": "2980", - "durability": null, + "id": "5296", "name": "Toadflax seed", - "tradeable": "true", + "examine": "A toadflax seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5296" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,38}", + "tradeable": "true" }, { - "requirements": "{19,44}", - "ge_buy_limit": "100", - "examine": "An irit seed - plant in a herb patch.", - "grand_exchange_price": "181", - "durability": null, + "id": "5297", "name": "Irit seed", - "tradeable": "true", + "examine": "An irit seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5297" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,44}", + "tradeable": "true" }, { - "requirements": "{19,50}", - "ge_buy_limit": "100", - "examine": "An avantoe seed - plant in a herb patch.", - "grand_exchange_price": "2492", - "durability": null, + "id": "5298", "name": "Avantoe seed", - "tradeable": "true", + "examine": "An avantoe seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5298" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,50}", + "tradeable": "true" }, { - "requirements": "{19,56}", - "ge_buy_limit": "100", - "examine": "A kwuarm seed - plant in a herb patch.", - "grand_exchange_price": "537", - "durability": null, + "id": "5299", "name": "Kwuarm seed", - "tradeable": "true", + "examine": "A kwuarm seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5299" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,56}", + "tradeable": "true" }, { - "requirements": "{19,62}", - "ge_buy_limit": "100", - "examine": "A snapdragon seed - plant in a herb patch.", - "grand_exchange_price": "47800", - "durability": null, + "id": "5300", "name": "Snapdragon seed", - "tradeable": "true", + "examine": "A snapdragon seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5300" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,62}", + "tradeable": "true" }, { - "requirements": "{19,67}", - "ge_buy_limit": "100", - "examine": "A cadantine seed - plant in a herb patch.", - "grand_exchange_price": "1253", - "durability": null, + "id": "5301", "name": "Cadantine seed", - "tradeable": "true", + "examine": "A cadantine seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5301" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,67}", + "tradeable": "true" }, { - "requirements": "{19,73}", - "ge_buy_limit": "100", - "examine": "A lantadyme seed - plant in a herb patch.", - "grand_exchange_price": "37100", - "durability": null, + "id": "5302", "name": "Lantadyme seed", - "tradeable": "true", - "weight": "0.1", + "examine": "A lantadyme seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5302" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,73}", + "tradeable": "true", + "weight": "0.1" }, { - "requirements": "{19,79}", - "ge_buy_limit": "100", - "examine": "A dwarf weed seed - plant in a herb patch.", - "grand_exchange_price": "41900", - "durability": null, + "id": "5303", "name": "Dwarf weed seed", - "tradeable": "true", + "examine": "A dwarf weed seed - plant in a herb patch.", "archery_ticket_price": "0", - "id": "5303" - }, - { - "requirements": "{19,85}", - "ge_buy_limit": "100", - "examine": "A Torstol seed - plant in a herb patch.", - "grand_exchange_price": "302300", "durability": null, - "name": "Torstol seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5304" + "ge_buy_limit": "100", + "requirements": "{19,79}", + "tradeable": "true" }, { + "id": "5304", + "name": "Torstol seed", + "examine": "A Torstol seed - plant in a herb patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,85}", + "tradeable": "true" + }, + { + "id": "5305", + "name": "Barley seed", + "examine": "A barley seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{3,19}", "shop_price": "38", - "ge_buy_limit": "100", - "examine": "A barley seed - plant in a hops patch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Barley seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5305" + "tradeable": "true" }, { + "id": "5306", + "name": "Jute seed", + "examine": "A jute seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{13,19}", "shop_price": "86", - "ge_buy_limit": "100", - "examine": "A jute seed - plant in a hops patch.", - "grand_exchange_price": "2", - "durability": null, - "name": "Jute seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5306" + "tradeable": "true" }, { + "id": "5307", + "name": "Hammerstone seed", + "examine": "A hammerstone hop seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{4,19}", "shop_price": "52", - "ge_buy_limit": "100", - "examine": "A hammerstone hop seed - plant in a hops patch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Hammerstone seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5307" + "tradeable": "true" }, { + "id": "5308", + "name": "Asgarnian seed", + "examine": "An Asgarnian hop seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{8,19}", "shop_price": "60", - "ge_buy_limit": "100", - "examine": "An Asgarnian hop seed - plant in a hops patch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Asgarnian seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5308" + "tradeable": "true" }, { + "id": "5309", + "name": "Yanillian seed", + "examine": "A yanillian hop seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{16,19}", "shop_price": "210", - "ge_buy_limit": "100", - "examine": "A yanillian hop seed - plant in a hops patch.", - "grand_exchange_price": "11", - "durability": null, - "name": "Yanillian seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5309" + "tradeable": "true" }, { + "id": "5310", + "name": "Krandorian seed", + "examine": "A krandorian hop seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{21,19}", "shop_price": "8", - "ge_buy_limit": "100", - "examine": "A krandorian hop seed - plant in a hops patch.", - "grand_exchange_price": "1", - "durability": null, - "name": "Krandorian seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5310" + "tradeable": "true" }, { + "id": "5311", + "name": "Wildblood seed", + "examine": "A wildblood hop seed - plant in a hops patch.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{19,28}", "shop_price": "14", - "ge_buy_limit": "100", - "examine": "A wildblood hop seed - plant in a hops patch.", - "grand_exchange_price": "12", - "durability": null, - "name": "Wildblood seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5311" + "tradeable": "true" }, { + "id": "5312", + "name": "Acorn", + "examine": "Plant this in a plantpot of soil to grow a sapling.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{15,19}", "shop_price": "100", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "22", - "durability": null, - "name": "Acorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5312" + "tradeable": "true" }, { - "requirements": "{19,30}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "945", - "durability": null, + "id": "5313", "name": "Willow seed", - "tradeable": "true", + "examine": "Plant this in a plantpot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5313" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,30}", + "tradeable": "true" }, { - "requirements": "{19,45}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "35400", - "durability": null, + "id": "5314", "name": "Maple seed", - "tradeable": "true", + "examine": "Plant this in a plantpot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5314" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,45}", + "tradeable": "true" }, { - "requirements": "{19,60}", - "ge_buy_limit": "100", - "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "81900", - "durability": null, + "id": "5315", "name": "Yew seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5315" - }, - { - "requirements": "{19,75}", - "ge_buy_limit": "100", "examine": "Plant this in a plantpot of soil to grow a sapling.", - "grand_exchange_price": "276900", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,60}", + "tradeable": "true" + }, + { + "id": "5316", "name": "Magic seed", - "tradeable": "true", + "examine": "Plant this in a plantpot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5316" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,75}", + "tradeable": "true" }, { - "requirements": "{19,83}", - "shop_price": "55000", - "examine": "Plant this in a plant pot of soil to grow a sapling.", - "durability": null, + "id": "5317", "name": "Spirit seed", + "examine": "Plant this in a plant pot of soil to grow a sapling.", "archery_ticket_price": "0", - "id": "5317" - }, - { - "shop_price": "8", - "ge_buy_limit": "500", - "examine": "A potato seed - plant in an allotment.", - "grand_exchange_price": "1", "durability": null, - "name": "Potato seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5318" + "requirements": "{19,83}", + "shop_price": "55000" }, { + "id": "5318", + "name": "Potato seed", + "examine": "A potato seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "shop_price": "8", + "tradeable": "true" + }, + { + "id": "5319", + "name": "Onion seed", + "examine": "An onion seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{5,19}", "shop_price": "10", - "ge_buy_limit": "500", - "examine": "An onion seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Onion seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5319" + "tradeable": "true" }, { - "requirements": "{20,19}", - "ge_buy_limit": "500", - "examine": "A sweetcorn seed - plant in an allotment.", - "grand_exchange_price": "2", - "durability": null, + "id": "5320", "name": "Sweetcorn seed", - "tradeable": "true", + "examine": "A sweetcorn seed - plant in an allotment.", "archery_ticket_price": "0", - "id": "5320" + "durability": null, + "ge_buy_limit": "500", + "requirements": "{20,19}", + "tradeable": "true" }, { + "id": "5321", + "name": "Watermelon seed", + "examine": "A watermelon seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{19,47}", "shop_price": "56", - "ge_buy_limit": "500", - "examine": "A watermelon seed - plant in an allotment.", - "grand_exchange_price": "2491", - "durability": null, - "name": "Watermelon seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5321" + "tradeable": "true" }, { + "id": "5322", + "name": "Tomato seed", + "examine": "A tomato seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{12,19}", "shop_price": "10", - "ge_buy_limit": "500", - "examine": "A tomato seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Tomato seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5322" + "tradeable": "true" }, { - "requirements": "{19,31}", - "ge_buy_limit": "500", - "examine": "A strawberry seed - plant in an allotment.", - "grand_exchange_price": "240", - "durability": null, + "id": "5323", "name": "Strawberry seed", - "tradeable": "true", + "examine": "A strawberry seed - plant in an allotment.", "archery_ticket_price": "0", - "id": "5323" + "durability": null, + "ge_buy_limit": "500", + "requirements": "{19,31}", + "tradeable": "true" }, { + "id": "5324", + "name": "Cabbage seed", + "examine": "A cabbage seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", "requirements": "{7,19}", "shop_price": "25", - "ge_buy_limit": "500", - "examine": "A cabbage seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Cabbage seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5324" + "tradeable": "true" }, { - "shop_price": "12", - "ge_buy_limit": "100", + "id": "5325", + "name": "Gardening trowel", "examine": "Not suitable for archaeological digs.", - "grand_exchange_price": "106", - "durability": null, - "name": "Gardening trowel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5325" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "106", - "durability": null, - "name": "Gardening trowel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5326" + "shop_price": "12", + "tradeable": "true" }, { - "examine": "I need to attach this to its head.", + "id": "5326", + "name": "Gardening trowel", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5327", "name": "Spade handle", - "tradeable": "false", + "examine": "I need to attach this to its head.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "5327" + "durability": null, + "tradeable": "false" }, { - "examine": "I need to attach this to its handle.", - "durability": null, + "id": "5328", "name": "Spade head", + "examine": "I need to attach this to its handle.", "archery_ticket_price": "0", - "id": "5328" + "durability": null }, { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Good for pruning away diseased leaves.", - "grand_exchange_price": "204", - "durability": null, - "name": "Secateurs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "5329", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "204", - "durability": null, "name": "Secateurs", - "tradeable": "true", + "examine": "Good for pruning away diseased leaves.", "archery_ticket_price": "0", - "id": "5330" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" }, { - "shop_price": "8", + "id": "5330", + "name": "Secateurs", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5331", + "name": "Watering can", "examine": "This watering can is empty.", - "grand_exchange_price": "298", - "durability": null, - "name": "Watering can", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.1", - "id": "5331" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "298", - "durability": null, - "name": "Watering can", + "shop_price": "8", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5332" + "weight": "0.1" }, { - "shop_price": "8", - "examine": " This watering can is almost empty.", - "grand_exchange_price": "206", + "id": "5332", + "name": "Watering can", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5333", "name": "Watering can(1)", + "examine": " This watering can is almost empty.", "archery_ticket_price": "0", - "weight": "0.2", - "id": "5333" + "durability": null, + "shop_price": "8", + "weight": "0.2" }, { - "shop_price": "8", - "examine": "This watering can is three-quarters empty.", - "grand_exchange_price": "206", - "durability": null, + "id": "5334", "name": "Watering can(2)", + "examine": "This watering can is three-quarters empty.", "archery_ticket_price": "0", - "weight": "0.3", - "id": "5334" + "durability": null, + "shop_price": "8", + "weight": "0.3" }, { - "shop_price": "8", - "examine": "This watering can is just under half-full.", - "grand_exchange_price": "206", - "durability": null, + "id": "5335", "name": "Watering can(3)", + "examine": "This watering can is just under half-full.", "archery_ticket_price": "0", - "weight": "0.4", - "id": "5335" + "durability": null, + "shop_price": "8", + "weight": "0.4" }, { - "shop_price": "8", - "examine": " Some would say this watering can is half-full, others half-empty.", - "grand_exchange_price": "206", - "durability": null, + "id": "5336", "name": "Watering can(4)", + "examine": " Some would say this watering can is half-full, others half-empty.", "archery_ticket_price": "0", - "weight": "0.5", - "id": "5336" + "durability": null, + "shop_price": "8", + "weight": "0.5" }, { - "shop_price": "8", - "examine": "This watering can is just over half-full.", - "grand_exchange_price": "206", - "durability": null, + "id": "5337", "name": "Watering can(5)", + "examine": "This watering can is just over half-full.", "archery_ticket_price": "0", - "weight": "0.6", - "id": "5337" + "durability": null, + "shop_price": "8", + "weight": "0.6" }, { - "shop_price": "8", - "examine": " This watering can is three quarters full.", - "grand_exchange_price": "206", - "durability": null, + "id": "5338", "name": "Watering can(6)", + "examine": " This watering can is three quarters full.", "archery_ticket_price": "0", - "weight": "0.7", - "id": "5338" + "durability": null, + "shop_price": "8", + "weight": "0.7" }, { - "shop_price": "8", - "examine": " This watering can is almost completely full.", - "grand_exchange_price": "206", - "durability": null, + "id": "5339", "name": "Watering can(7)", + "examine": " This watering can is almost completely full.", "archery_ticket_price": "0", - "weight": "0.8", - "id": "5339" - }, - { + "durability": null, "shop_price": "8", - "examine": " This watering can is completely full.", - "grand_exchange_price": "206", - "durability": null, + "weight": "0.8" + }, + { + "id": "5340", "name": "Watering can(8)", + "examine": " This watering can is completely full.", "archery_ticket_price": "0", - "weight": "0.9", - "id": "5340" + "durability": null, + "shop_price": "8", + "weight": "0.9" }, { - "shop_price": "6", - "ge_buy_limit": "100", + "id": "5341", + "name": "Rake", "examine": "Use this to clear weeds.", - "grand_exchange_price": "99", - "durability": null, - "name": "Rake", - "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", - "id": "5341" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "99", - "durability": null, - "name": "Rake", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5342" - }, - { "shop_price": "6", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "5342", + "name": "Rake", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5343", + "name": "Seed dibber", "examine": "Use this to plant seeds with.", - "grand_exchange_price": "117", - "durability": null, - "name": "Seed dibber", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5343" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "117", - "durability": null, + "id": "5344", "name": "Seed dibber", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5344" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A pair of gardening boots.", - "grand_exchange_price": "3186", - "durability": null, - "name": "Gardening boots", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "5345", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3186", - "durability": null, "name": "Gardening boots", - "tradeable": "true", + "examine": "A pair of gardening boots.", "archery_ticket_price": "0", - "id": "5346" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "examine": "I need to reattach this to its head.", + "id": "5346", + "name": "Gardening boots", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5347", "name": "Rake handle", - "weight": "0.4", + "examine": "I need to reattach this to its head.", "archery_ticket_price": "0", - "id": "5347" + "durability": null, + "weight": "0.4" }, { - "examine": "I need to reattach this to its handle.", - "durability": null, + "id": "5348", "name": "Rake head", - "weight": "0.9", + "examine": "I need to reattach this to its handle.", "archery_ticket_price": "0", - "id": "5348" + "durability": null, + "weight": "0.9" }, { - "shop_price": "1", - "ge_buy_limit": "100", + "id": "5350", + "name": "Plant pot", "examine": "An empty plant pot.", - "grand_exchange_price": "20", - "durability": null, - "name": "Plant pot", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "5350" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, + "id": "5351", "name": "Plant pot", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5351" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "9", - "ge_buy_limit": "100", + "id": "5352", + "name": "Unfired plant pot", "examine": "An unfired plant pot.", - "grand_exchange_price": "7", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "shop_price": "9", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5353", "name": "Unfired plant pot", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "5352" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7", "durability": null, - "name": "Unfired plant pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5353" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "1", - "ge_buy_limit": "100", + "id": "5354", + "name": "Plant pot", "examine": "A plant pot filled with soil.", - "grand_exchange_price": "340", - "durability": null, - "name": "Plant pot", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "5354" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "340", - "durability": null, - "name": "Plant pot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5355" - }, - { "shop_price": "1", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5355", + "name": "Plant pot", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5356", + "name": "Plant pot", "examine": "A plant pot filled with soil.", - "grand_exchange_price": "243", + "archery_ticket_price": "0", "durability": null, - "name": "Plant pot", + "shop_price": "1", "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5356" + "weight": "0.9" }, { - "durability": null, + "id": "5357", "name": "Plant pot", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5357" + "durability": null, + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An acorn has been sown and watered in this plant pot.", - "durability": null, + "id": "5358", "name": "Oak seedling", + "examine": "An acorn has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5358" + "durability": null, + "shop_price": "200" }, { - "examine": "A willow tree seed has been sown and watered in this plant pot", - "durability": null, + "id": "5359", "name": "Willow seedling", - "weight": "0.9", + "examine": "A willow tree seed has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5359" + "durability": null, + "weight": "0.9" }, { - "examine": "A maple tree has been sown (and watered) in this plant pot.", - "durability": null, + "id": "5360", "name": "Maple seedling", - "weight": "0.9", + "examine": "A maple tree seed has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5360" + "durability": null, + "weight": "0.9" }, { - "examine": "A yew seed has been sown in the plant pot", - "durability": null, + "id": "5361", "name": "Yew seedling", - "weight": "1", + "examine": "A yew tree seed has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5361" + "durability": null, + "weight": "1" }, { - "examine": "A magic tree seed has been sown and watered in this plant pot.", - "durability": null, + "id": "5362", "name": "Magic seedling", - "weight": "0.9", + "examine": "A magic tree seed has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5362" + "durability": null, + "weight": "0.9" }, { - "examine": "A spirit seedling is growing in this pot. It has been sown and watered.", - "durability": null, + "id": "5363", "name": "Spirit seedling", + "examine": "A spirit tree seed has been sown in this plant pot.", "archery_ticket_price": "0", - "id": "5363" + "durability": null }, { - "shop_price": "200", - "examine": "An acorn has been sown and watered in this plant pot.", - "durability": null, + "id": "5364", "name": "Oak seedling", + "examine": "An acorn has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5364" + "durability": null, + "shop_price": "200" }, { - "examine": "A willow tree seed has been sown and watered in this plant pot", - "durability": null, + "id": "5365", "name": "Willow seedling", - "weight": "0.9", + "examine": "A willow tree seed has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5365" + "durability": null, + "weight": "0.9" }, { - "examine": "A maple tree has been sown (and watered) in this plant pot.", - "durability": null, + "id": "5366", "name": "Maple seedling", - "weight": "0.9", + "examine": "A maple tree seed has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5366" + "durability": null, + "weight": "0.9" }, { - "examine": "A yew seed has been sown in the plant pot", - "durability": null, + "id": "5367", "name": "Yew seedling", - "weight": "1", + "examine": "A yew tree seed has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5367" + "durability": null, + "weight": "1" }, { - "examine": "A magic tree seed has been sown and watered in this plant pot.", - "durability": null, + "id": "5368", "name": "Magic seedling", - "weight": "0.9", + "examine": "A magic tree seed has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5368" + "durability": null, + "weight": "0.9" }, { - "examine": "A spirit seedling is growing in this pot. It has been sown and watered.", - "durability": null, + "id": "5369", "name": "Spirit seedling", + "examine": "A spirit tree seed has been sown and watered in this plant pot.", "archery_ticket_price": "0", - "id": "5369" + "durability": null }, { - "examine": "This sapling is ready to be planted in a tree patch.", - "durability": null, + "id": "5370", "name": "Oak sapling", - "weight": "0.9", + "examine": "This sapling is ready to be planted in a tree patch.", "archery_ticket_price": "0", - "id": "5370" + "durability": null, + "weight": "0.9" }, { - "examine": "This sapling is ready to be replanted in a tree patch.", - "durability": null, + "id": "5371", "name": "Willow sapling", - "weight": "0.9", + "examine": "This sapling is ready to be replanted in a tree patch.", "archery_ticket_price": "0", - "id": "5371" + "durability": null, + "weight": "0.9" }, { - "examine": "This sapling is ready to be planted in a tree patch.", - "durability": null, + "id": "5372", "name": "Maple sapling", - "weight": "0.9", + "examine": "This sapling is ready to be planted in a tree patch.", "archery_ticket_price": "0", - "id": "5372" + "durability": null, + "weight": "0.9" }, { - "examine": "This sapling is ready to be planted in a tree patch.", - "durability": null, + "id": "5373", "name": "Yew sapling", - "weight": "0.9", + "examine": "This sapling is ready to be planted in a tree patch.", "archery_ticket_price": "0", - "id": "5373" + "durability": null, + "weight": "0.9" }, { - "examine": "This sapling is ready to be planted in a tree patch.", - "durability": null, + "id": "5374", "name": "Magic sapling", - "weight": "0.9", + "examine": "This sapling is ready to be planted in a tree patch.", "archery_ticket_price": "0", - "id": "5374" + "durability": null, + "weight": "0.9" }, { - "examine": "This sapling is ready to be planted in a tree patch.", - "durability": null, + "id": "5375", "name": "Spirit sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5375" - }, - { - "shop_price": "1", - "ge_buy_limit": "1000", - "examine": "An empty fruit basket.", - "grand_exchange_price": "1", - "durability": null, - "name": "Basket", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5376" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1", - "durability": null, - "name": "Basket", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5377" - }, - { - "examine": "A fruit basket filled with apples.", - "grand_exchange_price": "1889", - "durability": null, - "name": "Apples(1)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5378" - }, - { - "durability": null, - "name": "Apples(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5379" - }, - { - "examine": "A fruit basket filled with apples.", - "grand_exchange_price": "1889", - "durability": null, - "name": "Apples(2)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5380" - }, - { - "durability": null, - "name": "Apples(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5381" - }, - { - "examine": "A fruit basket filled with apples.", - "grand_exchange_price": "1889", - "durability": null, - "name": "Apples(3)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5382" - }, - { - "durability": null, - "name": "Apples(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5383" - }, - { - "examine": "A fruit basket filled with apples.", - "grand_exchange_price": "1889", - "durability": null, - "name": "Apples(4)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5384" - }, - { - "durability": null, - "name": "Apples(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5385" - }, - { - "ge_buy_limit": "1000", - "examine": "A fruit basket filled with apples.", - "grand_exchange_price": "2448", - "durability": null, - "name": "Apples(5)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5386" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2448", - "durability": null, - "name": "Apples(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5387" - }, - { - "examine": "A fruit basket filled with oranges", - "grand_exchange_price": "3823", - "durability": null, - "name": "Oranges(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5388" - }, - { - "durability": null, - "name": "Oranges(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5389" - }, - { - "examine": "A fruit basket filled with oranges", - "grand_exchange_price": "3823", - "durability": null, - "name": "Oranges(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5390" - }, - { - "durability": null, - "name": "Oranges(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5391" - }, - { - "examine": "A fruit basket filled with oranges", - "grand_exchange_price": "3823", - "durability": null, - "name": "Oranges(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5392" - }, - { - "durability": null, - "name": "Oranges(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5393" - }, - { - "examine": "A fruit basket filled with oranges", - "grand_exchange_price": "3823", - "durability": null, - "name": "Oranges(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5394" - }, - { - "durability": null, - "name": "Oranges(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5395" - }, - { - "shop_price": "70", - "ge_buy_limit": "1000", - "examine": "A common fruit.", - "grand_exchange_price": "5649", - "durability": null, - "name": "Oranges(5)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5396" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "5649", - "durability": null, - "name": "Oranges(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5397" - }, - { - "examine": "A fruit basket filled with strawberries.", - "grand_exchange_price": "5", - "durability": null, - "name": "Strawberries(1)", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "5398" - }, - { - "durability": null, - "name": "Strawberries(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5399" - }, - { - "examine": "A fruit basket filled with strawberries.", - "grand_exchange_price": "5", - "durability": null, - "name": "Strawberries(2)", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "5400" - }, - { - "durability": null, - "name": "Strawberries(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5401" - }, - { - "examine": "A fruit basket filled with strawberries.", - "grand_exchange_price": "5", - "durability": null, - "name": "Strawberries(3)", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "5402" - }, - { - "durability": null, - "name": "Strawberries(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5403" - }, - { - "examine": "A fruit basket filled with strawberries.", - "grand_exchange_price": "5", - "durability": null, - "name": "Strawberries(4)", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "5404" - }, - { - "durability": null, - "name": "Strawberries(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5405" - }, - { - "ge_buy_limit": "10000", - "examine": "A fruit basket filled with strawberries.", - "grand_exchange_price": "406", - "durability": null, - "name": "Strawberries(5)", - "tradeable": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "5406" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "406", - "durability": null, - "name": "Strawberries(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5407" - }, - { - "examine": "A fruit basket filled with bananas.", - "grand_exchange_price": "1064", - "durability": null, - "name": "Bananas(1)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5408" - }, - { - "durability": null, - "name": "Bananas(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5409" - }, - { - "examine": "A fruit basket filled with bananas.", - "grand_exchange_price": "1064", - "durability": null, - "name": "Bananas(2)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5410" - }, - { - "durability": null, - "name": "Bananas(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5411" - }, - { - "examine": "A fruit basket filled with bananas.", - "grand_exchange_price": "1064", - "durability": null, - "name": "Bananas(3)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5412" - }, - { - "durability": null, - "name": "Bananas(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5413" - }, - { - "shop_price": "2", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "171", - "durability": null, - "name": "Bananas(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5414" - }, - { - "durability": null, - "name": "Bananas(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5415" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "1637", - "durability": null, - "name": "Bananas(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5416" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1637", - "durability": null, - "name": "Bananas(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5417" - }, - { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "An empty sack.", - "grand_exchange_price": "45", - "durability": null, - "name": "Empty sack", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "5418" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "45", - "durability": null, - "name": "Empty sack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5419" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(1)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5420" - }, - { - "durability": null, - "name": "Potatoes(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5421" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(2)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5422" - }, - { - "durability": null, - "name": "Potatoes(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5423" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(3)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5424" - }, - { - "durability": null, - "name": "Potatoes(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5425" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(4)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5426" - }, - { - "durability": null, - "name": "Potatoes(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5427" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(5)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5428" - }, - { - "durability": null, - "name": "Potatoes(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5429" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(6)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5430" - }, - { - "durability": null, - "name": "Potatoes(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5431" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(7)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5432" - }, - { - "durability": null, - "name": "Potatoes(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5433" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(8)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5434" - }, - { - "durability": null, - "name": "Potatoes(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5435" - }, - { - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1276", - "durability": null, - "name": "Potatoes(9)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5436" - }, - { - "durability": null, - "name": "Potatoes(9)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5437" - }, - { - "ge_buy_limit": "1000", - "examine": "There are <number of potatoes> in this sack.", - "grand_exchange_price": "1052", - "durability": null, - "name": "Potatoes(10)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5438" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1052", - "durability": null, - "name": "Potatoes(10)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5439" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(1)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5440" - }, - { - "durability": null, - "name": "Onions(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5441" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(2)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5442" - }, - { - "durability": null, - "name": "Onions(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5443" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(3)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5444" - }, - { - "durability": null, - "name": "Onions(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5445" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(4)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5446" - }, - { - "durability": null, - "name": "Onions(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5447" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(5)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5448" - }, - { - "durability": null, - "name": "Onions(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5449" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(6)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5450" - }, - { - "durability": null, - "name": "Onions(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5451" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(7)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5452" - }, - { - "durability": null, - "name": "Onions(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5453" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(8)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5454" - }, - { - "durability": null, - "name": "Onions(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5455" - }, - { - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "589", - "durability": null, - "name": "Onions(9)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5456" - }, - { - "durability": null, - "name": "Onions(9)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5457" - }, - { - "ge_buy_limit": "1000", - "examine": "There are/is (1-10) onion(s) in this sack.", - "grand_exchange_price": "489", - "durability": null, - "name": "Onions(10)", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5458" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "489", - "durability": null, - "name": "Onions(10)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5459" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5460" - }, - { - "durability": null, - "name": "Cabbages(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5461" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5462" - }, - { - "durability": null, - "name": "Cabbages(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5463" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5464" - }, - { - "durability": null, - "name": "Cabbages(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5465" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5466" - }, - { - "durability": null, - "name": "Cabbages(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5467" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5468" - }, - { - "durability": null, - "name": "Cabbages(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5469" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5470" - }, - { - "durability": null, - "name": "Cabbages(6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5471" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5472" - }, - { - "durability": null, - "name": "Cabbages(7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5473" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5474" - }, - { - "durability": null, - "name": "Cabbages(8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5475" - }, - { - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "1304", - "durability": null, - "name": "Cabbages(9)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5476" - }, - { - "durability": null, - "name": "Cabbages(9)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5477" - }, - { - "ge_buy_limit": "1000", - "examine": "There are <number of cabbages> in this sack.", - "grand_exchange_price": "917", - "durability": null, - "name": "Cabbages(10)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5478" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "917", - "durability": null, - "name": "Cabbages(10)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5479" - }, - { - "examine": "An apple tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Apple seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5480" - }, - { - "examine": "A banana tree seed has been sown and watered in this plant pot.", - "durability": null, - "name": "Banana seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5481" - }, - { - "examine": "An orange tree seed has been sown (and watered)in this plant pot.", - "durability": null, - "name": "Orange seedling", - "archery_ticket_price": "0", - "id": "5482" - }, - { - "examine": "A curry seed has been sown and watered in this plant pot.", - "durability": null, - "name": "Curry seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5483" - }, - { - "examine": "A pineapple tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Pineapple seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5484" - }, - { - "examine": "This needs watering before it will grow.", - "durability": null, - "name": "Papaya seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5485" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Palm seedling", - "weight": "0.66", - "archery_ticket_price": "0", - "id": "5486" - }, - { - "examine": "A calquat tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Calquat seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5487" - }, - { - "examine": "An apple tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Apple seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5488" - }, - { - "examine": "A banana tree seed has been sown and watered in this plant pot.", - "durability": null, - "name": "Banana seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5489" - }, - { - "examine": "An orange tree seed has been sown (and watered)in this plant pot.", - "durability": null, - "name": "Orange seedling", - "archery_ticket_price": "0", - "id": "5490" - }, - { - "examine": "A curry seed has been sown and watered in this plant pot.", - "durability": null, - "name": "Curry seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5491" - }, - { - "examine": "A pineapple tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Pineapple seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5492" - }, - { - "examine": "This needs watering before it will grow.", - "durability": null, - "name": "Papaya seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5493" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Palm seedling", - "weight": "0.66", - "archery_ticket_price": "0", - "id": "5494" - }, - { - "examine": "A calquat tree has been sown (and watered) in this plant pot.", - "durability": null, - "name": "Calquat seedling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5495" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Apple sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5496" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Banana sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5497" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Orange sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5498" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Curry sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5499" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Pineapple sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5500" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Papaya sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5501" - }, - { - "examine": "This sapling is ready to be replanted in a fruit tree patch.", - "durability": null, - "name": "Palm sapling", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5502" - }, - { "examine": "This sapling is ready to be planted in a tree patch.", + "archery_ticket_price": "0", "durability": null, + "weight": "0.9" + }, + { + "id": "5376", + "name": "Basket", + "examine": "An empty fruit basket.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "5377", + "name": "Basket", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5378", + "name": "Apples(1)", + "examine": "A fruit basket filled with apples.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5379", + "name": "Apples(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5380", + "name": "Apples(2)", + "examine": "A fruit basket filled with apples.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5381", + "name": "Apples(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5382", + "name": "Apples(3)", + "examine": "A fruit basket filled with apples.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5383", + "name": "Apples(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5384", + "name": "Apples(4)", + "examine": "A fruit basket filled with apples.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5385", + "name": "Apples(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5386", + "name": "Apples(5)", + "examine": "A fruit basket filled with apples.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5387", + "name": "Apples(5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5388", + "name": "Oranges(1)", + "examine": "A fruit basket filled with oranges", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5389", + "name": "Oranges(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5390", + "name": "Oranges(2)", + "examine": "A fruit basket filled with oranges", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5391", + "name": "Oranges(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5392", + "name": "Oranges(3)", + "examine": "A fruit basket filled with oranges", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5393", + "name": "Oranges(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5394", + "name": "Oranges(4)", + "examine": "A fruit basket filled with oranges", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5395", + "name": "Oranges(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5396", + "name": "Oranges(5)", + "examine": "A common fruit.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "70", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5397", + "name": "Oranges(5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5398", + "name": "Strawberries(1)", + "examine": "A fruit basket filled with strawberries.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "5399", + "name": "Strawberries(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5400", + "name": "Strawberries(2)", + "examine": "A fruit basket filled with strawberries.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "5401", + "name": "Strawberries(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5402", + "name": "Strawberries(3)", + "examine": "A fruit basket filled with strawberries.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "5403", + "name": "Strawberries(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5404", + "name": "Strawberries(4)", + "examine": "A fruit basket filled with strawberries.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "5405", + "name": "Strawberries(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5406", + "name": "Strawberries(5)", + "examine": "A fruit basket filled with strawberries.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "5407", + "name": "Strawberries(5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5408", + "name": "Bananas(1)", + "examine": "A fruit basket filled with bananas.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5409", + "name": "Bananas(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5410", + "name": "Bananas(2)", + "examine": "A fruit basket filled with bananas.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5411", + "name": "Bananas(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5412", + "name": "Bananas(3)", + "examine": "A fruit basket filled with bananas.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5413", + "name": "Bananas(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5414", + "name": "Bananas(4)", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5415", + "name": "Bananas(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5416", + "name": "Bananas(5)", + "examine": "Mmm this looks tasty.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5417", + "name": "Bananas(5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5418", + "name": "Empty sack", + "examine": "An empty sack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "5419", + "name": "Empty sack", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5420", + "name": "Potatoes(1)", + "examine": "There is one potato in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5421", + "name": "Potatoes(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5422", + "name": "Potatoes(2)", + "examine": "There are two potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5423", + "name": "Potatoes(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5424", + "name": "Potatoes(3)", + "examine": "There are three potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5425", + "name": "Potatoes(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5426", + "name": "Potatoes(4)", + "examine": "There are four potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5427", + "name": "Potatoes(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5428", + "name": "Potatoes(5)", + "examine": "There are five potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5429", + "name": "Potatoes(5)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5430", + "name": "Potatoes(6)", + "examine": "There are six potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5431", + "name": "Potatoes(6)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5432", + "name": "Potatoes(7)", + "examine": "There are seven potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5433", + "name": "Potatoes(7)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5434", + "name": "Potatoes(8)", + "examine": "There are eight potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5435", + "name": "Potatoes(8)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5436", + "name": "Potatoes(9)", + "examine": "There are nine potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5437", + "name": "Potatoes(9)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5438", + "name": "Potatoes(10)", + "examine": "There are ten potatoes in this sack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5439", + "name": "Potatoes(10)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5440", + "name": "Onions(1)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5441", + "name": "Onions(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5442", + "name": "Onions(2)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5443", + "name": "Onions(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5444", + "name": "Onions(3)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5445", + "name": "Onions(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5446", + "name": "Onions(4)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5447", + "name": "Onions(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5448", + "name": "Onions(5)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5449", + "name": "Onions(5)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5450", + "name": "Onions(6)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5451", + "name": "Onions(6)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5452", + "name": "Onions(7)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5453", + "name": "Onions(7)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5454", + "name": "Onions(8)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5455", + "name": "Onions(8)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5456", + "name": "Onions(9)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5457", + "name": "Onions(9)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5458", + "name": "Onions(10)", + "examine": "There are/is (1-10) onion(s) in this sack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5459", + "name": "Onions(10)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5460", + "name": "Cabbages(1)", + "examine": "There is one cabbage in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5461", + "name": "Cabbages(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5462", + "name": "Cabbages(2)", + "examine": "There are two cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5463", + "name": "Cabbages(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5464", + "name": "Cabbages(3)", + "examine": "There are three cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5465", + "name": "Cabbages(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5466", + "name": "Cabbages(4)", + "examine": "There are four cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5467", + "name": "Cabbages(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5468", + "name": "Cabbages(5)", + "examine": "There are five cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5469", + "name": "Cabbages(5)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5470", + "name": "Cabbages(6)", + "examine": "There are six cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5471", + "name": "Cabbages(6)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5472", + "name": "Cabbages(7)", + "examine": "There are seven cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5473", + "name": "Cabbages(7)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5474", + "name": "Cabbages(8)", + "examine": "There are eight cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5475", + "name": "Cabbages(8)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5476", + "name": "Cabbages(9)", + "examine": "There are nine cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5477", + "name": "Cabbages(9)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5478", + "name": "Cabbages(10)", + "examine": "There are ten cabbages in this sack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5479", + "name": "Cabbages(10)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5480", + "name": "Apple seedling", + "examine": "An apple tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5481", + "name": "Banana seedling", + "examine": "A banana tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5482", + "name": "Orange seedling", + "examine": "An orange tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "5483", + "name": "Curry seedling", + "examine": "A curry tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5484", + "name": "Pineapple seedling", + "examine": "A pineapple plant seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5485", + "name": "Papaya seedling", + "examine": "A papaya tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5486", + "name": "Palm seedling", + "examine": "A palm tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.66" + }, + { + "id": "5487", + "name": "Calquat seedling", + "examine": "A calquat tree seed has been sown in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5488", + "name": "Apple seedling", + "examine": "An apple tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5489", + "name": "Banana seedling", + "examine": "A banana tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5490", + "name": "Orange seedling", + "examine": "An orange tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "5491", + "name": "Curry seedling", + "examine": "A curry tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5492", + "name": "Pineapple seedling", + "examine": "A pineapple plant seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5493", + "name": "Papaya seedling", + "examine": "A papaya tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5494", + "name": "Palm seedling", + "examine": "A palm tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.66" + }, + { + "id": "5495", + "name": "Calquat seedling", + "examine": "A calquat tree seed has been sown and watered in this plant pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5496", + "name": "Apple sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5497", + "name": "Banana sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5498", + "name": "Orange sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5499", + "name": "Curry sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5500", + "name": "Pineapple sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5501", + "name": "Papaya sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5502", + "name": "Palm sapling", + "examine": "This sapling is ready to be replanted in a fruit tree patch.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "5503", "name": "Calquat sapling", - "weight": "0.9", + "examine": "This sapling is ready to be planted in a tree patch.", "archery_ticket_price": "0", - "id": "5503" + "durability": null, + "weight": "0.9" }, { - "ge_buy_limit": "10000", + "id": "5504", + "name": "Strawberry", "examine": "A freshly picked strawberry.", - "grand_exchange_price": "23", - "durability": null, - "name": "Strawberry", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "5504" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "23", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "5505", "name": "Strawberry", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5505" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "The Wise Old Man of Draynor Village asked you to take this to someone.", - "durability": null, + "id": "5506", "name": "Old man's message", + "examine": "The Wise Old Man of Draynor Village asked you to take this to someone.", "archery_ticket_price": "0", - "id": "5506" + "durability": null }, { - "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", - "durability": null, + "id": "5507", "name": "Strange book", - "archery_ticket_price": "0", - "id": "5507" - }, - { "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "5508", "name": "Book of folklore", + "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", "archery_ticket_price": "0", - "id": "5508" + "durability": null }, { - "examine": "A small pouch used for storing essence.", - "durability": null, + "id": "5509", "name": "Small pouch", - "weight": "1", + "examine": "A small pouch used for storing essence.", "archery_ticket_price": "0", - "id": "5509" + "durability": null, + "weight": "1" }, { - "examine": "A medium-sized pouch used for storing essence.", - "durability": null, + "id": "5510", "name": "Medium pouch", - "weight": "1", - "archery_ticket_price": "0", - "id": "5510" - }, - { "examine": "A medium-sized pouch used for storing essence.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "5511", "name": "Medium pouch", - "weight": "1", + "examine": "A medium-sized pouch used for storing essence.", "archery_ticket_price": "0", - "id": "5511" + "durability": null, + "weight": "1" }, { - "shop_price": "25000", - "examine": "A large pouch used for storing essence.", - "durability": null, + "id": "5512", "name": "Large pouch", - "weight": "1", - "archery_ticket_price": "0", - "id": "5512" - }, - { - "shop_price": "25000", "examine": "A large pouch used for storing essence.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "25000", + "weight": "1" + }, + { + "id": "5513", "name": "Large pouch", - "weight": "1", + "examine": "A large pouch used for storing essence.", "archery_ticket_price": "0", - "id": "5513" - }, - { - "shop_price": "50000", - "examine": "A giant-sized pouch used for storing essence.", "durability": null, - "name": "Giant pouch", - "weight": "1", - "archery_ticket_price": "0", - "id": "5514" + "shop_price": "25000", + "weight": "1" }, { - "shop_price": "50000", + "id": "5514", + "name": "Giant pouch", "examine": "A giant-sized pouch used for storing essence.", - "durability": null, - "name": "Giant pouch", - "weight": "1", "archery_ticket_price": "0", - "id": "5515" + "durability": null, + "shop_price": "50000", + "weight": "1" }, { - "ge_buy_limit": "500", + "id": "5515", + "name": "Giant pouch", + "examine": "A giant-sized pouch used for storing essence.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "50000", + "weight": "1" + }, + { + "id": "5516", + "name": "Elemental talisman", "examine": "A mysterious power emanates from the talisman...", - "grand_exchange_price": "186900", - "durability": null, - "name": "Elemental talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5516" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "186900", - "durability": null, + "tradeable": "true" + }, + { + "id": "5517", "name": "Elemental talisman", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5517" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "examine": "When empty:", - "durability": null, + "id": "5518", "name": "Scrying orb", - "weight": "1", + "examine": "This contains mystical teleport information...", "archery_ticket_price": "0", - "id": "5518" + "durability": null, + "weight": "1" }, { - "examine": "When empty:", - "durability": null, + "id": "5519", "name": "Scrying orb", - "weight": "1", + "examine": "This orb apparently contains a cypher spell.", "archery_ticket_price": "0", - "id": "5519" + "durability": null, + "weight": "1" }, { - "examine": "Some research notes on abyssal space.", - "durability": null, + "id": "5520", "name": "Abyssal book", - "tradeable": "true", + "examine": "Some research notes on abyssal space.", "archery_ticket_price": "0", - "id": "5520" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A necklace embedded with mystical power.", - "grand_exchange_price": "720", - "durability": null, - "name": "Binding necklace", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "5521", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "720", - "durability": null, "name": "Binding necklace", - "tradeable": "true", + "examine": "A necklace embedded with mystical power.", "archery_ticket_price": "0", - "id": "5522" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "100", + "id": "5522", + "name": "Binding necklace", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5523", + "name": "Tiara mould", "examine": "A mould for tiaras.", - "grand_exchange_price": "315", - "durability": null, - "name": "Tiara mould", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "5523" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "315", - "durability": null, - "name": "Tiara mould", + "shop_price": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5524" + "weight": "1" }, { - "ge_buy_limit": "1000", - "examine": "Makes me feel like a Princess.", - "grand_exchange_price": "13", - "durability": null, - "name": "Tiara", - "tradeable": "true", - "weight": "1", + "id": "5524", + "name": "Tiara mould", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5525", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, "name": "Tiara", - "tradeable": "true", + "examine": "Makes me feel like a Princess.", "archery_ticket_price": "0", - "id": "5526" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of air.", - "grand_exchange_price": "5", - "durability": null, - "name": "Air tiara", - "tradeable": "true", - "weight": "1", + "id": "5526", + "name": "Tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "5527", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5", - "durability": null, "name": "Air tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of air.", "archery_ticket_price": "0", - "id": "5528" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of the mind.", - "grand_exchange_price": "10", - "durability": null, - "name": "Mind tiara", - "tradeable": "true", - "weight": "1", + "id": "5528", + "name": "Air tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5529", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10", - "durability": null, "name": "Mind tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of the mind.", "archery_ticket_price": "0", - "id": "5530" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of water.", - "grand_exchange_price": "1351", - "durability": null, - "name": "Water tiara", - "tradeable": "true", - "weight": "1", + "id": "5530", + "name": "Mind tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5531", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1351", - "durability": null, "name": "Water tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of water.", "archery_ticket_price": "0", - "id": "5532" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of the body.", - "grand_exchange_price": "5", - "durability": null, - "name": "Body tiara", - "tradeable": "true", - "weight": "1", + "id": "5532", + "name": "Water tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5533", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5", - "durability": null, "name": "Body tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of the body.", "archery_ticket_price": "0", - "id": "5534" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of the earth.", - "grand_exchange_price": "4", - "durability": null, - "name": "Earth tiara", - "tradeable": "true", - "weight": "1", + "id": "5534", + "name": "Body tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5535", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4", - "durability": null, "name": "Earth tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of the earth.", "archery_ticket_price": "0", - "id": "5536" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of fire.", - "grand_exchange_price": "8", - "durability": null, - "name": "Fire tiara", - "tradeable": "true", - "weight": "1", + "id": "5536", + "name": "Earth tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5537", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, "name": "Fire tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of fire.", "archery_ticket_price": "0", - "id": "5538" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of the cosmos.", - "grand_exchange_price": "8", - "durability": null, - "name": "Cosmic tiara", - "tradeable": "true", - "weight": "1", + "id": "5538", + "name": "Fire tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5539", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, "name": "Cosmic tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of the cosmos.", "archery_ticket_price": "0", - "id": "5540" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of nature.", - "grand_exchange_price": "10", - "durability": null, - "name": "Nature tiara", - "tradeable": "true", - "weight": "1", + "id": "5540", + "name": "Cosmic tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5541", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10", - "durability": null, "name": "Nature tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of nature.", "archery_ticket_price": "0", - "id": "5542" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of chaos.", - "grand_exchange_price": "10", - "durability": null, - "name": "Chaos tiara", - "tradeable": "true", - "weight": "1", + "id": "5542", + "name": "Nature tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5543", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10", - "durability": null, "name": "Chaos tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of chaos.", "archery_ticket_price": "0", - "id": "5544" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of law.", - "grand_exchange_price": "11", - "durability": null, - "name": "Law tiara", - "tradeable": "true", - "weight": "1", + "id": "5544", + "name": "Chaos tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5545", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11", - "durability": null, "name": "Law tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of law.", "archery_ticket_price": "0", - "id": "5546" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of death.", - "grand_exchange_price": "65", - "durability": null, - "name": "Death tiara", - "tradeable": "true", - "weight": "1", + "id": "5546", + "name": "Law tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5547", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "65", - "durability": null, "name": "Death tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of death.", "archery_ticket_price": "0", - "id": "5548" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A tiara infused with the properties of Blood.", - "grand_exchange_price": "1058", - "durability": null, - "name": "Blood tiara", - "tradeable": "true", - "weight": "1", + "id": "5548", + "name": "Death tiara", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5549", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1058", - "durability": null, "name": "Blood tiara", - "tradeable": "true", + "examine": "A tiara infused with the properties of Blood.", "archery_ticket_price": "0", - "id": "5550" - }, - { + "durability": null, + "equipment_slot": "0", "ge_buy_limit": "100", - "examine": " A tiara infused with the properties of the soul.", - "durability": null, - "name": "Soul tiara", "tradeable": "true", + "weight": "1" + }, + { + "id": "5550", + "name": "Blood tiara", "archery_ticket_price": "0", - "weight": "1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5551", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Soul tiara", + "examine": " A tiara infused with the properties of the soul.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5552" + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Black banded leather armour, a rogue's dream!", - "durability": null, - "name": "Rogue top", - "weight": "0.4", + "id": "5552", + "name": "Soul tiara", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "5553", + "name": "Rogue top", + "examine": "Black banded leather armour, a rogue's dream!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,10,10,10,10,10,5,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "0.4" }, { - "remove_head": "true", - "examine": "Black banded leather armour, a rogue's dream!", - "durability": null, - "name": "Rogue mask", - "weight": "0.5", - "archery_ticket_price": "0", "id": "5554", - "bonuses": "0,0,0,0,0,5,5,5,5,5,0,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" - }, - { + "name": "Rogue mask", "examine": "Black banded leather armour, a rogue's dream!", - "durability": null, - "name": "Rogue trousers", - "weight": "0.4", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,5,5,5,5,5,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "weight": "0.5" + }, + { "id": "5555", + "name": "Rogue trousers", + "examine": "Black banded leather armour, a rogue's dream!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,7,7,7,7,7,0,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "weight": "0.4" }, { - "examine": "Black banded leather gloves, a rogue's dream!", - "durability": null, - "name": "Rogue gloves", - "weight": "0.4", - "archery_ticket_price": "0", "id": "5556", + "name": "Rogue gloves", + "examine": "Black banded leather gloves, a rogue's dream!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,2,2,2,2,2,2,0,0,0,0", - "equipment_slot": "9" + "durability": null, + "equipment_slot": "9", + "weight": "0.4" }, { - "examine": "Black banded leather boots, a rogue's dream!", - "durability": null, - "name": "Rogue boots", - "weight": "0.4", - "archery_ticket_price": "0", "id": "5557", + "name": "Rogue boots", + "examine": "Black banded leather boots, a rogue's dream!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,2,2,2,2,2,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "weight": "0.4" }, { - "shop_price": "35", - "examine": "It can do almost anything!", - "durability": null, + "id": "5558", "name": "Rogue kit", + "examine": "It can do almost anything!", "archery_ticket_price": "0", - "id": "5558" + "durability": null, + "shop_price": "35" }, { - "examine": "A small satchel of bright powder!", - "durability": null, + "id": "5559", "name": "Flash powder", - "weight": "1", + "examine": "A small satchel of bright powder!", "archery_ticket_price": "0", - "id": "5559" + "durability": null, + "weight": "1" }, { - "shop_price": "10", - "examine": "A useful hearing aid.", - "durability": null, - "name": "Stethoscope", - "archery_ticket_price": "0", "id": "5560", - "equipment_slot": "5" + "name": "Stethoscope", + "examine": "A useful hearing aid.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "shop_price": "10" }, { - "examine": "I can escape the Maze with this!", - "durability": null, + "id": "5561", "name": "Mystic jewel", + "examine": "I can escape the Maze with this!", "archery_ticket_price": "0", - "id": "5561" + "durability": null }, { - "examine": "A fraction of a roof.", - "durability": null, + "id": "5568", "name": "Picture", - "weight": "0.6", + "examine": "A fraction of a roof.", "archery_ticket_price": "0", - "id": "5568" + "durability": null, + "weight": "0.6" }, { + "id": "5574", + "name": "Initiate sallet", + "examine": "An initiate Temple Knight's helm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,13,14,11,-1,13,5,0,3,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,20}", "shop_price": "6000", - "ge_buy_limit": "100", - "examine": "An initiate Temple Knight's helm.", - "durability": null, - "weight": "2.2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "3512", - "name": "Initiate sallet", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5574", - "bonuses": "0,0,0,-6,-2,13,14,11,-1,13,5,0,3,0,0" + "weight": "2.2" }, { + "id": "5575", + "name": "Initiate hauberk", + "examine": "An initiate Temple Knight's Armour.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,6,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", "requirements": "{1,20}", "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "An initiate Temple Knight's Armour.", - "durability": null, - "weight": "8", - "absorb": "1,0,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "5776", - "name": "Initiate hauberk", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5575", - "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,6,0,0" + "weight": "8" }, { + "id": "5576", + "name": "Initiate cuisse", + "examine": "An initiate Temple Knight's leg armour.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,5,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{1,20}", "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "An initiate Temple Knight's leg armour.", - "durability": null, - "weight": "7", - "absorb": "1,0,2", - "equipment_slot": "7", - "grand_exchange_price": "4574", - "name": "Initiate cuisse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5576", - "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,5,0,0" + "weight": "7" }, { - "examine": "A vial of something labelled 'Cupric Sulfate'.", - "durability": null, + "id": "5577", "name": "Cupric sulphate", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Cupric Sulfate'.", "archery_ticket_price": "0", - "id": "5577" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled 'Acetic Acid'.", - "durability": null, + "id": "5578", "name": "Acetic acid", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Acetic Acid'.", "archery_ticket_price": "0", - "id": "5578" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled 'Gypsum'.", - "durability": null, + "id": "5579", "name": "Gypsum", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Gypsum'.", "archery_ticket_price": "0", - "id": "5579" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled 'Sodium Chloride'.", - "durability": null, + "id": "5580", "name": "Sodium chloride", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Sodium Chloride'.", "archery_ticket_price": "0", - "id": "5580" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled 'Nitrous Oxide'.", - "durability": null, + "id": "5581", "name": "Nitrous oxide", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Nitrous Oxide'.", "archery_ticket_price": "0", - "id": "5581" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled Dihydrogen Monoxide.", - "durability": null, + "id": "5582", "name": "Vial of liquid", + "examine": "A vial of something labelled Dihydrogen Monoxide.", "archery_ticket_price": "0", - "id": "5582" + "durability": null }, { - "examine": "A vial of something labelled 'Powdered Tin Ore'.", - "durability": null, + "id": "5583", "name": "Tin ore powder", - "tradeable": "false", - "destroy": "true", + "examine": "A vial of something labelled 'Powdered Tin Ore'.", "archery_ticket_price": "0", - "id": "5583" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A vial of something labelled 'Powdered Cupric Ore'.", - "durability": null, + "id": "5584", "name": "Cupric ore powder", - "tradeable": "false", + "examine": "A vial of something labelled 'Powdered Cupric Ore'.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "5584" + "durability": null, + "tradeable": "false" }, { - "examine": "It's a metal spade without a handle.", + "id": "5585", + "name": "Bronze Key", + "examine": "I hope the mould was accurate enough...", + "archery_ticket_price": "0", "durability": null, + "weight": "0.01" + }, + { + "id": "5586", "name": "Metal spade", - "weight": "2.5", + "examine": "It's a metal spade with a wooden handle.", "archery_ticket_price": "0", - "id": "5586" + "durability": null, + "weight": "1.814" }, { - "examine": "It's a metal spade without a handle.", - "durability": null, + "id": "5587", "name": "Metal spade", - "weight": "2.5", + "examine": "It's a metal spade without a handle.", "archery_ticket_price": "0", - "id": "5587" + "durability": null, + "weight": "1.814" }, { - "examine": "Looks like a pretty boring read.", - "durability": null, + "id": "5588", "name": "Alchemical notes", + "examine": "Looks like a pretty boring read.", "archery_ticket_price": "0", - "id": "5588" + "durability": null }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5592", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "A tin layered with some stuff from a vial.", "archery_ticket_price": "0", - "id": "5592" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5593", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "It's full of a white lumpy mixture that seems to be hardening.", "archery_ticket_price": "0", - "id": "5593" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5594", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is an impression of a key embedded in it.", "archery_ticket_price": "0", - "id": "5594" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5595", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is an impression of a key, filled with tin ore.", "archery_ticket_price": "0", - "id": "5595" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5596", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is an impression of a key, filled with copper ore.", "archery_ticket_price": "0", - "id": "5596" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5597", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is an impression of a key, filled with tin and copper ore.", "archery_ticket_price": "0", - "id": "5597" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5598", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is a bronze key surrounded by plaster in this tin.", "archery_ticket_price": "0", - "id": "5598" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5599", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "There is a strange concoction filling this tin.", "archery_ticket_price": "0", - "id": "5599" + "durability": null, + "shop_price": "20", + "tradeable": "false", + "weight": "0.101" }, { - "shop_price": "6", - "examine": "This needs refining.", - "grand_exchange_price": "42", - "durability": null, + "id": "5600", "name": "Tin", - "tradeable": "true", - "weight": "2.25", + "examine": "I could probably pour something into this.", "archery_ticket_price": "0", - "id": "5600" + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" }, { - "shop_price": "14", - "examine": "Good for detailed crafting.", - "grand_exchange_price": "33", - "durability": null, + "id": "5601", "name": "Chisel", - "tradeable": "true", - "weight": "0.4", + "examine": "Good for detailed crafting.", "archery_ticket_price": "0", - "id": "5601" + "durability": null, + "shop_price": "14", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "Useful for crafting items.", - "grand_exchange_price": "237", - "durability": null, + "id": "5602", "name": "Bronze wire", - "tradeable": "true", + "examine": "Useful for crafting items.", "archery_ticket_price": "0", - "id": "5602" + "durability": null, + "tradeable": "true" }, { - "shop_price": "1", - "examine": "For shearing sheep.", - "grand_exchange_price": "41", - "durability": null, + "id": "5603", "name": "Shears", - "tradeable": "true", - "weight": "0.1", + "examine": "For shearing sheep.", "archery_ticket_price": "0", - "id": "5603" - }, - { - "examine": "A very attractive magnet.", "durability": null, - "name": "Magnet", - "archery_ticket_price": "0", - "id": "5604" - }, - { - "shop_price": "25", - "examine": "A dangerous looking knife.", - "grand_exchange_price": "102", - "durability": null, - "name": "Knife", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "5605" - }, - { - "examine": "I can exchange this for one free makeover with the makeover mage.", - "durability": null, - "name": "Makeover voucher", - "weight": "1", - "archery_ticket_price": "0", - "id": "5606" - }, - { - "shop_price": "2", - "examine": "Some wheat heads.", - "grand_exchange_price": "20", - "durability": null, - "name": "Grain", - "tradeable": "true", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "5607", - "equipment_slot": "1" - }, - { - "examine": "A cunning animal.", - "durability": null, - "name": "Fox", - "archery_ticket_price": "0", - "id": "5608", - "equipment_slot": "3" - }, - { - "examine": "Normal: Yep. Definitely a chicken.", - "durability": null, - "name": "Chicken", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "5609", - "equipment_slot": "5" - }, - { - "examine": "There's not much sand left in the top half...", - "durability": null, - "name": "Hourglass", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "5610" - }, - { - "durability": null, - "name": "Initiate sallet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5611" - }, - { - "durability": null, - "name": "Initiate hauberk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5612" - }, - { - "durability": null, - "name": "Initiate cuisse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5613" - }, - { - "turn90cw_anim": "330", - "walk_anim": "330", - "durability": null, - "turn90ccw_anim": "330", - "turn180_anim": "330", - "render_anim": "191", - "equipment_slot": "3", - "stand_anim": "330", - "name": "Magic carpet", - "run_anim": "330", - "archery_ticket_price": "0", - "id": "5614", - "stand_turn_anim": "330" - }, - { - "examine": "A pot of crushed [type of bones].", - "durability": null, - "name": "Bonemeal", - "weight": "1", - "archery_ticket_price": "0", - "id": "5615" - }, - { - "shop_price": "7", - "ge_buy_limit": "10000", - "examine": "Arrows with bronze heads.", - "grand_exchange_price": "127", - "durability": null, - "name": "Bronze arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5616", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "grand_exchange_price": "138", - "durability": null, - "name": "Iron arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5617", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "grand_exchange_price": "159", - "durability": null, - "name": "Steel arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5618", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "grand_exchange_price": "176", - "durability": null, - "name": "Mithril arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5619", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "grand_exchange_price": "211", - "durability": null, - "name": "Adamant arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5620", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "grand_exchange_price": "415", - "durability": null, - "name": "Rune arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5621", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { - "shop_price": "7", - "ge_buy_limit": "10000", - "examine": "Arrows with bronze heads.", - "grand_exchange_price": "1076", - "durability": null, - "name": "Bronze arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5622", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "grand_exchange_price": "1037", - "durability": null, - "name": "Iron arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5623", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "grand_exchange_price": "1038", - "durability": null, - "name": "Steel arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5624", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "grand_exchange_price": "718", - "durability": null, - "name": "Mithril arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5625", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "grand_exchange_price": "101", - "durability": null, - "name": "Adamant arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5626", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "grand_exchange_price": "1300", - "durability": null, - "name": "Rune arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5627", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a bronze tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "162", - "attack_audios": "2547,0,0,0", - "name": "Bronze dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5628", - "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1" - }, - { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an iron tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "164", - "attack_audios": "2547,0,0,0", - "name": "Iron dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5629", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "requirements": "{4,5}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a steel tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "173", - "attack_audios": "2547,0,0,0", - "name": "Steel dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5630", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "requirements": "{4,10}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a black tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "822", - "attack_audios": "2547,0,0,0", - "name": "Black dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5631", - "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a mithril tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "196", - "attack_audios": "2547,0,0,0", - "name": "Mithril dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5632", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an adamantite tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "244", - "attack_audios": "2547,0,0,0", - "name": "Adamant dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5633", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a rune tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "370", - "attack_audios": "2547,0,0,0", - "name": "Rune dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5634", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a bronze tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1733", - "attack_audios": "2547,0,0,0", - "name": "Bronze dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5635", - "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1" - }, - { - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an iron tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1687", - "attack_audios": "2547,0,0,0", - "name": "Iron dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5636", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "requirements": "{4,5}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a steel tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1654", - "attack_audios": "2547,0,0,0", - "name": "Steel dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5637", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "requirements": "{4,10}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a black tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "2422", - "attack_audios": "2547,0,0,0", - "name": "Black dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5638", - "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a mithril tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1660", - "attack_audios": "2547,0,0,0", - "name": "Mithril dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5639", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with an adamantite tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1352", - "attack_audios": "2547,0,0,0", - "name": "Adamant dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5640", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "1000", - "examine": "A deadly throwing dart with a rune tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "582,582,582,582", - "grand_exchange_price": "1109", - "attack_audios": "2547,0,0,0", - "name": "Rune dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5641", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "shop_price": "6", - "ge_buy_limit": "1000", - "examine": "A bronze-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "115", - "name": "Bronze javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5642", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6" - }, - { - "shop_price": "8", - "ge_buy_limit": "1000", - "examine": "An iron-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "112", - "name": "Iron javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5643", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,5}", - "shop_price": "37", - "ge_buy_limit": "1000", - "examine": "A steel-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "128", - "name": "Steel javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5644", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12" - }, - { - "requirements": "{4,20}", - "shop_price": "79", - "ge_buy_limit": "1000", - "examine": "A mithril tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "141", - "name": "Mithril javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5645", - "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "100", - "shop_price": "100", - "examine": "An adamant tipped javelin.", - "durability": null, - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "179", - "name": "Adamant javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5646", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28" - }, - { - "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "302", - "name": "Rune javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5647", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "A bronze-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1755", - "name": "Bronze jav'n(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5648", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6" - }, - { - "shop_price": "8", - "ge_buy_limit": "1000", - "examine": "An iron-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1756", - "name": "Iron javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5649", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,5}", - "shop_price": "37", - "ge_buy_limit": "1000", - "examine": "A steel-tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1769", - "name": "Steel javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5650", - "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12" - }, - { - "requirements": "{4,20}", - "shop_price": "79", - "ge_buy_limit": "1000", - "examine": "A mithril tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1736", - "name": "Mithril javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5651", - "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "100", - "shop_price": "100", - "examine": "An adamant tipped javelin.", - "durability": null, - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1514", - "name": "Adamant javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5652", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28" - }, - { - "requirements": "{4,40}", - "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "attack_speed": "6", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1521", - "name": "Rune javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5653", - "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42" - }, - { - "shop_price": "14", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "164", - "attack_audios": "2704,0,0,0", - "name": "Bronze knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5654", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "shop_price": "58", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "170", - "attack_audios": "2704,0,0,0", - "name": "Iron knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5655", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "requirements": "{4,5}", - "shop_price": "68", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "212", - "attack_audios": "2704,0,0,0", - "name": "Steel knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5656", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "100", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "330", - "attack_audios": "2704,0,0,0", - "name": "Mithril knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5657", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,10}", - "shop_price": "15", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "66", - "attack_audios": "2704,0,0,0", - "name": "Black knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5658", - "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "609", - "attack_audios": "2704,0,0,0", - "name": "Adamant knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5659", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1511", - "attack_audios": "2704,0,0,0", - "name": "Rune knife(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5660", - "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24" - }, - { - "shop_price": "14", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1514", - "attack_audios": "2704,0,0,0", - "name": "Bronze knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5661", - "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3" - }, - { - "shop_price": "58", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1212", - "attack_audios": "2704,0,0,0", - "name": "Iron knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5662", - "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4" - }, - { - "requirements": "{4,5}", - "shop_price": "68", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1579", - "attack_audios": "2704,0,0,0", - "name": "Steel knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5663", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "100", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1368", - "attack_audios": "2704,0,0,0", - "name": "Mithril knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5664", - "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10" - }, - { - "requirements": "{4,10}", - "shop_price": "15", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "103", - "attack_audios": "2704,0,0,0", - "name": "Black knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5665", - "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "100", - "examine": "A finely balanced throwing knife.", - "durability": null, - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "1505", - "attack_audios": "2704,0,0,0", - "name": "Adamant knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5666", - "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "A finely balanced throwing knife.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "806,806,806,806", - "grand_exchange_price": "2207", - "attack_audios": "2704,0,0,0", - "name": "Rune knife(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5667", - "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24" - }, - { - "shop_price": "38", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "483", - "attack_audios": "2517,2517,2500,2517", - "name": "Iron dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5668", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "483", - "durability": null, - "name": "Iron dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5669" - }, - { - "shop_price": "11", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "350", - "attack_audios": "2517,2517,2500,2517", - "name": "Bronze dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5670", - "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "350", - "durability": null, - "name": "Bronze dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5671" - }, - { - "requirements": "{0,5}", - "shop_price": "135", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "575", - "attack_audios": "2517,2517,2500,2517", - "name": "Steel dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5672", - "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "575", - "durability": null, - "name": "Steel dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5673" - }, - { - "requirements": "{0,20}", - "shop_price": "130", - "ge_buy_limit": "100", - "examine": "A dangerous dagger.", - "durability": null, - "weight": "0.3", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "538", - "attack_audios": "2517,2517,2500,2517", - "name": "Mithril dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5674", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "538", - "durability": null, - "name": "Mithril dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5675" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "durability": null, - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "800", - "attack_audios": "2517,2517,2500,2517", - "name": "Adamant dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5676", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "800", - "durability": null, - "name": "Adamant dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5677" - }, - { - "requirements": "{0,40}", - "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "lendable": "true", - "grand_exchange_price": "4538", - "attack_audios": "2517,2517,2500,2517", - "name": "Rune dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5678", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4538", - "durability": null, - "name": "Rune dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5679" - }, - { - "requirements": "{0,60}", - "shop_price": "60000", - "ge_buy_limit": "10", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "lendable": "true", - "grand_exchange_price": "16800", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5680", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "attack_anims": "396,396,395,396", - "ge_buy_limit": "10", - "grand_exchange_price": "16800", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Dragon dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5681", - "defence_anim": "397" - }, - { - "requirements": "{0,10}", - "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious black dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "664", - "attack_audios": "2517,2517,2500,2517", - "name": "Black dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5682", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "664", - "durability": null, - "name": "Black dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5683" - }, - { - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Poison dagger(p+)", - "archery_ticket_price": "0", - "id": "5684", - "weapon_interface": "5", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", - "render_anim": "2584", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Poison dagger(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5685" - }, - { - "shop_price": "38", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "4128", - "attack_audios": "2517,2517,2500,2517", - "name": "Iron dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5686", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4128", - "durability": null, - "name": "Iron dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5687" - }, - { - "shop_price": "11", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "5088", - "attack_audios": "2517,2517,2500,2517", - "name": "Br'ze dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5688", - "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5088", - "durability": null, - "name": "Br'ze dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5689" - }, - { - "requirements": "{0,5}", - "shop_price": "135", - "ge_buy_limit": "100", - "examine": "Short but pointy.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "4729", - "attack_audios": "2517,2517,2500,2517", - "name": "Steel dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5690", - "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4729", - "durability": null, - "name": "Steel dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5691" - }, - { - "requirements": "{0,20}", - "shop_price": "130", - "ge_buy_limit": "100", - "examine": "A dangerous dagger.", - "durability": null, - "weight": "0.3", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "3476", - "attack_audios": "2517,2517,2500,2517", - "name": "Mithril dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5692", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3476", - "durability": null, - "name": "Mithril dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5693" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "durability": null, - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "4612", - "attack_audios": "2517,2517,2500,2517", - "name": "Adamant dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5694", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4612", - "durability": null, - "name": "Adamant dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5695" - }, - { - "requirements": "{0,40}", - "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "lendable": "true", - "grand_exchange_price": "5061", - "attack_audios": "2517,2517,2500,2517", - "name": "Rune dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5696", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5061", - "durability": null, - "name": "Rune dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5697" - }, - { - "requirements": "{0,60}", - "shop_price": "60000", - "ge_buy_limit": "10", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "lendable": "true", - "grand_exchange_price": "20300", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5698", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "attack_anims": "396,396,395,396", - "ge_buy_limit": "10", - "grand_exchange_price": "20300", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Dragon dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5699", - "defence_anim": "397" - }, - { - "requirements": "{0,10}", - "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious black dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "4921", - "attack_audios": "2517,2517,2500,2517", - "name": "Black dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5700", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4921", - "durability": null, - "name": "Black dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5701" - }, - { - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Poison dagger(p++)", - "archery_ticket_price": "0", - "id": "5702", - "weapon_interface": "5", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", - "render_anim": "2584", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Poison dagger(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5703" - }, - { - "ge_buy_limit": "100", - "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", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "861", - "stand_anim": "813", - "name": "Bronze spear(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5704", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "861", - "durability": null, - "name": "Bronze spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5705" - }, - { - "ge_buy_limit": "100", - "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", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "676", - "stand_anim": "813", - "name": "Iron spear(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5706", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "676", - "durability": null, - "name": "Iron spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5707" - }, - { - "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", - "grand_exchange_price": "596", - "stand_anim": "813", - "tradeable": "true", - "name": "Steel spear(p+)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5708", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "596", - "durability": null, - "name": "Steel spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5709" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "802", - "stand_anim": "813", - "tradeable": "true", - "name": "Mithril spear(p+)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5710", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "802", - "durability": null, - "name": "Mithril spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5711" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "durability": null, - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1394", - "stand_anim": "813", - "tradeable": "true", - "name": "Adamant spear(p+)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5712", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1394", - "durability": null, - "name": "Adamant spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12500", - "durability": null, - "name": "Rune spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5715" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A dragon tipped spear.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "41800", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5716", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "lendable": "true", - "name": "Dragon spear(p+)" - }, - { - "attack_anims": "2080,2081,2082,2080", - "ge_buy_limit": "10", - "grand_exchange_price": "41800", - "durability": null, - "name": "Dragon spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5717", - "defence_anim": "2079" - }, - { - "ge_buy_limit": "100", - "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", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "7557", - "stand_anim": "813", - "name": "Bronze spear(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5718", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7557", - "durability": null, - "name": "Bronze spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5719" - }, - { - "ge_buy_limit": "100", - "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", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "8394", - "stand_anim": "813", - "name": "Iron spear(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5720", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8394", - "durability": null, - "name": "Iron spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5721" - }, - { - "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", - "grand_exchange_price": "8336", - "stand_anim": "813", - "tradeable": "true", - "name": "Steel spear(p++)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5722", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8336", - "durability": null, - "name": "Steel spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5723" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "7035", - "stand_anim": "813", - "tradeable": "true", - "name": "Mithril spear(p++)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5724", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7035", - "durability": null, - "name": "Mithril spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5725" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "durability": null, - "turn90ccw_anim": "1208", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "7530", - "stand_anim": "813", - "name": "Adamant spear(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5726", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7530", - "durability": null, - "name": "Adamant spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17700", - "durability": null, - "name": "Rune spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5729" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A dragon tipped spear.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "37800", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5730", - "stand_turn_anim": "1209", - "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", - "render_anim": "28", - "lendable": "true", - "name": "Dragon spear(p++)" - }, - { - "attack_anims": "2080,2081,2082,2080", - "ge_buy_limit": "10", - "grand_exchange_price": "37800", - "durability": null, - "name": "Dragon spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5731", - "defence_anim": "2079" - }, - { - "durability": null, - "name": "Stool", - "archery_ticket_price": "0", - "id": "5732", - "equipment_slot": "5" - }, - { - "examine": "Yuk!", - "durability": null, - "name": "Rotten potato", - "archery_ticket_price": "0", - "id": "5733" - }, - { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black 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", - "grand_exchange_price": "3351", - "stand_anim": "813", - "tradeable": "true", - "name": "Black spear(p+)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5734", - "stand_turn_anim": "1209", - "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3351", - "durability": null, - "name": "Black spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5735" - }, - { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black 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", - "grand_exchange_price": "9065", - "stand_anim": "813", - "tradeable": "true", - "name": "Black spear(p++)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "5736", - "stand_turn_anim": "1209", - "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9065", - "durability": null, - "name": "Black spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5737" - }, - { - "shop_price": "15", - "examine": "A slightly bluish leaf.", - "grand_exchange_price": "19", - "durability": null, - "name": "Woad leaf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5738" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Asgarnian ale.", - "grand_exchange_price": "459", - "durability": null, - "name": "Asgarnian ale(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5739" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "459", - "durability": null, - "name": "Asgarnian ale(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5740" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", - "grand_exchange_price": "1193", - "durability": null, - "name": "Mature wmb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5741" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1193", - "durability": null, - "name": "Mature wmb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5742" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Greenman's Ale.", - "grand_exchange_price": "65400", - "durability": null, - "name": "Greenman's ale(m)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "5743" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "65400", - "durability": null, - "name": "Greenman's ale(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5744" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Dragon Bitter.", - "grand_exchange_price": "2458", - "durability": null, - "name": "Dragon bitter(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5745" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2458", - "durability": null, - "name": "Dragon bitter(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5746" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal dwarven stout.", - "grand_exchange_price": "56400", - "durability": null, - "name": "Dwarven stout(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5747" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "56400", - "durability": null, - "name": "Dwarven stout(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5748" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Moonlight Mead.", - "grand_exchange_price": "19", - "durability": null, - "name": "Moonlight mead(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5749" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, - "name": "Moonlight mead(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5750" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "890", - "durability": null, - "name": "Axeman's folly", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5751" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "890", - "durability": null, - "name": "Axeman's folly", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5752" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than a normal Axeman's Folly.", - "grand_exchange_price": "26000", - "durability": null, - "name": "Axeman's folly(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5753" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26000", - "durability": null, - "name": "Axeman's folly(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5754" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "3694", - "durability": null, - "name": "Chef's delight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5755" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3694", - "durability": null, - "name": "Chef's delight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5756" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Chef's Delight.", - "grand_exchange_price": "42800", - "durability": null, - "name": "Chef's delight(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5757" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "42800", - "durability": null, - "name": "Chef's delight(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5758" - }, - { - "ge_buy_limit": "100", - "examine": "Ale with bite.", - "grand_exchange_price": "7", - "durability": null, - "name": "Slayer's respite", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5759" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7", - "durability": null, - "name": "Slayer's respite", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5760" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Slayer's Respite.", - "grand_exchange_price": "1748", - "durability": null, - "name": "Slayer's respite(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5761" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1748", - "durability": null, - "name": "Slayer's respite(m)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5762" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of Cider", - "grand_exchange_price": "1279", - "durability": null, - "name": "Cider", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5763" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1279", - "durability": null, - "name": "Cider", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5764" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal cider.", - "grand_exchange_price": "3614", - "durability": null, - "name": "Mature cider", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5765" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3614", - "durability": null, - "name": "Mature cider", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5766" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A pot filled with ale yeast.", - "grand_exchange_price": "59", - "durability": null, - "name": "Ale yeast", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "5767" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "59", - "durability": null, - "name": "Ale yeast", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5768" - }, - { - "ge_buy_limit": "100", - "examine": "Sliced and hollowed out to form a keg.", - "grand_exchange_price": "8", - "durability": null, - "name": "Calquat keg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5769" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, - "name": "Calquat keg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5770" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A pint of thick dark beer.", - "grand_exchange_price": "1934", - "durability": null, - "name": "Dwarven stout(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5771" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1934", - "durability": null, - "name": "Dwarven stout(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5772" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A pint of thick dark beer.", - "grand_exchange_price": "3214", - "durability": null, - "name": "Dwarven stout(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5773" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3214", - "durability": null, - "name": "Dwarven stout(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5774" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A pint of thick dark beer.", - "grand_exchange_price": "4886", - "durability": null, - "name": "Dwarven stout(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5775" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4886", - "durability": null, - "name": "Dwarven stout(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5776" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "A pint of thick dark beer.", - "grand_exchange_price": "5159", - "durability": null, - "name": "Dwarven stout(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5777" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5159", - "durability": null, - "name": "Dwarven stout(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5778" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "3344", - "durability": null, - "name": "Asgarnian ale(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5779" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3344", - "durability": null, - "name": "Asgarnian ale(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5780" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "6103", - "durability": null, - "name": "Asgarnian ale(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5781" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6103", - "durability": null, - "name": "Asgarnian ale(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5782" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "9230", - "durability": null, - "name": "Asgarnian ale(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5783" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9230", - "durability": null, - "name": "Asgarnian ale(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5784" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "12900", - "durability": null, - "name": "Asgarnian ale(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5785" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12900", - "durability": null, - "name": "Asgarnian ale(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5786" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "3441", - "durability": null, - "name": "Greenmans ale(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5787" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3441", - "durability": null, - "name": "Greenmans ale(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5788" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "4670", - "durability": null, - "name": "Greenmans ale(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5789" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4670", - "durability": null, - "name": "Greenmans ale(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5790" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "7654", - "durability": null, - "name": "Greenmans ale(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5791" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7654", - "durability": null, - "name": "Greenmans ale(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5792" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "6317", - "durability": null, - "name": "Greenmans ale(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5793" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6317", - "durability": null, - "name": "Greenmans ale(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5794" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "It's got strange bubbles in it.", - "grand_exchange_price": "1495", - "durability": null, - "name": "Mind bomb(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5795" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1495", - "durability": null, - "name": "Mind bomb(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5796" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "It's got strange bubbles in it.", - "grand_exchange_price": "2721", - "durability": null, - "name": "Mind bomb(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5797" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2721", - "durability": null, - "name": "Mind bomb(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5798" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "It's got strange bubbles in it.", - "grand_exchange_price": "4038", - "durability": null, - "name": "Mind bomb(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5799" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4038", - "durability": null, - "name": "Mind bomb(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5800" - }, - { - "shop_price": "3", - "ge_buy_limit": "100", - "examine": "It's got strange bubbles in it.", - "grand_exchange_price": "5166", - "durability": null, - "name": "Mind bomb(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5801" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5166", - "durability": null, - "name": "Mind bomb(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5802" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "1673", - "durability": null, - "name": "Dragon bitter(1)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5803" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1673", - "durability": null, - "name": "Dragon bitter(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5804" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "3259", - "durability": null, - "name": "Dragon bitter(2)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5805" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3259", - "durability": null, - "name": "Dragon bitter(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5806" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "4845", - "durability": null, - "name": "Dragon bitter(3)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5807" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4845", - "durability": null, - "name": "Dragon bitter(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5808" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "5924", - "durability": null, - "name": "Dragon bitter(4)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5809" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5924", - "durability": null, - "name": "Dragon bitter(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5810" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "1298", - "durability": null, - "name": "Moonlight mead(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5811" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1298", - "durability": null, - "name": "Moonlight mead(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5812" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "2510", - "durability": null, - "name": "Moonlight mead(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5813" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2510", - "durability": null, - "name": "Moonlight mead(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5814" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "3721", - "durability": null, - "name": "Moonlight mead(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5815" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3721", - "durability": null, - "name": "Moonlight mead(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5816" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "4883", - "durability": null, - "name": "Moonlight mead(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5817" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4883", - "durability": null, - "name": "Moonlight mead(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5818" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "3333", - "durability": null, - "name": "Axeman's folly(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5819" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3333", - "durability": null, - "name": "Axeman's folly(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5820" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "6093", - "durability": null, - "name": "Axeman's folly(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5821" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6093", - "durability": null, - "name": "Axeman's folly(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5822" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "8978", - "durability": null, - "name": "Axeman's folly(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5823" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8978", - "durability": null, - "name": "Axeman's folly(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5824" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "9212", - "durability": null, - "name": "Axeman's folly(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5825" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9212", - "durability": null, - "name": "Axeman's folly(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5826" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "3851", - "durability": null, - "name": "Chef's delight(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5827" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3851", - "durability": null, - "name": "Chef's delight(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5828" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "6589", - "durability": null, - "name": "Chef's delight(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5829" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6589", - "durability": null, - "name": "Chef's delight(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5830" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "9536", - "durability": null, - "name": "Chef's delight(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5831" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9536", - "durability": null, - "name": "Chef's delight(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5832" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "13200", - "durability": null, - "name": "Chef's delight(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5833" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "13200", - "durability": null, - "name": "Chef's delight(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5834" - }, - { - "ge_buy_limit": "100", - "examine": "Ale with bite.", - "grand_exchange_price": "1127", - "durability": null, - "name": "Slayer's respite(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5835" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1127", - "durability": null, - "name": "Slayer's respite(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5836" - }, - { - "ge_buy_limit": "100", - "examine": "Ale with bite.", - "grand_exchange_price": "2168", - "durability": null, - "name": "Slayer's respite(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5837" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2168", - "durability": null, - "name": "Slayer's respite(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5838" - }, - { - "ge_buy_limit": "100", - "examine": "Ale with bite.", - "grand_exchange_price": "3209", - "durability": null, - "name": "Slayer's respite(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5839" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3209", - "durability": null, - "name": "Slayer's respite(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5840" - }, - { - "ge_buy_limit": "100", - "examine": "Ale with bite.", - "grand_exchange_price": "3582", - "durability": null, - "name": "Slayer's respite(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5841" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3582", - "durability": null, - "name": "Slayer's respite(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5842" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of Cider", - "grand_exchange_price": "6040", - "durability": null, - "name": "Cider(1)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5843" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6040", - "durability": null, - "name": "Cider(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5844" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of Cider", - "grand_exchange_price": "11000", - "durability": null, - "name": "Cider(2)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5845" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11000", - "durability": null, - "name": "Cider(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5846" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of Cider", - "grand_exchange_price": "15400", - "durability": null, - "name": "Cider(3)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5847" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15400", - "durability": null, - "name": "Cider(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5848" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of Cider", - "grand_exchange_price": "18900", - "durability": null, - "name": "Cider(4)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5849" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "18900", - "durability": null, - "name": "Cider(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5850" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal dwarven stout.", - "grand_exchange_price": "44600", - "durability": null, - "name": "Dwarven stout(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5851" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "44600", - "durability": null, - "name": "Dwarven stout(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5852" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal dwarven stout.", - "grand_exchange_price": "68900", - "durability": null, - "name": "Dwarven stout(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5853" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "68900", - "durability": null, - "name": "Dwarven stout(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5854" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal dwarven stout.", - "grand_exchange_price": "98800", - "durability": null, - "name": "Dwarven stout(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5855" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "98800", - "durability": null, - "name": "Dwarven stout(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5856" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal dwarven stout.", - "grand_exchange_price": "237400", - "durability": null, - "name": "Dwarven stout(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5857" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "237400", - "durability": null, - "name": "Dwarven stout(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5858" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Asgarnian ale.", - "grand_exchange_price": "1697", - "durability": null, - "name": "Asgarnian ale(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5859" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1697", - "durability": null, - "name": "Asgarnian ale(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5860" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Asgarnian ale.", - "grand_exchange_price": "3307", - "durability": null, - "name": "Asgarnian ale(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5861" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3307", - "durability": null, - "name": "Asgarnian ale(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5862" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Asgarnian ale.", - "grand_exchange_price": "4917", - "durability": null, - "name": "Asgarnian ale(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5863" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4917", - "durability": null, - "name": "Asgarnian ale(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5864" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Asgarnian ale.", - "grand_exchange_price": "6778", - "durability": null, - "name": "Asgarnian ale(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5865" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6778", - "durability": null, - "name": "Asgarnian ale(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5866" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Greenman's Ale.", - "grand_exchange_price": "57600", - "durability": null, - "name": "Greenmans ale(m1)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "5867" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "57600", - "durability": null, - "name": "Greenmans ale(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5868" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Greenman's Ale.", - "grand_exchange_price": "92000", - "durability": null, - "name": "Greenmans ale(m2)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "5869" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "92000", - "durability": null, - "name": "Greenmans ale(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5870" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Greenman's Ale.", - "grand_exchange_price": "136700", - "durability": null, - "name": "Greenmans ale(m3)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "5871" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "136700", - "durability": null, - "name": "Greenmans ale(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5872" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Greenman's Ale.", - "grand_exchange_price": "231700", - "durability": null, - "name": "Greenmans ale(m4)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "5873" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "231700", - "durability": null, - "name": "Greenmans ale(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5874" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", - "grand_exchange_price": "3032", - "durability": null, - "name": "Mind bomb(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5875" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3032", - "durability": null, - "name": "Mind bomb(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5876" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", - "grand_exchange_price": "5977", - "durability": null, - "name": "Mind bomb(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5877" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5977", - "durability": null, - "name": "Mind bomb(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5878" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", - "grand_exchange_price": "8922", - "durability": null, - "name": "Mind bomb(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5879" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8922", - "durability": null, - "name": "Mind bomb(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5880" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", - "grand_exchange_price": "12700", - "durability": null, - "name": "Mind bomb(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5881" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12700", - "durability": null, - "name": "Mind bomb(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5882" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Dragon Bitter.", - "grand_exchange_price": "2125", - "durability": null, - "name": "Dragon bitter(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5883" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2125", - "durability": null, - "name": "Dragon bitter(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5884" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Dragon Bitter.", - "grand_exchange_price": "4163", - "durability": null, - "name": "Dragon bitter(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5885" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4163", - "durability": null, - "name": "Dragon bitter(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5886" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Dragon Bitter.", - "grand_exchange_price": "6201", - "durability": null, - "name": "Dragon bitter(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5887" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6201", - "durability": null, - "name": "Dragon bitter(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5888" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Dragon Bitter.", - "grand_exchange_price": "8834", - "durability": null, - "name": "Dragon bitter(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5889" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8834", - "durability": null, - "name": "Dragon bitter(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5890" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "A foul smelling brew.", - "grand_exchange_price": "1315", - "durability": null, - "name": "M'light mead(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5891" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1315", - "durability": null, - "name": "M'light mead(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5892" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Moonlight Mead.", - "grand_exchange_price": "2543", - "durability": null, - "name": "M'light mead(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5893" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2543", - "durability": null, - "name": "M'light mead(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5894" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Moonlight Mead.", - "grand_exchange_price": "3771", - "durability": null, - "name": "M'light mead(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5895" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3771", - "durability": null, - "name": "M'light mead(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5896" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Moonlight Mead.", - "grand_exchange_price": "5000", - "durability": null, - "name": "M'light mead(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5897" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5000", - "durability": null, - "name": "M'light mead(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5898" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than a normal Axeman's Folly.", - "grand_exchange_price": "13500", - "durability": null, - "name": "Axeman's folly(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5899" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "13500", - "durability": null, - "name": "Axeman's folly(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5900" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than a normal Axeman's Folly.", - "grand_exchange_price": "24800", - "durability": null, - "name": "Axeman's folly(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5901" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24800", - "durability": null, - "name": "Axeman's folly(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5902" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than a normal Axeman's Folly.", - "grand_exchange_price": "37300", - "durability": null, - "name": "Axeman's folly(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5903" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "37300", - "durability": null, - "name": "Axeman's folly(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5904" - }, - { - "ge_buy_limit": "100", - "examine": "This might help me chop harder.", - "grand_exchange_price": "60700", - "durability": null, - "name": "Axeman's folly(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5905" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "60700", - "durability": null, - "name": "Axeman's folly(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5906" - }, - { - "ge_buy_limit": "100", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "18000", - "durability": null, - "name": "Chef's delight(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5907" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "18000", - "durability": null, - "name": "Chef's delight(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5908" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Chef's Delight.", - "grand_exchange_price": "34300", - "durability": null, - "name": "Chef's delight(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5909" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "34300", - "durability": null, - "name": "Chef's delight(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5910" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Chef's Delight.", - "grand_exchange_price": "51000", - "durability": null, - "name": "Chef's delight(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5911" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "51000", - "durability": null, - "name": "Chef's delight(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5912" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Chef's Delight.", - "grand_exchange_price": "98600", - "durability": null, - "name": "Chef's delight(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5913" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "98600", - "durability": null, - "name": "Chef's delight(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5914" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Slayer's Respite.", - "grand_exchange_price": "3672", - "durability": null, - "name": "Slayer respite(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5915" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3672", - "durability": null, - "name": "Slayer respite(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5916" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Slayer's Respite.", - "grand_exchange_price": "7258", - "durability": null, - "name": "Slayer respite(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5917" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7258", - "durability": null, - "name": "Slayer respite(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5918" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Slayer's Respite.", - "grand_exchange_price": "10800", - "durability": null, - "name": "Slayer respite(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5919" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10800", - "durability": null, - "name": "Slayer respite(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5920" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal Slayer's Respite.", - "grand_exchange_price": "15300", - "durability": null, - "name": "Slayer respite(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5921" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15300", - "durability": null, - "name": "Slayer respite(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5922" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal cider.", - "grand_exchange_price": "2966", - "durability": null, - "name": "Cider(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5923" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2966", - "durability": null, - "name": "Cider(m1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5924" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal cider.", - "grand_exchange_price": "5845", - "durability": null, - "name": "Cider(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5925" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5845", - "durability": null, - "name": "Cider(m2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5926" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal cider.", - "grand_exchange_price": "8724", - "durability": null, - "name": "Cider(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5927" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8724", - "durability": null, - "name": "Cider(m3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5928" - }, - { - "ge_buy_limit": "100", - "examine": "This looks a good deal stronger than normal cider.", - "grand_exchange_price": "11900", - "durability": null, - "name": "Cider(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5929" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11900", - "durability": null, - "name": "Cider(m4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5930" - }, - { - "ge_buy_limit": "5000", - "examine": "I can weave this to make sacks.", - "grand_exchange_price": "532", - "durability": null, - "name": "Jute fibre", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "5931" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "532", - "durability": null, - "name": "Jute fibre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5932" - }, - { - "ge_buy_limit": "200", - "examine": "A branch from a willow tree.", - "grand_exchange_price": "1255", - "durability": null, - "name": "Willow branch", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5933" - }, - { - "ge_buy_limit": "200", - "grand_exchange_price": "1255", - "durability": null, - "name": "Willow branch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5934" - }, - { - "ge_buy_limit": "10000", - "examine": "A vial filled with coconut milk.", - "grand_exchange_price": "1307", - "durability": null, - "name": "Coconut milk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5935" - }, - { - "ge_buy_limit": "1000", - "examine": "A vial of extra-strong weapon poison, for spears and daggers.", - "grand_exchange_price": "605", - "durability": null, - "name": "Weapon poison+", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5937" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "605", - "durability": null, - "name": "Weapon poison+", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5938" - }, - { - "ge_buy_limit": "1000", - "examine": "A vial of super strong weapon poison, for spears and daggers.", - "grand_exchange_price": "4817", - "durability": null, - "name": "Weapon poison++", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "5940" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "4817", - "durability": null, - "name": "Weapon poison++", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5941" - }, - { - "ge_buy_limit": "1000", - "examine": "N doses of extra-stong antipoison potion", - "grand_exchange_price": "1032", - "durability": null, - "name": "Antipoison+(4)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5943" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1032", - "durability": null, - "name": "Antipoison+(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5944" - }, - { - "ge_buy_limit": "1000", - "examine": "N doses of extra-stong antipoison potion", - "grand_exchange_price": "1499", - "durability": null, - "name": "Antipoison+(3)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5945" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1499", - "durability": null, - "name": "Antipoison+(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5946" - }, - { - "ge_buy_limit": "1000", - "examine": "N doses of extra-stong antipoison potion", - "grand_exchange_price": "1316", - "durability": null, - "name": "Antipoison+(2)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5947" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1316", - "durability": null, - "name": "Antipoison+(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5948" - }, - { - "ge_buy_limit": "1000", - "examine": "N doses of extra-stong antipoison potion", - "grand_exchange_price": "1599", - "durability": null, - "name": "Antipoison+(1)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5949" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1599", - "durability": null, - "name": "Antipoison+(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5950" - }, - { - "ge_buy_limit": "1000", - "examine": "4 doses of a super-strong antipoison potion.", - "grand_exchange_price": "1138", - "durability": null, - "name": "Antipoison++(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5952" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1138", - "durability": null, - "name": "Antipoison++(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5953" - }, - { - "ge_buy_limit": "1000", - "examine": "3 doses of a super-strong antipoison potion.", - "grand_exchange_price": "1071", - "durability": null, - "name": "Antipoison++(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5954" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1071", - "durability": null, - "name": "Antipoison++(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5955" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of a super-strong antipoison potion.", - "grand_exchange_price": "632", - "durability": null, - "name": "Antipoison++(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5956" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "632", - "durability": null, - "name": "Antipoison++(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5957" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of a super-strong antipoison potion.", - "grand_exchange_price": "821", - "durability": null, - "name": "Antipoison++(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5958" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "821", - "durability": null, - "name": "Antipoison++(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5959" - }, - { - "examine": "A basket filled with tomatoes.", - "grand_exchange_price": "663", - "durability": null, - "name": "Tomatoes(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5960" - }, - { - "durability": null, - "name": "Tomatoes(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5961" - }, - { - "examine": "A basket filled with tomatoes.", - "grand_exchange_price": "663", - "durability": null, - "name": "Tomatoes(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5962" - }, - { - "durability": null, - "name": "Tomatoes(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5963" - }, - { - "examine": "A basket filled with tomatoes.", - "grand_exchange_price": "663", - "durability": null, - "name": "Tomatoes(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5964" - }, - { - "durability": null, - "name": "Tomatoes(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5965" - }, - { - "examine": "A basket filled with tomatoes.", - "grand_exchange_price": "663", - "durability": null, - "name": "Tomatoes(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5966" - }, - { - "durability": null, - "name": "Tomatoes(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5967" - }, - { - "ge_buy_limit": "10000", - "examine": "A basket filled with tomatoes.", - "grand_exchange_price": "335", - "durability": null, - "name": "Tomatoes(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5968" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "335", - "durability": null, - "name": "Tomatoes(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5969" - }, - { - "ge_buy_limit": "5000", - "examine": "I could make a spicy curry with this.", - "grand_exchange_price": "318", - "durability": null, - "name": "Curry leaf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5970" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "318", - "durability": null, - "name": "Curry leaf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5971" - }, - { - "ge_buy_limit": "5000", - "examine": "Looks delicious.", - "grand_exchange_price": "2074", - "durability": null, - "name": "Papaya fruit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5972" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2074", - "durability": null, - "name": "Papaya fruit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5973" - }, - { - "ge_buy_limit": "5000", - "examine": "It's a coconut.", - "grand_exchange_price": "2568", - "durability": null, - "name": "Coconut", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5974" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2568", - "durability": null, - "name": "Coconut", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5975" - }, - { - "ge_buy_limit": "5000", - "examine": "It's a coconut.", - "grand_exchange_price": "1258", - "durability": null, - "name": "Coconut", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5976" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1258", - "durability": null, - "name": "Coconut", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5977" - }, - { - "ge_buy_limit": "10000", - "examine": "All the milk has been removed.", - "grand_exchange_price": "54", - "durability": null, - "name": "Coconut shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5978" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "54", - "durability": null, - "name": "Coconut shell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5979" - }, - { - "ge_buy_limit": "100", - "examine": "This is the largest fruit I've ever seen.", - "grand_exchange_price": "43", - "durability": null, - "name": "Calquat fruit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5980" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "43", - "durability": null, - "name": "Calquat fruit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5981" - }, - { - "shop_price": "48", - "ge_buy_limit": "10000", - "examine": "A juicy watermelon.", - "grand_exchange_price": "41", - "durability": null, - "name": "Watermelon", - "tradeable": "true", - "weight": "0.111", - "archery_ticket_price": "0", - "id": "5982" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "41", - "durability": null, - "name": "Watermelon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5983" - }, - { - "ge_buy_limit": "1000", - "examine": "A slice of watermelon.", - "grand_exchange_price": "40", - "durability": null, - "name": "Watermelon slice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5984" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "40", - "durability": null, - "name": "Watermelon slice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5985" - }, - { - "ge_buy_limit": "10000", - "examine": "Raw sweetcorn.", - "grand_exchange_price": "19", - "durability": null, - "name": "Sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5986" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "19", - "durability": null, - "name": "Sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5987" - }, - { - "ge_buy_limit": "10000", - "examine": "Delicious cooked sweetcorn.", - "grand_exchange_price": "15", - "durability": null, - "name": "Cooked sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5988" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "15", - "durability": null, - "name": "Cooked sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5989" - }, - { - "durability": null, - "name": "Burnt sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5991" - }, - { - "ge_buy_limit": "100", - "examine": "A bucket of apple mush.", - "grand_exchange_price": "425", - "durability": null, - "name": "Apple mush", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5992" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "425", - "durability": null, - "name": "Apple mush", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5993" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of Hammerstone hops.", - "grand_exchange_price": "14", - "durability": null, - "name": "Hammerstone hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5994" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "14", - "durability": null, - "name": "Hammerstone hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5995" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of Asgarnian hops.", - "grand_exchange_price": "2", - "durability": null, - "name": "Asgarnian hops", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "5996" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2", - "durability": null, - "name": "Asgarnian hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5997" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of Yanillian hops.", - "grand_exchange_price": "1", - "durability": null, - "name": "Yanillian hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5998" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1", - "durability": null, - "name": "Yanillian hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5999" - }, - { - "shop_price": "4", - "ge_buy_limit": "1000", - "examine": "A handful of Krandorian hops.", - "grand_exchange_price": "2", - "durability": null, - "name": "Krandorian hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6000" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2", - "durability": null, - "name": "Krandorian hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6001" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of wildblood hops.", - "grand_exchange_price": "66", - "durability": null, - "name": "Wildblood hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6002" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "66", - "durability": null, - "name": "Wildblood hops", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6003" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "219", - "examine": "A Bittercap Mushroom", - "durability": null, - "name": "Mushroom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6004" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "219", - "durability": null, - "name": "Mushroom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6005" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of Barley.", - "grand_exchange_price": "211", - "durability": null, - "name": "Barley", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6006" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "211", - "durability": null, - "name": "Barley", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6007" - }, - { - "ge_buy_limit": "1000", - "examine": "A handful of barley malt.", - "grand_exchange_price": "410", - "durability": null, - "name": "Barley malt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6008" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "410", - "durability": null, - "name": "Barley malt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6009" - }, - { - "ge_buy_limit": "1000", - "examine": "A bunch of marigolds.", - "grand_exchange_price": "3326", - "durability": null, - "name": "Marigolds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6010" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3326", - "durability": null, - "name": "Marigolds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6011" - }, - { - "ge_buy_limit": "1000", - "examine": "A bunch of nasturtiums.", - "grand_exchange_price": "52", - "durability": null, - "name": "Nasturtiums", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6012" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "52", - "durability": null, - "name": "Nasturtiums", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6013" - }, - { - "ge_buy_limit": "1000", - "examine": "Some rosemary.", - "grand_exchange_price": "29", - "durability": null, - "name": "Rosemary", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6014" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "29", - "durability": null, - "name": "Rosemary", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6015" - }, - { - "ge_buy_limit": "10000", - "examine": "Don't prick yourself with this.", - "grand_exchange_price": "6990", - "durability": null, - "name": "Cactus spine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6016" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "6990", - "durability": null, - "name": "Cactus spine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6017" - }, - { - "ge_buy_limit": "10000", - "examine": "They look sweet and juicy, but only a fool would eat them.", - "grand_exchange_price": "399", - "durability": null, - "name": "Poison ivy berries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6018" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "399", - "durability": null, - "name": "Poison ivy berries", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6019" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "152", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6020" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "152", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6021" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "27", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6022" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "27", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6023" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "21", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6024" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6025" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "7", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6026" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6027" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "11", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6028" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6029" - }, - { - "ge_buy_limit": "100", - "examine": "A pile of leaves.", - "grand_exchange_price": "4", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6030" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4", - "durability": null, - "name": "Leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6031" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Good for plants, helps them grow.", - "grand_exchange_price": "97", - "durability": null, - "name": "Compost", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6032" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "97", - "durability": null, - "name": "Compost", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6033" - }, - { - "ge_buy_limit": "10000", - "examine": "Super-good for the smallest or largest of plants.", - "grand_exchange_price": "909", - "durability": null, - "name": "Supercompost", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6034" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "909", - "durability": null, - "name": "Supercompost", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6035" - }, - { - "shop_price": "25", - "ge_buy_limit": "100", - "examine": "Use this on plants to cure disease.", - "grand_exchange_price": "170", - "durability": null, - "name": "Plant cure", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6036" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "170", - "durability": null, - "name": "Plant cure", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6037" - }, - { - "ge_buy_limit": "100", - "examine": "I could use this to make jewellery.", - "grand_exchange_price": "1078", - "durability": null, - "name": "Magic string", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6038" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1078", - "durability": null, - "name": "Magic string", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6039" - }, - { - "destroy_message": "You will need to create a new amulet if you destroy this one.", - "examine": "An Amulet of Nature.", - "durability": null, - "name": "Amulet of nature", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6040", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "examine": "Strung with the root of a Magic Tree. If I enchant this it will become an amulet of nature.", - "grand_exchange_price": "1996", - "durability": null, - "name": "Pre-nature amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6041", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1996", - "durability": null, - "name": "Pre-nature amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6042" - }, - { - "ge_buy_limit": "100", - "examine": "The roots of the Oak tree.", - "grand_exchange_price": "60", - "durability": null, - "name": "Oak roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6043" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "60", - "durability": null, - "name": "Oak roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6044" - }, - { - "ge_buy_limit": "100", - "examine": "The roots of the Willow tree.", - "grand_exchange_price": "41", - "durability": null, - "name": "Willow roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6045" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "41", - "durability": null, - "name": "Willow roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6046" - }, - { - "ge_buy_limit": "100", - "examine": "The roots of the Maple tree.", - "grand_exchange_price": "40", - "durability": null, - "name": "Maple roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6047" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "40", - "durability": null, - "name": "Maple roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6048" - }, - { - "ge_buy_limit": "100", - "examine": "The roots of a yew tree", - "grand_exchange_price": "570", - "durability": null, - "name": "Yew roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6049" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "570", - "durability": null, - "name": "Yew roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6050" - }, - { - "ge_buy_limit": "100", - "examine": "The roots of a magic tree.", - "grand_exchange_price": "5853", - "durability": null, - "name": "Magic roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6051" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5853", - "durability": null, - "name": "Magic roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6052" - }, - { - "durability": null, - "name": "Spirit roots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6054" - }, - { "shop_price": "1", - "examine": "A handful of weeds.", - "durability": null, - "name": "Weeds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6055" - }, - { - "durability": null, - "name": "Weeds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6056" - }, - { - "examine": "A sack filled with hay. / This sack of hay has a bronze spear sticking through it.", - "durability": null, - "name": "Hay sack", - "archery_ticket_price": "0", - "id": "6057" - }, - { - "examine": "A sack filled with hay. / This sack of hay has a bronze spear sticking through it.", - "durability": null, - "name": "Hay sack", - "archery_ticket_price": "0", - "id": "6058" - }, - { - "examine": "This should scare the birds.", - "durability": null, - "name": "Scarecrow", - "archery_ticket_price": "0", - "id": "6059" - }, - { - "durability": null, - "name": "Stool", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6060" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "Bronze crossbow bolts.", - "grand_exchange_price": "134", - "durability": null, - "name": "Bronze bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6061", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "Bronze crossbow bolts.", - "grand_exchange_price": "1162", - "durability": null, - "name": "Bronze bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6062", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", - "equipment_slot": "13" - }, - { - "examine": "How do I wash blood stains out?", - "durability": null, - "name": "Bloody mourner top", - "weight": "2.72", - "archery_ticket_price": "0", - "id": "6064" - }, - { - "remove_sleeves": "true", - "examine": "A thick heavy leather top.", - "durability": null, - "name": "Mourner top", - "archery_ticket_price": "0", - "id": "6065", - "equipment_slot": "4" - }, - { - "examine": "Damaged: These are in need of a good tailor.Repaired: A pair of mourner trousers.", - "durability": null, - "name": "Mourner trousers", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "6066" - }, - { - "examine": "Damaged: These are in need of a good tailor.Repaired: A pair of mourner trousers.", - "durability": null, - "name": "Mourner trousers", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "6067", - "equipment_slot": "7" - }, - { - "shop_price": "160", - "examine": "These will keep my hands warm.", - "durability": null, - "name": "Mourner gloves", - "weight": "2.3", - "archery_ticket_price": "0", - "id": "6068", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "160", - "examine": "Comfortable leather boots.", - "durability": null, - "name": "Mourner boots", - "weight": "1.36", - "archery_ticket_price": "0", - "id": "6069", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "A dull brown cape.", - "durability": null, - "name": "Mourner cloak", - "weight": "2.25", - "archery_ticket_price": "0", - "id": "6070", - "equipment_slot": "1" - }, - { - "shop_price": "160", - "examine": "A letter of recommendation.", - "durability": null, - "name": "Mourner letter", - "archery_ticket_price": "0", - "id": "6071" - }, - { - "examine": "A bar of soap taken from Tegid.", - "durability": null, - "name": "Tegid's soap", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6072" - }, - { - "examine": "A book on the history of Prifddinas.", - "durability": null, - "name": "Prifddinas' history", - "archery_ticket_price": "0", - "id": "6073" - }, - { - "examine": "A book on the exploration of the eastern realm.", - "durability": null, - "name": "Eastern discovery", - "archery_ticket_price": "0", - "id": "6075" - }, - { - "examine": "A book on the settlement of the eastern realm.", - "durability": null, - "name": "Eastern settlement", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6077" + "weight": "0.113" }, { - "examine": "A book about the great divide.", - "durability": null, - "name": "The great divide", - "weight": "0.5", + "id": "5604", + "name": "Magnet", + "examine": "A very attractive magnet.", "archery_ticket_price": "0", - "id": "6079" + "durability": null }, { - "examine": "A strange broken device of gnomic design.", - "durability": null, - "name": "Broken device", - "tradeable": "false", - "destroy": "true", + "id": "5605", + "name": "Knife", + "examine": "A dangerous looking knife.", "archery_ticket_price": "0", - "id": "6081" + "durability": null, + "shop_price": "25", + "tradeable": "true", + "weight": "0.4" }, { - "turn90cw_anim": "2319", - "examine": "A device for firing dye.", - "walk_anim": "2317", + "id": "5606", + "name": "Makeover voucher", + "examine": "I can exchange this for one free makeover with the makeover mage.", + "archery_ticket_price": "0", "durability": null, - "weight": "4", - "turn90ccw_anim": "2320", + "weight": "1" + }, + { + "id": "5607", + "name": "Grain", + "examine": "Some wheat heads.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "1", + "shop_price": "2", + "tradeable": "true", + "weight": "0.6" + }, + { + "id": "5608", + "name": "Fox", + "examine": "A cunning animal.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "5609", + "name": "Chicken", + "examine": "Normal: Yep. Definitely a chicken.", + "archery_ticket_price": "0", "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "20", - "turn180_anim": "2318", - "render_anim": "284", + "durability": null, + "equipment_slot": "5" + }, + { + "id": "5610", + "name": "Hourglass", + "examine": "There's not much sand left in the top half...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "5611", + "name": "Initiate sallet", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5612", + "name": "Initiate hauberk", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5613", + "name": "Initiate cuisse", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5614", + "name": "Magic carpet", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "3", - "stand_anim": "2316", - "name": "Fixed device", - "run_anim": "2322", + "render_anim": "191", + "run_anim": "330", + "stand_anim": "330", + "stand_turn_anim": "330", + "turn180_anim": "330", + "turn90ccw_anim": "330", + "turn90cw_anim": "330", + "walk_anim": "330" + }, + { + "id": "5615", + "name": "Bonemeal", + "examine": "A pot of crushed bearded gorilla monkey bones.", "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "5616", + "name": "Bronze arrow(p+)", + "examine": "Arrows with bronze heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "7", + "tradeable": "true" + }, + { + "id": "5617", + "name": "Iron arrow(p+)", + "examine": "Arrows with iron heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "5618", + "name": "Steel arrow(p+)", + "examine": "Arrows with steel heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "46", + "tradeable": "true" + }, + { + "id": "5619", + "name": "Mithril arrow(p+)", + "examine": "Arrows with mithril heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "shop_price": "76", + "tradeable": "true" + }, + { + "id": "5620", + "name": "Adamant arrow(p+)", + "examine": "Arrows with adamantite heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "shop_price": "172", + "tradeable": "true" + }, + { + "id": "5621", + "name": "Rune arrow(p+)", + "examine": "Arrows with rune heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "shop_price": "510", + "tradeable": "true" + }, + { + "id": "5622", + "name": "Bronze arrow(p++)", + "examine": "Arrows with bronze heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "7", + "tradeable": "true" + }, + { + "id": "5623", + "name": "Iron arrow(p++)", + "examine": "Arrows with iron heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "5624", + "name": "Steel arrow(p++)", + "examine": "Arrows with steel heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "46", + "tradeable": "true" + }, + { + "id": "5625", + "name": "Mithril arrow(p++)", + "examine": "Arrows with mithril heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "shop_price": "76", + "tradeable": "true" + }, + { + "id": "5626", + "name": "Adamant arrow(p++)", + "examine": "Arrows with adamantite heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "shop_price": "172", + "tradeable": "true" + }, + { + "id": "5627", + "name": "Rune arrow(p++)", + "examine": "Arrows with rune heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "shop_price": "510", + "tradeable": "true" + }, + { + "id": "5628", + "name": "Bronze dart(p+)", + "examine": "A deadly throwing dart with a bronze tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5629", + "name": "Iron dart(p+)", + "examine": "A deadly throwing dart with an iron tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5630", + "name": "Steel dart(p+)", + "examine": "A deadly throwing dart with a steel tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5631", + "name": "Black dart(p+)", + "examine": "A deadly throwing dart with a black tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,10}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5632", + "name": "Mithril dart(p+)", + "examine": "A deadly throwing dart with a mithril tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5633", + "name": "Adamant dart(p+)", + "examine": "A deadly throwing dart with an adamantite tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5634", + "name": "Rune dart(p+)", + "examine": "A deadly throwing dart with a rune tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5635", + "name": "Bronze dart(p++)", + "examine": "A deadly throwing dart with a bronze tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,3,0,0,0,0,0,0,0,0,0,1", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5636", + "name": "Iron dart(p++)", + "examine": "A deadly throwing dart with an iron tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5637", + "name": "Steel dart(p++)", + "examine": "A deadly throwing dart with a steel tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5638", + "name": "Black dart(p++)", + "examine": "A deadly throwing dart with a black tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,7,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,10}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5639", + "name": "Mithril dart(p++)", + "examine": "A deadly throwing dart with a mithril tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5640", + "name": "Adamant dart(p++)", + "examine": "A deadly throwing dart with an adamantite tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5641", + "name": "Rune dart(p++)", + "examine": "A deadly throwing dart with a rune tip.", + "archery_ticket_price": "0", + "attack_anims": "582,582,582,582", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5642", + "name": "Bronze javelin(p+)", + "examine": "A bronze-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "6", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5643", + "name": "Iron javelin(p+)", + "examine": "An iron-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "8", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5644", + "name": "Steel javelin(p+)", + "examine": "A steel-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "shop_price": "37", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5645", + "name": "Mithril javelin(p+)", + "examine": "A mithril tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "shop_price": "79", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5646", + "name": "Adamant javelin(p+)", + "examine": "An adamant tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,30}", + "shop_price": "100", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5647", + "name": "Rune javelin(p+)", + "examine": "A rune tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "624", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5648", + "name": "Bronze jav'n(p++)", + "examine": "A bronze-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,6", + "defence_anim": "424", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5649", + "name": "Iron javelin(p++)", + "examine": "An iron-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "shop_price": "8", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5650", + "name": "Steel javelin(p++)", + "examine": "A steel-tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,12,0,0,0,0,0,0,0,0,0,12", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,5}", + "shop_price": "37", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5651", + "name": "Mithril javelin(p++)", + "examine": "A mithril tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,17,0,0,0,0,0,0,0,0,0,18", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,20}", + "shop_price": "79", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5652", + "name": "Adamant javelin(p++)", + "examine": "An adamant tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,30}", + "shop_price": "100", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5653", + "name": "Rune javelin(p++)", + "examine": "A rune tipped javelin.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_speed": "6", + "bonuses": "0,0,0,0,38,0,0,0,0,0,0,0,0,0,42", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{4,40}", + "shop_price": "624", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5654", + "name": "Bronze knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "14", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5655", + "name": "Iron knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "58", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5656", + "name": "Steel knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "68", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5657", + "name": "Mithril knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5658", + "name": "Black knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,10}", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5659", + "name": "Adamant knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5660", + "name": "Rune knife(p+)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5661", + "name": "Bronze knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,4,0,0,0,0,0,0,0,0,0,3", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "14", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5662", + "name": "Iron knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,5,0,0,0,0,0,0,0,0,0,4", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "shop_price": "58", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5663", + "name": "Steel knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,7", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "68", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5664", + "name": "Mithril knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,11,0,0,0,0,0,0,0,0,0,10", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,20}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5665", + "name": "Black knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,8", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,10}", + "shop_price": "15", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5666", + "name": "Adamant knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "bonuses": "0,0,0,0,15,0,0,0,0,0,0,0,0,0,14", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{4,30}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5667", + "name": "Rune knife(p++)", + "examine": "A finely balanced throwing knife.", + "archery_ticket_price": "0", + "attack_anims": "806,806,806,806", + "attack_audios": "2704,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,25,0,0,0,0,0,0,0,0,0,24", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "5668", + "name": "Iron dagger(p+)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "38", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5669", + "name": "Iron dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5670", + "name": "Bronze dagger(p+)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "11", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5671", + "name": "Bronze dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5672", + "name": "Steel dagger(p+)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,5}", + "shop_price": "135", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5673", + "name": "Steel dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5674", + "name": "Mithril dagger(p+)", + "examine": "A dangerous dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,20}", + "shop_price": "130", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.3" + }, + { + "id": "5675", + "name": "Mithril dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5676", + "name": "Adamant dagger(p+)", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,30}", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "5677", + "name": "Adamant dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5678", + "name": "Rune dagger(p+)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,40}", + "shop_price": "8000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5679", + "name": "Rune dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5680", + "name": "Dragon dagger(p+)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,60}", + "shop_price": "60000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5681", + "name": "Dragon dagger(p+)", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "defence_anim": "397", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "5682", + "name": "Black dagger(p+)", + "examine": "A vicious black dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,10}", + "shop_price": "240", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5683", + "name": "Black dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5684", + "name": "Poison dagger(p+)", + "archery_ticket_price": "0", + "attack_audios": "2517,2517,2500,2517", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "5685", + "name": "Poison dagger(p+)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5686", + "name": "Iron dagger(p++)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "38", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5687", + "name": "Iron dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5688", + "name": "Br'ze dagger(p++)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "4,2,-4,1,0,0,0,0,1,0,0,3,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "shop_price": "11", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5689", + "name": "Br'ze dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5690", + "name": "Steel dagger(p++)", + "examine": "Short but pointy.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "8,4,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,5}", + "shop_price": "135", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5691", + "name": "Steel dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5692", + "name": "Mithril dagger(p++)", + "examine": "A dangerous dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,0,10,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,20}", + "shop_price": "130", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.3" + }, + { + "id": "5693", + "name": "Mithril dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5694", + "name": "Adamant dagger(p++)", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,30}", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "5695", + "name": "Adamant dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5696", + "name": "Rune dagger(p++)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,40}", + "shop_price": "8000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5697", + "name": "Rune dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5698", + "name": "Dragon dagger(p++)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "2584", + "requirements": "{0,60}", + "shop_price": "60000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5699", + "name": "Dragon dagger(p++)", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "defence_anim": "397", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "5700", + "name": "Black dagger(p++)", + "examine": "A vicious black dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", + "requirements": "{0,10}", + "shop_price": "240", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "5701", + "name": "Black dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5702", + "name": "Poison dagger(p++)", + "archery_ticket_price": "0", + "attack_audios": "2517,2517,2500,2517", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "5703", + "name": "Poison dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5704", + "name": "Bronze spear(p+)", + "examine": "A bronze tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5705", + "name": "Bronze spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5706", + "name": "Iron spear(p+)", + "examine": "An iron tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5707", + "name": "Iron spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5708", + "name": "Steel spear(p+)", + "examine": "A steel tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5709", + "name": "Steel spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5710", + "name": "Mithril spear(p+)", + "examine": "A mithril tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "5711", + "name": "Mithril spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5712", + "name": "Adamant spear(p+)", + "examine": "An adamant tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "5713", + "name": "Adamant spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5714", + "name": "Rune spear(p+)", + "examine": "A rune tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5715", + "name": "Rune spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5716", + "name": "Dragon spear(p+)", + "examine": "A dragon tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "5717", + "name": "Dragon spear(p+)", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "defence_anim": "2079", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "5718", + "name": "Bronze spear(p++)", + "examine": "A bronze tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,1,1,0,0,0,0,6,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5719", + "name": "Bronze spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5720", + "name": "Iron spear(p++)", + "examine": "An iron tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,1,1,0,0,0,0,10,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5721", + "name": "Iron spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5722", + "name": "Steel spear(p++)", + "examine": "A steel tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,1,1,0,0,0,0,12,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5723", + "name": "Steel spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5724", + "name": "Mithril spear(p++)", + "examine": "A mithril tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,1,1,0,0,0,0,18,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "5725", + "name": "Mithril spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5726", + "name": "Adamant spear(p++)", + "examine": "An adamant tipped spear.", + "archery_ticket_price": "0", + "bonuses": "24,24,24,0,0,1,1,0,0,0,0,28,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14" + }, + { + "id": "5727", + "name": "Adamant spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5728", + "name": "Rune spear(p++)", + "examine": "A rune tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5729", + "name": "Rune spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5730", + "name": "Dragon spear(p++)", + "examine": "A dragon tipped spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{0,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2" + }, + { + "id": "5731", + "name": "Dragon spear(p++)", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "defence_anim": "2079", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "5732", + "name": "Stool", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5" + }, + { + "id": "5733", + "name": "Rotten potato", + "examine": "Yuk!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "5734", + "name": "Black spear(p+)", + "examine": "A black tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5735", + "name": "Black spear(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5736", + "name": "Black spear(p++)", + "examine": "A black tipped spear.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "15,15,15,0,0,1,1,0,0,0,0,16,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "5737", + "name": "Black spear(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5738", + "name": "Woad leaf", + "examine": "A slightly bluish leaf.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "5739", + "name": "Asgarnian ale(m)", + "examine": "This looks a good deal stronger than normal Asgarnian ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5740", + "name": "Asgarnian ale(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5741", + "name": "Mature wmb", + "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5742", + "name": "Mature wmb", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5743", + "name": "Greenman's ale(m)", + "examine": "This looks a good deal stronger than normal Greenman's Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5744", + "name": "Greenman's ale(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5745", + "name": "Dragon bitter(m)", + "examine": "This looks a good deal stronger than normal Dragon Bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5746", + "name": "Dragon bitter(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5747", + "name": "Dwarven stout(m)", + "examine": "This looks a good deal stronger than normal Dwarven Stout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5748", + "name": "Dwarven stout(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5749", + "name": "Moonlight mead(m)", + "examine": "This looks a good deal stronger than normal Moonlight Mead.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5750", + "name": "Moonlight mead(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5751", + "name": "Axeman's folly", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5752", + "name": "Axeman's folly", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5753", + "name": "Axeman's folly(m)", + "examine": "This looks a good deal stronger than a normal Axeman's Folly.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5754", + "name": "Axeman's folly(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5755", + "name": "Chef's delight", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5756", + "name": "Chef's delight", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5757", + "name": "Chef's delight(m)", + "examine": "This looks a good deal stronger than normal Chef's Delight.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5758", + "name": "Chef's delight(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5759", + "name": "Slayer's respite", + "examine": "Ale with bite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5760", + "name": "Slayer's respite", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5761", + "name": "Slayer's respite(m)", + "examine": "This looks a good deal stronger than normal Slayer's Respite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5762", + "name": "Slayer's respite(m)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5763", + "name": "Cider", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5764", + "name": "Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5765", + "name": "Mature cider", + "examine": "This looks a good deal stronger than normal cider.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5766", + "name": "Mature cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5767", + "name": "Ale yeast", + "examine": "A pot filled with ale yeast.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "2" + }, + { + "id": "5768", + "name": "Ale yeast", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5769", + "name": "Calquat keg", + "examine": "Sliced and hollowed out to form a keg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5770", + "name": "Calquat keg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5771", + "name": "Dwarven stout(1)", + "examine": "A pint of thick dark beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5772", + "name": "Dwarven stout(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5773", + "name": "Dwarven stout(2)", + "examine": "A pint of thick dark beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5774", + "name": "Dwarven stout(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5775", + "name": "Dwarven stout(3)", + "examine": "A pint of thick dark beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5776", + "name": "Dwarven stout(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5777", + "name": "Dwarven stout(4)", + "examine": "A pint of thick dark beer.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5778", + "name": "Dwarven stout(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5779", + "name": "Asgarnian ale(1)", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5780", + "name": "Asgarnian ale(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5781", + "name": "Asgarnian ale(2)", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5782", + "name": "Asgarnian ale(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5783", + "name": "Asgarnian ale(3)", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5784", + "name": "Asgarnian ale(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5785", + "name": "Asgarnian ale(4)", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5786", + "name": "Asgarnian ale(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5787", + "name": "Greenmans ale(1)", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5788", + "name": "Greenmans ale(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5789", + "name": "Greenmans ale(2)", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5790", + "name": "Greenmans ale(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5791", + "name": "Greenmans ale(3)", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5792", + "name": "Greenmans ale(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5793", + "name": "Greenmans ale(4)", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "5794", + "name": "Greenmans ale(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5795", + "name": "Mind bomb(1)", + "examine": "It's got strange bubbles in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5796", + "name": "Mind bomb(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5797", + "name": "Mind bomb(2)", + "examine": "It's got strange bubbles in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5798", + "name": "Mind bomb(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5799", + "name": "Mind bomb(3)", + "examine": "It's got strange bubbles in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5800", + "name": "Mind bomb(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5801", + "name": "Mind bomb(4)", + "examine": "It's got strange bubbles in it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "5802", + "name": "Mind bomb(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5803", + "name": "Dragon bitter(1)", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5804", + "name": "Dragon bitter(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5805", + "name": "Dragon bitter(2)", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5806", + "name": "Dragon bitter(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5807", + "name": "Dragon bitter(3)", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5808", + "name": "Dragon bitter(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5809", + "name": "Dragon bitter(4)", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5810", + "name": "Dragon bitter(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5811", + "name": "Moonlight mead(1)", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "5812", + "name": "Moonlight mead(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5813", + "name": "Moonlight mead(2)", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "5814", + "name": "Moonlight mead(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5815", + "name": "Moonlight mead(3)", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "5816", + "name": "Moonlight mead(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5817", + "name": "Moonlight mead(4)", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "5818", + "name": "Moonlight mead(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5819", + "name": "Axeman's folly(1)", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5820", + "name": "Axeman's folly(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5821", + "name": "Axeman's folly(2)", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5822", + "name": "Axeman's folly(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5823", + "name": "Axeman's folly(3)", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5824", + "name": "Axeman's folly(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5825", + "name": "Axeman's folly(4)", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5826", + "name": "Axeman's folly(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5827", + "name": "Chef's delight(1)", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5828", + "name": "Chef's delight(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5829", + "name": "Chef's delight(2)", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5830", + "name": "Chef's delight(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5831", + "name": "Chef's delight(3)", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5832", + "name": "Chef's delight(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5833", + "name": "Chef's delight(4)", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5834", + "name": "Chef's delight(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5835", + "name": "Slayer's respite(1)", + "examine": "Ale with bite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5836", + "name": "Slayer's respite(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5837", + "name": "Slayer's respite(2)", + "examine": "Ale with bite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5838", + "name": "Slayer's respite(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5839", + "name": "Slayer's respite(3)", + "examine": "Ale with bite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5840", + "name": "Slayer's respite(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5841", + "name": "Slayer's respite(4)", + "examine": "Ale with bite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5842", + "name": "Slayer's respite(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5843", + "name": "Cider(1)", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5844", + "name": "Cider(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5845", + "name": "Cider(2)", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5846", + "name": "Cider(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5847", + "name": "Cider(3)", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5848", + "name": "Cider(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5849", + "name": "Cider(4)", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5850", + "name": "Cider(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5851", + "name": "Dwarven stout(m1)", + "examine": "This looks a good deal stronger than normal Dwarven Stout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5852", + "name": "Dwarven stout(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5853", + "name": "Dwarven stout(m2)", + "examine": "This looks a good deal stronger than normal Dwarven Stout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5854", + "name": "Dwarven stout(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5855", + "name": "Dwarven stout(m3)", + "examine": "This looks a good deal stronger than normal Dwarven Stout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5856", + "name": "Dwarven stout(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5857", + "name": "Dwarven stout(m4)", + "examine": "This looks a good deal stronger than normal Dwarven Stout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5858", + "name": "Dwarven stout(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5859", + "name": "Asgarnian ale(m1)", + "examine": "This looks a good deal stronger than normal Asgarnian ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5860", + "name": "Asgarnian ale(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5861", + "name": "Asgarnian ale(m2)", + "examine": "This looks a good deal stronger than normal Asgarnian ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5862", + "name": "Asgarnian ale(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5863", + "name": "Asgarnian ale(m3)", + "examine": "This looks a good deal stronger than normal Asgarnian ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5864", + "name": "Asgarnian ale(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5865", + "name": "Asgarnian ale(m4)", + "examine": "This looks a good deal stronger than normal Asgarnian ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5866", + "name": "Asgarnian ale(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5867", + "name": "Greenmans ale(m1)", + "examine": "This looks a good deal stronger than normal Greenman's Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5868", + "name": "Greenmans ale(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5869", + "name": "Greenmans ale(m2)", + "examine": "This looks a good deal stronger than normal Greenman's Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5870", + "name": "Greenmans ale(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5871", + "name": "Greenmans ale(m3)", + "examine": "This looks a good deal stronger than normal Greenman's Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5872", + "name": "Greenmans ale(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5873", + "name": "Greenmans ale(m4)", + "examine": "This looks a good deal stronger than normal Greenman's Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "5874", + "name": "Greenmans ale(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5875", + "name": "Mind bomb(m1)", + "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5876", + "name": "Mind bomb(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5877", + "name": "Mind bomb(m2)", + "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5878", + "name": "Mind bomb(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5879", + "name": "Mind bomb(m3)", + "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5880", + "name": "Mind bomb(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5881", + "name": "Mind bomb(m4)", + "examine": "This looks a good deal stronger than normal Wizards Mind Bomb.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5882", + "name": "Mind bomb(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5883", + "name": "Dragon bitter(m1)", + "examine": "This looks a good deal stronger than normal Dragon Bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5884", + "name": "Dragon bitter(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5885", + "name": "Dragon bitter(m2)", + "examine": "This looks a good deal stronger than normal Dragon Bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5886", + "name": "Dragon bitter(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5887", + "name": "Dragon bitter(m3)", + "examine": "This looks a good deal stronger than normal Dragon Bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5888", + "name": "Dragon bitter(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5889", + "name": "Dragon bitter(m4)", + "examine": "This looks a good deal stronger than normal Dragon Bitter.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5890", + "name": "Dragon bitter(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5891", + "name": "M'light mead(m1)", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "5892", + "name": "M'light mead(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5893", + "name": "M'light mead(m2)", + "examine": "This looks a good deal stronger than normal Moonlight Mead.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5894", + "name": "M'light mead(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5895", + "name": "M'light mead(m3)", + "examine": "This looks a good deal stronger than normal Moonlight Mead.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5896", + "name": "M'light mead(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5897", + "name": "M'light mead(m4)", + "examine": "This looks a good deal stronger than normal Moonlight Mead.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5898", + "name": "M'light mead(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5899", + "name": "Axeman's folly(m1)", + "examine": "This looks a good deal stronger than a normal Axeman's Folly.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5900", + "name": "Axeman's folly(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5901", + "name": "Axeman's folly(m2)", + "examine": "This looks a good deal stronger than a normal Axeman's Folly.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5902", + "name": "Axeman's folly(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5903", + "name": "Axeman's folly(m3)", + "examine": "This looks a good deal stronger than a normal Axeman's Folly.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5904", + "name": "Axeman's folly(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5905", + "name": "Axeman's folly(m4)", + "examine": "This might help me chop harder.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5906", + "name": "Axeman's folly(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5907", + "name": "Chef's delight(m1)", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5908", + "name": "Chef's delight(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5909", + "name": "Chef's delight(m2)", + "examine": "This looks a good deal stronger than normal Chef's Delight.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5910", + "name": "Chef's delight(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5911", + "name": "Chef's delight(m3)", + "examine": "This looks a good deal stronger than normal Chef's Delight.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5912", + "name": "Chef's delight(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5913", + "name": "Chef's delight(m4)", + "examine": "This looks a good deal stronger than normal Chef's Delight.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5914", + "name": "Chef's delight(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5915", + "name": "Slayer respite(m1)", + "examine": "This looks a good deal stronger than normal Slayer's Respite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5916", + "name": "Slayer respite(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5917", + "name": "Slayer respite(m2)", + "examine": "This looks a good deal stronger than normal Slayer's Respite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5918", + "name": "Slayer respite(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5919", + "name": "Slayer respite(m3)", + "examine": "This looks a good deal stronger than normal Slayer's Respite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5920", + "name": "Slayer respite(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5921", + "name": "Slayer respite(m4)", + "examine": "This looks a good deal stronger than normal Slayer's Respite.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5922", + "name": "Slayer respite(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5923", + "name": "Cider(m1)", + "examine": "This looks a good deal stronger than normal cider.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5924", + "name": "Cider(m1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5925", + "name": "Cider(m2)", + "examine": "This looks a good deal stronger than normal cider.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5926", + "name": "Cider(m2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5927", + "name": "Cider(m3)", + "examine": "This looks a good deal stronger than normal cider.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5928", + "name": "Cider(m3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5929", + "name": "Cider(m4)", + "examine": "This looks a good deal stronger than normal cider.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5930", + "name": "Cider(m4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5931", + "name": "Jute fibre", + "examine": "I can weave this to make sacks.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "5932", + "name": "Jute fibre", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5933", + "name": "Willow branch", + "examine": "A branch from a willow tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "5934", + "name": "Willow branch", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" + }, + { + "id": "5935", + "name": "Coconut milk", + "examine": "A vial filled with coconut milk.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5937", + "name": "Weapon poison+", + "examine": "A vial of extra-strong weapon poison, for spears and daggers.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5938", + "name": "Weapon poison+", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5940", + "name": "Weapon poison++", + "examine": "A vial of super strong weapon poison, for spears and daggers.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5941", + "name": "Weapon poison++", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5943", + "name": "Antipoison+(4)", + "examine": "N doses of extra-stong antipoison potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5944", + "name": "Antipoison+(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5945", + "name": "Antipoison+(3)", + "examine": "N doses of extra-stong antipoison potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5946", + "name": "Antipoison+(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5947", + "name": "Antipoison+(2)", + "examine": "N doses of extra-stong antipoison potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5948", + "name": "Antipoison+(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5949", + "name": "Antipoison+(1)", + "examine": "N doses of extra-stong antipoison potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5950", + "name": "Antipoison+(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5952", + "name": "Antipoison++(4)", + "examine": "4 doses of a super-strong antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5953", + "name": "Antipoison++(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5954", + "name": "Antipoison++(3)", + "examine": "3 doses of a super-strong antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5955", + "name": "Antipoison++(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5956", + "name": "Antipoison++(2)", + "examine": "2 doses of a super-strong antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5957", + "name": "Antipoison++(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5958", + "name": "Antipoison++(1)", + "examine": "1 dose of a super-strong antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5959", + "name": "Antipoison++(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5960", + "name": "Tomatoes(1)", + "examine": "A basket filled with tomatoes.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5961", + "name": "Tomatoes(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5962", + "name": "Tomatoes(2)", + "examine": "A basket filled with tomatoes.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5963", + "name": "Tomatoes(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5964", + "name": "Tomatoes(3)", + "examine": "A basket filled with tomatoes.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5965", + "name": "Tomatoes(3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5966", + "name": "Tomatoes(4)", + "examine": "A basket filled with tomatoes.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5967", + "name": "Tomatoes(4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5968", + "name": "Tomatoes(5)", + "examine": "A basket filled with tomatoes.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5969", + "name": "Tomatoes(5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5970", + "name": "Curry leaf", + "examine": "I could make a spicy curry with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5971", + "name": "Curry leaf", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5972", + "name": "Papaya fruit", + "examine": "Looks delicious.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5973", + "name": "Papaya fruit", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5974", + "name": "Coconut", + "examine": "It's a coconut.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5975", + "name": "Coconut", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5976", + "name": "Coconut", + "examine": "It's a coconut.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5977", + "name": "Coconut", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "5978", + "name": "Coconut shell", + "examine": "All the milk has been removed.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5979", + "name": "Coconut shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5980", + "name": "Calquat fruit", + "examine": "This is the largest fruit I've ever seen.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5981", + "name": "Calquat fruit", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5982", + "name": "Watermelon", + "examine": "A juicy watermelon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "48", + "tradeable": "true", + "weight": "0.111" + }, + { + "id": "5983", + "name": "Watermelon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5984", + "name": "Watermelon slice", + "examine": "A slice of watermelon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5985", + "name": "Watermelon slice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5986", + "name": "Sweetcorn", + "examine": "Raw sweetcorn.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5987", + "name": "Sweetcorn", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5988", + "name": "Cooked sweetcorn", + "examine": "Delicious cooked sweetcorn.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5989", + "name": "Cooked sweetcorn", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "5991", + "name": "Burnt sweetcorn", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "5992", + "name": "Apple mush", + "examine": "A bucket of apple mush.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5993", + "name": "Apple mush", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5994", + "name": "Hammerstone hops", + "examine": "A handful of Hammerstone hops.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5995", + "name": "Hammerstone hops", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5996", + "name": "Asgarnian hops", + "examine": "A handful of Asgarnian hops.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "5997", + "name": "Asgarnian hops", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5998", + "name": "Yanillian hops", + "examine": "A handful of Yanillian hops.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "5999", + "name": "Yanillian hops", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6000", + "name": "Krandorian hops", + "examine": "A handful of Krandorian hops.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "4", + "tradeable": "true" + }, + { + "id": "6001", + "name": "Krandorian hops", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6002", + "name": "Wildblood hops", + "examine": "A handful of wildblood hops.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6003", + "name": "Wildblood hops", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6004", + "name": "Mushroom", + "examine": "A Bittercap Mushroom", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6005", + "name": "Mushroom", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6006", + "name": "Barley", + "examine": "A handful of Barley.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6007", + "name": "Barley", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6008", + "name": "Barley malt", + "examine": "A handful of barley malt.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6009", + "name": "Barley malt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6010", + "name": "Marigolds", + "examine": "A bunch of marigolds.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6011", + "name": "Marigolds", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6012", + "name": "Nasturtiums", + "examine": "A bunch of nasturtiums.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6013", + "name": "Nasturtiums", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6014", + "name": "Rosemary", + "examine": "Some rosemary.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6015", + "name": "Rosemary", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6016", + "name": "Cactus spine", + "examine": "Don't prick yourself with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6017", + "name": "Cactus spine", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6018", + "name": "Poison ivy berries", + "examine": "They look sweet and juicy, but only a fool would eat them.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6019", + "name": "Poison ivy berries", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6020", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6021", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6022", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6023", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6024", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6025", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6026", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6027", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6028", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6029", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6030", + "name": "Leaves", + "examine": "A pile of leaves.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6031", + "name": "Leaves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6032", + "name": "Compost", + "examine": "Good for plants, helps them grow.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "20", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6033", + "name": "Compost", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6034", + "name": "Supercompost", + "examine": "Super-good for the smallest or largest of plants.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6035", + "name": "Supercompost", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6036", + "name": "Plant cure", + "examine": "Use this on plants to cure disease.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "25", + "tradeable": "true" + }, + { + "id": "6037", + "name": "Plant cure", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6038", + "name": "Magic string", + "examine": "I could use this to make jewellery.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6039", + "name": "Magic string", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6040", + "name": "Amulet of nature", + "examine": "An Amulet of Nature.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to create a new amulet if you destroy this one.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "1" + }, + { + "id": "6041", + "name": "Pre-nature amulet", + "examine": "Strung with the root of a Magic Tree. If I enchant this it will become an amulet of nature.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6042", + "name": "Pre-nature amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6043", + "name": "Oak roots", + "examine": "The roots of the Oak tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6044", + "name": "Oak roots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6045", + "name": "Willow roots", + "examine": "The roots of the Willow tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6046", + "name": "Willow roots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6047", + "name": "Maple roots", + "examine": "The roots of the Maple tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6048", + "name": "Maple roots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6049", + "name": "Yew roots", + "examine": "The roots of a yew tree", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6050", + "name": "Yew roots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6051", + "name": "Magic roots", + "examine": "The roots of a magic tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6052", + "name": "Magic roots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6054", + "name": "Spirit roots", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6055", + "name": "Weeds", + "examine": "A handful of weeds.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "6056", + "name": "Weeds", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6057", + "name": "Hay sack", + "examine": "A sack filled with hay. / This sack of hay has a bronze spear sticking through it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6058", + "name": "Hay sack", + "examine": "A sack filled with hay. / This sack of hay has a bronze spear sticking through it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6059", + "name": "Scarecrow", + "examine": "This should scare the birds.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6060", + "name": "Stool", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6061", + "name": "Bronze bolts(p+)", + "examine": "Bronze crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "6062", + "name": "Bronze bolts(p++)", + "examine": "Bronze crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "3", + "tradeable": "true" + }, + { + "id": "6064", + "name": "Bloody mourner top", + "examine": "How do I wash blood stains out?", + "archery_ticket_price": "0", + "durability": null, + "weight": "2.72" + }, + { + "id": "6065", + "name": "Mourner top", + "examine": "A thick heavy leather top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" + }, + { + "id": "6066", + "name": "Mourner trousers", + "examine": "These are in need of a good tailor.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2.25" + }, + { + "id": "6067", + "name": "Mourner trousers", + "examine": "A pair of mourner trousers.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2.25" + }, + { + "id": "6068", + "name": "Mourner gloves", + "examine": "These will keep my hands warm.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "shop_price": "160", + "weight": "2.3" + }, + { + "id": "6069", + "name": "Mourner boots", + "examine": "Comfortable leather boots.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "shop_price": "160", + "weight": "1.36" + }, + { + "id": "6070", + "name": "Mourner cloak", + "examine": "A dull brown cape.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "1", + "weight": "2.25" + }, + { + "id": "6071", + "name": "Mourner letter", + "examine": "A letter of recommendation.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160" + }, + { + "id": "6072", + "name": "Tegid's soap", + "examine": "A bar of soap taken from Tegid.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "6073", + "name": "Prifddinas' history", + "examine": "A book on the history of Prifddinas.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6075", + "name": "Eastern discovery", + "examine": "A book on the exploration of the eastern realm.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6077", + "name": "Eastern settlement", + "examine": "A book on the settlement of the eastern realm.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6079", + "name": "The great divide", + "examine": "A book about the great divide.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "6081", + "name": "Broken device", + "examine": "A strange broken device of gnomic design.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { "id": "6082", - "stand_turn_anim": "2321" + "name": "Fixed device", + "examine": "A device for firing dye.", + "archery_ticket_price": "0", + "attack_speed": "4", + "durability": null, + "equipment_slot": "3", + "render_anim": "284", + "run_anim": "2322", + "stand_anim": "2316", + "stand_turn_anim": "2321", + "turn180_anim": "2318", + "turn90ccw_anim": "2320", + "turn90cw_anim": "2319", + "two_handed": "true", + "walk_anim": "2317", + "weapon_interface": "20", + "weight": "4" }, { - "examine": "This key has seen a lot of use. (Mourning's Ends Part I)", - "durability": null, + "id": "6083", "name": "Tarnished key", + "examine": "This key has seen a lot of use. (Mourning's Ends Part I)", "archery_ticket_price": "0", - "id": "6083" + "durability": null }, { - "examine": "A large pair of ogre bellows filled with red dye.", - "durability": null, + "id": "6085", "name": "Red dye bellows", + "examine": "A large pair of ogre bellows filled with red dye.", "archery_ticket_price": "0", - "id": "6085" + "durability": null }, { - "examine": "A large pair of ogre bellows filled with blue dye.", - "durability": null, + "id": "6086", "name": "Blue dye bellows", + "examine": "A large pair of ogre bellows filled with blue dye.", "archery_ticket_price": "0", - "id": "6086" + "durability": null }, { - "examine": "A large pair of ogre bellows filled with yellow dye.", - "durability": null, + "id": "6087", "name": "Yellow dye bellows", + "examine": "A large pair of ogre bellows filled with yellow dye.", "archery_ticket_price": "0", - "id": "6087" + "durability": null }, { - "examine": "A large pair of ogre bellows filled with green dye.", - "durability": null, + "id": "6088", "name": "Green dye bellows", + "examine": "A large pair of ogre bellows filled with green dye.", "archery_ticket_price": "0", - "id": "6088" + "durability": null }, { - "examine": "A blue dye filled toad.", - "durability": null, + "id": "6089", "name": "Blue toad", - "weight": "1", + "examine": "A blue dye filled toad.", "archery_ticket_price": "0", - "id": "6089" + "durability": null, + "weight": "1" }, { - "examine": "A red dye filled toad.", - "durability": null, + "id": "6090", "name": "Red toad", - "weight": "0.7", + "examine": "A red dye filled toad.", "archery_ticket_price": "0", - "id": "6090" + "durability": null, + "weight": "0.7" }, { - "examine": "A yellow dye filled toad.", - "durability": null, + "id": "6091", "name": "Yellow toad", - "weight": "0.7", + "examine": "A yellow dye filled toad.", "archery_ticket_price": "0", - "id": "6091" + "durability": null, + "weight": "0.7" }, { - "examine": "A green dye filled toad.", - "durability": null, + "id": "6092", "name": "Green toad", - "weight": "1", + "examine": "A green dye filled toad.", "archery_ticket_price": "0", - "id": "6092" + "durability": null, + "weight": "1" }, { - "examine": "A barrel full of rotten apples.", - "durability": null, + "id": "6093", "name": "Rotten apples", - "weight": "17", + "examine": "A barrel full of rotten apples.", "archery_ticket_price": "0", - "id": "6093" + "durability": null, + "weight": "17" }, { - "examine": "A barrel full of mushed apples.", - "durability": null, + "id": "6094", "name": "Apple barrel", + "examine": "A barrel full of mushed apples.", "archery_ticket_price": "0", - "id": "6094" + "durability": null }, { - "examine": "A barrel full of rotten apples and naptha.", - "durability": null, + "id": "6095", "name": "Naphtha apple mix", + "examine": "A barrel full of rotten apples and naptha.", "archery_ticket_price": "0", - "id": "6095" + "durability": null }, { - "examine": "A barrel full of toxic naphtha.", - "durability": null, + "id": "6096", "name": "Toxic naphtha", - "weight": "32", + "examine": "A barrel full of toxic naphtha.", "archery_ticket_price": "0", - "id": "6096" + "durability": null, + "weight": "32" }, { - "examine": "It's a sieve.", - "durability": null, + "id": "6097", "name": "Sieve", + "examine": "It's a sieve.", "archery_ticket_price": "0", - "id": "6097" + "durability": null }, { - "examine": "A pile of toxic powder.", - "durability": null, + "id": "6098", "name": "Toxic powder", + "examine": "A pile of toxic powder.", "archery_ticket_price": "0", - "id": "6098" + "durability": null }, { - "examine": "A tiny crystal enchanted to return the user to Lletya.", - "durability": null, + "id": "6099", "name": "Teleport crystal (4)", + "examine": "A tiny crystal enchanted to return the user to Lletya.", "archery_ticket_price": "0", - "id": "6099" + "durability": null }, { - "examine": "A tiny crystal enchanted to return the user to Lletya.", - "durability": null, + "id": "6100", "name": "Teleport crystal (3)", + "examine": "A tiny crystal enchanted to return the user to Lletya.", "archery_ticket_price": "0", - "id": "6100" + "durability": null }, { - "examine": "A tiny crystal enchanted to return the user to Lletya.", - "durability": null, + "id": "6101", "name": "Teleport crystal (2)", - "archery_ticket_price": "0", - "id": "6101" - }, - { "examine": "A tiny crystal enchanted to return the user to Lletya.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6102", "name": "Teleport crystal (1)", + "examine": "A tiny crystal enchanted to return the user to Lletya.", "archery_ticket_price": "0", - "id": "6102" + "durability": null }, { - "examine": "A tiny Elf crystal, I need to have this re-enchanted", - "durability": null, + "id": "6103", "name": "Tiny elf crystal", + "examine": "A tiny Elf crystal, I need to have this re-enchanted", "archery_ticket_price": "0", - "id": "6103" + "durability": null }, { - "examine": "This key is newly cut. (Mourning's Ends Part II)", - "durability": null, + "id": "6104", "name": "New key", - "weight": "0.33", + "examine": "This key is newly cut. (Mourning's Ends Part II)", "archery_ticket_price": "0", - "id": "6104" + "durability": null, + "weight": "0.33" }, { - "examine": "They seem to be not quite of this world...", - "durability": null, - "name": "Ghostly boots", - "archery_ticket_price": "0", "id": "6106", + "name": "Ghostly boots", + "examine": "They seem to be not quite of this world...", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, "equipment_slot": "10" }, { - "remove_sleeves": "true", - "examine": "Varies", - "durability": null, - "name": "Ghostly robe", - "archery_ticket_price": "0", "id": "6107", + "name": "Ghostly robe", + "examine": "Varies", + "archery_ticket_price": "0", "bonuses": "0,0,0,5,0,0,0,0,5,0,5,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "examine": "Varies", - "durability": null, - "name": "Ghostly robe", - "archery_ticket_price": "0", "id": "6108", + "name": "Ghostly robe", + "examine": "Varies", + "archery_ticket_price": "0", "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, "equipment_slot": "7" }, { - "remove_head": "true", - "examine": "A ghostly hood, fit for a ghostly head.", - "durability": null, - "name": "Ghostly hood", - "archery_ticket_price": "0", "id": "6109", + "name": "Ghostly hood", + "examine": "A ghostly hood, fit for a ghostly head.", + "archery_ticket_price": "0", "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "remove_head": "true" }, { - "examine": "They seem to fade in and out of existence...", - "durability": null, - "name": "Ghostly gloves", - "archery_ticket_price": "0", "id": "6110", + "name": "Ghostly gloves", + "examine": "They seem to fade in and out of existence...", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, "equipment_slot": "9" }, { - "examine": "Made of a strange, ghostly material...", - "durability": null, - "name": "Ghostly cloak", - "archery_ticket_price": "0", "id": "6111", + "name": "Ghostly cloak", + "examine": "Made of a strange, ghostly material...", + "archery_ticket_price": "0", "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0", + "durability": null, "equipment_slot": "1" }, { - "destroy_message": "Return to the drunken dwarf's drunken relative to get your seeds back.", - "examine": "Kelda hop seeds can only be grown underground!", - "durability": null, + "id": "6112", "name": "Kelda seed", - "tradeable": "false", - "destroy": "true", + "examine": "Kelda hop seeds can only be grown underground!", "archery_ticket_price": "0", - "id": "6112" + "destroy": "true", + "destroy_message": "Return to the drunken dwarf's drunken relative to get your seeds back.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Return to the Keldagrim palace gardener to get your hops back.", - "examine": "A handful of Kelda Hops.", - "durability": null, + "id": "6113", "name": "Kelda hops", - "tradeable": "false", + "examine": "A handful of Kelda Hops.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "0.01", - "archery_ticket_price": "0", - "id": "6113" + "destroy_message": "Return to the Keldagrim palace gardener to get your hops back.", + "durability": null, + "tradeable": "false", + "weight": "0.01" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pint of bluish beer.", - "durability": null, + "id": "6118", "name": "Kelda stout", - "weight": "0.5", + "examine": "A pint of bluish beer.", "archery_ticket_price": "0", - "id": "6118" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "0.5" }, { - "examine": "There is a strange [green/yellow] marking on this stone.", - "durability": null, + "id": "6119", "name": "Square stone", - "weight": "1", - "archery_ticket_price": "0", - "id": "6119" - }, - { "examine": "There is a strange [green/yellow] marking on this stone.", - "durability": null, - "name": "Square stone", - "weight": "1", "archery_ticket_price": "0", - "id": "6120" + "durability": null, + "weight": "1" }, { + "id": "6120", + "name": "Square stone", + "examine": "There is a strange [green/yellow] marking on this stone.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "6122", "name": "A chair", "archery_ticket_price": "0", - "id": "6122", + "durability": null, "equipment_slot": "5" }, { - "shop_price": "1", - "examine": "I need to fill this with beer.", - "grand_exchange_price": "25", - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", "id": "6123", - "equipment_slot": "3" + "name": "Beer glass", + "examine": "I need to fill this with beer.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" }, { - "durability": null, + "id": "6124", "name": "Coconut milk", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6124" + "durability": null, + "tradeable": "true" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", - "durability": null, - "name": "Enchanted lyre(2)", - "weight": "1.8", - "archery_ticket_price": "0", "id": "6125", - "equipment_slot": "3" + "name": "Enchanted lyre(2)", + "examine": "This will teleport me to Rellekka when I play it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", - "durability": null, - "name": "Enchanted lyre(3)", - "weight": "1.8", - "archery_ticket_price": "0", "id": "6126", - "equipment_slot": "3" + "name": "Enchanted lyre(3)", + "examine": "This will teleport me to Rellekka when I play it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", - "durability": null, - "name": "Enchanted lyre(4)", - "weight": "1.8", - "archery_ticket_price": "0", "id": "6127", - "equipment_slot": "3" + "name": "Enchanted lyre(4)", + "examine": "This will teleport me to Rellekka when I play it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1.8" }, { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "100", - "examine": "Protective headwear made from crabs. Better than it sounds.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "21000", - "name": "Rock-shell helm", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6128", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,6,0,0,0,0" - }, - { - "lendable": "true", - "requirements": "{1,40}", - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "40600", - "durability": null, - "name": "Rock-shell plate", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6129", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,30,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "100", - "examine": "Some tough leggings made from rock crab parts.", - "durability": null, - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "39400", - "name": "Rock-shell legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6130", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,10,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "100", - "examine": "A helm fit for any Fremennik ranger.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "35800", - "name": "Spined helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6131", - "bonuses": "-6,-6,-6,-6,6,6,6,6,6,0,6,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "35800", - "durability": null, - "name": "Spined helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6132" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "100", - "examine": "A constant reminder that I'm above a Dagannoth in the food chain.", - "durability": null, - "weight": "6", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "4386", - "name": "Spined body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6133", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,30,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4386", - "durability": null, - "name": "Spined body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6134" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "100", - "examine": "Stylish leg armour for rangers with a lingering smell of raw fish...", - "durability": null, - "weight": "5", - "absorb": "0,4,2", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "2132", - "name": "Spined chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6135", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,10,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2132", - "durability": null, - "name": "Spined chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6136" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "Make your foes cower by wearing a skull as a helmet!", - "durability": null, - "weight": "1.3", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "29500", - "name": "Skeletal helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6137", - "bonuses": "0,0,0,2,-2,10,9,11,3,0,6,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "29500", - "durability": null, - "name": "Skeletal helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6138" - }, - { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "The bones in this armour seems to vibrate with a magic quality...", - "durability": null, - "weight": "4.9", - "absorb": "6,3,0", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "27800", - "name": "Skeletal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6139", - "bonuses": "0,0,0,8,-10,35,25,42,15,0,30,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "27800", - "durability": null, - "name": "Skeletal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6140" - }, - { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "A superior set of strengthened slacks for any self respecting seer.", - "durability": null, - "weight": "4", - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "23600", - "name": "Skeletal bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6141", - "bonuses": "0,0,0,6,-7,22,20,24,10,0,10,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "23600", - "durability": null, - "name": "Skeletal bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1671", - "durability": null, - "name": "Spined boots", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "756", - "durability": null, - "name": "Rock-shell boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6146" - }, - { - "lendable": "true", - "ge_buy_limit": "100", - "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", - "grand_exchange_price": "4958", - "durability": null, - "name": "Skeletal boots", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6147", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4958", - "durability": null, - "name": "Skeletal boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6148" - }, - { - "lendable": "true", - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from spined dagannoth hide.", - "grand_exchange_price": "955", - "durability": null, - "name": "Spined gloves", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6149", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "955", - "durability": null, - "name": "Spined gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6150" - }, - { - "lendable": "true", - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from rock crab shell shards.", - "grand_exchange_price": "951", - "durability": null, - "name": "Rock-shell gloves", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "6151", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "951", - "durability": null, - "name": "Rock-shell gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6152" - }, - { - "lendable": "true", - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", - "grand_exchange_price": "21800", - "durability": null, - "name": "Skeletal gloves", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6153", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21800", - "durability": null, - "name": "Skeletal gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6154" - }, - { - "ge_buy_limit": "500", - "examine": "A sturdy piece of dagannoth hide.", - "grand_exchange_price": "3978", - "durability": null, - "name": "Dagannoth hide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6155" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "3978", - "durability": null, - "name": "Dagannoth hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6156" - }, - { - "ge_buy_limit": "100", - "examine": "A spherical chunk of rock-shell.", - "grand_exchange_price": "7371", - "durability": null, - "name": "Rock-shell chunk", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6157" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7371", - "durability": null, - "name": "Rock-shell chunk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6158" - }, - { - "ge_buy_limit": "100", - "examine": "A curved piece of rock-shell.", - "grand_exchange_price": "11300", - "durability": null, - "name": "Rock-shell shard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6159" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11300", - "durability": null, - "name": "Rock-shell shard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6160" - }, - { - "ge_buy_limit": "100", - "examine": "A slim piece of rock-shell.", - "grand_exchange_price": "16100", - "durability": null, - "name": "Rock-shell splinter", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6161" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16100", - "durability": null, - "name": "Rock-shell splinter", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6162" - }, - { - "ge_buy_limit": "100", - "examine": "A fearsome looking skull.", - "grand_exchange_price": "9859", - "durability": null, - "name": "Skull piece", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6163" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9859", - "durability": null, - "name": "Skull piece", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6164" - }, - { - "ge_buy_limit": "100", - "examine": "A slightly damaged ribcage.", - "grand_exchange_price": "11300", - "durability": null, - "name": "Ribcage piece", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6165" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11300", - "durability": null, - "name": "Ribcage piece", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6166" - }, - { - "ge_buy_limit": "100", - "examine": "An interesting looking bone shard.", - "grand_exchange_price": "10300", - "durability": null, - "name": "Fibula piece", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6167" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10300", - "durability": null, - "name": "Fibula piece", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6168" - }, - { - "ge_buy_limit": "100", - "examine": "A toughened chunk of dagannoth hide.", - "grand_exchange_price": "23400", - "durability": null, - "name": "Circular hide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6169" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "23400", - "durability": null, - "name": "Circular hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6170" - }, - { - "ge_buy_limit": "100", - "examine": "A tattered chunk of dagannoth hide.", - "grand_exchange_price": "502", - "durability": null, - "name": "Flattened hide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6171" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "502", - "durability": null, - "name": "Flattened hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6172" - }, - { - "ge_buy_limit": "100", - "examine": "A weathered chunk of dagannoth hide.", - "grand_exchange_price": "398", - "durability": null, - "name": "Stretched hide", - "tradeable": "true", - "weight": "3.1", - "archery_ticket_price": "0", - "id": "6173" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "398", - "durability": null, - "name": "Stretched hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6174" - }, - { - "durability": null, "name": "Rock-shell helm", - "tradeable": "true", + "examine": "Protective headwear made from crabs. Better than it sounds.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "6175" + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.7" }, { - "examine": "A sturdy body armour made from rock crab pieces.", - "durability": null, + "id": "6129", "name": "Rock-shell plate", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "9", - "id": "6176" + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,30,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true" }, { - "durability": null, + "id": "6130", "name": "Rock-shell legs", + "examine": "Some tough leggings made from rock crab parts.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6177" + "weight": "9" }, { - "examine": "I need to cook this first.", - "durability": null, - "name": "Raw pheasant", - "weight": "10", + "id": "6131", + "name": "Spined helm", + "examine": "A helm fit for any Fremennik ranger.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "6178" - }, - { - "examine": "I need to cook this first.", + "bonuses": "-6,-6,-6,-6,6,6,6,6,6,0,6,0,0,0,0", "durability": null, - "name": "Raw pheasant", - "weight": "10", - "archery_ticket_price": "0", - "id": "6179" - }, - { - "remove_sleeves": "true", - "examine": "A leather strapped top.", - "durability": null, - "name": "Lederhosen top", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "6180", - "equipment_slot": "4" - }, - { - "examine": "Brown leather shorts with bright white socks?", - "durability": null, - "name": "Lederhosen shorts", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "6181", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "examine": "A hat with a goat's hair attached.", - "durability": null, - "name": "Lederhosen hat", - "weight": "2", - "archery_ticket_price": "0", - "id": "6182", - "equipment_slot": "0" - }, - { - "examine": "I can use this at the Varrock clothes shop.", - "durability": null, - "name": "Frog token", - "archery_ticket_price": "0", - "id": "6183" - }, - { - "remove_sleeves": "true", - "examine": "Very posh!", - "durability": null, - "name": "Prince tunic", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "6184", - "equipment_slot": "4" - }, - { - "examine": "Very posh!", - "durability": null, - "name": "Prince leggings", - "weight": "2", - "archery_ticket_price": "0", - "id": "6185", - "equipment_slot": "7" - }, - { - "remove_sleeves": "true", - "examine": "Very posh!", - "durability": null, - "name": "Princess blouse", - "weight": "2", - "archery_ticket_price": "0", - "id": "6186", - "equipment_slot": "4" - }, - { - "examine": "Very posh!", - "durability": null, - "name": "Princess skirt", - "weight": "2", - "archery_ticket_price": "0", - "id": "6187", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "examine": "Now that's just silly.", - "durability": null, - "name": "Frog mask", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "6188", + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "1" }, { - "examine": "Could there be something valuable in here?", - "durability": null, - "name": "Mystery box", - "weight": "1", + "id": "6132", + "name": "Spined helm", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6133", + "name": "Spined body", + "examine": "A constant reminder that I'm above a Dagannoth in the food chain.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,30,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "6134", + "name": "Spined body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6135", + "name": "Spined chaps", + "examine": "Stylish leg armour for rangers with a lingering smell of raw fish...", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "6136", + "name": "Spined chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6137", + "name": "Skeletal helm", + "examine": "Make your foes cower by wearing a skull as a helmet!", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,-2,10,9,11,3,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "6138", + "name": "Skeletal helm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6139", + "name": "Skeletal top", + "examine": "The bones in this armour seems to vibrate with a magic quality...", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,8,-10,35,25,42,15,0,30,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "4.9" + }, + { + "id": "6140", + "name": "Skeletal top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6141", + "name": "Skeletal bottoms", + "examine": "A superior set of strengthened slacks for any self respecting seer.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,-7,22,20,24,10,0,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "lendable": "true", + "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "6142", + "name": "Skeletal bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6143", + "name": "Spined boots", + "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6144", + "name": "Spined boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6145", + "name": "Rock-shell boots", + "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6146", + "name": "Rock-shell boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6147", + "name": "Skeletal boots", + "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6148", + "name": "Skeletal boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6149", + "name": "Spined gloves", + "examine": "Fremennik gloves stitched together from spined dagannoth hide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6150", + "name": "Spined gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6151", + "name": "Rock-shell gloves", + "examine": "Fremennik gloves stitched together from rock crab shell shards.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "6152", + "name": "Rock-shell gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6153", + "name": "Skeletal gloves", + "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6154", + "name": "Skeletal gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6155", + "name": "Dagannoth hide", + "examine": "A sturdy piece of dagannoth hide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6156", + "name": "Dagannoth hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "6157", + "name": "Rock-shell chunk", + "examine": "A spherical chunk of rock-shell.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6158", + "name": "Rock-shell chunk", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6159", + "name": "Rock-shell shard", + "examine": "A curved piece of rock-shell.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6160", + "name": "Rock-shell shard", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6161", + "name": "Rock-shell splinter", + "examine": "A slim piece of rock-shell.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3" + }, + { + "id": "6162", + "name": "Rock-shell splinter", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6163", + "name": "Skull piece", + "examine": "A fearsome looking skull.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6164", + "name": "Skull piece", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6165", + "name": "Ribcage piece", + "examine": "A slightly damaged ribcage.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6166", + "name": "Ribcage piece", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6167", + "name": "Fibula piece", + "examine": "An interesting looking bone shard.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6168", + "name": "Fibula piece", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6169", + "name": "Circular hide", + "examine": "A toughened chunk of dagannoth hide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6170", + "name": "Circular hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6171", + "name": "Flattened hide", + "examine": "A tattered chunk of dagannoth hide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6172", + "name": "Flattened hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6173", + "name": "Stretched hide", + "examine": "A weathered chunk of dagannoth hide.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "3.1" + }, + { + "id": "6174", + "name": "Stretched hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6175", + "name": "Rock-shell helm", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6176", + "name": "Rock-shell plate", + "examine": "A sturdy body armour made from rock crab pieces.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "9" + }, + { + "id": "6177", + "name": "Rock-shell legs", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6178", + "name": "Raw pheasant", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "weight": "10" + }, + { + "id": "6179", + "name": "Raw pheasant", + "examine": "I need to cook this first.", + "archery_ticket_price": "0", + "durability": null, + "weight": "10" + }, + { + "id": "6180", + "name": "Lederhosen top", + "examine": "A leather strapped top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.2" + }, + { + "id": "6181", + "name": "Lederhosen shorts", + "examine": "Brown leather shorts with bright white socks?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2.2" + }, + { + "id": "6182", + "name": "Lederhosen hat", + "examine": "A hat with a goat's hair attached.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "2" + }, + { + "id": "6183", + "name": "Frog token", + "examine": "I can use this at the Varrock clothes shop.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6184", + "name": "Prince tunic", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.7" + }, + { + "id": "6185", + "name": "Prince leggings", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2" + }, + { + "id": "6186", + "name": "Princess blouse", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" + }, + { + "id": "6187", + "name": "Princess skirt", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2" + }, + { + "id": "6188", + "name": "Frog mask", + "examine": "Now that's just silly.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "weight": "0.4" + }, + { "id": "6199", - "point_price": "2" - }, - { - "destroy_message": "A ghastly fish", - "examine": "A raw...fish? Is this a fish??", - "durability": null, - "name": "Raw fishlike thing", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "name": "Mystery box", + "examine": "Could there be something valuable in here?", "archery_ticket_price": "0", - "id": "6200" + "durability": null, + "point_price": "2", + "weight": "1" }, { - "examine": "Fish-tastic!", + "id": "6200", + "name": "Raw fishlike thing", + "examine": "A raw...fish? Is this a fish??", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "A ghastly fish", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "6201", "name": "Picture", + "examine": "Fish-tastic!", "archery_ticket_price": "0", - "id": "6201" + "durability": null }, { - "destroy_message": "A ghastly fish", - "examine": "It's a fish-like thing that appears to already be cooked. It looks disgusting.", - "durability": null, + "id": "6202", "name": "Fishlike thing", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "It's a fish-like thing that appears to already be cooked. It looks disgusting.", "archery_ticket_price": "0", - "id": "6202" + "destroy": "true", + "destroy_message": "A ghastly fish", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "A ghastly fish", - "examine": "A raw...fish? Is this a fish??", - "durability": null, + "id": "6204", "name": "Raw fishlike thing", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A raw...fish? Is this a fish??", "archery_ticket_price": "0", - "id": "6204" - }, - { + "destroy": "true", "destroy_message": "A ghastly fish", - "examine": "It's a fish-like thing that appears to already be cooked. It looks disgusting.", "durability": null, - "name": "Fishlike thing", "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6206" + "weight": "1" }, { - "examine": "It's an amulet of Man speak. It makes vague grunting noises.", - "durability": null, - "name": "Man speak amulet", + "id": "6206", + "name": "Fishlike thing", + "examine": "It's a fish-like thing that appears to already be cooked. It looks disgusting.", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "A ghastly fish", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { "id": "6208", + "name": "Man speak amulet", + "examine": "It's an amulet of Man speak. It makes vague grunting noises.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "shop_price": "40", - "examine": "Useful for catching small fish.", - "grand_exchange_price": "433", - "durability": null, + "id": "6209", "name": "Small fishing net", - "tradeable": "true", - "weight": "4.5", + "examine": "Useful for catching small fish.", "archery_ticket_price": "0", - "id": "6209" + "durability": null, + "shop_price": "40", + "tradeable": "true", + "weight": "4.5" }, { - "ge_buy_limit": "1000", + "id": "6211", + "name": "Teak pyre logs", "examine": "Teak logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "3365", - "durability": null, - "name": "Teak pyre logs", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "6211" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "3365", - "durability": null, + "id": "6212", "name": "Teak pyre logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6212" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "6213", + "name": "Mahogany pyre log", "examine": "Mahogany logs prepared with sacred oil for a funeral pyre.", - "grand_exchange_price": "3574", - "durability": null, - "name": "Mahogany pyre log", - "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", - "id": "6213" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "3574", - "durability": null, - "name": "Mahogany pyre log", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6214" + "weight": "1.3" }, { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "2007", - "name": "Broodoo shield (10)", - "tradeable": "true", + "id": "6214", + "name": "Mahogany pyre log", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "6215", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2007", - "durability": null, "name": "Broodoo shield (10)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6216" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (9)", - "tradeable": "true", - "weight": "5.4", + "id": "6216", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6217", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (9)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6218" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (8)", - "tradeable": "true", - "weight": "5.4", + "id": "6218", + "name": "Broodoo shield (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6219", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (8)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6220" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (7)", - "tradeable": "true", - "weight": "5.4", + "id": "6220", + "name": "Broodoo shield (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6221", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (7)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6222" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (6)", - "tradeable": "true", - "weight": "5.4", + "id": "6222", + "name": "Broodoo shield (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6223", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (6)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6224" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (5)", - "tradeable": "true", - "weight": "5.4", + "id": "6224", + "name": "Broodoo shield (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6225", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (5)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6226" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (4)", - "tradeable": "true", - "weight": "5.4", + "id": "6226", + "name": "Broodoo shield (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6227", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (4)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6228" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (3)", - "tradeable": "true", - "weight": "5.4", + "id": "6228", + "name": "Broodoo shield (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6229", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (3)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6230" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (2)", - "tradeable": "true", - "weight": "5.4", + "id": "6230", + "name": "Broodoo shield (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6231", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (2)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6232" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (1)", - "tradeable": "true", - "weight": "5.4", + "id": "6232", + "name": "Broodoo shield (2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6233", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (1)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6234" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "2572", - "name": "Broodoo shield", - "tradeable": "true", + "id": "6234", + "name": "Broodoo shield (1)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6235", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2572", - "durability": null, "name": "Broodoo shield", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6236" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "2200", - "name": "Broodoo shield (10)", - "tradeable": "true", + "id": "6236", + "name": "Broodoo shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6237", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2200", - "durability": null, "name": "Broodoo shield (10)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6238" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (9)", - "tradeable": "true", - "weight": "5.4", + "id": "6238", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6239", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (9)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6240" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (8)", - "tradeable": "true", - "weight": "5.4", + "id": "6240", + "name": "Broodoo shield (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6241", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (8)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6242" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (7)", - "tradeable": "true", - "weight": "5.4", + "id": "6242", + "name": "Broodoo shield (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6243", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (7)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6244" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (6)", - "tradeable": "true", - "weight": "5.4", + "id": "6244", + "name": "Broodoo shield (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6245", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (6)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6246" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (5)", - "tradeable": "true", - "weight": "5.4", + "id": "6246", + "name": "Broodoo shield (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6247", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (5)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6248" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (4)", - "tradeable": "true", - "weight": "5.4", + "id": "6248", + "name": "Broodoo shield (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6249", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (4)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6250" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (3)", - "tradeable": "true", - "weight": "5.4", + "id": "6250", + "name": "Broodoo shield (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6251", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (3)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6252" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (2)", - "tradeable": "true", - "weight": "5.4", + "id": "6252", + "name": "Broodoo shield (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6253", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (2)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6254" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (1)", - "tradeable": "true", - "weight": "5.4", + "id": "6254", + "name": "Broodoo shield (2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6255", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, "name": "Broodoo shield (1)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6256" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "2731", - "name": "Broodoo shield", - "tradeable": "true", + "id": "6256", + "name": "Broodoo shield (1)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6257", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2731", - "durability": null, "name": "Broodoo shield", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6258" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1494", - "name": "Broodoo shield (10)", - "tradeable": "true", + "id": "6258", + "name": "Broodoo shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6259", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "attack_anims": "7060", - "ge_buy_limit": "100", - "grand_exchange_price": "1494", - "durability": null, "name": "Broodoo shield (10)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6260" - }, - { - "requirements": "{1,25}-{6,25}", "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (9)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6261", "absorb": "4,2,0", + "archery_ticket_price": "0", "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { "durability": null, - "name": "Broodoo shield (9)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6262" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (8)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6263", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (8)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6264" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (7)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6265", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (7)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6266" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (6)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6267", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (6)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6268" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (5)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6269", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6270" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (4)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6271", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6272" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (3)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6273", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6274" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (2)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6275", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6276" - }, - { - "requirements": "{1,25}-{6,25}", - "examine": "A scary broodoo shield.", - "grand_exchange_price": "1", - "durability": null, - "name": "Broodoo shield (1)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "6277", - "absorb": "4,2,0", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", - "equipment_slot": "5" - }, - { - "durability": null, - "name": "Broodoo shield (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6278" - }, - { - "requirements": "{1,25}-{6,25}", - "ge_buy_limit": "100", - "examine": "A scary broodoo shield.", - "durability": null, - "weight": "5.4", - "absorb": "4,2,0", "equipment_slot": "5", - "grand_exchange_price": "2957", - "name": "Broodoo shield", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6260", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "attack_anims": "7060", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6261", + "name": "Broodoo shield (9)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6262", + "name": "Broodoo shield (9)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6263", + "name": "Broodoo shield (8)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6264", + "name": "Broodoo shield (8)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6265", + "name": "Broodoo shield (7)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6266", + "name": "Broodoo shield (7)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6267", + "name": "Broodoo shield (6)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6268", + "name": "Broodoo shield (6)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6269", + "name": "Broodoo shield (5)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6270", + "name": "Broodoo shield (5)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6271", + "name": "Broodoo shield (4)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6272", + "name": "Broodoo shield (4)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6273", + "name": "Broodoo shield (3)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6274", + "name": "Broodoo shield (3)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6275", + "name": "Broodoo shield (2)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6276", + "name": "Broodoo shield (2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6277", + "name": "Broodoo shield (1)", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "6278", + "name": "Broodoo shield (1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6279", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2957", - "durability": null, "name": "Broodoo shield", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6280" + "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}-{6,25}", + "tradeable": "true", + "weight": "5.4" }, { - "ge_buy_limit": "100", - "examine": "A wooden pole for use in primitive construction.", - "grand_exchange_price": "1", - "durability": null, - "name": "Thatch spar light", - "tradeable": "true", - "weight": "0.1", + "id": "6280", + "name": "Broodoo shield", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6281", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, "name": "Thatch spar light", - "tradeable": "true", + "examine": "A wooden pole for use in primitive construction.", "archery_ticket_price": "0", - "id": "6282" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "examine": "A wooden pole for use in primitive construction.", - "grand_exchange_price": "2", - "durability": null, - "name": "Thatch spar med", - "tradeable": "true", - "weight": "0.1", + "id": "6282", + "name": "Thatch spar light", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6283", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, "name": "Thatch spar med", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6284" - }, - { - "ge_buy_limit": "100", "examine": "A wooden pole for use in primitive construction.", - "grand_exchange_price": "3", - "durability": null, - "name": "Thatch spar dense", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "6284", + "name": "Thatch spar med", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6285", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3", - "durability": null, "name": "Thatch spar dense", - "tradeable": "true", + "examine": "A wooden pole for use in primitive construction.", "archery_ticket_price": "0", - "id": "6286" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "10000", + "id": "6286", + "name": "Thatch spar dense", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6287", + "name": "Snake hide", "examine": "Scaly but not slimy!", - "grand_exchange_price": "2084", - "durability": null, - "name": "Snake hide", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "6287" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "2084", - "durability": null, + "id": "6288", "name": "Snake hide", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6288" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "6289", + "name": "Snakeskin", "examine": "Scaley but not slimy!", - "grand_exchange_price": "2019", - "durability": null, - "name": "Snakeskin", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "6289" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "2019", - "durability": null, - "name": "Snakeskin", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6290" + "weight": "0.5" }, { - "ge_buy_limit": "1000", + "id": "6290", + "name": "Snakeskin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6291", + "name": "Spider carcass", "examine": "Its creeping days are over!", - "grand_exchange_price": "1949", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "6292", "name": "Spider carcass", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "6291" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1949", "durability": null, - "name": "Spider carcass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6292" + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "50", - "ge_buy_limit": "1000", + "id": "6293", + "name": "Spider on stick", "examine": "Raw: A raw spider threaded onto a skewer stick.Cooked: A nicely roasted spider threaded onto a skewer stick.", - "grand_exchange_price": "207", - "durability": null, - "name": "Spider on stick", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "6293" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "207", - "durability": null, - "name": "Spider on stick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6294" - }, - { - "shop_price": "40", - "ge_buy_limit": "1000", - "examine": "A raw spider threaded onto an arrow shaft. (Raw) A nicely roasted spider threaded onto an arrow shaft. (Cooked) A badly burnt spider threaded onto a charred arrow shaft. (Burnt)", - "grand_exchange_price": "106", - "durability": null, - "name": "Spider on shaft", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6295" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "106", - "durability": null, - "name": "Spider on shaft", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6296" - }, - { "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "Raw: A raw spider threaded onto a skewer stick.Cooked: A nicely roasted spider threaded onto a skewer stick.", - "grand_exchange_price": "54", - "durability": null, - "name": "Spider on stick", "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "6297" + "weight": "0.9" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "54", - "durability": null, + "id": "6294", "name": "Spider on stick", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6298" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "40", - "ge_buy_limit": "1000", + "id": "6295", + "name": "Spider on shaft", "examine": "A raw spider threaded onto an arrow shaft. (Raw) A nicely roasted spider threaded onto an arrow shaft. (Cooked) A badly burnt spider threaded onto a charred arrow shaft. (Burnt)", - "grand_exchange_price": "78", - "durability": null, - "name": "Spider on shaft", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "6299" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "78", - "durability": null, - "name": "Spider on shaft", + "shop_price": "40", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6300" + "weight": "1" }, { + "id": "6296", + "name": "Spider on shaft", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6297", + "name": "Spider on stick", + "examine": "Raw: A raw spider threaded onto a skewer stick.Cooked: A nicely roasted spider threaded onto a skewer stick.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "6298", + "name": "Spider on stick", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6299", + "name": "Spider on shaft", + "examine": "A raw spider threaded onto an arrow shaft. (Raw) A nicely roasted spider threaded onto an arrow shaft. (Cooked) A badly burnt spider threaded onto a charred arrow shaft. (Burnt)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "40", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6300", + "name": "Spider on shaft", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6302", "name": "Burnt spider", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6302" + "durability": null, + "tradeable": "true" }, { - "shop_price": "40", + "id": "6303", + "name": "Spider on shaft", "examine": "A raw spider threaded onto an arrow shaft. (Raw) A nicely roasted spider threaded onto an arrow shaft. (Cooked) A badly burnt spider threaded onto a charred arrow shaft. (Burnt)", - "grand_exchange_price": "106", - "durability": null, - "name": "Spider on shaft", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "6303" + "durability": null, + "shop_price": "40", + "tradeable": "true", + "weight": "1" }, { - "durability": null, + "id": "6304", "name": "Spider on shaft", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6304" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A sharp pointed stick, quite resistant to fire.", - "grand_exchange_price": "1", - "durability": null, + "id": "6305", "name": "Skewer stick", - "tradeable": "true", + "examine": "A sharp pointed stick, quite resistant to fire.", "archery_ticket_price": "0", - "id": "6305" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Karamja currency.", - "grand_exchange_price": "2", - "durability": null, + "id": "6306", "name": "Trading sticks", - "tradeable": "true", + "examine": "Karamja currency.", "archery_ticket_price": "0", - "id": "6306" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "6311", + "name": "Gout tuber", "examine": "Plant this in a herb patch to grow Goutweed.", - "grand_exchange_price": "6991", - "durability": null, - "name": "Gout tuber", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "6311" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "6991", - "durability": null, + "id": "6312", "name": "Gout tuber", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6312" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "500", - "ge_buy_limit": "100", - "examine": "A jungle specific slashing device.", - "durability": null, - "weight": "1.35", - "weapon_interface": "6", - "render_anim": "2554", - "equipment_slot": "3", - "grand_exchange_price": "179", - "name": "Opal machete", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6313", - "bonuses": "0,8,-2,0,0,0,1,1,0,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "179", - "durability": null, "name": "Opal machete", - "tradeable": "true", + "examine": "A jungle specific slashing device.", "archery_ticket_price": "0", - "id": "6314" + "bonuses": "0,8,-2,0,0,0,1,1,0,0,0,4,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2554", + "shop_price": "500", + "tradeable": "true", + "weapon_interface": "6", + "weight": "1.35" }, { - "shop_price": "1000", - "ge_buy_limit": "100", - "examine": "A jungle specific slashing device.", - "durability": null, - "weapon_interface": "6", - "render_anim": "2554", - "equipment_slot": "3", - "grand_exchange_price": "4473", - "name": "Jade machete", - "tradeable": "true", + "id": "6314", + "name": "Opal machete", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6315", - "bonuses": "0,11,-2,0,0,0,1,1,0,0,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4473", - "durability": null, "name": "Jade machete", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6316" - }, - { - "shop_price": "2000", - "ge_buy_limit": "100", "examine": "A jungle specific slashing device.", + "archery_ticket_price": "0", + "bonuses": "0,11,-2,0,0,0,1,1,0,0,0,6,0,0,0", "durability": null, - "weight": "1.3", - "attack_speed": "4", - "weapon_interface": "6", - "render_anim": "2554", "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "8774", - "name": "Red topaz machete", + "ge_buy_limit": "100", + "render_anim": "2554", + "shop_price": "1000", "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "6316", + "name": "Jade machete", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6317", - "bonuses": "0,16,-2,0,0,0,1,1,0,0,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8774", - "durability": null, "name": "Red topaz machete", - "tradeable": "true", + "examine": "A jungle specific slashing device.", "archery_ticket_price": "0", - "id": "6318" - }, - { - "ge_buy_limit": "100", - "examine": "A giant mosquito's proboscis: aerodynamic, sharp and pointy!", - "grand_exchange_price": "9733", + "attack_anims": "390,390,381,390", + "attack_speed": "4", + "bonuses": "0,16,-2,0,0,0,1,1,0,0,0,10,0,0,0", "durability": null, - "name": "Proboscis", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2554", + "shop_price": "2000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6319" + "weapon_interface": "6", + "weight": "1.3" }, { + "id": "6318", + "name": "Red topaz machete", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6319", + "name": "Proboscis", + "examine": "A giant mosquito's proboscis: aerodynamic, sharp and pointy!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6322", + "name": "Snakeskin body", + "examine": "Made from 100% real snakeskin.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,12,25,28,32,15,35,30,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", "requirements": "{1,30}-{4,30}", "shop_price": "1588", - "ge_buy_limit": "100", - "examine": "Made from 100% real snakeskin.", - "durability": null, - "weight": "10", - "absorb": "0,4,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "23100", - "name": "Snakeskin body", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6322", - "bonuses": "0,0,0,-5,12,25,28,32,15,35,30,0,0,0,0" + "weight": "10" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "23100", - "durability": null, + "id": "6323", "name": "Snakeskin body", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6323" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,30}-{4,30}", - "ge_buy_limit": "100", - "examine": "Made from 100% real snake.", - "durability": null, - "weight": "3.6", - "absorb": "0,3,1", - "equipment_slot": "7", - "grand_exchange_price": "702", - "name": "Snakeskin chaps", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6324", - "bonuses": "0,0,0,-5,6,8,8,10,4,10,10,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "702", - "durability": null, "name": "Snakeskin chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6325" - }, - { - "remove_head": "true", - "requirements": "{1,30}-{4,30}", - "ge_buy_limit": "100", - "examine": "Lightweight head protection.", - "durability": null, - "weight": "0.9", - "absorb": "0,2,1", - "equipment_slot": "0", - "grand_exchange_price": "871", - "name": "Snakeskin bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6326", - "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "871", - "durability": null, - "name": "Snakeskin bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6327" - }, - { - "requirements": "{1,30}-{4,30}", - "ge_buy_limit": "100", - "examine": "Made from snakes.", - "grand_exchange_price": "10800", - "durability": null, - "name": "Snakeskin boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "6328", - "bonuses": "0,0,0,-10,3,1,1,2,1,0,9,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10800", - "durability": null, - "name": "Snakeskin boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6329" - }, - { - "requirements": "{1,30}-{4,30}", - "ge_buy_limit": "10000", "examine": "Made from 100% real snake.", - "grand_exchange_price": "914", - "durability": null, - "name": "Snakeskin v'brace", - "tradeable": "true", - "weight": "1", + "absorb": "0,3,1", "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,6,8,8,10,4,10,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,30}-{4,30}", + "tradeable": "true", + "weight": "3.6" + }, + { + "id": "6325", + "name": "Snakeskin chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6326", + "name": "Snakeskin bandana", + "examine": "Lightweight head protection.", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "requirements": "{1,30}-{4,30}", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "6327", + "name": "Snakeskin bandana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6328", + "name": "Snakeskin boots", + "examine": "Made from snakes.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,3,1,1,2,1,0,9,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "requirements": "{1,30}-{4,30}", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "6329", + "name": "Snakeskin boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6330", - "bonuses": "0,0,0,-5,6,2,2,2,1,0,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "914", - "durability": null, "name": "Snakeskin v'brace", - "tradeable": "true", + "examine": "Made from 100% real snake.", "archery_ticket_price": "0", - "id": "6331" + "bonuses": "0,0,0,-5,6,2,2,2,1,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10000", + "requirements": "{1,30}-{4,30}", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "25000", - "examine": "Some well-cut mahogany logs.", - "grand_exchange_price": "503", + "id": "6331", + "name": "Snakeskin v'brace", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6332", "name": "Mahogany logs", - "tradeable": "true", - "weight": "1.33", + "examine": "Some well-cut mahogany logs.", "archery_ticket_price": "0", - "id": "6332" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "1.33" }, { - "ge_buy_limit": "25000", + "id": "6333", + "name": "Teak logs", "examine": "Some well-cut teak logs.", - "grand_exchange_price": "92", - "durability": null, - "name": "Teak logs", - "tradeable": "true", - "weight": "1.35", "archery_ticket_price": "0", - "id": "6333" - }, - { + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "92", - "durability": null, + "tradeable": "true", + "weight": "1.35" + }, + { + "id": "6334", "name": "Teak logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6334" + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1306", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "6335", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1306", - "durability": null, "name": "Tribal mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6336" - }, - { - "ge_buy_limit": "100", "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "997", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" + }, + { + "id": "6336", + "name": "Tribal mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6337", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "997", - "durability": null, "name": "Tribal mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6338" - }, - { - "ge_buy_limit": "100", "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1018", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" + }, + { + "id": "6338", + "name": "Tribal mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6339", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1018", - "durability": null, "name": "Tribal mask", - "tradeable": "true", + "examine": "A ceremonial wooden mask.", "archery_ticket_price": "0", - "id": "6340" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "remove_sleeves": "true", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", - "grand_exchange_price": "3331", - "durability": null, - "name": "Tribal top", - "tradeable": "true", - "weight": "1", + "id": "6340", + "name": "Tribal mask", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6341", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3331", - "durability": null, "name": "Tribal top", - "tradeable": "true", + "examine": "Local dress.", "archery_ticket_price": "0", - "id": "6342" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "250", - "ge_buy_limit": "100", - "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "1382", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "weight": "2", + "id": "6342", + "name": "Tribal top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6343", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1382", - "durability": null, "name": "Villager robe", - "tradeable": "true", + "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", "archery_ticket_price": "0", - "id": "6344" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "250", + "tradeable": "true", + "weight": "2" }, { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "2337", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "weight": "1", + "id": "6344", + "name": "Villager robe", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6345", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2337", - "durability": null, "name": "Villager hat", - "tradeable": "true", + "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", "archery_ticket_price": "0", - "id": "6346" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "grand_exchange_price": "734", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "weight": "0.5", + "id": "6346", + "name": "Villager hat", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6347", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "734", - "durability": null, "name": "Villager armband", - "tradeable": "true", + "examine": "A brown armband, as worn by the Tai Bwo Wannai locals.", "archery_ticket_price": "0", - "id": "6348" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "150", + "tradeable": "true", + "weight": "0.5" }, { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", - "grand_exchange_price": "2793", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "weight": "0.6", + "id": "6348", + "name": "Villager armband", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6349", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2793", - "durability": null, "name": "Villager sandals", - "tradeable": "true", + "examine": "A brightly coloured pair of local sandals.", "archery_ticket_price": "0", - "id": "6350" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "100", + "tradeable": "true", + "weight": "0.6" }, { - "remove_sleeves": "true", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", - "grand_exchange_price": "3171", - "durability": null, - "name": "Tribal top", - "tradeable": "true", - "weight": "1", + "id": "6350", + "name": "Villager sandals", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6351", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3171", - "durability": null, "name": "Tribal top", - "tradeable": "true", + "examine": "Local dress.", "archery_ticket_price": "0", - "id": "6352" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "250", - "ge_buy_limit": "100", - "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "1416", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "weight": "2", + "id": "6352", + "name": "Tribal top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6353", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1416", - "durability": null, "name": "Villager robe", - "tradeable": "true", + "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", "archery_ticket_price": "0", - "id": "6354" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "250", + "tradeable": "true", + "weight": "2" }, { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "2785", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "weight": "1", + "id": "6354", + "name": "Villager robe", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6355", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2785", - "durability": null, "name": "Villager hat", - "tradeable": "true", + "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", "archery_ticket_price": "0", - "id": "6356" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", - "grand_exchange_price": "944", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "weight": "0.6", + "id": "6356", + "name": "Villager hat", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6357", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "944", - "durability": null, "name": "Villager sandals", - "tradeable": "true", + "examine": "A brightly coloured pair of local sandals.", "archery_ticket_price": "0", - "id": "6358" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "100", + "tradeable": "true", + "weight": "0.6" }, { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "grand_exchange_price": "672", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "weight": "0.5", + "id": "6358", + "name": "Villager sandals", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6359", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "672", - "durability": null, "name": "Villager armband", - "tradeable": "true", + "examine": "A blue armband, as worn by the Tai Bwo Wannai locals.", "archery_ticket_price": "0", - "id": "6360" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "150", + "tradeable": "true", + "weight": "0.5" }, { - "remove_sleeves": "true", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", - "grand_exchange_price": "2484", - "durability": null, - "name": "Tribal top", - "tradeable": "true", - "weight": "1", + "id": "6360", + "name": "Villager armband", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6361", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2484", - "durability": null, "name": "Tribal top", - "tradeable": "true", + "examine": "Local dress.", "archery_ticket_price": "0", - "id": "6362" - }, - { - "shop_price": "250", - "ge_buy_limit": "100", - "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "499", "durability": null, - "name": "Villager robe", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "6363", - "equipment_slot": "7" - }, - { + "equipment_slot": "4", "ge_buy_limit": "100", - "grand_exchange_price": "499", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6364" - }, - { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "2440", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6365", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2440", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6366" - }, - { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", - "grand_exchange_price": "1479", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "6367", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1479", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6368" - }, - { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "grand_exchange_price": "823", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6369", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "823", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6370" - }, - { "remove_sleeves": "true", "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", - "grand_exchange_price": "2683", - "durability": null, - "name": "Tribal top", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6371", - "equipment_slot": "4" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2683", - "durability": null, + "id": "6362", "name": "Tribal top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6372" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "250", - "ge_buy_limit": "100", + "id": "6363", + "name": "Villager robe", "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "331", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "6373", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "250", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "331", - "durability": null, + "id": "6364", "name": "Villager robe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6374" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "200", - "ge_buy_limit": "100", + "id": "6365", + "name": "Villager hat", "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "grand_exchange_price": "3010", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "6375", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3010", - "durability": null, + "id": "6366", "name": "Villager hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6376" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "100", - "ge_buy_limit": "100", + "id": "6367", + "name": "Villager sandals", "examine": "A brightly coloured pair of local sandals.", - "grand_exchange_price": "2310", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "weight": "0.6", "archery_ticket_price": "0", - "id": "6377", - "equipment_slot": "10" - }, - { + "durability": null, + "equipment_slot": "10", "ge_buy_limit": "100", - "grand_exchange_price": "2310", - "durability": null, - "name": "Villager sandals", + "shop_price": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6378" + "weight": "0.6" }, { + "id": "6368", + "name": "Villager sandals", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6369", + "name": "Villager armband", + "examine": "A yellow armband, as worn by the Tai Bwo Wannai locals.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "grand_exchange_price": "1697", - "durability": null, - "name": "Villager armband", "tradeable": "true", - "weight": "0.5", + "weight": "0.5" + }, + { + "id": "6370", + "name": "Villager armband", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6371", + "name": "Tribal top", + "examine": "Local dress.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6372", + "name": "Tribal top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6373", + "name": "Villager robe", + "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "250", + "tradeable": "true", + "weight": "2" + }, + { + "id": "6374", + "name": "Villager robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6375", + "name": "Villager hat", + "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6376", + "name": "Villager hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6377", + "name": "Villager sandals", + "examine": "A brightly coloured pair of local sandals.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "100", + "tradeable": "true", + "weight": "0.6" + }, + { + "id": "6378", + "name": "Villager sandals", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6379", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1697", - "durability": null, "name": "Villager armband", - "tradeable": "true", + "examine": "A pink armband, as worn by the Tai Bwo Wannai locals.", "archery_ticket_price": "0", - "id": "6380" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "150", + "tradeable": "true", + "weight": "0.5" }, { - "shop_price": "24", - "ge_buy_limit": "100", - "examine": "A fez hat. Juss like that.", - "grand_exchange_price": "690", - "durability": null, - "name": "Fez", - "tradeable": "true", + "id": "6380", + "name": "Villager armband", "archery_ticket_price": "0", - "hat": true, + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6382", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "690", - "durability": null, "name": "Fez", - "tradeable": "true", + "examine": "A fez hat. Juss like that.", "archery_ticket_price": "0", - "id": "6383" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "hat": true, + "shop_price": "24", + "tradeable": "true" }, { - "remove_sleeves": "true", - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A bit itchy.", - "grand_exchange_price": "649", - "durability": null, - "name": "Desert top", - "tradeable": "true", - "weight": "1", + "id": "6383", + "name": "Fez", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6384", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "649", - "durability": null, "name": "Desert top", - "tradeable": "true", + "examine": "A bit itchy.", "archery_ticket_price": "0", - "id": "6385" - }, - { - "shop_price": "25", - "ge_buy_limit": "100", - "examine": "Has a coarse hard wearing texture.", - "grand_exchange_price": "533", "durability": null, - "name": "Desert robes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6386", - "equipment_slot": "7" - }, - { + "equipment_slot": "4", "ge_buy_limit": "100", - "grand_exchange_price": "533", - "durability": null, - "name": "Desert robes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6387" - }, - { "remove_sleeves": "true", "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A bit itchy.", - "grand_exchange_price": "1016", - "durability": null, - "name": "Desert top", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6388", - "equipment_slot": "4" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1016", - "durability": null, + "id": "6385", "name": "Desert top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6389" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6386", + "name": "Desert robes", + "examine": "Has a coarse hard wearing texture.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "shop_price": "25", - "ge_buy_limit": "100", - "examine": "Better than factor 50 sun cream.", - "grand_exchange_price": "515", - "durability": null, - "name": "Desert legs", - "tradeable": "true", + "tradeable": "true" + }, + { + "id": "6387", + "name": "Desert robes", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6388", + "name": "Desert top", + "examine": "A bit itchy.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "15", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6389", + "name": "Desert top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6390", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "515", - "durability": null, "name": "Desert legs", - "tradeable": "true", + "examine": "Better than factor 50 sun cream.", "archery_ticket_price": "0", - "id": "6391" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "25", + "tradeable": "true" }, { - "remove_head": "true", - "shop_price": "35", - "ge_buy_limit": "100", - "examine": "Good for keeping the sun off my neck.", - "grand_exchange_price": "531", - "durability": null, - "name": "Menap headgear", - "tradeable": "true", + "id": "6391", + "name": "Desert legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6392", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "531", - "durability": null, "name": "Menap headgear", - "tradeable": "true", + "examine": "Good for keeping the sun off my neck.", "archery_ticket_price": "0", - "id": "6393" - }, - { - "remove_sleeves": "true", - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Colourful.", - "grand_exchange_price": "650", "durability": null, - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6394", - "equipment_slot": "4" - }, - { + "equipment_slot": "0", "ge_buy_limit": "100", - "grand_exchange_price": "650", - "durability": null, - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6395" - }, - { - "shop_price": "40", - "ge_buy_limit": "100", - "examine": "A cool light Menaphite robe.", - "grand_exchange_price": "440", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6396", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "440", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6397" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Look at those nobbily knees.", - "grand_exchange_price": "246", - "durability": null, - "name": "Menap action kilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6398", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "246", - "durability": null, - "name": "Menap action kilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6399" - }, - { "remove_head": "true", "shop_price": "35", - "ge_buy_limit": "100", - "examine": "Good for keeping the sun off my neck.", - "grand_exchange_price": "484", - "durability": null, - "name": "Menap headgear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6400", - "equipment_slot": "0" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "484", - "durability": null, + "id": "6393", "name": "Menap headgear", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6401" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6394", + "name": "Menaphite top", + "examine": "Colourful.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Colourful.", - "grand_exchange_price": "557", - "durability": null, - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6402", - "equipment_slot": "4" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "557", - "durability": null, + "id": "6395", "name": "Menaphite top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6403" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "40", - "ge_buy_limit": "100", + "id": "6396", + "name": "Menaphite robe", "examine": "A cool light Menaphite robe.", - "grand_exchange_price": "449", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6404", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "40", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "449", - "durability": null, + "id": "6397", "name": "Menaphite robe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6405" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "100", + "id": "6398", + "name": "Menap action kilt", "examine": "Look at those nobbily knees.", - "grand_exchange_price": "236", - "durability": null, - "name": "Menap action kilt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6406", - "equipment_slot": "7" - }, - { + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "236", - "durability": null, - "name": "Menap action kilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6407" + "shop_price": "20", + "tradeable": "true" }, { + "id": "6399", + "name": "Menap action kilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6400", + "name": "Menap headgear", + "examine": "Good for keeping the sun off my neck.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "shop_price": "35", + "tradeable": "true" + }, + { + "id": "6401", + "name": "Menap headgear", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6402", + "name": "Menaphite top", + "examine": "Colourful.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "6403", + "name": "Menaphite top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6404", + "name": "Menaphite robe", + "examine": "A cool light Menaphite robe.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "40", + "tradeable": "true" + }, + { + "id": "6405", + "name": "Menaphite robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6406", + "name": "Menap action kilt", + "examine": "Look at those nobbily knees.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "6407", + "name": "Menap action kilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6408", + "name": "Oak blackjack(o)", + "examine": "An offensive blackjack.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,4,0,0,0,0,0,0,0,0,4,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{17,10}-{0,10}", "shop_price": "400", - "ge_buy_limit": "100", - "examine": "An offensive blackjack.", - "durability": null, - "weight": "1", - "attack_speed": "4", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "852", - "name": "Oak blackjack(o)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6408", - "bonuses": "0,0,4,0,0,0,0,0,0,0,0,4,0,0,0" + "weapon_interface": "10", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "852", - "durability": null, + "id": "6409", "name": "Oak blackjack(o)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6409" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{17,10}-{1,10}", - "ge_buy_limit": "100", - "examine": "An defensive blackjack.", - "durability": null, - "weight": "1", - "attack_speed": "4", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "818", - "name": "Oak blackjack(d)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6410", - "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "818", - "durability": null, "name": "Oak blackjack(d)", - "tradeable": "true", + "examine": "An defensive blackjack.", "archery_ticket_price": "0", - "id": "6411" + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{17,10}-{1,10}", + "tradeable": "true", + "weapon_interface": "10", + "weight": "1" }, { - "requirements": "{17,20}-{0,20}", - "ge_buy_limit": "100", - "examine": "An offensive blackjack.", - "durability": null, - "weight": "1", - "attack_speed": "4", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "1418", - "name": "Willow blackjack(o)", - "tradeable": "true", + "id": "6411", + "name": "Oak blackjack(d)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6412", - "bonuses": "0,0,8,0,0,0,0,0,0,0,0,8,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1418", - "durability": null, "name": "Willow blackjack(o)", - "tradeable": "true", + "examine": "An offensive blackjack.", "archery_ticket_price": "0", - "id": "6413" - }, - { - "requirements": "{17,20}-{1,20}", - "ge_buy_limit": "100", - "examine": "A defensive blackjack.", - "durability": null, - "weight": "1", "attack_speed": "4", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "1285", - "name": "Willow blackjack(d)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6414", - "bonuses": "0,0,0,0,0,0,0,8,0,0,0,8,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1285", + "bonuses": "0,0,8,0,0,0,0,0,0,0,0,8,0,0,0", "durability": null, - "name": "Willow blackjack(d)", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{17,20}-{0,20}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6415" + "weapon_interface": "10", + "weight": "1" }, { + "id": "6413", + "name": "Willow blackjack(o)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6414", + "name": "Willow blackjack(d)", + "examine": "A defensive blackjack.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,8,0,0,0,8,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{17,20}-{1,20}", + "tradeable": "true", + "weapon_interface": "10", + "weight": "1" + }, + { + "id": "6415", + "name": "Willow blackjack(d)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6416", + "name": "Maple blackjack", + "examine": "A solid bit of maple.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,20,0,0,0", + "defence_anim": "425", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{17,30}", "shop_price": "1200", - "ge_buy_limit": "100", - "examine": "A solid bit of maple.", - "durability": null, - "weight": "1.8", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "10", - "defence_anim": "425", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "1432", - "name": "Maple blackjack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6416", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,20,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1432", + "id": "6417", + "name": "Maple blackjack", + "archery_ticket_price": "0", "durability": null, - "name": "Maple blackjack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6417" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6418", + "name": "Maple blackjack(o)", + "examine": "An offensive blackjack.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,24,0,0,0,0,0,0,0,0,20,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{17,30}-{0,30}", "shop_price": "1600", - "ge_buy_limit": "100", - "examine": "An offensive blackjack.", - "durability": null, - "weight": "1", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "2079", - "name": "Maple blackjack(o)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6418", - "bonuses": "0,0,24,0,0,0,0,0,0,0,0,20,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2079", + "id": "6419", + "name": "Maple blackjack(o)", + "archery_ticket_price": "0", "durability": null, - "name": "Maple blackjack(o)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6419" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6420", + "name": "Maple blackjack(d)", + "examine": "A defensive blackjack.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,24,0,0,0,20,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{17,30}-{1,30}", "shop_price": "952", - "ge_buy_limit": "100", - "examine": "A defensive blackjack.", - "durability": null, - "weight": "1", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "2407", - "name": "Maple blackjack(d)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6420", - "bonuses": "0,0,0,0,0,0,0,24,0,0,0,20,0,0,0" + "weight": "1" }, { + "id": "6421", + "name": "Maple blackjack(d)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "2407", - "durability": null, - "name": "Maple blackjack(d)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6421" + "tradeable": "true" }, { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "9", - "durability": null, + "id": "6422", "name": "Air rune", - "tradeable": "true", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "6422" + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "durability": null, + "id": "6423", "name": "Air rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6423" + "durability": null, + "tradeable": "true" }, { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "6", - "durability": null, + "id": "6424", "name": "Water rune", - "tradeable": "true", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "6424" + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "durability": null, + "id": "6425", "name": "Water rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6425" + "durability": null, + "tradeable": "true" }, { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "7", - "durability": null, + "id": "6426", "name": "Earth rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6426" - }, - { - "durability": null, - "name": "Earth rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6427" - }, - { - "shop_price": "17", "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "16", - "durability": null, - "name": "Fire rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6428" + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "durability": null, - "name": "Fire rune", - "tradeable": "true", + "id": "6427", + "name": "Earth rune", "archery_ticket_price": "0", - "id": "6429" + "durability": null, + "tradeable": "true" }, { - "shop_price": "140", + "id": "6428", + "name": "Fire rune", + "examine": "One of the 4 basic elemental Runes.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "6429", + "name": "Fire rune", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6430", + "name": "Chaos rune", "examine": "Used for small missile spells.", - "grand_exchange_price": "65", - "durability": null, - "name": "Chaos rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6430" + "durability": null, + "shop_price": "140", + "tradeable": "true" }, { - "durability": null, + "id": "6431", "name": "Chaos rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6431" + "durability": null, + "tradeable": "true" }, { - "shop_price": "310", + "id": "6432", + "name": "Death rune", "examine": "Used for medium missile spells.", - "grand_exchange_price": "399", - "durability": null, - "name": "Death rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6432" + "durability": null, + "shop_price": "310", + "tradeable": "true" }, { - "durability": null, + "id": "6433", "name": "Death rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6433" + "durability": null, + "tradeable": "true" }, { - "shop_price": "378", + "id": "6434", + "name": "Law rune", "examine": "Used for teleport spells.", - "grand_exchange_price": "277", - "durability": null, - "name": "Law rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6434" + "durability": null, + "shop_price": "378", + "tradeable": "true" }, { - "durability": null, + "id": "6435", "name": "Law rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6435" + "durability": null, + "tradeable": "true" }, { - "shop_price": "17", + "id": "6436", + "name": "Mind rune", "examine": "Used for basic missile spells.", - "grand_exchange_price": "6", - "durability": null, - "name": "Mind rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6436" + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "durability": null, + "id": "6437", "name": "Mind rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6437" + "durability": null, + "tradeable": "true" }, { - "shop_price": "16", + "id": "6438", + "name": "Body rune", "examine": "Used for Curse spells", - "grand_exchange_price": "6", - "durability": null, - "name": "Body rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6438" + "durability": null, + "shop_price": "16", + "tradeable": "true" }, { - "durability": null, + "id": "6439", "name": "Body rune", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6439" + "durability": null, + "tradeable": "true" }, { - "examine": "A spadeful of refined coal.", - "durability": null, + "id": "6448", "name": "Spadeful of coke", - "weight": "2", + "examine": "A spadeful of refined coal.", "archery_ticket_price": "0", - "id": "6448" + "durability": null, + "weight": "2" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A white rosebush seed.", - "durability": null, + "id": "6453", "name": "White rose seed", - "tradeable": "false", - "destroy": "true", - "weight": "28.928", + "examine": "A white rosebush seed.", "archery_ticket_price": "0", - "id": "6453" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "28.928" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A red rosebush seed.", - "durability": null, + "id": "6454", "name": "Red rose seed", - "tradeable": "false", - "destroy": "true", + "examine": "A red rosebush seed.", "archery_ticket_price": "0", - "id": "6454" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pink rosebush seed.", - "durability": null, + "id": "6455", "name": "Pink rose seed", - "tradeable": "false", - "destroy": "true", + "examine": "A pink rosebush seed.", "archery_ticket_price": "0", - "id": "6455" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get some more Burthorpe vine seeds from Berald in Taverly.", - "examine": "A grapevine seed.", - "durability": null, + "id": "6456", "name": "Vine seed", - "tradeable": "false", - "destroy": "true", + "examine": "A grapevine seed.", "archery_ticket_price": "0", - "id": "6456" + "destroy": "true", + "destroy_message": "You can get some more Burthorpe vine seeds from Berald in Taverly.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A delphinium seed.", - "durability": null, + "id": "6457", "name": "Delphinium seed", - "tradeable": "false", - "destroy": "true", + "examine": "A delphinium seed.", "archery_ticket_price": "0", - "id": "6457" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A snowdrop seed.", - "durability": null, + "id": "6460", "name": "Snowdrop seed", - "tradeable": "false", + "examine": "A snowdrop seed.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "6460" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Shoot: A shoot that has been cut from a dying White Tree.Plant pot: A young White Tree sapling.Plant pot after watering: This shoot from a White Tree has been watered and will soon grow.", - "durability": null, + "id": "6461", "name": "White tree shoot", - "archery_ticket_price": "0", - "id": "6461" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", "examine": "Shoot: A shoot that has been cut from a dying White Tree.Plant pot: A young White Tree sapling.Plant pot after watering: This shoot from a White Tree has been watered and will soon grow.", - "durability": null, - "name": "White tree shoot", "archery_ticket_price": "0", - "id": "6462" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", + "id": "6462", + "name": "White tree shoot", "examine": "Shoot: A shoot that has been cut from a dying White Tree.Plant pot: A young White Tree sapling.Plant pot after watering: This shoot from a White Tree has been watered and will soon grow.", - "durability": null, - "name": "White tree shoot", "archery_ticket_price": "0", - "id": "6463" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { + "id": "6463", + "name": "White tree shoot", + "examine": "Shoot: A shoot that has been cut from a dying White Tree.Plant pot: A young White Tree sapling.Plant pot after watering: This shoot from a White Tree has been watered and will soon grow.", + "archery_ticket_price": "0", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A young White Tree sapling", - "durability": null, + "durability": null + }, + { + "id": "6464", "name": "White tree sapling", - "tradeable": "false", - "destroy": "true", - "weight": "0.9", + "examine": "A young White Tree sapling", "archery_ticket_price": "0", - "id": "6464" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "0.9" }, { - "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.", - "durability": null, - "name": "Ring of charos(a)", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "6465", - "equipment_slot": "12" + "name": "Ring of charos(a)", + "examine": "The power within this ring has been activated.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Another can be received from pickpocketing Dr. Fenkenstrain in the tower.", + "durability": null, + "equipment_slot": "12", + "tradeable": "false" }, { - "examine": "A rune essence chip that has been broken into shards.", - "durability": null, + "id": "6466", "name": "Rune shards", + "examine": "A rune essence chip that has been broken into shards.", "archery_ticket_price": "0", - "id": "6466" + "durability": null }, { - "examine": "Crushed rune essence.", - "durability": null, + "id": "6467", "name": "Rune dust", + "examine": "Crushed rune essence.", "archery_ticket_price": "0", - "id": "6467" + "durability": null }, { - "shop_price": "25", - "examine": "Use this on plants to cure disease.", - "grand_exchange_price": "199", - "durability": null, + "id": "6468", "name": "Plant cure", - "tradeable": "true", + "examine": "Use this on plants to cure disease.", "archery_ticket_price": "0", - "id": "6468" + "durability": null, + "shop_price": "25", + "tradeable": "true" }, { - "examine": "Looks delicious.", - "durability": null, + "id": "6469", "name": "White tree fruit", + "examine": "Looks delicious.", "archery_ticket_price": "0", - "id": "6469" + "durability": null }, { - "ge_buy_limit": "100", - "examine": "Pour this onto compost to make it into super-compost.", - "grand_exchange_price": "11500", - "durability": null, + "id": "6470", "name": "Compost potion(4)", - "tradeable": "true", + "examine": "Pour this onto compost to make it into super-compost.", "archery_ticket_price": "0", - "id": "6470" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "11500", - "durability": null, + "id": "6471", "name": "Compost potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6471" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "Pour this onto compost to make it into super-compost.", - "grand_exchange_price": "6804", - "durability": null, + "id": "6472", "name": "Compost potion(3)", - "tradeable": "true", + "examine": "Pour this onto compost to make it into super-compost.", "archery_ticket_price": "0", - "id": "6472" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "6804", - "durability": null, + "id": "6473", "name": "Compost potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6473" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "Pour this onto compost to make it into super-compost.", - "grand_exchange_price": "3338", - "durability": null, + "id": "6474", "name": "Compost potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6474" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3338", - "durability": null, - "name": "Compost potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6475" - }, - { - "ge_buy_limit": "100", "examine": "Pour this onto compost to make it into super-compost.", - "grand_exchange_price": "1734", - "durability": null, - "name": "Compost potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6476" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1734", - "durability": null, - "name": "Compost potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6477" + "tradeable": "true" }, { - "destroy_message": "You can get another one from Queen Ellamaria", - "examine": "I can use this to move heavy objects. (or) An empty trolley.", + "id": "6475", + "name": "Compost potion(2)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6476", + "name": "Compost potion(1)", + "examine": "Pour this onto compost to make it into super-compost.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6477", + "name": "Compost potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6478", "name": "Trolley", - "tradeable": "false", - "destroy": "true", - "weight": "2", + "examine": "I can use this to move heavy objects. (or) An empty trolley.", "archery_ticket_price": "0", - "id": "6478" - }, - { - "destroy_message": "You can get another list from Queen Ellamaria in Varrock", - "examine": "A list of things that I must collect for Queen Ellamaria.", + "destroy": "true", + "destroy_message": "You can get another one from Queen Ellamaria", "durability": null, - "name": "List", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6479" + "weight": "2" }, { + "id": "6479", + "name": "List", + "examine": "A list of things that I must collect for Queen Ellamaria.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another list from Queen Ellamaria in Varrock", + "durability": null, + "tradeable": "false" + }, + { + "id": "6522", + "name": "Toktz-xil-ul", + "examine": "A razor sharp ring of obsidian.", + "archery_ticket_price": "0", + "attack_anims": "2614,2614,2614,2614", + "attack_speed": "4", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,49", + "defence_anim": "424", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{4,60}", "shop_price": "375", - "ge_buy_limit": "100", - "examine": "A razor sharp ring of obsidian.", - "durability": null, "tokkul_price": "375", - "attack_speed": "4", - "weapon_interface": "18", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2614,2614,2614,2614", - "grand_exchange_price": "340", - "name": "Toktz-xil-ul", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6522", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,49" + "weapon_interface": "18" }, { + "id": "6523", + "name": "Toktz-xil-ak", + "examine": "A razor sharp sword of obsidian.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "4", + "bonuses": "47,38,-2,0,0,2,3,0,0,0,0,49,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", "requirements": "{0,60}", "shop_price": "60000", - "ge_buy_limit": "10", - "examine": "A razor sharp sword of obsidian.", - "durability": null, "tokkul_price": "60000", - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "5", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "30200", - "name": "Toktz-xil-ak", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6523", - "bonuses": "47,38,-2,0,0,2,3,0,0,0,0,49,0,0,0" + "weapon_interface": "5", + "weight": "1.8" }, { + "id": "6524", + "name": "Toktz-ket-xil", + "examine": "A spiked shield of Obsidian.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-12,-8,40,42,38,0,65,60,5,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", "requirements": "{1,60}", "shop_price": "67500", - "ge_buy_limit": "10", - "examine": "A spiked shield of Obsidian.", - "durability": null, "tokkul_price": "67500", - "weight": "3.6", - "absorb": "5,0,11", - "equipment_slot": "5", - "grand_exchange_price": "34700", - "name": "Toktz-ket-xil", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6524", - "bonuses": "0,0,0,-12,-8,40,42,38,0,65,60,5,0,0,0" + "weight": "3.6" }, { + "id": "6525", + "name": "Toktz-xil-ek", + "examine": "A large knife of Obsidian.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "4", + "bonuses": "16,48,0,0,0,0,0,0,0,0,0,39,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "2584", "requirements": "{0,60}", "shop_price": "37500", - "ge_buy_limit": "10", - "examine": "A large knife of Obsidian.", - "durability": null, "tokkul_price": "37500", - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "6", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "16900", - "name": "Toktz-xil-ek", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6525", - "bonuses": "16,48,0,0,0,0,0,0,0,0,0,39,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "A staff of obsidian.", - "walk_anim": "1205", - "tokkul_price": "52500", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "26300", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "6526", - "stand_turn_anim": "1209", + "name": "Toktz-mej-tal", + "examine": "A staff of obsidian.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "6", "bonuses": "15,-1,55,15,0,10,15,5,15,0,0,55,5,0,0", - "requirements": "{0,60}-{6,60}", - "shop_price": "52500", + "defence_anim": "397", "durability": null, - "weight": "1", - "weapon_interface": "1", + "equipment_slot": "3", + "ge_buy_limit": "10", "render_anim": "28", - "name": "Toktz-mej-tal" + "requirements": "{0,60}-{6,60}", + "run_anim": "1210", + "shop_price": "52500", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tokkul_price": "52500", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1" }, { + "id": "6527", + "name": "Tzhaar-ket-em", + "examine": "A mace of obsidian.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "5", + "bonuses": "-4,-4,62,0,0,0,0,0,0,0,0,56,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", "requirements": "{0,60}", "shop_price": "45000", - "ge_buy_limit": "10", - "examine": "A mace of obsidian.", - "durability": null, "tokkul_price": "45000", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "10", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "22600", - "name": "Tzhaar-ket-em", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6527", - "bonuses": "-4,-4,62,0,0,0,0,0,0,0,0,56,0,0,0" + "weapon_interface": "10", + "weight": "1.8" }, { - "ge_buy_limit": "10", - "turn90cw_anim": "1663", - "examine": "A maul of obsidian.", - "walk_anim": "1663", - "tokkul_price": "75000", - "turn90ccw_anim": "1663", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1663", - "defence_anim": "1666", - "equipment_slot": "3", - "attack_anims": "2661,2661,2661,2661", - "grand_exchange_price": "31000", - "stand_anim": "1662", - "tradeable": "true", - "run_anim": "1664", - "archery_ticket_price": "0", "id": "6528", - "stand_turn_anim": "823", - "bonuses": "0,0,80,-4,0,0,0,0,0,0,0,85,0,0,0", - "requirements": "{2,60}", - "shop_price": "75000", - "durability": null, - "weight": "3.6", - "weapon_interface": "10", - "render_anim": "27", - "attack_audios": "2520,0,0,0", - "name": "Tzhaar-ket-om" - }, - { - "examine": "It's a Token of some kind made from Obsidian.", - "durability": null, - "name": "Tokkul", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "6529" - }, - { - "durability": null, - "name": "Toktz-xil-ak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6535" - }, - { - "durability": null, - "name": "Toktz-ket-xil", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6536" - }, - { - "durability": null, - "name": "Toktz-xil-ek", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6537" - }, - { - "durability": null, - "name": "Toktz-mej-tal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6538" - }, - { - "durability": null, - "name": "Tzhaar-ket-em", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6539" - }, - { - "durability": null, "name": "Tzhaar-ket-om", - "tradeable": "true", + "examine": "A maul of obsidian.", "archery_ticket_price": "0", - "id": "6540" + "attack_anims": "2661,2661,2661,2661", + "attack_audios": "2520,0,0,0", + "attack_speed": "7", + "bonuses": "0,0,80,-4,0,0,0,0,0,0,0,85,0,0,0", + "defence_anim": "1666", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "27", + "requirements": "{2,60}", + "run_anim": "1664", + "shop_price": "75000", + "stand_anim": "1662", + "stand_turn_anim": "823", + "tokkul_price": "75000", + "tradeable": "true", + "turn180_anim": "1663", + "turn90ccw_anim": "1663", + "turn90cw_anim": "1663", + "two_handed": "true", + "walk_anim": "1663", + "weapon_interface": "10", + "weight": "3.6" }, { - "destroy_message": "You can get a replacement mouse toy by speaking to Bob, the Jagex cat.", - "examine": "An Advanced Combat Training Device.", - "durability": null, - "name": "Mouse toy", - "tradeable": "false", - "destroy": "true", - "weight": "0.1", + "id": "6529", + "name": "Tokkul", + "examine": "It's a Token of some kind made from Obsidian.", "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "6535", + "name": "Toktz-xil-ak", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6536", + "name": "Toktz-ket-xil", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6537", + "name": "Toktz-xil-ek", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6538", + "name": "Toktz-mej-tal", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6539", + "name": "Tzhaar-ket-em", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6540", + "name": "Tzhaar-ket-om", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6541", - "weapon_interface": "12", + "name": "Mouse toy", + "examine": "An Advanced Combat Training Device.", + "archery_ticket_price": "0", "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", - "equipment_slot": "3" + "destroy": "true", + "destroy_message": "You can get a replacement mouse toy by speaking to Bob, the Jagex cat.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false", + "weapon_interface": "12", + "weight": "0.1" }, { - "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.", - "durability": null, + "id": "6542", "name": "Present", - "tradeable": "false", - "destroy": "true", + "examine": "Thanks for all your help! Love, Bob & Neite.", "archery_ticket_price": "0", - "id": "6542" + "destroy": "true", + "destroy_message": "You will not get a replacement for the present. Open it for your reward.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, + "id": "6543", "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "examine": "I wonder what happens if I rub it.", "archery_ticket_price": "0", - "id": "6543" + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Amulet of Catspeak from the Sphinx in Sophanem.", - "examine": "It's an amulet of cat speak. It makes vague purring noises.", - "durability": null, - "name": "Catspeak amulet(e)", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "6544", - "equipment_slot": "2" + "name": "Catspeak amulet(e)", + "examine": "It's an amulet of cat speak. It makes vague purring noises.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Amulet of Catspeak from the Sphinx in Sophanem.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You have completed the chores; you can safely destroy the list.", - "examine": "A list of chores that Bob gave you to do.", - "durability": null, + "id": "6545", "name": "Chores", - "tradeable": "false", - "destroy": "true", + "examine": "A list of chores that Bob gave you to do.", "archery_ticket_price": "0", - "id": "6545" + "destroy": "true", + "destroy_message": "You have completed the chores; you can safely destroy the list.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You found the recipe in Unferth's bookcase, which is in his Burthorpe house, you will be able to get a replacement.", - "examine": "It says on the back 'My favourite recipe.", - "durability": null, + "id": "6546", "name": "Recipe", - "tradeable": "false", - "destroy": "true", + "examine": "It says on the back 'My favourite recipe.", "archery_ticket_price": "0", - "id": "6546" + "destroy": "true", + "destroy_message": "You found the recipe in Unferth's bookcase, which is in his Burthorpe house, you will be able to get a replacement.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Doctor's hat from the Apothecary in Varrock.", - "examine": "A mirror helps reflect light on the subject.", - "durability": null, - "name": "Doctors hat", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "6547", - "equipment_slot": "0" - }, - { - "destroy_message": "You can get another Nurse's hat from the Apothecary in Varrock.", - "examine": "A nurse's hat, but does it have healing powers?", - "durability": null, - "name": "Nurse hat", - "tradeable": "false", + "name": "Doctors hat", + "examine": "A mirror helps reflect light on the subject.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", + "destroy_message": "You can get another Doctor's hat from the Apothecary in Varrock.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { "id": "6548", - "equipment_slot": "0" + "name": "Nurse hat", + "examine": "A nurse's hat, but does it have healing powers?", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Nurse's hat from the Apothecary in Varrock.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6549", "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", - "id": "6549" + "durability": null }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6550", "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", - "id": "6550" + "durability": null }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6551", "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", - "id": "6551" + "durability": null }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6552", "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", - "id": "6552" + "durability": null }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6553", "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", - "id": "6553" + "durability": null }, { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, + "id": "6554", "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6554" - }, - { "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6555" + "durability": null }, { + "id": "6555", + "name": "Wily cat", "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6556" + "durability": null }, { + "id": "6556", + "name": "Wily cat", "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6557" + "durability": null }, { + "id": "6557", + "name": "Wily cat", "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6558" + "durability": null }, { + "id": "6558", + "name": "Wily cat", "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6559" + "durability": null }, { + "id": "6559", + "name": "Wily cat", "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", "archery_ticket_price": "0", - "id": "6560" + "durability": null }, { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "368700", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "6560", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", "archery_ticket_price": "0", + "durability": null + }, + { "id": "6562", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "weight": "2.2", - "weapon_interface": "1", - "equip_audio": "2230", - "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", + "name": "Mud battlestaff", "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", - "grand_exchange_price": "423100", - "stand_anim": "813", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", - "tradeable": "true", - "name": "Mystic mud staff", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6563", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0" + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "examine": "A valuable ring.", - "grand_exchange_price": "6609743", + "id": "6563", + "name": "Mystic mud staff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "shop_price": "40000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "6564", "name": "Onyx ring", - "tradeable": "true", + "examine": "A valuable ring.", "archery_ticket_price": "0", - "id": "6564" + "durability": null, + "tradeable": "true" }, { - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "4501000", - "durability": null, + "id": "6565", "name": "Onyx necklace", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "6565" + "durability": null, + "tradeable": "true" }, { - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "9058124", - "durability": null, + "id": "6566", "name": "Onyx amulet", - "tradeable": "true", + "examine": "I wonder if I can get this enchanted.", "archery_ticket_price": "0", - "id": "6566" + "durability": null, + "tradeable": "true" }, { - "shop_price": "90000", - "ge_buy_limit": "100", - "examine": "A cape woven of obsidian plates.", - "durability": null, - "tokkul_price": "90000", - "weight": "1.8", - "equipment_slot": "1", - "lendable": "true", - "grand_exchange_price": "42000", - "name": "Obsidian cape", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6568", - "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "42000", - "durability": null, "name": "Obsidian cape", - "tradeable": "true", + "examine": "A cape woven of obsidian plates.", "archery_ticket_price": "0", - "id": "6569" - }, - { - "examine": "A cape of fire.", + "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", "durability": null, - "name": "Fire cape", - "tradeable": "false", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "6570", - "bonuses": "1,1,1,1,1,11,11,11,11,11,11,4,2,0,0", - "equipment_slot": "1" - }, - { - "shop_price": "2700000", + "equipment_slot": "1", "ge_buy_limit": "100", - "examine": "An uncut onyx.", - "grand_exchange_price": "10700000", - "tokkul_price": "300000", - "durability": null, - "name": "Uncut onyx", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6571" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10700000", - "durability": null, - "name": "Uncut onyx", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6572" - }, - { - "ge_buy_limit": "100", - "examine": "This looks valuable.", - "grand_exchange_price": "10700000", - "durability": null, - "name": "Onyx", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6573" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10700000", - "durability": null, - "name": "Onyx", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6574" - }, - { - "ge_buy_limit": "100", - "examine": "A valuable ring.", - "grand_exchange_price": "11900000", - "durability": null, - "name": "Onyx ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6575", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11900000", - "durability": null, - "name": "Onyx ring", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6576" - }, - { - "ge_buy_limit": "100", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "4500000", - "durability": null, - "name": "Onyx necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6577", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4500000", - "durability": null, - "name": "Onyx necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6578" - }, - { - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "9058124", - "durability": null, - "name": "Onyx amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6579" - }, - { - "durability": null, - "name": "Onyx amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6580" - }, - { - "ge_buy_limit": "100", - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "10900000", - "durability": null, - "name": "Onyx amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6581", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10900000", - "durability": null, - "name": "Onyx amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6582" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring.", - "grand_exchange_price": "803700", - "durability": null, - "name": "Ring of stone", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6583", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "803700", - "durability": null, - "name": "Ring of stone", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6584" - }, - { "lendable": "true", - "ge_buy_limit": "100", - "examine": "A very powerful onyx amulet.", - "grand_exchange_price": "9200000", - "durability": null, - "name": "Amulet of fury", + "shop_price": "90000", + "tokkul_price": "90000", "tradeable": "true", + "weight": "1.8" + }, + { + "id": "6569", + "name": "Obsidian cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6570", + "name": "Fire cape", + "examine": "A cape of fire.", + "archery_ticket_price": "0", + "bonuses": "1,1,1,1,1,11,11,11,11,11,11,4,2,0,0", + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1.8" + }, + { + "id": "6571", + "name": "Uncut onyx", + "examine": "An uncut onyx.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2700000", + "tokkul_price": "300000", + "tradeable": "true" + }, + { + "id": "6572", + "name": "Uncut onyx", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6573", + "name": "Onyx", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6574", + "name": "Onyx", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6575", + "name": "Onyx ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6576", + "name": "Onyx ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6577", + "name": "Onyx necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6578", + "name": "Onyx necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6579", + "name": "Onyx amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6580", + "name": "Onyx amulet", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6581", + "name": "Onyx amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6582", + "name": "Onyx amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6583", + "name": "Ring of stone", + "examine": "An enchanted ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6584", + "name": "Ring of stone", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6585", - "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,5,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9200000", - "durability": null, "name": "Amulet of fury", - "tradeable": "true", + "examine": "A very powerful onyx amulet.", "archery_ticket_price": "0", - "id": "6586" + "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,5,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "lendable": "true", + "tradeable": "true" }, { + "id": "6586", + "name": "Amulet of fury", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6587", + "name": "White claws", + "examine": "A set of fighting claws.", + "archery_ticket_price": "0", + "attack_anims": "390,390,390,390", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "10,14,-4,0,0,4,7,2,0,0,0,14,1,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2583", "requirements": "{0,10}-{5,10}", "shop_price": "360", - "ge_buy_limit": "100", - "examine": "A set of fighting claws.", - "durability": null, - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", - "weapon_interface": "9", - "equip_audio": "1003", - "render_anim": "2583", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,390,390", - "grand_exchange_price": "1013", - "name": "White claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6587", - "bonuses": "10,14,-4,0,0,4,7,2,0,0,0,14,1,0,0" + "weapon_interface": "9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1013", + "id": "6588", + "name": "White claws", + "archery_ticket_price": "0", "durability": null, - "name": "White claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6588" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6589", + "name": "White battleaxe", + "examine": "A vicious looking axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,20,15,0,0,0,0,0,0,-1,0,24,1,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2586", "requirements": "{0,10}-{5,10}", "shop_price": "1248", - "ge_buy_limit": "100", - "examine": "A vicious looking axe.", - "durability": null, - "weight": "2.7", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "1459", - "name": "White battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6589", - "bonuses": "-2,20,15,0,0,0,0,0,0,-1,0,24,1,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1459", - "durability": null, + "id": "6590", "name": "White battleaxe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6590" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,10}-{5,10}", - "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious white dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,400,400", - "grand_exchange_price": "479", - "attack_audios": "2517,2517,2500,2517", - "name": "White dagger", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6591", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "479", - "durability": null, "name": "White dagger", - "tradeable": "true", + "examine": "A vicious white dagger.", "archery_ticket_price": "0", - "id": "6592" - }, - { + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,10}-{5,10}", "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious white dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "497", - "attack_audios": "2517,2517,2500,2517", - "name": "White dagger(p)", "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "6592", + "name": "White dagger", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6593", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "497", - "durability": null, "name": "White dagger(p)", - "tradeable": "true", + "examine": "A vicious white dagger.", "archery_ticket_price": "0", - "id": "6594" - }, - { + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,10}-{5,10}", "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious white dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "982", - "attack_audios": "2517,2517,2500,2517", - "name": "White dagger(p+)", "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "6594", + "name": "White dagger(p)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6595", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "982", - "durability": null, "name": "White dagger(p+)", - "tradeable": "true", + "examine": "A vicious white dagger.", "archery_ticket_price": "0", - "id": "6596" - }, - { + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "2584", "requirements": "{0,10}-{5,10}", "shop_price": "240", - "ge_buy_limit": "100", - "examine": "A vicious white dagger.", - "durability": null, - "weight": "0.4", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "render_anim": "2584", - "equipment_slot": "3", - "grand_exchange_price": "8422", - "attack_audios": "2517,2517,2500,2517", - "name": "White dagger(p++)", - "tradeable": "true", + "weight": "0.4" + }, + { + "id": "6596", + "name": "White dagger(p+)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6597", - "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8422", - "durability": null, "name": "White dagger(p++)", - "tradeable": "true", + "examine": "A vicious white dagger.", "archery_ticket_price": "0", - "id": "6598" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A white halberd.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "430", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "10,5,-4,1,0,0,0,0,1,0,0,7,1,0,0", + "durability": null, + "equip_audio": "2248", "equipment_slot": "3", - "attack_anims": "440,440,412,440", - "grand_exchange_price": "1834", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "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}", - "shop_price": "1920", - "durability": null, - "weight": "3.1", - "weapon_interface": "15", - "render_anim": "28", - "name": "White halberd" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1834", - "durability": null, - "name": "White halberd", + "render_anim": "2584", + "requirements": "{0,10}-{5,10}", + "shop_price": "240", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6600" + "weapon_interface": "5", + "weight": "0.4" }, { + "id": "6598", + "name": "White dagger(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6599", + "name": "White halberd", + "examine": "A white halberd.", + "archery_ticket_price": "0", + "attack_anims": "440,440,412,440", + "attack_speed": "7", + "bonuses": "19,25,0,-4,0,-1,2,3,0,0,0,20,1,0,0", + "defence_anim": "430", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}-{2,5}", + "run_anim": "1210", + "shop_price": "1920", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "15", + "weight": "3.1" + }, + { + "id": "6600", + "name": "White halberd", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6601", + "name": "White mace", + "examine": "A spiky mace.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "8,-2,16,0,0,0,0,0,0,0,0,13,3,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,10}-{5,10}", "shop_price": "432", - "ge_buy_limit": "100", - "examine": "A spiky mace.", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "481", - "name": "White mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6601", - "bonuses": "8,-2,16,0,0,0,0,0,0,0,0,13,3,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "481", - "durability": null, + "id": "6602", "name": "White mace", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6602" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,10}-{5,10}", - "ge_buy_limit": "100", - "shop_price": "200", - "turn90cw_anim": "1207", - "examine": "A Magical staff.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "650", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "tradeable": "true", - "name": "White magic staff", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "6603", - "stand_turn_anim": "1209", - "bonuses": "2,-1,10,10,0,2,3,1,10,0,0,7,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "650", - "durability": null, "name": "White magic staff", - "tradeable": "true", + "examine": "A Magical staff.", "archery_ticket_price": "0", - "id": "6604" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A razor sharp sword.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "397", + "attack_audios": "2555,0,0,0", + "bonuses": "2,-1,10,10,0,2,3,1,10,0,0,7,1,0,0", + "durability": null, + "equip_audio": "2247", "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "grand_exchange_price": "1187", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,10}-{5,10}", + "run_anim": "1210", + "shop_price": "200", "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "true", - "run_anim": "824", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { + "id": "6604", + "name": "White magic staff", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6605", - "stand_turn_anim": "823", - "bonuses": "14,10,-2,0,0,0,2,1,0,0,0,12,1,0,0", - "requirements": "{0,10}-{5,10}", - "shop_price": "624", - "durability": null, - "weight": "1.8", - "weapon_interface": "5", - "render_anim": "1381", - "name": "White sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1187", - "durability": null, "name": "White sword", - "tradeable": "true", + "examine": "A razor sharp sword.", "archery_ticket_price": "0", - "id": "6606" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A razor sharp longsword.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "2060", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "6607", - "stand_turn_anim": "823", - "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,1,0,0", - "requirements": "{0,10}-{5,10}", - "shop_price": "960", - "durability": null, - "weight": "1.8", - "weapon_interface": "6", - "render_anim": "1", - "name": "White longsword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2060", - "durability": null, - "name": "White longsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6608" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A two handed sword.", - "walk_anim": "7046", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "4029", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "6609", - "stand_turn_anim": "7040", - "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,26,1,0,0", - "requirements": "{0,10}-{5,10}", - "shop_price": "1920", - "durability": null, - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "attack_audios": "2503,0,2504,0", - "name": "White 2h sword" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4029", - "durability": null, - "name": "White 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6610" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A vicious, curved sword.", - "walk_anim": "819", - "turn90ccw_anim": "822", + "attack_anims": "381,390,390,390", "attack_speed": "4", - "turn180_anim": "820", + "bonuses": "14,10,-2,0,0,0,2,1,0,0,0,12,1,0,0", "defence_anim": "397", + "durability": null, + "equip_audio": "2248", "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "1404", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "6611", - "stand_turn_anim": "823", - "bonuses": "4,19,-2,0,0,0,1,0,0,0,0,14,1,0,0", - "requirements": "{0,10}-{5,10}", - "shop_price": "768", - "durability": null, - "weight": "1", - "weapon_interface": "6", - "render_anim": "1", - "attack_audios": "2500,0,2517,0", - "name": "White scimitar" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1404", - "durability": null, - "name": "White scimitar", + "render_anim": "1381", + "requirements": "{0,10}-{5,10}", + "run_anim": "824", + "shop_price": "624", + "stand_anim": "813", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6612" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5", + "weight": "1.8" }, { + "id": "6606", + "name": "White sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6607", + "name": "White longsword", + "examine": "A razor sharp longsword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "5", + "bonuses": "13,18,-2,0,0,0,3,2,0,0,0,16,1,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,10}-{5,10}", + "run_anim": "824", + "shop_price": "960", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "6608", + "name": "White longsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6609", + "name": "White 2h sword", + "examine": "A two handed sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,26,1,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,10}-{5,10}", + "run_anim": "7039", + "shop_price": "1920", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" + }, + { + "id": "6610", + "name": "White 2h sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6611", + "name": "White scimitar", + "examine": "A vicious, curved sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "4,19,-2,0,0,0,1,0,0,0,0,14,1,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,10}-{5,10}", + "run_anim": "824", + "shop_price": "768", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6", + "weight": "1" + }, + { + "id": "6612", + "name": "White scimitar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6613", + "name": "White warhammer", + "examine": "I don't think it's intended for joinery.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,22,-4,0,0,0,0,0,0,0,19,1,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,10}-{5,10}", "shop_price": "980", - "ge_buy_limit": "100", - "examine": "I don't think it's intended for joinery.", - "durability": null, - "weight": "1.8", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "10", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "1178", - "attack_audios": "2504,0,0,0", - "name": "White warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6613", - "bonuses": "-4,-4,22,-4,0,0,0,0,0,0,0,19,1,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1178", + "id": "6614", + "name": "White warhammer", + "archery_ticket_price": "0", "durability": null, - "name": "White warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6614" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6615", + "name": "White chainbody", + "examine": "A series of connected metal rings.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,22,32,39,-3,24,10,0,1,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "1440", - "ge_buy_limit": "100", - "examine": "A series of connected metal rings.", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "6616", + "name": "White chainbody", + "archery_ticket_price": "0", "durability": null, - "weight": "6.8", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6617", + "name": "White platebody", + "examine": "Provides excellent protection.", "absorb": "1,0,2", - "equipment_slot": "4", - "grand_exchange_price": "788", - "name": "White chainbody", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6615", - "bonuses": "0,0,0,-15,0,22,32,39,-3,24,10,0,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "788", + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,1,0,0", "durability": null, - "name": "White chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6616" - }, - { + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", "requirements": "{1,10}-{5,10}", "shop_price": "3840", - "ge_buy_limit": "100", - "examine": "Provides excellent protection.", - "durability": null, - "weight": "9.9", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "5837", - "name": "White platebody", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6617", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,10,0,1,0,0" + "weight": "9.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "5837", - "durability": null, + "id": "6618", "name": "White platebody", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6618" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6619", + "name": "White boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,1,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "576", - "ge_buy_limit": "100", - "examine": "These will protect my feet.", - "durability": null, - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "1016", - "name": "White boots", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6619", - "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,1,0,0" + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1016", - "durability": null, + "id": "6620", "name": "White boots", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6620" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6621", + "name": "White med helm", + "examine": "A medium sized helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,4,0,1,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{1,10}-{5,10}", "shop_price": "576", - "ge_buy_limit": "100", - "examine": "A medium sized helmet.", + "tradeable": "true", + "weight": "1.9" + }, + { + "id": "6622", + "name": "White med helm", + "archery_ticket_price": "0", "durability": null, - "weight": "1.9", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6623", + "name": "White full helm", + "examine": "A full face helmet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,1,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", - "grand_exchange_price": "853", - "name": "White med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6621", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,4,0,1,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "853", - "durability": null, - "name": "White med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6622" - }, - { + "remove_beard": "true", "remove_head": "true", "requirements": "{1,10}-{5,10}", "shop_price": "1056", - "ge_buy_limit": "100", - "examine": "A full face helmet.", - "durability": null, - "weight": "2.7", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "2624", - "name": "White full helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6623", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,1,0,0" + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2624", - "durability": null, + "id": "6624", "name": "White full helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6624" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,10}-{5,10}", - "shop_price": "1920", - "ge_buy_limit": "100", - "examine": "Big, white and heavy looking.", - "durability": null, - "weight": "9", - "equipment_slot": "7", - "grand_exchange_price": "2115", - "name": "White platelegs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6625", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2115", - "durability": null, "name": "White platelegs", - "tradeable": "true", + "examine": "Big, white and heavy looking.", "archery_ticket_price": "0", - "id": "6626" - }, - { + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,1,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "1920", - "ge_buy_limit": "100", - "examine": "Big, white, and heavy looking.", - "durability": null, - "weight": "8.1", - "equipment_slot": "7", - "grand_exchange_price": "2000", - "name": "White plateskirt", "tradeable": "true", + "weight": "9" + }, + { + "id": "6626", + "name": "White platelegs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6627", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2000", - "durability": null, "name": "White plateskirt", - "tradeable": "true", + "examine": "Big, white, and heavy looking.", "archery_ticket_price": "0", - "id": "6628" + "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,1,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,10}-{5,10}", + "shop_price": "1920", + "tradeable": "true", + "weight": "8.1" }, { + "id": "6628", + "name": "White plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6629", + "name": "White gloves", + "examine": "These will keep my hands warm!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,1,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "6", - "ge_buy_limit": "100", - "examine": "These will keep my hands warm!", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "2036", - "name": "White gloves", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6629", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,1,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2036", - "durability": null, + "id": "6630", "name": "White gloves", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6630" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "6631", + "name": "White sq shield", + "examine": "A medium square shield.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,15,16,14,0,15,9,0,1,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "1152", - "ge_buy_limit": "100", - "examine": "A medium square shield.", + "tradeable": "true", + "weight": "3.6" + }, + { + "id": "6632", + "name": "White sq shield", + "archery_ticket_price": "0", "durability": null, - "weight": "3.6", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6633", + "name": "White kiteshield", + "examine": "A large metal shield.", "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "1360", - "name": "White sq shield", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6631", - "bonuses": "0,0,0,-6,-2,15,16,14,0,15,9,0,1,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1360", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,1,0,0", "durability": null, - "name": "White sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6632" - }, - { + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{1,10}-{5,10}", "shop_price": "1632", - "ge_buy_limit": "100", - "examine": "A large metal shield.", - "durability": null, - "weight": "5.4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "2390", - "name": "White kiteshield", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6633", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,1,0,0" + "weight": "5.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2390", - "durability": null, + "id": "6634", "name": "White kiteshield", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6634" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A Temple Knight Communication Orb. Top Secret! / Use this to stay in touch with Korasi and Jessika. (A Void Dance version) / Tyr's commorb, given to him by Sir Tiffy. (Tyr's commorb)", - "durability": null, + "id": "6635", "name": "Commorb", - "weight": "1", + "examine": "A Temple Knight Communication Orb. Top Secret! / Use this to stay in touch with Korasi and Jessika. (A Void Dance version) / Tyr's commorb, given to him by Sir Tiffy. (Tyr's commorb)", "archery_ticket_price": "0", - "id": "6635" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "1" }, { - "examine": "Proof that I have defeated the evil mage Solus.", - "durability": null, + "id": "6636", "name": "Solus's hat", + "examine": "Proof that I have defeated the evil mage Solus.", "archery_ticket_price": "0", - "id": "6636" + "durability": null }, { - "examine": "From a darker dimension.", - "durability": null, + "id": "6637", "name": "Dark beast", + "examine": "From a darker dimension.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "6637" + "durability": null }, { - "examine": "A key to the nature of light itself. (Mourning's Ends Part II)", - "durability": null, + "id": "6638", "name": "Colour wheel", + "examine": "A key to the nature of light itself. (Mourning's Ends Part II)", "archery_ticket_price": "0", - "id": "6638" + "durability": null }, { - "destroy_message": "Resetting the light maze will generate a new mirror.", - "examine": "A small hand mirror.", - "durability": null, + "id": "6639", "name": "Hand mirror", - "tradeable": "false", - "destroy": "true", + "examine": "A small hand mirror.", "archery_ticket_price": "0", - "id": "6639" + "destroy": "true", + "destroy_message": "Resetting the light maze will generate a new mirror.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Resetting the light maze will generate a new crystal.", - "examine": "A yellow crystal.", - "durability": null, + "id": "6641", "name": "Yellow crystal", - "tradeable": "false", - "destroy": "true", + "examine": "A yellow crystal.", "archery_ticket_price": "0", - "id": "6641" + "destroy": "true", + "destroy_message": "Resetting the light maze will generate a new crystal.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Resetting the light maze will generate a new crystal.", - "examine": "A cyan crystal.", - "durability": null, + "id": "6643", "name": "Cyan crystal", - "tradeable": "false", - "destroy": "true", + "examine": "A cyan crystal.", "archery_ticket_price": "0", - "id": "6643" + "destroy": "true", + "destroy_message": "Resetting the light maze will generate a new crystal.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Resetting the light maze will generate a new crystal.", - "examine": "A blue crystal.", - "durability": null, + "id": "6644", "name": "Blue crystal", - "tradeable": "false", - "destroy": "true", + "examine": "A blue crystal.", "archery_ticket_price": "0", - "id": "6644" - }, - { + "destroy": "true", "destroy_message": "Resetting the light maze will generate a new crystal.", - "examine": "A fractured crystal, one of the edges is clear.", "durability": null, - "name": "Fractured crystal", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6646" + "tradeable": "false" }, { + "id": "6646", + "name": "Fractured crystal", + "examine": "A fractured crystal, one of the edges is clear.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "Resetting the light maze will generate a new crystal.", - "examine": "A fractured crystal, one of the edges is clear.", "durability": null, - "name": "Fractured crystal", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6647" + "tradeable": "false" }, { - "destroy_message": "You can obtain another list from Thorgel.", - "examine": "It's a list of items I need to collect.", + "id": "6647", + "name": "Fractured crystal", + "examine": "A fractured crystal, one of the edges is clear.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Resetting the light maze will generate a new crystal.", "durability": null, + "tradeable": "false" + }, + { + "id": "6648", "name": "Item list", - "tradeable": "false", - "destroy": "true", + "examine": "It's a list of items I need to collect.", "archery_ticket_price": "0", - "id": "6648" + "destroy": "true", + "destroy_message": "You can obtain another list from Thorgel.", + "durability": null, + "tradeable": "false" }, { - "examine": "The journal of Nissyen Edern.", - "durability": null, + "id": "6649", "name": "Edern's journal", - "tradeable": "false", - "destroy": "true", + "examine": "The journal of Nissyen Edern.", "archery_ticket_price": "0", - "id": "6649" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A blackened crystal sample.", - "durability": null, + "id": "6650", "name": "Blackened crystal", + "examine": "A blackened crystal sample.", "archery_ticket_price": "0", - "id": "6650" + "durability": null }, { - "examine": "A newly formed crystal / A warm energy radiates from this crystal.", - "durability": null, + "id": "6651", "name": "Newly made crystal", - "archery_ticket_price": "0", - "id": "6651" - }, - { "examine": "A newly formed crystal / A warm energy radiates from this crystal.", - "durability": null, - "name": "Newly made crystal", "archery_ticket_price": "0", - "id": "6652" + "durability": null }, { - "destroy_message": "You can get another Crystal trinket from Arianwyn in Lletya.", - "examine": "A small Crystal trinket.", - "durability": null, + "id": "6652", + "name": "Newly made crystal", + "examine": "A newly formed crystal / A warm energy radiates from this crystal.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6653", "name": "Crystal trinket", - "tradeable": "false", + "examine": "A small Crystal trinket.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "6653" + "destroy_message": "You can get another Crystal trinket from Arianwyn in Lletya.", + "durability": null, + "tradeable": "false" }, { - "remove_sleeves": "true", - "examine": "Examine what?", - "durability": null, - "name": "Camo top", - "weight": "2.7", - "archery_ticket_price": "0", "id": "6654", - "equipment_slot": "4" - }, - { - "examine": "Examine what?", - "durability": null, - "name": "Camo bottoms", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "6655", - "equipment_slot": "7" - }, - { - "examine": "Examine what?", - "durability": null, - "name": "Camo helmet", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "6656", - "equipment_slot": "0" - }, - { - "remove_sleeves": "true", - "examine": "Examine what?", - "durability": null, "name": "Camo top", - "weight": "2.7", + "examine": "Examine what?", "archery_ticket_price": "0", - "id": "6657", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.7" }, { - "examine": "Examine what?", - "durability": null, + "id": "6655", "name": "Camo bottoms", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "6658", - "equipment_slot": "7" - }, - { "examine": "Examine what?", + "archery_ticket_price": "0", "durability": null, + "equipment_slot": "7", + "weight": "2.2" + }, + { + "id": "6656", "name": "Camo helmet", - "weight": "2.2", + "examine": "Examine what?", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "weight": "2.2" + }, + { + "id": "6657", + "name": "Camo top", + "examine": "Examine what?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.7" + }, + { + "id": "6658", + "name": "Camo bottoms", + "examine": "Examine what?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "2.2" + }, + { "id": "6659", - "equipment_slot": "0" - }, - { - "shop_price": "60", - "examine": "The jar keeps shaking... I'm scared.", - "durability": null, - "name": "Fishing explosive", + "name": "Camo helmet", + "examine": "Examine what?", "archery_ticket_price": "0", - "id": "6660" + "durability": null, + "equipment_slot": "0", + "weight": "2.2" }, { - "examine": "An angry Ogre in a funny hat.", + "id": "6660", + "name": "Fishing explosive", + "examine": "The jar keeps shaking... I'm scared.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "60" + }, + { + "id": "6661", "name": "Mogre", + "examine": "An angry Ogre in a funny hat.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "6661" + "durability": null }, { - "examine": "This fishing rod seems to have been bitten in half.", - "durability": null, + "id": "6662", "name": "Broken fishing rod", + "examine": "This fishing rod seems to have been bitten in half.", "archery_ticket_price": "0", - "id": "6662" + "durability": null }, { - "examine": "It seems someone vacated this boot in a hurry.", - "durability": null, + "id": "6663", "name": "Forlorn boot", + "examine": "It seems someone vacated this boot in a hurry.", "archery_ticket_price": "0", - "id": "6663" + "durability": null }, { - "requirements": "{18,32}", - "shop_price": "60", - "examine": "The jar keeps shaking... I'm scared.", - "durability": null, + "id": "6664", "name": "Fishing explosive", + "examine": "The jar keeps shaking... I'm scared.", "archery_ticket_price": "0", - "id": "6664" + "durability": null, + "requirements": "{18,32}", + "shop_price": "60" }, { - "examine": "Fishy, damp and smelly.", - "durability": null, - "name": "Mudskipper hat", - "weight": "1", - "archery_ticket_price": "0", "id": "6665", - "equipment_slot": "0" + "name": "Mudskipper hat", + "examine": "Fishy, damp and smelly.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "weight": "1" }, { - "examine": "Strangely uncomfortable flippers.", - "durability": null, - "name": "Flippers", - "weight": "2", - "archery_ticket_price": "0", "id": "6666", + "name": "Flippers", + "examine": "Strangely uncomfortable flippers.", + "archery_ticket_price": "0", "bonuses": "-2,-2,-2,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "weight": "2" }, { - "ge_buy_limit": "10000", + "id": "6667", + "name": "Fishbowl", "examine": "An empty fishbowl.", - "grand_exchange_price": "431", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "1.3", "archery_ticket_price": "0", - "id": "6667" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.3" }, { + "id": "6668", + "name": "Fishbowl", "examine": "A fishless fishbowl.", - "grand_exchange_price": "619", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "2.3", "archery_ticket_price": "0", - "id": "6668" + "durability": null, + "tradeable": "true", + "weight": "2.3" }, { + "id": "6669", + "name": "Fishbowl", "examine": "A fishless fishbowl with some seaweed.", - "grand_exchange_price": "619", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "2.3", "archery_ticket_price": "0", - "id": "6669" + "durability": null, + "tradeable": "true", + "weight": "2.3" }, { + "id": "6670", + "name": "Fishbowl", "examine": "A fishbowl with a Tiny Bluefish in it.", - "grand_exchange_price": "619", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "2.3", "archery_ticket_price": "0", - "id": "6670" + "durability": null, + "tradeable": "true", + "weight": "2.3" }, { + "id": "6671", + "name": "Fishbowl", "examine": "A fishbowl with a Tiny Greenfish in it.", - "grand_exchange_price": "619", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "2.3", "archery_ticket_price": "0", - "id": "6671" + "durability": null, + "tradeable": "true", + "weight": "2.3" }, { + "id": "6672", + "name": "Fishbowl", "examine": "A fishbowl with a Tiny Spinefish in it.", - "grand_exchange_price": "619", - "durability": null, - "name": "Fishbowl", - "tradeable": "true", - "weight": "2.3", "archery_ticket_price": "0", - "id": "6672" + "durability": null, + "tradeable": "true", + "weight": "2.3" }, { - "examine": "An empty fishbowl in a net.", - "durability": null, + "id": "6673", "name": "Fishbowl and net", - "weight": "2", + "examine": "An empty fishbowl in a net.", "archery_ticket_price": "0", - "id": "6673" + "durability": null, + "weight": "2" }, { - "shop_price": "10", - "examine": "A tiny net for grabbing tiny fish.", - "durability": null, + "id": "6674", "name": "Tiny net", - "tradeable": "false", + "examine": "A tiny net for grabbing tiny fish.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6674" + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "1" }, { - "examine": "'Ingredients: Ground Guam and Ground Seaweed.", - "durability": null, + "id": "6675", "name": "An empty box", + "examine": "'Ingredients: Ground Guam and Ground Seaweed.", "archery_ticket_price": "0", - "id": "6675" + "durability": null }, { - "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Guam Leaf...", - "durability": null, + "id": "6677", "name": "Guam in a box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6677" - }, - { "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Guam Leaf...", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "6678", "name": "Guam in a box?", - "tradeable": "true", + "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Guam Leaf...", "archery_ticket_price": "0", - "id": "6678" + "durability": null, + "tradeable": "true" }, { - "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Seaweed...", - "durability": null, + "id": "6679", "name": "Seaweed in a box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6679" - }, - { "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Seaweed...", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "6680", "name": "Seaweed in a box?", - "tradeable": "true", + "examine": "'Ingredients: Ground Guam and Ground Seaweed.' Well, I have the Seaweed...", "archery_ticket_price": "0", - "id": "6680" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "One of the ingredients for making fish food.", - "grand_exchange_price": "1", - "durability": null, + "id": "6681", "name": "Ground guam", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6681" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Ground guam", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6682" - }, - { - "ge_buy_limit": "100", "examine": "One of the ingredients for making fish food.", - "grand_exchange_price": "14", - "durability": null, - "name": "Ground seaweed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6683" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "14", + "tradeable": "true" + }, + { + "id": "6682", + "name": "Ground guam", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6683", "name": "Ground seaweed", - "tradeable": "true", + "examine": "One of the ingredients for making fish food.", "archery_ticket_price": "0", - "id": "6684" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Saradomin Brew.", - "grand_exchange_price": "15300", + "id": "6684", + "name": "Ground seaweed", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6685", "name": "Saradomin brew(4)", - "tradeable": "true", + "examine": "A Saradomin Brew.", "archery_ticket_price": "0", - "id": "6685" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "15300", - "durability": null, + "id": "6686", "name": "Saradomin brew(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6686" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Saradomin Brew.", - "grand_exchange_price": "11400", - "durability": null, + "id": "6687", "name": "Saradomin brew(3)", - "tradeable": "true", + "examine": "A Saradomin Brew.", "archery_ticket_price": "0", - "id": "6687" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "11400", - "durability": null, + "id": "6688", "name": "Saradomin brew(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6688" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A Saradomin Brew.", - "grand_exchange_price": "7566", - "durability": null, + "id": "6689", "name": "Saradomin brew(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6689" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "7566", - "durability": null, - "name": "Saradomin brew(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6690" - }, - { - "ge_buy_limit": "1000", "examine": "A Saradomin Brew.", - "grand_exchange_price": "3783", - "durability": null, - "name": "Saradomin brew(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6691" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "3783", - "durability": null, - "name": "Saradomin brew(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6692" + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "6690", + "name": "Saradomin brew(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6691", + "name": "Saradomin brew(1)", + "examine": "A Saradomin Brew.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6692", + "name": "Saradomin brew(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6693", + "name": "Crushed nest", "examine": "It's a crushed bird's nest.", - "grand_exchange_price": "10500", - "durability": null, - "name": "Crushed nest", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6693" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "10500", - "durability": null, - "name": "Crushed nest", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6694" + "tradeable": "true" }, { - "examine": "\"A cold-blooded creature, partial to warmth.", + "id": "6694", + "name": "Crushed nest", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6695", "name": "Desert lizard", + "examine": "\"A cold-blooded creature, partial to warmth.", "archery_ticket_price": "0", - "id": "6695" + "durability": null }, { - "requirements": "{22,18}", - "shop_price": "1", - "examine": "Contains ice-cold water.", - "durability": null, + "id": "6696", "name": "Ice cooler", + "examine": "Contains ice-cold water.", "archery_ticket_price": "0", - "id": "6696" + "durability": null, + "requirements": "{22,18}", + "shop_price": "1" }, { - "shop_price": "5", - "ge_buy_limit": "10000", + "id": "6697", + "name": "Pat of butter", "examine": "A pat of freshly churned butter.", - "grand_exchange_price": "405", - "durability": null, - "name": "Pat of butter", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "6697" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "405", - "durability": null, - "name": "Pat of butter", + "shop_price": "5", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6698" + "weight": "0.2" }, { + "id": "6698", + "name": "Pat of butter", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6700", "name": "Burnt potato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6700" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "6701", + "name": "Baked potato", "examine": "It'd taste even better with some toppings.", - "grand_exchange_price": "326", - "durability": null, - "name": "Baked potato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "6701" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "326", - "durability": null, + "id": "6702", "name": "Baked potato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6702" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "6703", + "name": "Potato with butter", "examine": "A baked potato with butter.", - "grand_exchange_price": "745", - "durability": null, - "name": "Potato with butter", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "6703" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "745", - "durability": null, + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "6704", "name": "Potato with butter", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6704" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "350", - "ge_buy_limit": "10000", + "id": "6705", + "name": "Potato with cheese", "examine": "A baked potato with butter and cheese.", - "grand_exchange_price": "604", - "durability": null, - "name": "Potato with cheese", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "6705" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "604", - "durability": null, - "name": "Potato with cheese", + "shop_price": "350", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6706" + "weight": "0.5" }, { - "examine": "An amulet of Camel-speak. It makes vague braying noises.", - "durability": null, - "name": "Camulet", + "id": "6706", + "name": "Potato with cheese", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { "id": "6707", + "name": "Camulet", + "examine": "An amulet of Camel-speak. It makes vague braying noises.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "shop_price": "200", - "examine": "Especially good against diseased arachnids.", - "durability": null, - "name": "Slayer gloves", - "archery_ticket_price": "0", "id": "6708", - "bonuses": "0,0,0,0,0,4,5,3,0,0,1,0,0,0,0" + "name": "Slayer gloves", + "examine": "Especially good against diseased arachnids.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,4,5,3,0,0,1,0,0,0,0", + "durability": null, + "shop_price": "200" }, { - "examine": "A bunch of legs, eyes and teeth.", - "durability": null, + "id": "6709", "name": "Fever spider", + "examine": "A bunch of legs, eyes and teeth.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "6709" + "durability": null }, { - "examine": "A blindweed seed - plant in a Blindweed patch.", - "durability": null, + "id": "6710", "name": "Blindweed seed", + "examine": "A blindweed seed - plant in a Blindweed patch.", "archery_ticket_price": "0", - "id": "6710" + "durability": null }, { - "examine": "An inedible, foul smelling herb.", - "durability": null, + "id": "6711", "name": "Blindweed", + "examine": "An inedible, foul smelling herb.", "archery_ticket_price": "0", - "id": "6711" + "durability": null }, { - "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?", - "grand_exchange_price": "74", - "durability": null, + "id": "6712", "name": "Bucket of water", + "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?", + "archery_ticket_price": "0", + "durability": null, "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6712" + "weight": "3" }, { - "examine": "A heavy metal wrench.", - "durability": null, + "id": "6713", "name": "Wrench", + "examine": "A heavy metal wrench.", "archery_ticket_price": "0", - "id": "6713" + "durability": null }, { - "examine": "A shining paragon of wrenchly virtue.", - "durability": null, + "id": "6714", "name": "Holy wrench", + "examine": "A shining paragon of wrenchly virtue.", "archery_ticket_price": "0", - "id": "6714" + "durability": null }, { - "examine": "They look at you balefully. 'Feed us...", - "durability": null, + "id": "6715", "name": "Sluglings", + "examine": "They look at you balefully. 'Feed us...", "archery_ticket_price": "0", - "id": "6715" + "durability": null }, { - "examine": "A sinister looking squid.", - "durability": null, + "id": "6716", "name": "Karamthulhu", - "archery_ticket_price": "0", - "id": "6716" - }, - { "examine": "A sinister looking squid.", - "durability": null, - "name": "Karamthulhu", "archery_ticket_price": "0", - "id": "6717" + "durability": null }, { - "examine": "A diseased deceased Fever Spider. Handle with care.", - "durability": null, + "id": "6717", + "name": "Karamthulhu", + "examine": "A sinister looking squid.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6718", "name": "Fever spider body", + "examine": "A diseased deceased Fever Spider. Handle with care.", "archery_ticket_price": "0", - "id": "6718" + "durability": null }, { - "examine": "Sorry, I mean a bucket of 'rum'.", - "durability": null, + "id": "6719", "name": "Unsanitary swill", + "examine": "Sorry, I mean a bucket of 'rum'.", "archery_ticket_price": "0", - "id": "6719" + "durability": null }, { - "requirements": "{18,42}", - "shop_price": "200", - "examine": "Especially good against diseased arachnids.", - "durability": null, - "name": "Slayer gloves", - "archery_ticket_price": "0", "id": "6720", + "name": "Slayer gloves", + "examine": "Especially good against diseased arachnids.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,4,5,3,0,0,1,0,0,0,0", - "equipment_slot": "9" + "durability": null, + "equipment_slot": "9", + "requirements": "{18,42}", + "shop_price": "200" }, { + "id": "6721", + "name": "Rusty scimitar", "examine": "A decent enough weapon gone rusty.", + "archery_ticket_price": "0", "attack_audios": "2500,0,2517,0", "durability": null, - "name": "Rusty scimitar", - "weight": "2", - "archery_ticket_price": "0", - "id": "6721" + "weight": "2" }, { - "examine": "Alas...I hardly knew him.", - "durability": null, + "id": "6722", "name": "Zombie head", + "examine": "Alas...I hardly knew him.", "archery_ticket_price": "0", - "id": "6722" + "durability": null }, { - "durability": null, + "id": "6723", "name": "Fishbowl", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6723" + "durability": null, + "tradeable": "true" }, { - "requirements": "{4,50}", - "ge_buy_limit": "100", + "id": "6724", + "name": "Seercull", "examine": "An ancient Fremennik bow that was once used to battle the Moon Clan.", - "has_special": "true", - "durability": null, - "rare_item": "true", - "weight": "1", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", "attack_speed": "5", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "2588", + "requirements": "{4,50}", + "tradeable": "true", "two_handed": "true", "weapon_interface": "16", - "render_anim": "2588", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "lendable": "true", - "grand_exchange_price": "12200", - "name": "Seercull", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6724", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" + "weight": "1" }, { + "id": "6725", + "name": "Seercull", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "12200", - "durability": null, - "name": "Seercull", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6725" + "tradeable": "true" }, { - "durability": null, + "id": "6726", "name": "Mud battlestaff", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6726" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "6727", "name": "Mystic mud staff", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6727" + "durability": null, + "tradeable": "true" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "6728", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed small zombie monkey bones.", "archery_ticket_price": "0", - "id": "6728" + "durability": null, + "weight": "1" }, { - "ge_buy_limit": "10000", + "id": "6729", + "name": "Dagannoth bones", "examine": "These would feed a dogfish for months!", - "grand_exchange_price": "9616", - "durability": null, - "name": "Dagannoth bones", - "tradeable": "true", - "weight": "1.5", "archery_ticket_price": "0", - "id": "6729" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "9616", - "durability": null, + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "6730", "name": "Dagannoth bones", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6730" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "10", - "examine": "A mysterious ring that can fill the wearer with magical power...", - "grand_exchange_price": "478800", - "rare_item": "true", - "durability": null, - "name": "Seers ring", - "tradeable": "true", - "archery_ticket_price": "0", "id": "6731", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "478800", - "durability": null, "name": "Seers ring", - "tradeable": "true", + "examine": "A mysterious ring that can fill the wearer with magical power...", "archery_ticket_price": "0", - "id": "6732" + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "10", - "examine": "A fabled ring that improves the wearer's skill with a bow...", - "grand_exchange_price": "1400000", - "rare_item": "true", - "durability": null, - "name": "Archers ring", - "tradeable": "true", + "id": "6732", + "name": "Seers ring", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "6733", - "bonuses": "0,0,0,0,4,0,0,0,0,4,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1400000", - "durability": null, "name": "Archers ring", - "tradeable": "true", + "examine": "A fabled ring that improves the wearer's skill with a bow...", "archery_ticket_price": "0", - "id": "6734" + "bonuses": "0,0,0,0,4,0,0,0,0,4,0,0,0,0,0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "tradeable": "true" }, { - "lendable": "true", - "ge_buy_limit": "10", - "examine": "A legendary ring once worn by Fremennik warriors.", - "grand_exchange_price": "178100", - "rare_item": "true", - "durability": null, - "name": "Warrior ring", - "tradeable": "true", + "id": "6734", + "name": "Archers ring", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "6735", - "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "178100", - "durability": null, "name": "Warrior ring", - "tradeable": "true", + "examine": "A legendary ring once worn by Fremennik warriors.", "archery_ticket_price": "0", - "id": "6736" - }, - { + "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10", "lendable": "true", - "ge_buy_limit": "10", - "examine": "A ring reputed to bring out a berserk fury in its wearer.", - "grand_exchange_price": "2700000", "rare_item": "true", - "durability": null, - "name": "Berserker ring", - "tradeable": "true", + "tradeable": "true" + }, + { + "id": "6736", + "name": "Warrior ring", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "6737", - "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "2700000", - "durability": null, "name": "Berserker ring", - "tradeable": "true", + "examine": "A ring reputed to bring out a berserk fury in its wearer.", "archery_ticket_price": "0", - "id": "6738" + "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "tradeable": "true" }, { - "requirements": "{0,60}", + "id": "6738", + "name": "Berserker ring", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6739", + "name": "Dragon axe", "examine": "A very powerful axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2498,2498,2497,2498", + "bonuses": "-2,38,32,0,0,0,1,0,0,0,0,42,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "ge_buy_limit": "10", "has_special": "true", "rare_item": "true", - "durability": null, - "weapon_interface": "2", "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "grand_exchange_price": "813100", - "name": "Dragon axe", + "requirements": "{0,60}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6739", - "bonuses": "-2,38,32,0,0,0,1,0,0,0,0,42,0,0,0" + "weapon_interface": "2" }, { + "id": "6740", + "name": "Dragon axe", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "813100", - "durability": null, - "name": "Dragon axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6740" + "tradeable": "true" }, { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "weight": "2.5", - "archery_ticket_price": "0", "id": "6741", - "equipment_slot": "3" - }, - { - "durability": null, "name": "Broken axe", - "tradeable": "true", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "6742" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { + "id": "6742", + "name": "Broken axe", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "6744", "name": "Picture", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6744" + "durability": null, + "tradeable": "true" }, { - "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", - "durability": null, - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "6", - "render_anim": "2554", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "name": "Silverlight", - "archery_ticket_price": "0", "id": "6745", - "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0" - }, - { - "examine": "The magical sword Silverlight, enhanced with the blood of Agrith-Naar.", - "has_special": "true", - "durability": null, - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "6", - "render_anim": "2554", - "defence_anim": "397", - "equipment_slot": "3", + "name": "Silverlight", + "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", + "archery_ticket_price": "0", "attack_anims": "390,390,381,390", - "name": "Darklight", - "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "render_anim": "2554", + "weapon_interface": "6", + "weight": "1.8" + }, + { "id": "6746", - "bonuses": "10,16,-2,0,0,0,3,2,2,0,0,13,0,0,0" + "name": "Darklight", + "examine": "The magical sword Silverlight, enhanced with the blood of Agrith-Naar.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "5", + "bonuses": "10,16,-2,0,0,0,3,2,2,0,0,13,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "2554", + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "Used to make the sigil of the demon Agrith-Naar.", - "durability": null, + "id": "6747", "name": "Demonic sigil mould", + "examine": "Used to make the sigil of the demon Agrith-Naar.", "archery_ticket_price": "0", - "id": "6747" + "durability": null }, { - "examine": "A sigil used for the summoning of the demon Agrith-Naar.", - "durability": null, + "id": "6748", "name": "Demonic sigil", - "weight": "1.8", + "examine": "A sigil used for the summoning of the demon Agrith-Naar.", "archery_ticket_price": "0", - "id": "6748" + "durability": null, + "weight": "1.8" }, { - "examine": "Will this book help in summoning Agrith-Naar?", - "durability": null, + "id": "6749", "name": "Demonic tome", - "weight": "0.4", + "examine": "Will this book help in summoning Agrith-Naar?", "archery_ticket_price": "0", - "id": "6749" + "durability": null, + "weight": "0.4" }, { - "remove_sleeves": "true", - "examine": "A desert shirt stained black with mushroom ink.", - "durability": null, - "name": "Black desert shirt", - "archery_ticket_price": "0", "id": "6750", - "equipment_slot": "4" + "name": "Black desert shirt", + "examine": "A desert shirt stained black with mushroom ink.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "examine": "A desert robe stained black with mushroom ink.", - "durability": null, - "name": "Black desert robe", - "archery_ticket_price": "0", "id": "6752", + "name": "Black desert robe", + "examine": "A desert robe stained black with mushroom ink.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "7" }, { - "destroy_message": "Hopefully Erin has a copy!", - "examine": "It changes temperature as I walk. (Meeting History)", - "durability": null, - "name": "Enchanted key", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "6754", - "equipment_slot": "3" + "name": "Enchanted key", + "examine": "It changes temperature as I walk. (Meeting History)", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Hopefully Erin has a copy!", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Guthix Mjolnir.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "You can reclaim this item from the place you found it.", - "grand_exchange_price": "2875", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "6760", - "stand_turn_anim": "1209", - "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", - "shop_price": "250", - "durability": null, - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Guthix mjolnir" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2875", - "durability": null, "name": "Guthix mjolnir", - "tradeable": "true", + "examine": "A Guthix Mjolnir.", "archery_ticket_price": "0", - "id": "6761" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Saradomin Mjolnir.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", - "destroy_message": "You can reclaim this item from the place you found it.", - "grand_exchange_price": "17200", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6762", - "stand_turn_anim": "1209", + "attack_speed": "5", "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", + "defence_anim": "2079", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", "shop_price": "250", - "durability": null, - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Saradomin mjolnir" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17200", - "durability": null, - "name": "Saradomin mjolnir", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6763" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Zamorak mjolnir.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "You can reclaim this item from the place you found it.", - "grand_exchange_price": "4115", "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6764", "stand_turn_anim": "1209", - "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", - "durability": null, - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Zamorak mjolnir" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4115", - "durability": null, - "name": "Zamorak mjolnir", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6765" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" }, { - "examine": "Antipoison for Pox.", + "id": "6761", + "name": "Guthix mjolnir", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6762", + "name": "Saradomin mjolnir", + "examine": "A Saradomin Mjolnir.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", + "defence_anim": "2079", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "250", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" + }, + { + "id": "6763", + "name": "Saradomin mjolnir", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6764", + "name": "Zamorak mjolnir", + "examine": "A Zamorak mjolnir.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", + "defence_anim": "2079", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" + }, + { + "id": "6765", + "name": "Zamorak mjolnir", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6766", "name": "Cat antipoison", + "examine": "Antipoison for Pox.", "archery_ticket_price": "0", - "id": "6766" + "durability": null }, { - "examine": "A little more smelly than usual.", - "durability": null, + "id": "6768", "name": "Poisoned cheese", - "weight": "0.2", + "examine": "A little more smelly than usual.", "archery_ticket_price": "0", - "id": "6768" + "durability": null, + "weight": "0.2" }, { - "destroy_message": "You can obtain another sheet of music by talking to the snakecharmer of Pollnivneach.", - "examine": "Charming.", - "durability": null, + "id": "6769", "name": "Music scroll", - "tradeable": "false", - "destroy": "true", + "examine": "Charming.", "archery_ticket_price": "0", - "id": "6769" + "destroy": "true", + "destroy_message": "You can obtain another sheet of music by talking to the snakecharmer of Pollnivneach.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can obtain another set of directions to the party by talking to Jimmy Dazzler.", - "examine": "Jimmy Dazzler's directions.", - "durability": null, + "id": "6770", "name": "Directions", - "tradeable": "false", + "examine": "Jimmy Dazzler's directions.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "6770" + "destroy_message": "You can obtain another set of directions to the party by talking to Jimmy Dazzler.", + "durability": null, + "tradeable": "false" }, { - "examine": "Contains garden weeds.", - "durability": null, + "id": "6771", "name": "Pot of weeds", + "examine": "Contains garden weeds.", "archery_ticket_price": "0", - "id": "6771" + "durability": null }, { - "examine": "Contains slowly burning garden weeds.", - "durability": null, + "id": "6772", "name": "Smouldering pot", + "examine": "Contains slowly burning garden weeds.", "archery_ticket_price": "0", - "id": "6772" + "durability": null }, { - "turn90cw_anim": "1424", - "examine": "A pole for putting rats on.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6773", + "name": "Rat pole", + "examine": "A pole for putting rats on.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "turn90cw_anim": "1424", - "examine": "A pole with one rat on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6774", + "name": "Rat pole", + "examine": "A pole with one rat on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "turn90cw_anim": "1424", - "examine": "A pole with two rats on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6775", + "name": "Rat pole", + "examine": "A pole with two rats on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "turn90cw_anim": "1424", - "examine": "A pole with three rats on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6776", + "name": "Rat pole", + "examine": "A pole with three rats on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "turn90cw_anim": "1424", - "examine": "A pole with four rats on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6777", + "name": "Rat pole", + "examine": "A pole with four rats on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "turn90cw_anim": "1424", - "examine": "A pole with five rats on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Rat pole", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "6778", - "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1424", - "examine": "A pole with six rats on it.", - "walk_anim": "1422", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1425", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "10", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", "name": "Rat pole", + "examine": "A pole with five rats on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", "run_anim": "1427", - "archery_ticket_price": "0", - "id": "6779", + "stand_anim": "1421", "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { - "examine": "Menaphite thug.", + "id": "6779", + "name": "Rat pole", + "examine": "A pole with six rats on it.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" + }, + { + "id": "6780", "name": "Menaphite thug", + "examine": "Menaphite thug.", "archery_ticket_price": "0", - "id": "6780" + "durability": null }, { - "remove_sleeves": "true", - "examine": "This looks quite old.", - "durability": null, - "name": "Robe of elidinis", - "archery_ticket_price": "0", "id": "6786", - "equipment_slot": "4" + "name": "Robe of elidinis", + "examine": "This looks quite old.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "examine": "This looks quite old.", - "durability": null, - "name": "Robe of elidinis", - "archery_ticket_price": "0", "id": "6787", + "name": "Robe of elidinis", + "examine": "This looks quite old.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "7" }, { - "examine": "This robe is too torn to wear.", - "durability": null, + "id": "6788", "name": "Torn robe", - "weight": "1", + "examine": "This robe is too torn to wear.", "archery_ticket_price": "0", - "id": "6788" + "durability": null, + "weight": "1" }, { - "examine": "This robe is too torn to wear.", - "durability": null, + "id": "6789", "name": "Torn robe", - "weight": "1", + "examine": "This robe is too torn to wear.", "archery_ticket_price": "0", - "id": "6789" + "durability": null, + "weight": "1" }, { - "durability": null, + "id": "6790", "name": "Shoes", "archery_ticket_price": "0", - "id": "6790", + "durability": null, "equipment_slot": "10" }, { - "examine": "Awusah's Sole.", - "durability": null, + "id": "6791", "name": "Sole", - "weight": "1", + "examine": "Awusah's Sole.", "archery_ticket_price": "0", - "id": "6791" + "durability": null, + "weight": "1" }, { - "examine": "An ancient key from the shrine in Nardah. (Spirits of the Elid)", - "durability": null, + "id": "6792", "name": "Ancestral key", + "examine": "An ancient key from the shrine in Nardah. (Spirits of the Elid)", "archery_ticket_price": "0", - "id": "6792" + "durability": null }, { - "examine": "The Ballad of Jaresh.", - "durability": null, + "id": "6793", "name": "Ballad", + "examine": "The Ballad of Jaresh.", "archery_ticket_price": "0", - "id": "6793" + "durability": null }, { - "shop_price": "30", - "ge_buy_limit": "1000", + "id": "6794", + "name": "Choc-ice", "examine": "A tasty treat from Nardah - better eat this before it melts.", - "grand_exchange_price": "210", - "durability": null, - "name": "Choc-ice", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6794" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "210", - "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "6795", "name": "Choc-ice", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6795" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "bankable": "false", - "examine": "Wonder what happens if I rub it...", - "durability": null, + "id": "6796", "name": "Lamp", - "weight": "0.1", + "examine": "Wonder what happens if I rub it...", "archery_ticket_price": "0", - "id": "6796" + "bankable": "false", + "durability": null, + "weight": "0.1" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "6797", "name": "Watering can", + "examine": "See article", "archery_ticket_price": "0", - "id": "6797" + "durability": null, + "shop_price": "8" }, { - "examine": "A scroll once used by a champion.", - "durability": null, + "id": "6798", "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6798" - }, - { "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6799" + "durability": null }, { + "id": "6799", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6800" + "durability": null }, { + "id": "6800", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6801" + "durability": null }, { + "id": "6801", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6802" + "durability": null }, { + "id": "6802", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6803" + "durability": null }, { + "id": "6803", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6804" + "durability": null }, { + "id": "6804", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6805" + "durability": null }, { + "id": "6805", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6806" + "durability": null }, { + "id": "6806", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6807" + "durability": null }, { + "id": "6807", + "name": "Champion scroll", "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", "archery_ticket_price": "0", - "id": "6808" + "durability": null }, { - "requirements": "{1,50}-{2,50}", - "ge_buy_limit": "10", - "examine": "These look pretty heavy.", - "durability": null, - "weight": "15", - "absorb": "2,0,5", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "39400", - "name": "Granite legs", - "tradeable": "true", + "id": "6808", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", "archery_ticket_price": "0", + "durability": null + }, + { "id": "6809", - "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0" - }, - { - "examine": "A pot of crushed [type of bones].", - "durability": null, - "name": "Bonemeal", - "weight": "1", + "name": "Granite legs", + "examine": "These look pretty heavy.", + "absorb": "2,0,5", "archery_ticket_price": "0", - "id": "6810" + "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "lendable": "true", + "requirements": "{1,50}-{2,50}", + "tradeable": "true", + "weight": "15" }, { - "examine": "A very dangerous pile of animated Wyvern bones.", + "id": "6810", + "name": "Bonemeal", + "examine": "A pot of crushed large zombie monkey bones.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "6811", "name": "Skeletal wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", "archery_ticket_price": "0", "attack_speed": "6", - "id": "6811" + "durability": null }, { - "ge_buy_limit": "1000", + "id": "6812", + "name": "Wyvern bones", "examine": "Bones of a huge flying creature.", - "grand_exchange_price": "4845", - "durability": null, - "name": "Wyvern bones", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6812" - }, - { - "durability": null, - "name": "Granite legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6813" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "This would make warm clothing.", - "grand_exchange_price": "90", - "durability": null, - "name": "Fur", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "6814" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "90", - "durability": null, - "name": "Fur", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6815" - }, - { - "durability": null, - "name": "Wyvern bones", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6816" - }, - { - "turn90cw_anim": "7044", - "examine": "A slender two-handed sword.", - "walk_anim": "7046", - "durability": null, - "destroy": "true", - "weight": "3", - "turn90ccw_anim": "7043", - "two_handed": "true", - "turn180_anim": "7045", - "render_anim": "124", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "7047", - "name": "Slender blade", - "tradeable": "false", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "6817", - "stand_turn_anim": "7040" - }, - { - "turn90cw_anim": "1207", - "examine": "A sharp sword that can also fire arrows.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "3", - "turn90ccw_anim": "1208", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You can make a replacement.", - "stand_anim": "813", - "name": "Bow-sword", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6818", - "stand_turn_anim": "1209" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "25000", - "examine": "A large pouch used for storing essence.", - "durability": null, - "name": "Large pouch", - "weight": "1", - "archery_ticket_price": "0", - "id": "6819" - }, - { - "destroy_message": "I can get another from the assassin beside the winch that leads down into Senntisten temple.", - "examine": "It seems to have pieces missing. After Temple at Senntisten quest: The assassin's plunder from the church on Entrana.", - "durability": null, - "name": "Relic", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6820" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This looks valuable.", - "durability": null, - "name": "Orb", - "archery_ticket_price": "0", - "id": "6821" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6822" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6823" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6824" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6825" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6826" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Star bauble", - "archery_ticket_price": "0", - "id": "6827" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6828" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6829" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6830" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6831" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6832" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Box bauble", - "archery_ticket_price": "0", - "id": "6833" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6834" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6835" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6836" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6837" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6838" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Diamond bauble", - "archery_ticket_price": "0", - "id": "6839" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6840" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6841" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6842" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6843" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6844" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Tree bauble", - "archery_ticket_price": "0", - "id": "6845" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6846" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6847" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6848" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6849" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6850" - }, - { - "examine": "See individual articles.", - "durability": null, - "name": "Bell bauble", - "archery_ticket_price": "0", - "id": "6851" - }, - { - "examine": "A box for storing completed puppets.", - "durability": null, - "name": "Puppet box", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6852" - }, - { - "examine": "A box for storing painted baubles.", - "durability": null, - "name": "Bauble box", - "archery_ticket_price": "0", - "id": "6853" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A box for storing completed puppets.", - "durability": null, - "name": "Puppet box", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6854" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A box for storing painted baubles.", - "durability": null, - "name": "Bauble box", - "archery_ticket_price": "0", - "id": "6855" - }, - { - "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", - "examine": "A woolly bobble hat.", - "durability": null, - "name": "Bobble hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6856", - "equipment_slot": "0" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A woolly scarf.", - "durability": null, - "name": "Bobble scarf", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6857", - "equipment_slot": "2" - }, - { - "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", - "shop_price": "160", - "examine": "A woolly Jester hat.", - "durability": null, - "name": "Jester hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6858", - "equipment_slot": "0" - }, - { - "destroy_message": "You can get another Jester scarf from Diango.", - "examine": "A woolly jester scarf.", - "durability": null, - "name": "Jester scarf", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6859", - "equipment_slot": "2" - }, - { - "destroy_message": "You may get another from Diango in Draynor Village.", - "examine": "A woolly triple bobble jester hat.", - "durability": null, - "name": "Tri-jester hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6860", - "equipment_slot": "0" - }, - { - "destroy_message": "You can obtain another jester scarf from Diango in the Draynor Market.", - "examine": "A woolly jester scarf.", - "durability": null, - "name": "Tri-jester scarf", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6861", - "equipment_slot": "2" - }, - { - "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", - "examine": "A woolly tobogganing hat.", - "durability": null, - "name": "Woolly hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6862", - "equipment_slot": "0" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A woolly tobogganing scarf.", - "durability": null, - "name": "Woolly scarf", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "6863", - "equipment_slot": "2" - }, - { - "examine": "The controlling part of a marionette.", - "durability": null, - "name": "Marionette handle", - "archery_ticket_price": "0", - "id": "6864", - "equipment_slot": "3" - }, - { - "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6865" - }, - { - "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6866" - }, - { - "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6867" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6868" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6869" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6870" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6871" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6872" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6873" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Red marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6874" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6875" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "6876" - }, - { - "examine": "I've got no strings ... oh hang on!", "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6877" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6878" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6879" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6880" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6881" - }, - { - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Green marionette", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6882" - }, - { - "examine": "A tasty fruit.", - "durability": null, - "name": "Peach", - "archery_ticket_price": "0", - "id": "6883" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A magic training arena progress hat", - "durability": null, - "name": "Progress hat", - "archery_ticket_price": "0", - "id": "6885", - "equipment_slot": "0" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A magic training arena progress hat", - "durability": null, - "name": "Progress hat", - "archery_ticket_price": "0", - "id": "6886", - "equipment_slot": "0" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A magic training arena progress hat", - "durability": null, - "name": "Progress hat", - "archery_ticket_price": "0", - "id": "6887", - "equipment_slot": "0" - }, - { - "bankable": "false", - "examine": "A guardian of the arena.", - "durability": null, - "name": "Guardian statue", - "archery_ticket_price": "0", - "id": "6888" - }, - { - "requirements": "{6,60}", - "ge_buy_limit": "10", - "examine": "The magical book of the Mage.", - "grand_exchange_price": "3800000", - "durability": null, - "name": "Mage's book", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6889", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "3800000", - "durability": null, - "name": "Mage's book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6890" - }, - { - "shop_price": "200", - "examine": "A book about the Training Arena.", - "durability": null, - "name": "Arena book", - "weight": "1", - "archery_ticket_price": "0", - "id": "6891" - }, - { - "bankable": "false", - "shop_price": "6", - "examine": "Comfortable leather boots.", - "grand_exchange_price": "189", - "durability": null, - "name": "Leather boots", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "6893", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0" - }, - { - "bankable": "false", - "examine": "A large metal shield.", - "grand_exchange_price": "3084", - "durability": null, - "name": "Adamant kiteshield", - "tradeable": "true", - "weight": "5.8", - "archery_ticket_price": "0", - "id": "6894", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "bankable": "false", - "examine": "A medium sized helmet.", - "grand_exchange_price": "1049", - "durability": null, - "name": "Adamant med helm", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "6895", - "absorb": "1,0,2", - "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0" - }, - { - "bankable": "false", - "shop_price": "350", - "examine": "This looks valuable.", - "grand_exchange_price": "644", - "durability": null, - "name": "Emerald", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6896" - }, - { - "bankable": "false", - "shop_price": "32000", - "examine": "A razor sharp longsword", - "durability": null, - "weight": "1.8", - "attack_speed": "5", - "lendable": "true", - "grand_exchange_price": "18938", - "attack_audios": "2500,2500,2517,2500", - "name": "Rune longsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6897", - "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0" - }, - { - "bankable": "false", - "examine": "A green cylinder", - "durability": null, - "name": "Cylinder", - "archery_ticket_price": "0", - "id": "6898" - }, - { - "bankable": "false", - "examine": "A yellow cube", - "durability": null, - "name": "Cube", - "archery_ticket_price": "0", - "id": "6899" - }, - { - "bankable": "false", - "examine": "A blue icosahedron.", - "durability": null, - "name": "Icosahedron", - "archery_ticket_price": "0", - "id": "6900" - }, - { - "bankable": "false", - "examine": "A red pentamid.", - "durability": null, - "name": "Pentamid", - "archery_ticket_price": "0", - "id": "6901" - }, - { - "bankable": "false", - "examine": "This looks valuable.", - "durability": null, - "name": "Orb", - "archery_ticket_price": "0", - "id": "6902" - }, - { - "bankable": "false", - "examine": "This looks valuable.", - "durability": null, - "name": "Dragonstone", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "6903" - }, - { - "examine": "Various animals' bones.", - "durability": null, - "name": "Animals' bones", - "archery_ticket_price": "0", - "id": "6904" - }, - { - "examine": "Various animals' bones.", - "durability": null, - "name": "Animals' bones", - "archery_ticket_price": "0", - "id": "6905" - }, - { - "examine": "Various animals' bones.", - "durability": null, - "name": "Animals' bones", - "archery_ticket_price": "0", - "id": "6906" - }, - { - "examine": "Various animals' bones.", - "durability": null, - "name": "Animals' bones", - "archery_ticket_price": "0", - "id": "6907" - }, - { - "requirements": "{6,45}", - "shop_price": "303030030", - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A beginner level wand", - "walk_anim": "1146", - "durability": null, - "weight": "0.2", - "turn90ccw_anim": "822", - "weapon_interface": "1", - "turn180_anim": "820", - "render_anim": "1426", - "equipment_slot": "3", - "grand_exchange_price": "11500", - "stand_anim": "809", - "name": "Beginner wand", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6908", - "stand_turn_anim": "823", - "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "11500", - "durability": null, - "name": "Beginner wand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6909" - }, - { - "requirements": "{6,50}", - "ge_buy_limit": "10", - "shop_price": "606060060", - "turn90cw_anim": "821", - "examine": "An apprentice level wand.", - "walk_anim": "1146", - "durability": null, - "turn90ccw_anim": "822", - "weapon_interface": "1", - "turn180_anim": "820", - "defence_anim": "420", - "render_anim": "1426", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "82000", - "stand_anim": "809", - "tradeable": "true", - "name": "Apprentice wand", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6910", - "stand_turn_anim": "823", - "bonuses": "0,0,0,10,0,0,0,0,10,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "82000", - "durability": null, - "name": "Apprentice wand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6911" - }, - { - "requirements": "{6,55}", - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A teacher level wand.", - "walk_anim": "1146", - "durability": null, - "turn90ccw_anim": "822", - "weapon_interface": "1", - "turn180_anim": "820", - "render_anim": "1426", - "equipment_slot": "3", - "grand_exchange_price": "944100", - "stand_anim": "809", - "name": "Teacher wand", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6912", - "stand_turn_anim": "823", - "bonuses": "0,0,0,15,0,0,0,0,15,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "944100", - "durability": null, - "name": "Teacher wand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6913" - }, - { - "requirements": "{6,60}", - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A master level wand.", - "walk_anim": "1146", - "durability": null, - "weight": "0.1", - "turn90ccw_anim": "822", - "weapon_interface": "1", - "turn180_anim": "820", - "defence_anim": "420", - "render_anim": "1426", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "1700000", - "stand_anim": "8980", - "tradeable": "true", - "name": "Master wand", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6914", - "stand_turn_anim": "823", - "bonuses": "0,0,0,20,0,0,0,0,20,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1700000", - "durability": null, - "name": "Master wand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6915" - }, - { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", - "examine": "Mystical robes.", - "durability": null, - "weight": "2.2", - "absorb": "3,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "3300000", - "name": "Infinity top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6916", - "bonuses": "0,0,0,22,0,0,0,0,22,0,20,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "3300000", - "durability": null, - "name": "Infinity top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6917" - }, - { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", - "examine": "A mystic hat.", - "durability": null, - "weight": "0.4", - "absorb": "1,0,0", - "equipment_slot": "0", - "grand_exchange_price": "1800000", - "name": "Infinity hat", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "6918", - "bonuses": "0,0,0,6,0,0,0,0,6,0,7,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1800000", - "durability": null, - "name": "Infinity hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6919" - }, - { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", - "examine": "Mystical boots.", - "durability": null, - "destroy": "true", - "weight": "0.4", - "equipment_slot": "10", - "grand_exchange_price": "1200000", - "name": "Infinity boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6920", - "bonuses": "0,0,0,5,0,0,0,0,5,0,5,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Infinity boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6921" - }, - { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", - "examine": "Mystical gloves.", - "grand_exchange_price": "1900000", - "durability": null, - "name": "Infinity gloves", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "6922", - "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1900000", - "durability": null, - "name": "Infinity gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6923" - }, - { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", - "examine": "Mystical robes.", - "durability": null, - "weight": "1.8", - "absorb": "2,1,0", - "equipment_slot": "7", - "grand_exchange_price": "2500000", - "name": "Infinity bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6924", - "bonuses": "0,0,0,17,0,0,0,0,17,0,17,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "2500000", - "durability": null, - "name": "Infinity bottoms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6925" - }, - { - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "983", - "durability": null, - "name": "Bones to peaches", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6926" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A severed hand covered with sand.", - "durability": null, - "name": "Sandy hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6945" - }, - { - "destroy_message": "Speak to the Guard Captain in Yanille to get another beer soaked hand.", - "examine": "A severed hand dripping with beer.", - "durability": null, - "name": "Beer-soaked hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6946" - }, - { - "destroy_message": "Speak to Bert to replace this Rota.", - "examine": "A copy of a work rota.", - "durability": null, - "name": "Bert's rota", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6947" - }, - { - "destroy_message": "Search Sandy's office in Brimhaven for another Rota.", - "examine": "An original work rota.", - "durability": null, - "name": "Sandy's rota", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6948" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Maybe I should read this...", - "durability": null, - "name": "A magic scroll", - "archery_ticket_price": "0", - "id": "6949" - }, - { - "destroy_message": "Speak to Zavistic Rarve to replace your magical orb.", - "examine": "An ordinary looking scrying orb.", - "durability": null, - "name": "Magical orb", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6950" - }, - { - "destroy_message": "Speak to Zavistic Rarve to replace your magical orb.", - "examine": "This magical scrying orb pulsates as it stores information.", - "durability": null, - "name": "Magical orb (a)", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6951" - }, - { - "destroy_message": "Speak to Betty in Port Sarim to get another bottle of truth serum.", - "examine": "Fluid sloshes innocently in this vial.", - "durability": null, - "name": "Truth serum", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6952" - }, - { - "destroy_message": "Speak to Betty in Port Sarim to get another bottle.", - "examine": "A bottle of water.", - "durability": null, - "name": "Bottled water", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6953" - }, - { - "destroy_message": "Speak to Betty in Port Sarim to get another bottle to make some more red berry juice.", - "examine": "Redberry juice sloshes around in this vial, waiting for white berries to be added.", - "durability": null, - "name": "Redberry juice", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6954" - }, - { - "destroy_message": "Speak to Betty in Port Sarim to make more pink dye.", - "shop_price": "20", - "examine": "A vial of pink dye.", - "durability": null, - "name": "Pink dye", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6955" - }, - { - "destroy_message": "Speak to Betty in Port Sarim to get another bottle to make the lens again.", - "examine": "This lens has a pinkish tinge to it.", - "durability": null, - "name": "Rose-tinted lens", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6956", - "equipment_slot": "3" - }, - { - "destroy_message": "Speak to Mazion near the sandpit on Entrana to find the Wizard Head again.", - "examine": "A decapitated, sand-covered head.", - "durability": null, - "name": "Wizard's head", - "tradeable": "false", - "destroy": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "6957" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A handful of sand from Sandy's pocket.", - "durability": null, - "name": "Sand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6958" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Use pink dye on a cape and this is what you get!", - "durability": null, - "destroy": "true", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "919", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6959", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "919", - "durability": null, - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6960" - }, - { - "examine": "A freshly baked baguette.", - "durability": null, - "name": "Baguette", - "archery_ticket_price": "0", - "id": "6961" - }, - { "ge_buy_limit": "1000", - "examine": "A freshly made triangle sandwich.", - "grand_exchange_price": "110", - "durability": null, - "name": "Triangle sandwich", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6962" + "weight": "0.5" }, { - "examine": "A freshly made roll.", + "id": "6813", + "name": "Granite legs", + "archery_ticket_price": "0", "durability": null, - "name": "Roll", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6963" + "tradeable": "true" }, { - "bankable": "false", - "examine": "Lovely money!", + "id": "6814", + "name": "Fur", + "examine": "This would make warm clothing.", + "archery_ticket_price": "0", "durability": null, - "name": "Coins", - "archery_ticket_price": "0", - "id": "6964" - }, - { - "examine": "A freshly made square sandwich.", - "durability": null, - "name": "Square sandwich", - "archery_ticket_price": "0", - "id": "6965" - }, - { - "lendable": "true", - "examine": "Makes the wearer pretty intimidating.", - "grand_exchange_price": "61200", - "durability": null, - "name": "Dragon med helm", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "6967", - "absorb": "2,0,4", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { - "durability": null, - "name": "Triangle sandwich", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6968" - }, - { - "examine": "I'd better be careful eating this.", - "grand_exchange_price": "694", - "durability": null, - "name": "Shark", - "tradeable": "true", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "6969" - }, - { - "examine": "It's a solid gold pyramid!", - "durability": null, - "name": "Pyramid top", - "weight": "11", - "archery_ticket_price": "0", - "id": "6970" - }, - { "ge_buy_limit": "100", - "examine": "A large chunk of sandstone.", - "grand_exchange_price": "4", - "durability": null, - "name": "Sandstone (1kg)", + "shop_price": "20", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6971" + "weight": "0.4" }, { + "id": "6815", + "name": "Fur", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "4", - "durability": null, - "name": "Sandstone (1kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6972" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A large chunk of sandstone.", - "grand_exchange_price": "27", - "durability": null, - "name": "Sandstone (2kg)", - "tradeable": "true", - "weight": "1", + "id": "6816", + "name": "Wyvern bones", "archery_ticket_price": "0", - "id": "6973" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "27", - "durability": null, - "name": "Sandstone (2kg)", - "tradeable": "true", + "id": "6817", + "name": "Slender blade", + "examine": "A slender two-handed sword.", "archery_ticket_price": "0", - "id": "6974" - }, - { - "ge_buy_limit": "100", - "examine": "A large chunk of sandstone.", - "grand_exchange_price": "30", - "durability": null, - "name": "Sandstone (5kg)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6975" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "30", - "durability": null, - "name": "Sandstone (5kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6976" - }, - { - "ge_buy_limit": "100", - "examine": "A large chunk of sandstone.", - "grand_exchange_price": "120", - "durability": null, - "name": "Sandstone (10kg)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6977" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "120", - "durability": null, - "name": "Sandstone (10kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6978" - }, - { - "ge_buy_limit": "10000", - "examine": "See article", - "grand_exchange_price": "304", - "durability": null, - "name": "Granite (500g)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6979" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "304", - "durability": null, - "name": "Granite (500g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6980" - }, - { - "ge_buy_limit": "10000", - "examine": "See article", - "grand_exchange_price": "845", - "durability": null, - "name": "Granite (2kg)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6981" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "845", - "durability": null, - "name": "Granite (2kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6982" - }, - { - "ge_buy_limit": "10000", - "examine": "See article", - "grand_exchange_price": "2153", - "durability": null, - "name": "Granite (5kg)", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6983", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2153", - "durability": null, - "name": "Granite (5kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6984" - }, - { - "destroy_message": "You can make another 20 kg block by giving enough stone to Lazim.", - "examine": "A huge twenty-kilo block of sandstone.", - "durability": null, - "name": "Sandstone (20kg)", "destroy": "true", - "weight": "20", - "archery_ticket_price": "0", - "id": "6985" - }, - { - "destroy_message": "You can make another 32 kg sandstone block by giving enough stone to Lazim.", - "examine": "A huge thirty-two-kilo block of sandstone.", - "durability": null, - "name": "Sandstone (32kg)", - "destroy": "true", - "weight": "32", - "archery_ticket_price": "0", - "id": "6986" - }, - { - "destroy_message": "You can make another statue body by giving Lazim 20 kg of stone and then crafting it.", - "examine": "The body of a sandstone statue.", - "durability": null, - "name": "Sandstone body", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6987" - }, - { - "destroy_message": "You can make another statue base by giving Lazim 32 kg of stone and then crafting it.", - "examine": "The base and legs of a sandstone statue.", - "durability": null, - "name": "Sandstone base", - "destroy": "true", - "weight": "32", - "archery_ticket_price": "0", - "id": "6988" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A metal sigil in the shape of a Z.", "durability": null, - "name": "Z sigil", - "archery_ticket_price": "0", - "id": "6993" + "equipment_slot": "3", + "render_anim": "124", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "false", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weight": "3" }, { + "id": "6818", + "name": "Bow-sword", + "examine": "A sharp sword that can also fire arrows.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make a replacement.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weight": "3" + }, + { + "id": "6819", + "name": "Large pouch", + "examine": "A large pouch used for storing essence.", + "archery_ticket_price": "0", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A metal sigil in the shape of an M.", "durability": null, - "name": "M sigil", - "archery_ticket_price": "0", - "id": "6994" + "shop_price": "25000", + "weight": "1" }, { + "id": "6820", + "name": "Relic", + "examine": "It seems to have pieces missing. After Temple at Senntisten quest: The assassin's plunder from the church on Entrana.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can get another from the assassin beside the winch that leads down into Senntisten temple.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6821", + "name": "Orb", + "examine": "This looks valuable.", + "archery_ticket_price": "0", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A metal sigil in the shape of an R.", - "durability": null, - "name": "R sigil", - "archery_ticket_price": "0", - "id": "6995" + "durability": null }, { + "id": "6822", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6823", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6824", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6825", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6826", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6827", + "name": "Star bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6828", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6829", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6830", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6831", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6832", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6833", + "name": "Box bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6834", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6835", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6836", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6837", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6838", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6839", + "name": "Diamond bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6840", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6841", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6842", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6843", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6844", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6845", + "name": "Tree bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6846", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6847", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6848", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6849", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6850", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6851", + "name": "Bell bauble", + "examine": "See individual articles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6852", + "name": "Puppet box", + "examine": "A box for storing completed puppets.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6853", + "name": "Bauble box", + "examine": "A box for storing painted baubles.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6854", + "name": "Puppet box", + "examine": "A box for storing completed puppets.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A metal sigil in the shape of a K.", "durability": null, - "name": "K sigil", - "archery_ticket_price": "0", - "id": "6996" + "tradeable": "false" }, { - "destroy_message": "You can get the statue's left arm back from Lazim.", - "examine": "The left arm of a large stone statue.", - "durability": null, - "name": "Stone left arm", - "tradeable": "false", + "id": "6855", + "name": "Bauble box", + "examine": "A box for storing painted baubles.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6856", + "name": "Bobble hat", + "examine": "A woolly bobble hat.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "6997" - }, - { - "destroy_message": "You can get the statue's right arm back from Lazim.", - "examine": "The right arm of a large stone statue.", + "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", "durability": null, - "name": "Stone right arm", + "equipment_slot": "0", "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "6998" + "weight": "0.25" }, { - "destroy_message": "You can get the statue's left leg back from Lazim.", - "examine": "The left leg of a large stone statue.", + "id": "6857", + "name": "Bobble scarf", + "examine": "A woolly scarf.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, - "name": "Stone left leg", + "equipment_slot": "2", + "weight": "0.25" + }, + { + "id": "6858", + "name": "Jester hat", + "examine": "A woolly Jester hat.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", + "durability": null, + "equipment_slot": "0", + "shop_price": "160", "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "6999" + "weight": "0.25" }, { - "destroy_message": "You can get the statue's right leg back from Lazim.", - "examine": "The right leg of a large stone statue.", + "id": "6859", + "name": "Jester scarf", + "examine": "A woolly jester scarf.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Jester scarf from Diango.", "durability": null, - "name": "Stone right leg", + "equipment_slot": "2", "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "7000" + "weight": "0.25" }, { - "destroy_message": "You can make another positive mould by using soft clay on the pedestal in Enakhra's temple.", - "examine": "A positive clay mould of a camel's head.", + "id": "6860", + "name": "Tri-jester hat", + "examine": "A woolly triple bobble jester hat.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You may get another from Diango in Draynor Village.", "durability": null, - "name": "Camel mould (p)", + "equipment_slot": "0", "tradeable": "false", + "weight": "0.25" + }, + { + "id": "6861", + "name": "Tri-jester scarf", + "examine": "A woolly jester scarf.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7001" + "destroy_message": "You can obtain another jester scarf from Diango in the Draynor Market.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "0.25" }, { - "remove_head": "true", - "examine": "Blend in in the desert.", - "durability": null, - "name": "Camel mask", - "weight": "0.5", + "id": "6862", + "name": "Woolly hat", + "examine": "A woolly tobogganing hat.", "archery_ticket_price": "0", - "id": "7003", - "equipment_slot": "0" + "destroy": "true", + "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.25" }, { - "shop_price": "14", - "examine": "Good for detailed crafting.", - "grand_exchange_price": "33", - "durability": null, - "name": "Chisel", - "tradeable": "true", - "weight": "0.4", + "id": "6863", + "name": "Woolly scarf", + "examine": "A woolly tobogganing scarf.", "archery_ticket_price": "0", - "id": "7004", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "0.25" + }, + { + "id": "6864", + "name": "Marionette handle", + "examine": "The controlling part of a marionette.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "3" }, { + "id": "6865", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6866", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6867", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6868", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6869", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6870", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6871", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6872", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6873", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6874", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6875", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6876", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6877", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6878", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6879", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6880", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6881", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6882", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "6883", + "name": "Peach", + "examine": "A tasty fruit.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6885", + "name": "Progress hat", + "examine": "A magic training arena progress hat", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "0" + }, + { + "id": "6886", + "name": "Progress hat", + "examine": "A magic training arena progress hat", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "0" + }, + { + "id": "6887", + "name": "Progress hat", + "examine": "A magic training arena progress hat", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "0" + }, + { + "id": "6888", + "name": "Guardian statue", + "examine": "A guardian of the arena.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6889", + "name": "Mage's book", + "examine": "The magical book of the Mage.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "requirements": "{6,60}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6890", + "name": "Mage's book", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6891", + "name": "Arena book", + "examine": "A book about the Training Arena.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "200", + "weight": "1" + }, + { + "id": "6893", + "name": "Leather boots", + "examine": "Comfortable leather boots.", + "archery_ticket_price": "0", + "bankable": "false", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "6", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "6894", + "name": "Adamant kiteshield", + "examine": "A large metal shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bankable": "false", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "tradeable": "true", + "weight": "5.8" + }, + { + "id": "6895", + "name": "Adamant med helm", + "examine": "A medium sized helmet.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bankable": "false", + "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0", + "durability": null, + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "6896", + "name": "Emerald", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "6897", + "name": "Rune longsword", + "examine": "A razor sharp longsword", + "archery_ticket_price": "0", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bankable": "false", + "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0", + "durability": null, + "lendable": "true", + "shop_price": "32000", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "6898", + "name": "Cylinder", + "examine": "A green cylinder", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6899", + "name": "Cube", + "examine": "A yellow cube", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6900", + "name": "Icosahedron", + "examine": "A blue icosahedron.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6901", + "name": "Pentamid", + "examine": "A red pentamid.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6902", + "name": "Orb", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6903", + "name": "Dragonstone", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "6904", + "name": "Animals' bones", + "examine": "Various animals' bones.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6905", + "name": "Animals' bones", + "examine": "Various animals' bones.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6906", + "name": "Animals' bones", + "examine": "Various animals' bones.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6907", + "name": "Animals' bones", + "examine": "Various animals' bones.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6908", + "name": "Beginner wand", + "examine": "A beginner level wand", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "1426", + "requirements": "{6,45}", + "run_anim": "1210", + "shop_price": "303030030", + "stand_anim": "809", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1", + "weight": "0.2" + }, + { + "id": "6909", + "name": "Beginner wand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6910", + "name": "Apprentice wand", + "examine": "An apprentice level wand.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "bonuses": "0,0,0,10,0,0,0,0,10,0,0,0,0,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "1426", + "requirements": "{6,50}", + "run_anim": "1210", + "shop_price": "606060060", + "stand_anim": "809", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1" + }, + { + "id": "6911", + "name": "Apprentice wand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6912", + "name": "Teacher wand", + "examine": "A teacher level wand.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,15,0,0,0,0,15,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "1426", + "requirements": "{6,55}", + "run_anim": "1210", + "stand_anim": "809", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1" + }, + { + "id": "6913", + "name": "Teacher wand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6914", + "name": "Master wand", + "examine": "A master level wand.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "bonuses": "0,0,0,20,0,0,0,0,20,0,0,0,0,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "1426", + "requirements": "{6,60}", + "run_anim": "1210", + "stand_anim": "8980", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1", + "weight": "0.1" + }, + { + "id": "6915", + "name": "Master wand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6916", + "name": "Infinity top", + "examine": "Mystical robes.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,22,0,0,0,0,22,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "6917", + "name": "Infinity top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6918", + "name": "Infinity hat", + "examine": "A mystic hat.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "hat": true, + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "6919", + "name": "Infinity hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6920", + "name": "Infinity boots", + "examine": "Mystical boots.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,0,0,0,5,0,5,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "10", + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "6921", + "name": "Infinity boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6922", + "name": "Infinity gloves", + "examine": "Mystical gloves.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "10", + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "6923", + "name": "Infinity gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6924", + "name": "Infinity bottoms", + "examine": "Mystical robes.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,17,0,0,0,0,17,0,17,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "6925", + "name": "Infinity bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "6926", + "name": "Bones to peaches", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6945", + "name": "Sandy hand", + "examine": "A severed hand covered with sand.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6946", + "name": "Beer-soaked hand", + "examine": "A severed hand dripping with beer.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to the Guard Captain in Yanille to get another beer soaked hand.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6947", + "name": "Bert's rota", + "examine": "A copy of a work rota.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Bert to replace this Rota.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6948", + "name": "Sandy's rota", + "examine": "An original work rota.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Search Sandy's office in Brimhaven for another Rota.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6949", + "name": "A magic scroll", + "examine": "Maybe I should read this...", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6950", + "name": "Magical orb", + "examine": "An ordinary looking scrying orb.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Zavistic Rarve to replace your magical orb.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6951", + "name": "Magical orb (a)", + "examine": "This magical scrying orb pulsates as it stores information.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Zavistic Rarve to replace your magical orb.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6952", + "name": "Truth serum", + "examine": "Fluid sloshes innocently in this vial.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Betty in Port Sarim to get another bottle of truth serum.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6953", + "name": "Bottled water", + "examine": "A bottle of water.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Betty in Port Sarim to get another bottle.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6954", + "name": "Redberry juice", + "examine": "Redberry juice sloshes around in this vial, waiting for white berries to be added.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Betty in Port Sarim to get another bottle to make some more red berry juice.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6955", + "name": "Pink dye", + "examine": "A vial of pink dye.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Betty in Port Sarim to make more pink dye.", + "durability": null, + "shop_price": "20", + "tradeable": "false" + }, + { + "id": "6956", + "name": "Rose-tinted lens", + "examine": "This lens has a pinkish tinge to it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Betty in Port Sarim to get another bottle to make the lens again.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "6957", + "name": "Wizard's head", + "examine": "A decapitated, sand-covered head.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Speak to Mazion near the sandpit on Entrana to find the Wizard Head again.", + "durability": null, + "tradeable": "false", + "weight": "3" + }, + { + "id": "6958", + "name": "Sand", + "examine": "A handful of sand from Sandy's pocket.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6959", + "name": "Cape", + "examine": "Use pink dye on a cape and this is what you get!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "6960", + "name": "Cape", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6961", + "name": "Baguette", + "examine": "A freshly baked baguette.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6962", + "name": "Triangle sandwich", + "examine": "A freshly made triangle sandwich.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6963", + "name": "Roll", + "examine": "A freshly made roll.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.5" + }, + { + "id": "6964", + "name": "Coins", + "examine": "Lovely money!", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null + }, + { + "id": "6965", + "name": "Square sandwich", + "examine": "A freshly made square sandwich.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6967", + "name": "Dragon med helm", + "examine": "Makes the wearer pretty intimidating.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "durability": null, + "lendable": "true", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "6968", + "name": "Triangle sandwich", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6969", + "name": "Shark", + "examine": "I'd better be careful eating this.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.6" + }, + { + "id": "6970", + "name": "Pyramid top", + "examine": "It's a solid gold pyramid!", + "archery_ticket_price": "0", + "durability": null, + "weight": "11" + }, + { + "id": "6971", + "name": "Sandstone (1kg)", + "examine": "A large chunk of sandstone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6972", + "name": "Sandstone (1kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6973", + "name": "Sandstone (2kg)", + "examine": "A large chunk of sandstone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6974", + "name": "Sandstone (2kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6975", + "name": "Sandstone (5kg)", + "examine": "A large chunk of sandstone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6976", + "name": "Sandstone (5kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6977", + "name": "Sandstone (10kg)", + "examine": "A large chunk of sandstone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6978", + "name": "Sandstone (10kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6979", + "name": "Granite (500g)", + "examine": "See article", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "6980", + "name": "Granite (500g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6981", + "name": "Granite (2kg)", + "examine": "See article", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "6982", + "name": "Granite (2kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6983", + "name": "Granite (5kg)", + "examine": "See article", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "6984", + "name": "Granite (5kg)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "6985", + "name": "Sandstone (20kg)", + "examine": "A huge twenty-kilo block of sandstone.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another 20 kg block by giving enough stone to Lazim.", + "durability": null, + "weight": "20" + }, + { + "id": "6986", + "name": "Sandstone (32kg)", + "examine": "A huge thirty-two-kilo block of sandstone.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another 32 kg sandstone block by giving enough stone to Lazim.", + "durability": null, + "weight": "32" + }, + { + "id": "6987", + "name": "Sandstone body", + "examine": "The body of a sandstone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another statue body by giving Lazim 20 kg of stone and then crafting it.", + "durability": null + }, + { + "id": "6988", + "name": "Sandstone base", + "examine": "The base and legs of a sandstone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another statue base by giving Lazim 32 kg of stone and then crafting it.", + "durability": null, + "weight": "32" + }, + { + "id": "6993", + "name": "Z sigil", + "examine": "A metal sigil in the shape of a Z.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6994", + "name": "M sigil", + "examine": "A metal sigil in the shape of an M.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6995", + "name": "R sigil", + "examine": "A metal sigil in the shape of an R.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6996", + "name": "K sigil", + "examine": "A metal sigil in the shape of a K.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "6997", + "name": "Stone left arm", + "examine": "The left arm of a large stone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the statue's left arm back from Lazim.", + "durability": null, + "tradeable": "false", + "weight": "9" + }, + { + "id": "6998", + "name": "Stone right arm", + "examine": "The right arm of a large stone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the statue's right arm back from Lazim.", + "durability": null, + "tradeable": "false", + "weight": "9" + }, + { + "id": "6999", + "name": "Stone left leg", + "examine": "The left leg of a large stone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the statue's left leg back from Lazim.", + "durability": null, + "tradeable": "false", + "weight": "9" + }, + { + "id": "7000", + "name": "Stone right leg", + "examine": "The right leg of a large stone statue.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the statue's right leg back from Lazim.", + "durability": null, + "tradeable": "false", + "weight": "9" + }, + { + "id": "7001", + "name": "Camel mould (p)", + "examine": "A positive clay mould of a camel's head.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another positive mould by using soft clay on the pedestal in Enakhra's temple.", + "durability": null, + "tradeable": "false", + "weight": "0.5" + }, + { + "id": "7003", + "name": "Camel mask", + "examine": "Blend in in the desert.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "0.5" + }, + { + "id": "7004", + "name": "Chisel", + "examine": "Good for detailed crafting.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "shop_price": "14", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "7051", + "name": "Unlit bug lantern", + "examine": "A lantern to aid attacking Harpie bugs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", "requirements": "{18,33}-{11,33}", "shop_price": "130", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "7052", + "name": "Unlit bug lantern", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7053", + "name": "Lit bug lantern", "examine": "A lantern to aid attacking Harpie bugs.", - "grand_exchange_price": "466", - "durability": null, - "name": "Unlit bug lantern", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "7051", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "466", "durability": null, - "name": "Unlit bug lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7052" - }, - { + "equipment_slot": "5", "requirements": "{18,33}", "shop_price": "130", - "examine": "A lantern to aid attacking Harpie bugs.", - "durability": null, - "name": "Lit bug lantern", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "7053", - "equipment_slot": "5" + "weight": "2.2" }, { - "ge_buy_limit": "10000", + "id": "7054", + "name": "Chilli potato", "examine": "A baked potato with chilli con carne", - "grand_exchange_price": "415", - "durability": null, - "name": "Chilli potato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7054" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "415", - "durability": null, + "id": "7055", "name": "Chilli potato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7055" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "7056", + "name": "Egg potato", "examine": "A baked potato with egg and tomato.", - "grand_exchange_price": "441", - "durability": null, - "name": "Egg potato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7056" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "441", - "durability": null, + "id": "7057", "name": "Egg potato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7057" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "7058", + "name": "Mushroom potato", "examine": "A baked potato with mushroom and onions.", - "grand_exchange_price": "1165", - "durability": null, - "name": "Mushroom potato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7058" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1165", - "durability": null, + "id": "7059", "name": "Mushroom potato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7059" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "7060", + "name": "Tuna potato", "examine": "A baked potato with tuna and sweetcorn.", - "grand_exchange_price": "2134", - "durability": null, - "name": "Tuna potato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7060" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "2134", - "durability": null, - "name": "Tuna potato", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7061" + "weight": "0.5" }, { - "ge_buy_limit": "1000", + "id": "7061", + "name": "Tuna potato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7062", + "name": "Chilli con carne", "examine": "A bowl of meat in chilli-con-carne sauce.", - "grand_exchange_price": "215", - "durability": null, - "name": "Chilli con carne", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7062" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "215", - "durability": null, + "id": "7063", "name": "Chilli con carne", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7063" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7064", + "name": "Egg and tomato", "examine": "A bowl of scrambled eggs and tomato", - "grand_exchange_price": "126", - "durability": null, - "name": "Egg and tomato", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7064" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "126", - "durability": null, + "id": "7065", "name": "Egg and tomato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7065" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7066", + "name": "Mushroom & onion", "examine": "A bowl of fried mushroom and onions.", - "grand_exchange_price": "368", - "durability": null, - "name": "Mushroom & onion", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7066" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "368", - "durability": null, - "name": "Mushroom & onion", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7067" + "weight": "0.5" }, { - "ge_buy_limit": "10000", + "id": "7067", + "name": "Mushroom & onion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7068", + "name": "Tuna and corn", "examine": "A bowl of cooked tuna and sweetcorn.", - "grand_exchange_price": "514", - "durability": null, - "name": "Tuna and corn", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7068" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "514", - "durability": null, - "name": "Tuna and corn", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7069" + "weight": "0.5" }, { - "ge_buy_limit": "1000", + "id": "7069", + "name": "Tuna and corn", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7070", + "name": "Minced meat", "examine": "A bowl of finely minced meat.", - "grand_exchange_price": "22", - "durability": null, - "name": "Minced meat", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7070" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "22", - "durability": null, + "id": "7071", "name": "Minced meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7071" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7072", + "name": "Spicy sauce", "examine": "A bowl of spicy sauce.", - "grand_exchange_price": "70", - "durability": null, - "name": "Spicy sauce", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7072" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "70", - "durability": null, + "id": "7073", "name": "Spicy sauce", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7073" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7074", + "name": "Chopped garlic", "examine": "A bowl of chopped garlic.", - "grand_exchange_price": "39", - "durability": null, - "name": "Chopped garlic", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7074" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "39", - "durability": null, + "id": "7075", "name": "Chopped garlic", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7075" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7076", + "name": "Uncooked egg", "examine": "A bowl of uncooked egg.", - "grand_exchange_price": "34", - "durability": null, - "name": "Uncooked egg", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7076" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "34", - "durability": null, + "id": "7077", "name": "Uncooked egg", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7077" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7078", + "name": "Scrambled egg", "examine": "A bowl of scrambled egg.", - "grand_exchange_price": "24", - "durability": null, - "name": "Scrambled egg", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7078" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "24", - "durability": null, + "id": "7079", "name": "Scrambled egg", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7079" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7080", + "name": "Sliced mushrooms", "examine": "A bowl of sliced Bittercap mushrooms.", - "grand_exchange_price": "149", - "durability": null, - "name": "Sliced mushrooms", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7080" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "149", - "durability": null, + "id": "7081", "name": "Sliced mushrooms", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7081" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7082", + "name": "Fried mushrooms", "examine": "A bowl of fried Bittercap mushrooms.", - "grand_exchange_price": "189", - "durability": null, - "name": "Fried mushrooms", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7082" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "189", - "durability": null, + "id": "7083", "name": "Fried mushrooms", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7083" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7084", + "name": "Fried onions", "examine": "A bowl of sliced, fried onions.", - "grand_exchange_price": "40", - "durability": null, - "name": "Fried onions", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7084" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "40", - "durability": null, + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7085", "name": "Fried onions", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7085" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "7086", + "name": "Chopped tuna", "examine": "A bowl of finely chopped tuna.", - "grand_exchange_price": "233", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7087", "name": "Chopped tuna", - "tradeable": "true", - "weight": "0.5", "archery_ticket_price": "0", - "id": "7086" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "233", - "durability": null, - "name": "Chopped tuna", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7087" - }, - { - "ge_buy_limit": "10000", - "examine": "Raw sweetcorn.", - "grand_exchange_price": "110", - "durability": null, + "id": "7088", "name": "Sweetcorn", - "tradeable": "true", + "examine": "A bowl of cooked sweetcorn.", "archery_ticket_price": "0", - "id": "7088" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "110", - "durability": null, - "name": "Sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7089" + "tradeable": "true" }, { + "id": "7089", + "name": "Sweetcorn", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7091", "name": "Burnt egg", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7091" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7093", "name": "Burnt onion", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7093" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7095", "name": "Burnt mushroom", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7095" + "durability": null, + "tradeable": "true" }, { - "examine": "Best keep this away from naked flames.", - "durability": null, + "id": "7108", "name": "Gunpowder", - "tradeable": "false", - "destroy": "true", + "examine": "Best keep this away from naked flames.", "archery_ticket_price": "0", - "id": "7108" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A fuse.", - "durability": null, + "id": "7109", "name": "Fuse", - "tradeable": "false", - "destroy": "true", + "examine": "A fuse.", "archery_ticket_price": "0", - "id": "7109" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "48", - "examine": "A seaworthy grey shirt.", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "7110", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "48", - "durability": null, "name": "Stripy pirate shirt", - "tradeable": "true", + "examine": "A seaworthy grey shirt.", "archery_ticket_price": "0", - "id": "7111" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "grand_exchange_price": "714", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "weight": "0.1", + "id": "7111", + "name": "Stripy pirate shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7112", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "714", - "durability": null, "name": "Pirate bandana", - "tradeable": "true", + "examine": "Essential pirate wear.", "archery_ticket_price": "0", - "id": "7113" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "shop_price": "350", - "ge_buy_limit": "100", - "examine": "Not for land lubbers.", - "grand_exchange_price": "2530", - "durability": null, - "name": "Pirate boots", - "tradeable": "true", - "weight": "4", + "id": "7113", + "name": "Pirate bandana", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7114", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2530", - "durability": null, "name": "Pirate boots", - "tradeable": "true", + "examine": "Not for land lubbers.", "archery_ticket_price": "0", - "id": "7115" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "4" }, { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "77", - "examine": "Well, okay, they're beige, but they're part of the white pirate clothing set, so...", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", + "id": "7115", + "name": "Pirate boots", "archery_ticket_price": "0", - "weight": "1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7116", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "77", - "durability": null, "name": "Pirate leggings", - "tradeable": "true", + "examine": "Well, okay, they're beige, but they're part of the white pirate clothing set, so...", "archery_ticket_price": "0", - "id": "7117" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" }, { - "examine": "A cannister holding shrapnel.", + "id": "7117", + "name": "Pirate leggings", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7118", "name": "Canister", - "tradeable": "false", - "destroy": "true", + "examine": "A cannister holding shrapnel.", "archery_ticket_price": "0", - "id": "7118" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "For cleaning and packing the cannon.", - "durability": null, + "id": "7120", "name": "Ramrod", - "tradeable": "false", + "examine": "For cleaning and packing the cannon.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "7120" + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "examine": "A plank of wood to repair the hull with.", - "durability": null, + "id": "7121", "name": "Repair plank", + "examine": "A plank of wood to repair the hull with.", "archery_ticket_price": "0", - "id": "7121" + "durability": null }, { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "904", - "examine": "A sea worthy shirt.", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "7122", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "904", - "durability": null, "name": "Stripy pirate shirt", - "tradeable": "true", + "examine": "A sea worthy shirt.", "archery_ticket_price": "0", - "id": "7123" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "grand_exchange_price": "340", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "weight": "0.1", + "id": "7123", + "name": "Stripy pirate shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7124", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "340", - "durability": null, "name": "Pirate bandana", - "tradeable": "true", + "examine": "Essential pirate wear.", "archery_ticket_price": "0", - "id": "7125" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "examine": "A sea worthy pair of trousers.", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", + "id": "7125", + "name": "Pirate bandana", "archery_ticket_price": "0", - "weight": "1", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7126", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "durability": null, "name": "Pirate leggings", - "tradeable": "true", + "examine": "A sea worthy pair of trousers.", "archery_ticket_price": "0", - "id": "7127" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "744", - "examine": "A sea worthy shirt.", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "weight": "1", + "id": "7127", + "name": "Pirate leggings", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "7128", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "744", - "durability": null, "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7129" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "grand_exchange_price": "154", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7130", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "154", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7131" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "253", - "examine": "A sea worthy pair of trousers.", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "1", - "id": "7132", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "253", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7133" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "421", "examine": "A sea worthy shirt.", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "7134", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "421", - "durability": null, + "id": "7129", "name": "Stripy pirate shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7135" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "remove_head": "true", - "ge_buy_limit": "100", + "id": "7130", + "name": "Pirate bandana", "examine": "Essential pirate wear.", - "grand_exchange_price": "469", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "7136", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "469", - "durability": null, + "id": "7131", "name": "Pirate bandana", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7137" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "83", + "id": "7132", + "name": "Pirate leggings", "examine": "A sea worthy pair of trousers.", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "7138", - "equipment_slot": "7" - }, - { + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Pirate leggings", + "shop_price": "350", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7139" + "weight": "1" }, { - "shop_price": "2560", - "examine": "Feels quite lucky.", + "id": "7133", + "name": "Pirate leggings", + "archery_ticket_price": "0", "durability": null, - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "6", - "equipment_slot": "3", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7134", + "name": "Stripy pirate shirt", + "examine": "A sea worthy shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7135", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7136", + "name": "Pirate bandana", + "examine": "Essential pirate wear.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "7137", + "name": "Pirate bandana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7138", + "name": "Pirate leggings", + "examine": "A sea worthy pair of trousers.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7139", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7140", + "name": "Lucky cutlass", + "examine": "Feels quite lucky.", + "archery_ticket_price": "0", "attack_anims": "390,390,381,390", "attack_audios": "2500,2500,2517,2500", - "name": "Lucky cutlass", - "archery_ticket_price": "0", - "id": "7140", - "bonuses": "5,20,0,-5,0,6,6,6,0,0,0,25,0,0,0" - }, - { - "shop_price": "1040", - "examine": "I hope he doesn't want it back.", - "durability": null, - "name": "Harry's cutlass", - "weight": "1", - "archery_ticket_price": "0", "attack_speed": "4", - "weapon_interface": "6", - "id": "7141", - "bonuses": "3,14,0,-5,0,4,4,4,0,0,0,22,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "25600", - "examine": "The very butcher of a silk button.", + "bonuses": "5,20,0,-5,0,6,6,6,0,0,0,25,0,0,0", "durability": null, - "name": "Rapier", - "weight": "1", - "archery_ticket_price": "0", - "attack_speed": "4", - "weapon_interface": "5", - "id": "7142", - "bonuses": "45,7,-2,0,0,0,1,0,0,0,0,44,0,0,0", - "render_anim": "2622", - "equipment_slot": "3" - }, - { - "examine": "Looks valuable.", - "durability": null, - "name": "Plunder", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7143" - }, - { - "examine": "By Cap'n Hook-Hand Morrisane.", - "durability": null, - "name": "Book o' piracy", - "archery_ticket_price": "0", - "id": "7144" - }, - { - "examine": "A working cannon barrel.", - "durability": null, - "name": "Cannon barrel", - "weight": "32", - "archery_ticket_price": "0", - "id": "7145" - }, - { - "examine": "Not likely to work again.", - "durability": null, - "name": "Broken cannon", - "weight": "32", - "archery_ticket_price": "0", - "id": "7146" - }, - { - "examine": "A plank of wood to repair the hull with.", - "durability": null, - "name": "Repair plank", - "archery_ticket_price": "0", - "id": "7148" - }, - { - "examine": "A cannister holding shrapnel.", - "durability": null, - "name": "Canister", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7149" - }, - { - "examine": "Useful for pinning up paintings.", - "durability": null, - "name": "Tacks", - "archery_ticket_price": "0", - "id": "7150" - }, - { - "shop_price": "18", - "examine": "A coil of rope.", - "grand_exchange_price": "101", - "durability": null, - "name": "Rope", - "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "7155" - }, - { - "shop_price": "1", - "examine": "Useful for lighting a fire.", - "grand_exchange_price": "121", - "durability": null, - "name": "Tinderbox", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "7156" - }, - { - "shop_price": "30", - "examine": "I think it is eating through the bottle.", - "durability": null, - "name": "Braindeath 'rum", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7157" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7044", - "examine": "A two-handed dragon sword.", - "walk_anim": "7046", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "1000000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "7158", - "stand_turn_anim": "7040", - "bonuses": "-4,92,80,-4,0,0,0,0,0,-1,0,93,0,0,0", - "requirements": "{0,60}", - "durability": null, - "weight": "3", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "2503,0,2504,0", - "name": "Dragon 2h sword" + "shop_price": "2560", + "weapon_interface": "6", + "weight": "1.8" }, { + "id": "7141", + "name": "Harry's cutlass", + "examine": "I hope he doesn't want it back.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,14,0,-5,0,4,4,4,0,0,0,22,0,0,0", + "durability": null, + "equipment_slot": "3", + "shop_price": "1040", + "weapon_interface": "6", + "weight": "1" + }, + { + "id": "7142", + "name": "Rapier", + "examine": "The very butcher of a silk button.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "45,7,-2,0,0,0,1,0,0,0,0,44,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "2622", + "shop_price": "25600", + "weapon_interface": "5", + "weight": "1" + }, + { + "id": "7143", + "name": "Plunder", + "examine": "Looks valuable.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7144", + "name": "Book o' piracy", + "examine": "By Cap'n Hook-Hand Morrisane.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7145", + "name": "Cannon barrel", + "examine": "A working cannon barrel.", + "archery_ticket_price": "0", + "durability": null, + "weight": "32" + }, + { + "id": "7146", + "name": "Broken cannon", + "examine": "Not likely to work again.", + "archery_ticket_price": "0", + "durability": null, + "weight": "32" + }, + { + "id": "7148", + "name": "Repair plank", + "examine": "A plank of wood to repair the hull with.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7149", + "name": "Canister", + "examine": "A cannister holding shrapnel.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7150", + "name": "Tacks", + "examine": "Useful for pinning up paintings.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7155", + "name": "Rope", + "examine": "A coil of rope.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "18", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "7156", + "name": "Tinderbox", + "examine": "Useful for lighting a fire.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "7157", + "name": "Braindeath 'rum", + "examine": "I think it is eating through the bottle.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "30", + "weight": "0.9" + }, + { + "id": "7158", + "name": "Dragon 2h sword", + "examine": "A two-handed dragon sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", + "attack_speed": "7", + "bonuses": "-4,92,80,-4,0,0,0,0,0,-1,0,93,0,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "124", + "requirements": "{0,60}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3" + }, + { + "id": "7159", + "name": "Insulated boots", + "examine": "They're heavily insulated wellies.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,0,0,1,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", "requirements": "{18,37}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "They're heavily insulated wellies.", - "durability": null, - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "269", - "name": "Insulated boots", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7159", - "bonuses": "0,0,0,0,0,1,1,0,0,1,0,0,0,0,0" + "weight": "1.3" }, { - "examine": "Before being attacked: A ball of Electrical energy.", - "durability": null, + "id": "7160", "name": "Killerwatt", + "examine": "Before being attacked: A ball of Electrical energy.", "archery_ticket_price": "0", "attack_speed": "2", - "id": "7160" + "durability": null }, { - "durability": null, + "id": "7161", "name": "Insulated boots", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7161" + "durability": null, + "tradeable": "true" }, { - "shop_price": "5", - "ge_buy_limit": "100", + "id": "7162", + "name": "Pie recipe book", "examine": "Lots of pie recipes for me to try.", - "grand_exchange_price": "62", - "durability": null, - "name": "Pie recipe book", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7162" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "62", - "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "7163", "name": "Pie recipe book", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7163" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "54", - "ge_buy_limit": "1000", + "id": "7164", + "name": "Part mud pie", "examine": "Still needs two more ingredients.", - "grand_exchange_price": "77", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7164" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "77", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7165" - }, - { "shop_price": "54", - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "284", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7166" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "284", + "id": "7165", + "name": "Part mud pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7167" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "7166", + "name": "Part mud pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "shop_price": "54", + "tradeable": "true" + }, + { + "id": "7167", + "name": "Part mud pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7168", + "name": "Raw mud pie", "examine": "Needs to be baked before I can use it.", - "grand_exchange_price": "1625", - "durability": null, - "name": "Raw mud pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7168" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1625", + "id": "7169", + "name": "Raw mud pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw mud pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7169" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "54", - "ge_buy_limit": "1000", - "turn90cw_anim": "821", - "examine": "All the good of the earth.", - "walk_anim": "819", - "durability": null, - "turn90ccw_anim": "822", - "weapon_interface": "13", - "turn180_anim": "820", - "render_anim": "1", - "equipment_slot": "3", - "grand_exchange_price": "2024", - "stand_anim": "808", - "name": "Mud pie", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", "id": "7170", - "stand_turn_anim": "823" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2024", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Mud pie", - "tradeable": "true", + "examine": "All the good of the earth.", "archery_ticket_price": "0", - "id": "7171" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "render_anim": "1", + "run_anim": "824", + "shop_price": "54", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "13" }, { + "id": "7171", + "name": "Mud pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7172", + "name": "Part garden pie", "examine": "Still needs two more ingredients.", - "grand_exchange_price": "94", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7172" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "94", + "id": "7173", + "name": "Part garden pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7173" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7174", + "name": "Part garden pie", "examine": "Still needs one more ingredient.", - "grand_exchange_price": "150", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7174" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "150", + "id": "7175", + "name": "Part garden pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7175" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7176", + "name": "Raw garden pie", "examine": "Needs cooking before I eat it.", - "grand_exchange_price": "1052", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7177", "name": "Raw garden pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7176" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1052", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7177" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "What I wouldn't give for a good steak about now...", - "grand_exchange_price": "1667", - "durability": null, + "id": "7178", "name": "Garden pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7178" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1667", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Garden pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7179" - }, - { - "ge_buy_limit": "1000", "examine": "What I wouldn't give for a good steak about now...", - "grand_exchange_price": "442", - "durability": null, - "name": "Half a garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7180" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "442", + "id": "7179", + "name": "Garden pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a garden pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7181" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "7180", + "name": "Half a garden pie", + "examine": "What I wouldn't give for a good steak about now...", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7181", + "name": "Half a garden pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7182", + "name": "Part fish pie", "examine": "Still needs two more ingredients.", - "grand_exchange_price": "51", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7182" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "51", + "id": "7183", + "name": "Part fish pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7183" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7184", + "name": "Part fish pie", "examine": "Still needs one more ingredient.", - "grand_exchange_price": "691", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7184" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "691", + "id": "7185", + "name": "Part fish pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7185" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7186", + "name": "Raw fish pie", "examine": "Raw fish is risky, better cook it.", - "grand_exchange_price": "1198", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7187", "name": "Raw fish pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7186" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1198", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7187" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Bounty of the sea.", - "grand_exchange_price": "121", - "durability": null, + "id": "7188", "name": "Fish pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7188" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "121", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Fish pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7189" - }, - { - "ge_buy_limit": "1000", "examine": "Bounty of the sea.", - "grand_exchange_price": "56", - "durability": null, - "name": "Half a fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7190" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "56", + "id": "7189", + "name": "Fish pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a fish pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7191" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "7190", + "name": "Half a fish pie", + "examine": "Bounty of the sea.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7191", + "name": "Half a fish pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7192", + "name": "Part admiral pie", "examine": "Still needs two more ingredients. ", - "grand_exchange_price": "159", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7192" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "159", + "id": "7193", + "name": "Part admiral pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7193" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7194", + "name": "Part admiral pie", "examine": "Still needs one more ingredient.", - "grand_exchange_price": "537", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7194" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "537", + "id": "7195", + "name": "Part admiral pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7195" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7196", + "name": "Raw admiral pie", "examine": "This would taste a lot better cooked.", - "grand_exchange_price": "1813", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7197", "name": "Raw admiral pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7196" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1813", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7197" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Much tastier than a normal fish pie.", - "grand_exchange_price": "532", - "durability": null, + "id": "7198", "name": "Admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7198" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "532", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7199" - }, - { - "ge_buy_limit": "1000", "examine": "Much tastier than a normal fish pie.", - "grand_exchange_price": "184", - "durability": null, - "name": "Half an admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7200" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "184", + "id": "7199", + "name": "Admiral pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half an admiral pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7201" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "7200", + "name": "Half an admiral pie", + "examine": "Much tastier than a normal fish pie.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7201", + "name": "Half an admiral pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7202", + "name": "Part wild pie", "examine": "Still needs two more ingredients.", - "grand_exchange_price": "126", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7202" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "126", + "id": "7203", + "name": "Part wild pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7203" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7204", + "name": "Part wild pie", "examine": "Still needs one more ingredient.", - "grand_exchange_price": "1116", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7204" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1116", + "id": "7205", + "name": "Part wild pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7205" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7206", + "name": "Raw wild pie", "examine": "Good as it looks, I'd better cook it.", - "grand_exchange_price": "3787", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7207", "name": "Raw wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7206" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3787", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7207" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A triumph of man over nature.", - "grand_exchange_price": "1322", - "durability": null, + "id": "7208", "name": "Wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7208" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1322", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7209" - }, - { - "ge_buy_limit": "1000", "examine": "A triumph of man over nature.", - "grand_exchange_price": "112", - "durability": null, - "name": "Half a wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7210" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "112", + "id": "7209", + "name": "Wild pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a wild pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7211" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "7210", + "name": "Half a wild pie", + "examine": "A triumph of man over nature.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7211", + "name": "Half a wild pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7212", + "name": "Part summer pie", "examine": "Still needs two more ingredients.", - "grand_exchange_price": "495", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7212" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "495", + "id": "7213", + "name": "Part summer pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7213" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7214", + "name": "Part summer pie", "examine": "Still needs one more ingredient.", - "grand_exchange_price": "980", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7214" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "980", + "id": "7215", + "name": "Part summer pie", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7215" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7216", + "name": "Raw summer pie", "examine": "Fresh fruit may be good for you, but I should really cook this.", - "grand_exchange_price": "2577", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7217", "name": "Raw summer pie", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7216" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2577", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7217" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "All the fruits of a very small forest.", - "grand_exchange_price": "1288", - "durability": null, + "id": "7218", "name": "Summer pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7218" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1288", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Summer pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7219" - }, - { - "ge_buy_limit": "1000", "examine": "All the fruits of a very small forest.", - "grand_exchange_price": "430", - "durability": null, - "name": "Half a summer pie", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7220" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "430", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a summer pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7221" + "tradeable": "true" }, { - "shop_price": "19", + "id": "7219", + "name": "Summer pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7220", + "name": "Half a summer pie", + "examine": "All the fruits of a very small forest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7221", + "name": "Half a summer pie", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7222", + "name": "Burnt rabbit", "examine": "Mmm this looks tasty.", - "grand_exchange_price": "32", - "durability": null, - "name": "Burnt rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7222" + "durability": null, + "shop_price": "19", + "tradeable": "true" }, { - "shop_price": "25", - "ge_buy_limit": "1000", + "id": "7223", + "name": "Roast rabbit", "examine": "A delicious looking piece of roast rabbit.", - "grand_exchange_price": "37", - "durability": null, - "name": "Roast rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7223" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "25", + "tradeable": "true" }, { - "shop_price": "67", - "ge_buy_limit": "1000", + "id": "7224", + "name": "Skewered rabbit", "examine": "Might taste better cooked.", - "grand_exchange_price": "112", - "durability": null, - "name": "Skewered rabbit", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "7224" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "67", + "tradeable": "true", + "weight": "0.1" }, { - "shop_price": "81", - "ge_buy_limit": "100", + "id": "7225", + "name": "Iron spit", "examine": "An iron spit.", - "grand_exchange_price": "18", - "durability": null, - "name": "Iron spit", - "tradeable": "true", - "weight": "1.5", "archery_ticket_price": "0", - "id": "7225" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "81", + "tradeable": "true", + "weight": "1.5" }, { - "durability": null, + "id": "7227", "name": "Burnt chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7227" + "durability": null, + "tradeable": "true" }, { - "shop_price": "130", - "ge_buy_limit": "1000", + "id": "7228", + "name": "Cooked chompy", "examine": "It might look delicious to an ogre.Roasted chompy bird.", - "grand_exchange_price": "164", - "durability": null, - "name": "Cooked chompy", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "7228" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "130", + "tradeable": "true", + "weight": "10" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "164", - "durability": null, + "id": "7229", "name": "Cooked chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7229" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "7230", + "name": "Skewered chompy", "examine": "A skewered chompy bird.", - "grand_exchange_price": "294", - "durability": null, - "name": "Skewered chompy", - "tradeable": "true", - "destroy": "true", "archery_ticket_price": "0", - "id": "7230" + "destroy": "true", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "durability": null, + "id": "7231", "name": "Burnt rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7231" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7232", "name": "Roast rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7232" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7233", "name": "Skewered rabbit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7233" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7234", "name": "Iron spit", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7234" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7235", "name": "Skewered chompy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7235" + "durability": null, + "tradeable": "true" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7236", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7236" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "7237", "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7237" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7238" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7239" - }, - { "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7240" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7238", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7241" + "durability": null, + "shop_price": "5000" }, { + "id": "7239", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7240", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7242" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7241", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7243" + "durability": null, + "shop_price": "5000" }, { + "id": "7242", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7244" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7243", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7245" + "durability": null, + "shop_price": "5000" }, { + "id": "7244", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7246" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7245", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7247" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "7248" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7249" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7250" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7251" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7252" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7253" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7254" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7255" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7256" + "durability": null, + "shop_price": "5000" }, { + "id": "7246", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7257" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7247", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7258" + "durability": null, + "shop_price": "5000" }, { + "id": "7248", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7249", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7250", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7251", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7252", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7253", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7254", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7255", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7256", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7257", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7259" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7258", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7260" + "durability": null, + "shop_price": "5000" }, { + "id": "7259", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7261" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7260", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7262" + "durability": null, + "shop_price": "5000" }, { + "id": "7261", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7263" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7262", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7264" + "durability": null, + "shop_price": "5000" }, { + "id": "7263", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7265" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7264", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7266" + "durability": null, + "shop_price": "5000" }, { + "id": "7265", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7267" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7266", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7268" + "durability": null, + "shop_price": "5000" }, { - "examine": "I need to answer this correctly.", + "id": "7267", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7268", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7269", "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7269" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7270" - }, - { "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7271" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7270", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7272" + "durability": null, + "shop_price": "5000" }, { + "id": "7271", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7273" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7272", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7274" + "durability": null, + "shop_price": "5000" }, { + "id": "7273", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7275" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7274", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7276" + "durability": null, + "shop_price": "5000" }, { + "id": "7275", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7277" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7276", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7278" + "durability": null, + "shop_price": "5000" }, { + "id": "7277", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7279" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7278", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7280" + "durability": null, + "shop_price": "5000" }, { + "id": "7279", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7281" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7280", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7282" + "durability": null, + "shop_price": "5000" }, { + "id": "7281", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", "archery_ticket_price": "0", - "id": "7283" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7282", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7284" + "durability": null, + "shop_price": "5000" }, { + "id": "7283", + "name": "Challenge scroll", "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7284", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7285", "name": "Challenge scroll", + "examine": "I need to answer this correctly.", "archery_ticket_price": "0", - "id": "7285" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7286", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7286" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "7287", "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7287" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7288" - }, - { "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7289" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7288", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7290" + "durability": null, + "shop_price": "5000" }, { + "id": "7289", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7291" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7290", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7292" + "durability": null, + "shop_price": "5000" }, { + "id": "7291", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7293" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7292", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7294" + "durability": null, + "shop_price": "5000" }, { + "id": "7293", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "archery_ticket_price": "0", "durability": null, - "name": "Casket", "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7295" + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7294", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7296" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", "archery_ticket_price": "0", - "id": "7298" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7300" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7301" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7303" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7304" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7305" + "durability": null, + "shop_price": "5000" }, { + "id": "7295", + "name": "Casket", "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", - "id": "7306" + "durability": null, + "tradeable": "true", + "weight": "5" }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "7296", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7307" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7308" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7298", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7309" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7310" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7300", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7311" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7312" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7301", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7313" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7314" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7303", "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7315" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "7316" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7304", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "7317" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", + "id": "7305", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "5000" + }, + { + "id": "7306", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "7318" + "durability": null, + "tradeable": "true", + "weight": "5" }, { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "36600", - "durability": null, - "name": "Red boater", - "tradeable": "true", + "id": "7307", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7308", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7309", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7310", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7311", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7312", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7313", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7314", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7315", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7316", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7317", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7318", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { "id": "7319", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "36600", - "durability": null, "name": "Red boater", - "tradeable": "true", + "examine": "Stylish!", "archery_ticket_price": "0", - "id": "7320" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "16600", - "durability": null, - "name": "Orange boater", - "tradeable": "true", + "id": "7320", + "name": "Red boater", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7321", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "16600", - "durability": null, "name": "Orange boater", - "tradeable": "true", + "examine": "Stylish!", "archery_ticket_price": "0", - "id": "7322" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "43000", - "durability": null, - "name": "Green boater", - "tradeable": "true", + "id": "7322", + "name": "Orange boater", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7323", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "43000", - "durability": null, "name": "Green boater", - "tradeable": "true", + "examine": "Stylish!", "archery_ticket_price": "0", - "id": "7324" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "37600", - "durability": null, - "name": "Blue boater", - "tradeable": "true", + "id": "7324", + "name": "Green boater", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7325", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "37600", - "durability": null, "name": "Blue boater", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7326" - }, - { - "ge_buy_limit": "2", "examine": "Stylish!", - "grand_exchange_price": "41700", - "durability": null, - "name": "Black boater", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7326", + "name": "Blue boater", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7327", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "41700", - "durability": null, "name": "Black boater", - "tradeable": "true", + "examine": "Stylish!", "archery_ticket_price": "0", - "id": "7328" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "93", + "id": "7328", + "name": "Black boater", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7329", "name": "Red firelighter", - "tradeable": "true", + "examine": "Makes firelighting a lot easier.", "archery_ticket_price": "0", - "id": "7329" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "94", - "durability": null, + "id": "7330", "name": "Green firelighter", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7330" - }, - { - "ge_buy_limit": "100", "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "113", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7331", "name": "Blue firelighter", - "tradeable": "true", + "examine": "Makes firelighting a lot easier.", "archery_ticket_price": "0", - "id": "7331" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black kitesheild with a heraldic design.", - "durability": null, - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "14300", - "name": "Black shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "7332", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "14300", - "durability": null, "name": "Black shield(h1)", - "tradeable": "true", + "examine": "A black kitesheild with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "7333" + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "16400", - "name": "Adamant shield(h1)", - "tradeable": "true", + "id": "7333", + "name": "Black shield(h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7334", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "16400", - "durability": null, "name": "Adamant shield(h1)", - "tradeable": "true", + "examine": "A shield with a heraldic design.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "7335" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "34200", - "name": "Rune shield(h1)", - "tradeable": "true", + "id": "7335", + "name": "Adamant shield(h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7336", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "34200", - "durability": null, "name": "Rune shield(h1)", - "tradeable": "true", + "examine": "A rune kiteshield with a heraldic design.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "7337" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black kiteshield with a heraldic design.", - "durability": null, - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "9797", - "name": "Black shield(h2)", - "tradeable": "true", + "id": "7337", + "name": "Rune shield(h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7338", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "9797", - "durability": null, "name": "Black shield(h2)", - "tradeable": "true", + "examine": "A black kiteshield with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "7339" + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3248", - "name": "Adamant shield(h2)", - "tradeable": "true", + "id": "7339", + "name": "Black shield(h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7340", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3248", - "durability": null, "name": "Adamant shield(h2)", - "tradeable": "true", + "examine": "A shield with a heraldic design.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "7341" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32400", - "name": "Rune shield(h2)", - "tradeable": "true", + "id": "7341", + "name": "Adamant shield(h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7342", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "32400", - "durability": null, "name": "Rune shield(h2)", - "tradeable": "true", + "examine": "A rune kiteshield with a heraldic design.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "7343" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black shield with a heraldic design.", - "durability": null, - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "7405", - "name": "Black shield(h3)", - "tradeable": "true", + "id": "7343", + "name": "Rune shield(h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7344", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7405", - "durability": null, "name": "Black shield(h3)", - "tradeable": "true", + "examine": "A black shield with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "7345" + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "5271", - "name": "Adamant shield(h3)", - "tradeable": "true", + "id": "7345", + "name": "Black shield(h3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7346", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "5271", - "durability": null, "name": "Adamant shield(h3)", - "tradeable": "true", + "examine": "A shield with a heraldic design.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "7347" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32700", - "name": "Rune shield(h3)", - "tradeable": "true", + "id": "7347", + "name": "Adamant shield(h3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7348", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "32700", - "durability": null, "name": "Rune shield(h3)", - "tradeable": "true", + "examine": "A rune kiteshield with a heraldic design.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "7349" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black shield with a heraldic design.", - "durability": null, - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "5572", - "name": "Black shield(h4)", - "tradeable": "true", + "id": "7349", + "name": "Rune shield(h3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7350", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "5572", - "durability": null, "name": "Black shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7351" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3366", - "name": "Adamant shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7352", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3366", - "durability": null, - "name": "Adamant shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7353" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32500", - "name": "Rune shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7354", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "32500", - "durability": null, - "name": "Rune shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7355" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", "examine": "A black shield with a heraldic design.", - "durability": null, - "weight": "4", "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "7159", - "name": "Black shield(h5)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7356", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7159", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", "durability": null, - "name": "Black shield(h5)", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7357" + "weight": "4" }, { - "requirements": "{1,30}", + "id": "7351", + "name": "Black shield(h4)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7352", + "name": "Adamant shield(h4)", "examine": "A shield with a heraldic design.", - "durability": null, - "weight": "5", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "6328", - "name": "Adamant shield(h5)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7358", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "6328", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", "durability": null, - "name": "Adamant shield(h5)", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7359" + "weight": "5" }, { - "requirements": "{1,40}", + "id": "7353", + "name": "Adamant shield(h4)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7354", + "name": "Rune shield(h4)", "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "weight": "5", "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "32400", - "name": "Rune shield(h5)", + "ge_buy_limit": "2", + "requirements": "{1,40}", "tradeable": "true", + "weight": "5" + }, + { + "id": "7355", + "name": "Rune shield(h4)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7356", + "name": "Black shield(h5)", + "examine": "A black shield with a heraldic design.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "7357", + "name": "Black shield(h5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7358", + "name": "Adamant shield(h5)", + "examine": "A shield with a heraldic design.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7359", + "name": "Adamant shield(h5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7360", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "requirements": "", - "ge_buy_limit": "2", - "grand_exchange_price": "32400", - "durability": null, "name": "Rune shield(h5)", - "tradeable": "true", + "examine": "A rune kiteshield with a heraldic design.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "7361" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,20}-{4,20}", - "ge_buy_limit": "2", - "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "weight": "5.4", - "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "101600", - "name": "Studded body (g)", - "tradeable": "true", + "id": "7361", + "name": "Rune shield(h5)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "", + "tradeable": "true" + }, + { "id": "7362", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "101600", - "durability": null, "name": "Studded body (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7363" - }, - { - "requirements": "{1,20}-{4,20}", - "ge_buy_limit": "2", "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "weight": "5.4", "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "27300", - "name": "Studded body (t)", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "requirements": "{1,20}-{4,20}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "7363", + "name": "Studded body (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "7364", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "27300", - "durability": null, "name": "Studded body (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7365" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "2", "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "weight": "4.5", - "absorb": "0,2,1", - "equipment_slot": "7", - "grand_exchange_price": "114600", - "name": "Studded chaps (g)", - "tradeable": "true", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "7366", - "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "114600", + "bonuses": "0,0,0,-4,8,18,25,22,8,25,20,0,0,0,0", "durability": null, - "name": "Studded chaps (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7367" - }, - { - "requirements": "{4,20}", - "ge_buy_limit": "2", - "examine": "Those studs should provide a bit more protection. Nice trim, too!", - "durability": null, - "weight": "4.5", - "absorb": "0,2,1", - "equipment_slot": "7", - "grand_exchange_price": "36800", - "name": "Studded chaps (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7368", - "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "36800", - "durability": null, - "name": "Studded chaps (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7369" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide. With colourful trim!", - "grand_exchange_price": "212400", - "durability": null, - "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" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "212400", - "durability": null, - "name": "D'hide body(g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7371" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide. With colourful trim!", - "grand_exchange_price": "38800", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "38800", - "durability": null, - "name": "D'hide body (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7373" - }, - { - "requirements": "{1,50}-{4,40}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "6883", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "6883", - "durability": null, - "name": "D'hide body (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7375" - }, - { - "requirements": "{1,50}-{4,40}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "5268", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "5268", - "durability": null, - "name": "D'hide body (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7377" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "1300000", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1300000", - "durability": null, - "name": "D'hide chaps (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7379" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "304300", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "304300", - "durability": null, - "name": "D'hide chaps (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7381" - }, - { - "requirements": "{4,50}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "21300", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "21300", - "durability": null, - "name": "D'hide chaps (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7383" - }, - { - "requirements": "{4,50}", - "ge_buy_limit": "2", - "examine": "Made from 100% real dragonhide, with colourful trim!", - "grand_exchange_price": "2554", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2554", - "durability": null, - "name": "D'hide chaps (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7385" - }, - { - "ge_buy_limit": "2", - "examine": "Leg covering favoured by women and wizards. With a colourful trim!", - "grand_exchange_price": "298900", - "durability": null, - "name": "Blue skirt (g)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7386", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "298900", - "durability": null, - "name": "Blue skirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7387" - }, - { - "ge_buy_limit": "2", - "examine": "Leg covering favoured by women and wizards. With a colourful trim!", - "grand_exchange_price": "126800", - "durability": null, - "name": "Blue skirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "1", - "id": "7388", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "126800", - "durability": null, - "name": "Blue skirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7389" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "I can practise magic better in this.", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Wizard robe (g)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7390", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Wizard robe (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7391" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "I can do magic better in this.", - "grand_exchange_price": "244600", - "durability": null, - "name": "Wizard robe (t)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7392", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "244600", - "durability": null, - "name": "Wizard robe (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7393" - }, - { - "ge_buy_limit": "2", - "examine": "A silly pointed hat with colourful trim.", - "grand_exchange_price": "1400000", - "durability": null, - "name": "Wizard hat (g)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "hat": "true", - "id": "7394", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1400000", - "durability": null, - "name": "Wizard hat (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7395" - }, - { - "ge_buy_limit": "2", - "examine": "A silly pointed hat, with colourful trim.", - "grand_exchange_price": "352700", - "durability": null, - "name": "Wizard hat (t)", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "hat": "true", - "id": "7396", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "352700", - "durability": null, - "name": "Wizard hat (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7397" - }, - { - "requirements": "{1,40}-{6,20}", - "ge_buy_limit": "2", - "examine": "Enchanted Wizards robes.", - "durability": null, - "weight": "1.8", - "absorb": "2,1,0", - "equipment_slot": "7", - "grand_exchange_price": "50000", - "name": "Enchanted robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7398", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,40}", - "ge_buy_limit": "2", - "examine": "Enchanted Wizards robes.", - "durability": null, - "weight": "1", - "absorb": "3,1,0", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "75100", - "name": "Enchanted top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7399", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{6,20}", "ge_buy_limit": "2", - "examine": "A three pointed hat of magic.", - "grand_exchange_price": "8845", - "durability": null, - "name": "Enchanted hat", + "requirements": "{1,20}-{4,20}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7400", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", - "equipment_slot": "0" + "weight": "5.4" }, { + "id": "7365", + "name": "Studded body (t)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7366", + "name": "Studded chaps (g)", + "examine": "Those studs should provide a bit more protection. Nice trim too!", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,20}", + "tradeable": "true", + "weight": "4.5" + }, + { + "id": "7367", + "name": "Studded chaps (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7368", + "name": "Studded chaps (t)", + "examine": "Those studs should provide a bit more protection. Nice trim, too!", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,6,15,16,17,6,16,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,20}", + "tradeable": "true", + "weight": "4.5" + }, + { + "id": "7369", + "name": "Studded chaps (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7370", + "name": "D'hide body(g)", + "examine": "Made from 100% real dragonhide. With colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "5000", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "7371", + "name": "D'hide body(g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7372", + "name": "D'hide body (t)", + "examine": "Made from 100% real dragonhide. With colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "7373", + "name": "D'hide body (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7374", + "name": "D'hide body (g)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "7375", + "name": "D'hide body (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7376", + "name": "D'hide body (t)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "7377", + "name": "D'hide body (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7378", + "name": "D'hide chaps (g)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7379", + "name": "D'hide chaps (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7380", + "name": "D'hide chaps (t)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7381", + "name": "D'hide chaps (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7382", + "name": "D'hide chaps (g)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,50}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7383", + "name": "D'hide chaps (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7384", + "name": "D'hide chaps (t)", + "examine": "Made from 100% real dragonhide, with colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{4,50}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7385", + "name": "D'hide chaps (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7386", + "name": "Blue skirt (g)", + "examine": "Leg covering favoured by women and wizards. With a colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7387", + "name": "Blue skirt (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7388", + "name": "Blue skirt (t)", + "examine": "Leg covering favoured by women and wizards. With a colourful trim!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7389", + "name": "Blue skirt (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7390", + "name": "Wizard robe (g)", + "examine": "I can practise magic better in this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7391", + "name": "Wizard robe (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7392", + "name": "Wizard robe (t)", + "examine": "I can do magic better in this.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7393", + "name": "Wizard robe (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7394", + "name": "Wizard hat (g)", + "examine": "A silly pointed hat with colourful trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7395", + "name": "Wizard hat (g)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7396", + "name": "Wizard hat (t)", + "examine": "A silly pointed hat, with colourful trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": "true", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "7397", + "name": "Wizard hat (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7398", "name": "Enchanted robe", - "tradeable": "true", + "examine": "Enchanted Wizards robes.", + "absorb": "2,1,0", "archery_ticket_price": "0", - "id": "7401" + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,20}-{6,40}", + "tradeable": "true", + "weight": "1.8" }, { - "durability": null, + "id": "7399", "name": "Enchanted top", - "tradeable": "true", + "examine": "Enchanted Wizards robes.", + "absorb": "3,1,0", "archery_ticket_price": "0", - "id": "7402" + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "tradeable": "true", + "weight": "1" }, { - "durability": null, + "id": "7400", "name": "Enchanted hat", - "tradeable": "true", + "examine": "A three pointed hat of magic.", "archery_ticket_price": "0", - "id": "7403" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,20}-{6,40}", + "tradeable": "true" }, { - "examine": "A number of chemical covered wooden logs.", + "id": "7401", + "name": "Enchanted robe", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "7402", + "name": "Enchanted top", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7403", + "name": "Enchanted hat", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7404", "name": "Red logs", + "examine": "A number of chemical covered wooden logs.", "archery_ticket_price": "0", - "id": "7404" + "durability": null }, { - "examine": "A number of chemical covered wooden logs.", - "durability": null, + "id": "7405", "name": "Green logs", - "weight": "2", - "archery_ticket_price": "0", - "id": "7405" - }, - { "examine": "A number of chemical covered wooden logs.", + "archery_ticket_price": "0", "durability": null, + "weight": "2" + }, + { + "id": "7406", "name": "Blue logs", - "weight": "2", + "examine": "A number of chemical covered wooden logs.", "archery_ticket_price": "0", - "id": "7406" + "durability": null, + "weight": "2" }, { - "durability": null, + "id": "7407", "name": "Dragon 2h sword", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7407" + "durability": null, + "tradeable": "true" }, { - "destroy_message": "I found this in a grave at Draynor Manor.", - "examine": "I shouldn't joke; this is a grave matter.", - "durability": null, + "id": "7408", "name": "Draynor skull", - "tradeable": "false", - "destroy": "true", + "examine": "I shouldn't joke; this is a grave matter.", "archery_ticket_price": "0", - "id": "7408" + "destroy": "true", + "destroy_message": "I found this in a grave at Draynor Manor.", + "durability": null, + "tradeable": "false" }, { - "shop_price": "40000", - "examine": "The only way to kill a Tanglefoot.", - "durability": null, - "destroy": "true", - "weight": "0.4", - "attack_speed": "5", - "weapon_interface": "5", - "equipment_slot": "3", - "destroy_message": "I'll chat to Malignius Mortifer if I want another pair.", - "name": "Magic secateurs", - "tradeable": "false", - "archery_ticket_price": "0", "id": "7409", - "bonuses": "7,9,-5,1,0,0,1,0,1,0,0,1,0,0,0" - }, - { - "destroy_message": "I can always find another Tanglefoot if I need more.", - "examine": "Contains the Fairy Queen's magical essence.", - "durability": null, - "name": "Queen's secateurs", + "name": "Magic secateurs", + "examine": "The only way to kill a Tanglefoot.", "archery_ticket_price": "0", - "id": "7410", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A list of the Fairy Queen's symptoms.", - "durability": null, - "name": "Symptoms list", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7411" - }, - { - "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "archery_ticket_price": "0", - "id": "7413" - }, - { - "durability": null, - "name": "Paddle", - "archery_ticket_price": "0", - "id": "7414", - "weapon_interface": "1", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Paddle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7415" - }, - { - "ge_buy_limit": "5000", - "examine": "A mole claw.", - "grand_exchange_price": "11800", - "durability": null, - "name": "Mole claw", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7416" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "11800", - "durability": null, - "name": "Mole claw", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7417" - }, - { - "ge_buy_limit": "5000", - "examine": "The skin of a large mole.", - "grand_exchange_price": "12000", - "durability": null, - "name": "Mole skin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7418" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "12000", - "durability": null, - "name": "Mole skin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7419" - }, - { - "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense. (As Fungi) A bouncy fungus. (Level 74) A fun guy. No wait, that's awful.. (Level 86)", - "durability": null, - "name": "Mutated zygomite", - "archery_ticket_price": "0", - "id": "7420" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 10", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7421", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 9", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7422", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 8", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7423", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 7", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7424", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 6", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7425", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 5", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7426", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 4", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7427", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 3", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7428", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 2", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7429", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 1", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7430", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "300", - "turn90cw_anim": "821", - "examine": "Pumps fungicide.", - "walk_anim": "3334", - "durability": null, - "weight": "2", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "618", - "equipment_slot": "3", - "stand_anim": "3332", - "name": "Fungicide spray 0", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7431", - "stand_turn_anim": "823" - }, - { - "requirements": "{18,57}", - "shop_price": "10", - "examine": "Does exactly what it says on the tin. (Kills Fungi.)", - "durability": null, - "name": "Fungicide", - "archery_ticket_price": "0", - "id": "7432" - }, - { - "shop_price": "35", - "ge_buy_limit": "100", - "examine": "Spoooooon!", - "durability": null, - "weight": "0.4", "attack_speed": "5", - "weapon_interface": "6", + "bonuses": "7,9,-5,1,0,0,1,0,1,0,0,1,0,0,0", + "destroy": "true", + "destroy_message": "I'll chat to Malignius Mortifer if I want another pair.", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "44", - "name": "Wooden spoon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7433", - "bonuses": "4,5,-2,0,0,0,3,2,0,0,0,7,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "44", - "durability": null, - "name": "Wooden spoon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7434" - }, - { - "shop_price": "45", - "ge_buy_limit": "100", - "examine": "A large whisk of death.", - "durability": null, - "attack_speed": "4", + "shop_price": "40000", + "tradeable": "false", "weapon_interface": "5", - "equipment_slot": "3", - "grand_exchange_price": "11", - "name": "Egg whisk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7435", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "11", + "id": "7410", + "name": "Queen's secateurs", + "examine": "Contains the Fairy Queen's magical essence.", + "archery_ticket_price": "0", + "destroy_message": "I can always find another Tanglefoot if I need more.", "durability": null, - "name": "Egg whisk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7436" + "equipment_slot": "3" }, { + "id": "7411", + "name": "Symptoms list", + "examine": "A list of the Fairy Queen's symptoms.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7413", + "name": "Bird's nest", + "examine": "Full: ", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7414", + "name": "Paddle", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "1" + }, + { + "id": "7415", + "name": "Paddle", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7416", + "name": "Mole claw", + "examine": "A mole claw.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7417", + "name": "Mole claw", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7418", + "name": "Mole skin", + "examine": "The skin of a large mole.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7419", + "name": "Mole skin", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7420", + "name": "Mutated zygomite", + "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense. (As Fungi) A bouncy fungus. (Level 74) A fun guy. No wait, that's awful.. (Level 86)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7421", + "name": "Fungicide spray 10", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7422", + "name": "Fungicide spray 9", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7423", + "name": "Fungicide spray 8", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7424", + "name": "Fungicide spray 7", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7425", + "name": "Fungicide spray 6", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7426", + "name": "Fungicide spray 5", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7427", + "name": "Fungicide spray 4", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7428", + "name": "Fungicide spray 3", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7429", + "name": "Fungicide spray 2", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7430", + "name": "Fungicide spray 1", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7431", + "name": "Fungicide spray 0", + "examine": "Pumps fungicide.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "618", + "requirements": "{18,57}", + "run_anim": "824", + "shop_price": "300", + "stand_anim": "3332", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "3334", + "weight": "2" + }, + { + "id": "7432", + "name": "Fungicide", + "examine": "Does exactly what it says on the tin. (Kills Fungi.)", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{18,57}", + "shop_price": "10" + }, + { + "id": "7433", + "name": "Wooden spoon", + "examine": "Spoooooon!", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "4,5,-2,0,0,0,3,2,0,0,0,7,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "35", + "tradeable": "true", + "weapon_interface": "6", + "weight": "0.4" + }, + { + "id": "7434", + "name": "Wooden spoon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7435", + "name": "Egg whisk", + "examine": "A large whisk of death.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "45", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "7436", + "name": "Egg whisk", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7437", + "name": "Spork", + "examine": "Use the spork.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "11,8,-2,0,0,0,2,1,0,0,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,10}", "shop_price": "292", - "ge_buy_limit": "100", - "examine": "Use the spork.", - "durability": null, - "weight": "1.8", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "5", - "equipment_slot": "3", - "grand_exchange_price": "196", - "name": "Spork", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7437", - "bonuses": "11,8,-2,0,0,0,2,1,0,0,0,12,0,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "196", - "durability": null, + "id": "7438", "name": "Spork", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7438" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,10}", - "ge_buy_limit": "100", - "shop_price": "1728", - "turn90cw_anim": "7044", - "examine": "A large spatula... of doom!", - "walk_anim": "7046", - "durability": null, - "weight": "3.6", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "7", - "turn180_anim": "7045", - "render_anim": "124", - "equipment_slot": "3", - "grand_exchange_price": "953", - "stand_anim": "7047", - "tradeable": "true", - "name": "Spatula", - "run_anim": "7039", - "archery_ticket_price": "0", "id": "7439", - "stand_turn_anim": "7040", - "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,22,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "953", - "durability": null, "name": "Spatula", - "tradeable": "true", + "examine": "A large spatula... of doom!", "archery_ticket_price": "0", - "id": "7440" + "attack_speed": "7", + "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,22,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "124", + "requirements": "{0,10}", + "run_anim": "7039", + "shop_price": "1728", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" }, { + "id": "7440", + "name": "Spatula", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7441", + "name": "Frying pan", + "examine": "Looks like it's non-stick too!", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "-4,-4,25,-4,0,0,0,0,0,0,0,20,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,20}", "shop_price": "1494", - "ge_buy_limit": "100", - "examine": "Looks like it's non-stick too!", - "durability": null, - "weight": "1.5", - "attack_speed": "6", + "tradeable": "true", "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "836", - "name": "Frying pan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7441", - "bonuses": "-4,-4,25,-4,0,0,0,0,0,0,0,20,0,0,0" + "weight": "1.5" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "836", + "id": "7442", + "name": "Frying pan", + "archery_ticket_price": "0", "durability": null, - "name": "Frying pan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7442" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "7443", + "name": "Skewer", + "examine": "Generally used for impaling fresh meat.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,30}", "shop_price": "2880", - "ge_buy_limit": "100", - "examine": "Generally used for impaling fresh meat.", - "durability": null, - "weight": "2", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "6", - "equipment_slot": "3", - "grand_exchange_price": "1712", - "name": "Skewer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7443", - "bonuses": "20,29,-2,0,0,0,3,2,0,0,0,31,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1712", + "id": "7444", + "name": "Skewer", + "archery_ticket_price": "0", "durability": null, - "name": "Skewer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7444" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "7445", + "name": "Rolling pin", + "examine": "That's how I roll!", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,4,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,40}", "shop_price": "12960", - "ge_buy_limit": "100", - "examine": "That's how I roll!", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "8445", - "name": "Rolling pin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7445", - "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,4,0,0" + "weight": "1.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8445", + "id": "7446", + "name": "Rolling pin", + "archery_ticket_price": "0", "durability": null, - "name": "Rolling pin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7446" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "7447", + "name": "Kitchen knife", + "examine": "A sharp, dependable knife, for filleting meat.", + "archery_ticket_price": "0", + "attack_audios": "2704,0,0,0", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,40}", "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A sharp, dependable knife, for filleting meat.", - "durability": null, - "attack_speed": "4", - "weapon_interface": "5", - "equipment_slot": "3", - "grand_exchange_price": "4686", - "attack_audios": "2704,0,0,0", - "name": "Kitchen knife", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7447", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" + "weapon_interface": "5" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4686", - "durability": null, + "id": "7448", "name": "Kitchen knife", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7448" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "Often used to soften tough meat up.", - "walk_anim": "819", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "defence_anim": "1666", - "equipment_slot": "3", - "attack_anims": "2067,2066,2068", - "grand_exchange_price": "24700", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", "id": "7449", - "stand_turn_anim": "823", - "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0", - "requirements": "{0,40}", - "shop_price": "41500", - "durability": null, - "weight": "1", - "weapon_interface": "10", - "render_anim": "1", - "name": "Meat tenderiser" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24700", - "durability": null, "name": "Meat tenderiser", - "tradeable": "true", + "examine": "Often used to soften tough meat up.", "archery_ticket_price": "0", - "id": "7450" + "attack_anims": "2067,2066,2068", + "attack_speed": "6", + "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0", + "defence_anim": "1666", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1", + "requirements": "{0,40}", + "run_anim": "824", + "shop_price": "41500", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10", + "weight": "1" }, { + "id": "7450", + "name": "Meat tenderiser", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7451", + "name": "Cleaver", + "examine": "An effective tool for chopping tough meat.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0", + "defence_anim": "397", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{0,40}", "shop_price": "24040", - "ge_buy_limit": "100", - "examine": "An effective tool for chopping tough meat.", - "durability": null, - "weight": "0.5", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "6", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "15000", - "attack_audios": "2500,0,2517,0", - "name": "Cleaver", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7451", - "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0" + "weight": "0.5" }, { + "id": "7452", + "name": "Cleaver", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "15000", - "durability": null, - "name": "Cleaver", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7452" + "tradeable": "true" }, { - "shop_price": "50", - "examine": "A pair of plain gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7453", + "name": "Gloves", + "examine": "A pair of plain gloves.", + "archery_ticket_price": "0", "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "50", + "weight": "0.2" }, { - "shop_price": "100", - "examine": "A pair of bronze-coloured gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7454", + "name": "Gloves", + "examine": "A pair of bronze-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "2,2,2,1,2,2,2,2,1,2,1,2,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "100", + "weight": "0.2" }, { - "shop_price": "325", - "examine": "A pair of iron-coloured gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7455", + "name": "Gloves", + "examine": "A pair of iron-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "3,3,3,2,3,3,3,3,2,3,2,3,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "325", + "weight": "0.2" }, { - "shop_price": "500", - "examine": "A pair of steel-coloured gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7456", + "name": "Gloves", + "examine": "A pair of steel-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "4,4,4,2,4,4,4,4,2,4,2,4,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "500", + "weight": "0.2" }, { - "shop_price": "1000", - "examine": "A pair of black-coloured gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7457", + "name": "Gloves", + "examine": "A pair of black-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "5,5,5,3,5,5,5,5,3,5,3,5,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "1000", + "weight": "0.2" }, { - "shop_price": "1500", - "examine": "A pair of mithril-coloured gloves.", - "durability": null, - "name": "Gloves", - "archery_ticket_price": "0", "id": "7458", + "name": "Gloves", + "examine": "A pair of mithril-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "6,6,6,3,6,6,6,6,3,6,3,6,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "1500" }, { - "shop_price": "2500", - "examine": "A pair of adamant-coloured gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "7459", + "name": "Gloves", + "examine": "A pair of adamant-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "7,7,7,4,7,7,7,7,4,7,4,7,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "2500", + "weight": "0.2" }, { - "requirements": "{1,13}", - "shop_price": "5000", - "examine": "A pair of rune-coloured gloves.", - "durability": null, - "name": "Gloves", - "archery_ticket_price": "0", "id": "7460", + "name": "Gloves", + "examine": "A pair of rune-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "8,8,8,4,8,8,8,8,4,8,4,8,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "requirements": "{1,13}", + "shop_price": "5000" }, { - "requirements": "{1,41}", - "shop_price": "100000", - "examine": "A pair of dragon-coloured gloves.", - "durability": null, - "name": "Gloves", - "archery_ticket_price": "0", "id": "7461", + "name": "Gloves", + "examine": "A pair of dragon-coloured gloves.", + "archery_ticket_price": "0", "bonuses": "9,9,9,5,9,9,9,9,5,9,5,9,0,0,0", - "equipment_slot": "9" + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "requirements": "{1,41}", + "shop_price": "100000" }, { + "id": "7462", + "name": "Gloves", + "examine": "A pair of Barrows-themed gloves.", + "archery_ticket_price": "0", + "bonuses": "12,12,12,6,12,12,12,12,6,12,6,12,0,0,0", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", "requirements": "{1,41}", "shop_price": "100000", - "examine": "A pair of Barrows-themed gloves.", - "durability": null, - "name": "Gloves", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7462", - "bonuses": "12,12,12,6,12,12,12,12,6,12,6,12,0,0,0", - "equipment_slot": "9" + "weight": "0.2" }, { - "examine": "It's cornflour in a pot.", - "durability": null, + "id": "7463", "name": "Cornflour", - "weight": "1", + "examine": "It's cornflour in a pot.", "archery_ticket_price": "0", - "id": "7463" + "durability": null, + "weight": "1" }, { - "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", - "durability": null, + "id": "7464", "name": "Book on chickens", + "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", "archery_ticket_price": "0", - "id": "7464" + "durability": null }, { - "examine": "Surprise, it looks like a vanilla pod.", - "durability": null, + "id": "7465", "name": "Vanilla pod", + "examine": "Surprise, it looks like a vanilla pod.", "archery_ticket_price": "0", - "id": "7465" + "durability": null }, { - "examine": "It's cornflour in a pot.", - "durability": null, + "id": "7466", "name": "Cornflour", - "weight": "1", - "archery_ticket_price": "0", - "id": "7466" - }, - { "examine": "It's cornflour in a pot.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "7468", "name": "Pot of cornflour", - "weight": "1", + "examine": "It's cornflour in a pot.", "archery_ticket_price": "0", - "id": "7468" + "durability": null, + "weight": "1" }, { - "destroy_message": "I'll have to get all the ingredients again.", - "examine": "A mixture of milk, cream and cornflour.", - "durability": null, + "id": "7470", "name": "Cornflour mixture", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A mixture of milk, cream and cornflour.", "archery_ticket_price": "0", - "id": "7470" - }, - { + "destroy": "true", "destroy_message": "I'll have to get all the ingredients again.", - "examine": "It's a bucket of milk and cream.", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "7471", "name": "Milky mixture", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "It's a bucket of milk and cream.", "archery_ticket_price": "0", - "id": "7471" + "destroy": "true", + "destroy_message": "I'll have to get all the ingredients again.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "Some cinnamon sticks.", - "durability": null, + "id": "7472", "name": "Cinnamon", + "examine": "Some cinnamon sticks.", "archery_ticket_price": "0", - "id": "7472" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pot of brulee supreme.", - "durability": null, + "id": "7476", "name": "Brulee supreme", + "examine": "A pot of brulee supreme.", "archery_ticket_price": "0", - "id": "7476" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "What came first, the chicken or...", - "durability": null, + "id": "7477", "name": "Evil chicken's egg", + "examine": "What came first, the chicken or...", "archery_ticket_price": "0", - "id": "7477" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "I got this by killing a Black Dragon. It might be hard to get another one.", - "examine": "It's got a dragon on it.", - "durability": null, + "id": "7478", "name": "Dragon token", - "tradeable": "false", - "destroy": "true", + "examine": "It's got a dragon on it.", "archery_ticket_price": "0", - "id": "7478" + "destroy": "true", + "destroy_message": "I got this by killing a Black Dragon. It might be hard to get another one.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It's a meat and potato stew with fancy seasoning.", - "durability": null, + "id": "7479", "name": "Spicy stew", - "weight": "1.5", + "examine": "It's a meat and potato stew with fancy seasoning.", "archery_ticket_price": "0", - "id": "7479" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "1.5" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7480", "name": "Red spice (4)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7480" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7481", "name": "Red spice (3)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7481" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7482", "name": "Red spice (2)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7482" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7483", "name": "Red spice (1)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7483" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7484", "name": "Orange spice (4)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7484" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7485", "name": "Orange spice (3)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7485" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7486", "name": "Orange spice (2)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7486" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7487", "name": "Orange spice (1)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7487" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7488", "name": "Brown spice (4)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7488" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7489", "name": "Brown spice (3)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7489" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7490", "name": "Brown spice (2)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7490" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7491", "name": "Brown spice (1)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7491" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7492", "name": "Yellow spice (4)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7492" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7493", "name": "Yellow spice (3)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7493" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7494", "name": "Yellow spice (2)", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7494" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, + "id": "7495", "name": "Yellow spice (1)", - "archery_ticket_price": "0", - "id": "7495" - }, - { "examine": "Allows for equal distribution of spice.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7496", "name": "Empty spice shaker", + "examine": "Allows for equal distribution of spice.", "archery_ticket_price": "0", - "id": "7496" + "durability": null }, { - "examine": "A cool refreshing fruit mix. With ash in for some reason.", - "durability": null, + "id": "7497", "name": "Dirty blast", + "examine": "A cool refreshing fruit mix. With ash in for some reason.", "archery_ticket_price": "0", - "id": "7497" + "durability": null }, { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, + "id": "7498", "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "examine": "I wonder what happens if I rub it.", "archery_ticket_price": "0", - "id": "7498" + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" }, { - "examine": "He seems to like wearing black.", - "durability": null, + "id": "7499", "name": "Evil dave", + "examine": "He seems to like wearing black.", "archery_ticket_price": "0", - "id": "7499" + "durability": null }, { - "examine": "A short angry guy.", - "durability": null, + "id": "7500", "name": "Dwarf", + "examine": "A short angry guy.", "archery_ticket_price": "0", - "id": "7500" + "durability": null }, { - "examine": "He provides new players with useful information.", - "durability": null, + "id": "7502", "name": "Lumbridge guide", + "examine": "He provides new players with useful information.", "archery_ticket_price": "0", - "id": "7502" + "durability": null }, { - "examine": "He looks a little shifty.", - "durability": null, + "id": "7504", "name": "Osman", + "examine": "He looks a little shifty.", "archery_ticket_price": "0", - "id": "7504" + "durability": null }, { - "examine": "A shifty-looking character.", - "durability": null, + "id": "7505", "name": "Pirate pete", + "examine": "A shifty-looking character.", "archery_ticket_price": "0", - "id": "7505" + "durability": null }, { - "examine": "Leader of the White Knights.", - "durability": null, + "id": "7506", "name": "Sir amik varze", + "examine": "Leader of the White Knights.", "archery_ticket_price": "0", - "id": "7506" + "durability": null }, { - "examine": "He's been frozen in time.", - "durability": null, + "id": "7507", "name": "Skrach", + "examine": "He's been frozen in time.", "archery_ticket_price": "0", - "id": "7507" + "durability": null }, { - "shop_price": "4", - "examine": "There appears to be a coin in the bottom. Liked by dwarves.", - "durability": null, + "id": "7508", "name": "Asgoldian ale", + "examine": "There appears to be a coin in the bottom. Liked by dwarves.", "archery_ticket_price": "0", - "id": "7508" + "durability": null, + "shop_price": "4" }, { + "id": "7509", + "name": "Dwarven rock cake", "examine": "Red hot and glowing, ouch! Only for dwarf consumption.", - "durability": null, - "name": "Dwarven rock cake", - "weight": "8.5", "archery_ticket_price": "0", - "id": "7509" + "durability": null, + "weight": "8.5" }, { + "id": "7510", + "name": "Dwarven rock cake", "examine": "Cool and heavy as a brick. Only for dwarf consumption.", - "durability": null, - "name": "Dwarven rock cake", - "weight": "8.5", "archery_ticket_price": "0", - "id": "7510" + "durability": null, + "weight": "8.5" }, { - "examine": "Two out of two goblin generals prefer it!", - "durability": null, + "id": "7511", "name": "Slop of compromise", + "examine": "Two out of two goblin generals prefer it!", "archery_ticket_price": "0", - "id": "7511" + "durability": null }, { - "examine": "Previously a nice crispy loaf of bread. Now just kind of icky.", - "durability": null, + "id": "7512", "name": "Soggy bread", + "examine": "Previously a nice crispy loaf of bread. Now just kind of icky.", "archery_ticket_price": "0", - "id": "7512" + "durability": null }, { - "examine": "They clearly taste so much better this way!", - "durability": null, + "id": "7513", "name": "Spicy maggots", + "examine": "They clearly taste so much better this way!", "archery_ticket_price": "0", - "id": "7513" + "durability": null }, { - "examine": "Orange slices which have been dyed, but it looks more like they died.", - "durability": null, + "id": "7514", "name": "Dyed orange", - "tradeable": "true", + "examine": "Orange slices which have been dyed, but it looks more like they died.", "archery_ticket_price": "0", - "id": "7514" + "durability": null, + "tradeable": "true" }, { - "examine": "Glad these aren't in my bed.", - "durability": null, + "id": "7515", "name": "Breadcrumbs", + "examine": "Glad these aren't in my bed.", "archery_ticket_price": "0", - "id": "7515" + "durability": null }, { - "examine": "Those leaves look useful!", - "durability": null, + "id": "7516", "name": "Kelp", + "examine": "Those leaves look useful!", "archery_ticket_price": "0", - "id": "7516" + "durability": null }, { - "examine": "Kelp flakes. Smells of the sea.", - "durability": null, + "id": "7517", "name": "Ground kelp", - "weight": "2", + "examine": "Kelp flakes. Smells of the sea.", "archery_ticket_price": "0", - "id": "7517" + "durability": null, + "weight": "2" }, { - "examine": "A smelly meat.", - "durability": null, + "id": "7518", "name": "Crab meat", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7518" - }, - { "examine": "A smelly meat.", + "archery_ticket_price": "0", "durability": null, + "weight": "0.9" + }, + { + "id": "7519", "name": "Crab meat", - "weight": "0.9", + "examine": "A smelly meat.", "archery_ticket_price": "0", - "id": "7519" + "durability": null, + "weight": "0.9" }, { + "id": "7521", + "name": "Cooked crab meat", + "examine": "Nice and Tasty!", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "examine": "Nice and Tasty!", - "grand_exchange_price": "219", - "durability": null, - "name": "Cooked crab meat", "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7521" + "weight": "0.9" }, { + "id": "7522", + "name": "Cooked crab meat", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "219", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7522" + "tradeable": "true" }, { + "id": "7523", + "name": "Cooked crab meat", "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "7523" + "durability": null, + "tradeable": "true", + "weight": "0.9" }, { + "id": "7524", + "name": "Cooked crab meat", "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "7524" + "durability": null, + "tradeable": "true", + "weight": "0.9" }, { + "id": "7525", + "name": "Cooked crab meat", "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "7525" + "durability": null, + "tradeable": "true", + "weight": "0.9" }, { + "id": "7526", + "name": "Cooked crab meat", "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "7526" + "durability": null, + "tradeable": "true", + "weight": "0.9" }, { - "examine": "A smelly paste.", - "durability": null, + "id": "7527", "name": "Ground crab meat", - "archery_ticket_price": "0", - "id": "7527" - }, - { "examine": "A smelly paste.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7528", "name": "Ground cod", + "examine": "A smelly paste.", "archery_ticket_price": "0", - "id": "7528" + "durability": null }, { - "examine": "Would taste nicer if I cooked it.", - "durability": null, + "id": "7529", "name": "Raw fishcake", + "examine": "Would taste nicer if I cooked it.", "archery_ticket_price": "0", - "id": "7529" + "durability": null }, { - "examine": "Mmmm, reminds me of the seaside.", - "durability": null, + "id": "7530", "name": "Cooked fishcake", + "examine": "Mmmm, reminds me of the seaside.", "archery_ticket_price": "0", - "id": "7530" + "durability": null }, { - "examine": "Hmmm, what can I use this for?", - "durability": null, + "id": "7532", "name": "Mudskipper hide", - "weight": "1", + "examine": "Hmmm, what can I use this for?", "archery_ticket_price": "0", - "id": "7532" + "durability": null, + "weight": "1" }, { - "remove_head": "true", - "examine": "You'll look daft, but at least you won't drown!", - "durability": null, - "name": "Fishbowl helmet", - "weight": "5", - "archery_ticket_price": "0", "id": "7534", + "name": "Fishbowl helmet", + "examine": "You'll look daft, but at least you won't drown!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" - }, - { - "examine": "I'll need a helmet to make this work.", - "durability": null, - "name": "Diving apparatus", - "weight": "10", - "archery_ticket_price": "0", - "id": "7535", - "equipment_slot": "1" - }, - { - "examine": "Fresh off the crab itself.", - "durability": null, - "name": "Fresh crab claw", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7536" - }, - { - "examine": "If it is good enough for crabs, it's good enough for me.", - "durability": null, - "name": "Crab claw", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7537", - "bonuses": "0,0,0,0,0,3,4,2,0,0,1,1,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Fresh off the crab itself.", - "durability": null, - "name": "Fresh crab shell", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7538" - }, - { "remove_head": "true", - "examine": "If it's good enough for crabs, it's good enough for me!", - "durability": null, - "name": "Crab helmet", - "weight": "0.9", + "weight": "5" + }, + { + "id": "7535", + "name": "Diving apparatus", + "examine": "I'll need a helmet to make this work.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "1", + "weight": "10" + }, + { + "id": "7536", + "name": "Fresh crab claw", + "examine": "Fresh off the crab itself.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7537", + "name": "Crab claw", + "examine": "If it is good enough for crabs, it's good enough for me.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,3,4,2,0,0,1,1,0,0,0", + "durability": null, + "equipment_slot": "9", + "weight": "0.9" + }, + { + "id": "7538", + "name": "Fresh crab shell", + "examine": "Fresh off the crab itself.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { "id": "7539", + "name": "Crab helmet", + "examine": "If it's good enough for crabs, it's good enough for me!", + "archery_ticket_price": "0", "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "0.9" }, { - "examine": "Darn, it's useless now.", - "durability": null, + "id": "7540", "name": "Broken crab claw", - "archery_ticket_price": "0", - "id": "7540" - }, - { "examine": "Darn, it's useless now.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7541", "name": "Broken crab shell", + "examine": "Darn, it's useless now.", "archery_ticket_price": "0", - "id": "7541" + "durability": null }, { - "destroy_message": "Maybe Traiborn will have another.", - "examine": "Imbued with knowledge itself.", - "durability": null, + "id": "7542", "name": "Cake of guidance", - "tradeable": "false", - "destroy": "true", + "examine": "Imbued with knowledge itself.", "archery_ticket_price": "0", - "id": "7542" + "destroy": "true", + "destroy_message": "Maybe Traiborn will have another.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Now all I need to do is cook it.", - "durability": null, + "id": "7543", "name": "Raw guide cake", + "examine": "Now all I need to do is cook it.", "archery_ticket_price": "0", - "id": "7543" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Egg containing knowledge.", - "durability": null, + "id": "7544", "name": "Enchanted egg", + "examine": "Egg containing knowledge.", "archery_ticket_price": "0", - "id": "7544" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Guiding milk.", - "durability": null, + "id": "7545", "name": "Enchanted milk", - "weight": "2", + "examine": "Guiding milk.", "archery_ticket_price": "0", - "id": "7545" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pot of special flour.", "durability": null, + "weight": "2" + }, + { + "id": "7546", "name": "Enchanted flour", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A pot of special flour.", "archery_ticket_price": "0", - "id": "7546" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "A druid pouch.", - "durability": null, + "id": "7547", "name": "Druid pouch", + "examine": "A druid pouch.", "archery_ticket_price": "0", - "id": "7547" + "durability": null }, { - "shop_price": "8", + "id": "7548", + "name": "Potato seed", "examine": "A potato seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Potato seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7548" + "durability": null, + "shop_price": "8", + "tradeable": "true" }, { - "durability": null, + "id": "7549", "name": "Potato seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7549" + "durability": null, + "tradeable": "true" }, { - "shop_price": "10", + "id": "7550", + "name": "Onion seed", "examine": "An onion seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Onion seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7550" - }, - { "durability": null, - "name": "Onion seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7551" - }, - { - "shop_price": "76", - "examine": "Arrows with mithril heads.", - "grand_exchange_price": "24", - "durability": null, - "name": "Mithril arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7552", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,22,0,0" - }, - { - "durability": null, - "name": "Mithril arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7553" - }, - { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "16", - "durability": null, - "name": "Fire rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7554" - }, - { - "durability": null, - "name": "Fire rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7555" - }, - { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "6", - "durability": null, - "name": "Water rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7556" - }, - { - "durability": null, - "name": "Water rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7557" - }, - { - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "9", - "durability": null, - "name": "Air rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7558" - }, - { - "durability": null, - "name": "Air rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7559" - }, - { - "shop_price": "140", - "examine": "Used for small missile spells.", - "grand_exchange_price": "65", - "durability": null, - "name": "Chaos rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7560" - }, - { - "durability": null, - "name": "Chaos rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7561" - }, - { "shop_price": "10", + "tradeable": "true" + }, + { + "id": "7551", + "name": "Onion seed", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7552", + "name": "Mithril arrow", + "examine": "Arrows with mithril heads.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,22,0,0", + "durability": null, + "shop_price": "76", + "tradeable": "true" + }, + { + "id": "7553", + "name": "Mithril arrow", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7554", + "name": "Fire rune", + "examine": "One of the 4 basic elemental Runes.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "7555", + "name": "Fire rune", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7556", + "name": "Water rune", + "examine": "One of the 4 basic elemental Runes.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "7557", + "name": "Water rune", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7558", + "name": "Air rune", + "examine": "One of the 4 basic elemental Runes.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "7559", + "name": "Air rune", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7560", + "name": "Chaos rune", + "examine": "Used for small missile spells.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "140", + "tradeable": "true" + }, + { + "id": "7561", + "name": "Chaos rune", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7562", + "name": "Tomato seed", "examine": "A tomato seed - plant in an allotment.", - "grand_exchange_price": "1", + "archery_ticket_price": "0", "durability": null, + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "7563", "name": "Tomato seed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7562" + "durability": null, + "tradeable": "true" }, { - "durability": null, - "name": "Tomato seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7563" - }, - { - "examine": "An inflated toad tied to a rock like a balloon.", - "durability": null, + "id": "7564", "name": "Balloon toad", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7564" - }, - { "examine": "An inflated toad tied to a rock like a balloon.", - "durability": null, - "name": "Balloon toad", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "7565" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "ge_buy_limit": "100", + "id": "7565", + "name": "Balloon toad", + "examine": "An inflated toad tied to a rock like a balloon.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7566", + "name": "Raw jubbly", "examine": "The uncooked meat of a Jubbly bird.", - "grand_exchange_price": "422", - "durability": null, - "name": "Raw jubbly", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "7566" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "422", - "durability": null, + "id": "7567", "name": "Raw jubbly", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7567" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "7568", + "name": "Cooked jubbly", "examine": "Lovely jubbly!", - "grand_exchange_price": "944", - "durability": null, - "name": "Cooked jubbly", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "7568" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "944", - "durability": null, + "tradeable": "true", + "weight": "10" + }, + { + "id": "7569", "name": "Cooked jubbly", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7569" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "durability": null, + "id": "7571", "name": "Burnt jubbly", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7571" + "durability": null, + "tradeable": "true" }, { - "examine": "Like a banana only redder.", - "durability": null, - "name": "Red banana", - "archery_ticket_price": "0", "id": "7572", + "name": "Red banana", + "examine": "Like a banana only redder.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "5" }, { - "examine": "Like monkey nuts only tchikier.", - "durability": null, + "id": "7573", "name": "Tchiki monkey nuts", + "examine": "Like monkey nuts only tchikier.", "archery_ticket_price": "0", - "id": "7573" + "durability": null }, { - "examine": "Perfect for stuffing snakes.", - "durability": null, + "id": "7574", "name": "Sliced red banana", + "examine": "Perfect for stuffing snakes.", "archery_ticket_price": "0", - "id": "7574" + "durability": null }, { - "examine": "Mixing this with jam would just be wrong.", - "durability": null, + "id": "7575", "name": "Tchiki nut paste", + "examine": "Mixing this with jam would just be wrong.", "archery_ticket_price": "0", - "id": "7575" + "durability": null }, { - "examine": "Like a snake only not alive.", - "durability": null, + "id": "7576", "name": "Snake corpse", - "weight": "3", + "examine": "Like a snake only not alive.", "archery_ticket_price": "0", - "id": "7576" + "durability": null, + "weight": "3" }, { - "examine": "This snake is stuffed right up.", - "durability": null, + "id": "7577", "name": "Raw stuffed snake", - "weight": "3", + "examine": "This snake is stuffed right up.", "archery_ticket_price": "0", - "id": "7577" + "durability": null, + "weight": "3" }, { - "examine": "Is this really what you wanted to do?", - "durability": null, + "id": "7578", "name": "Odd stuffed snake", + "examine": "Is this really what you wanted to do?", "archery_ticket_price": "0", - "id": "7578" + "durability": null }, { - "examine": "Fit for a Monkey King. (cooked)", - "durability": null, + "id": "7579", "name": "Stuffed snake", - "weight": "3", + "examine": "Fit for a Monkey King. (cooked)", "archery_ticket_price": "0", - "id": "7579" + "durability": null, + "weight": "3" }, { - "examine": "Your hellish pet cat!!", - "durability": null, + "id": "7581", "name": "Overgrown hellcat", + "examine": "Your hellish pet cat!!", "archery_ticket_price": "0", - "id": "7581" + "durability": null }, { - "examine": "Your hellish pet cat!!", - "durability": null, + "id": "7582", "name": "Hell cat", + "examine": "Your hellish pet cat!!", "archery_ticket_price": "0", - "id": "7582" + "durability": null }, { - "examine": "Your hellish pet cat!!", - "durability": null, + "id": "7583", "name": "Hell-kitten", - "archery_ticket_price": "0", - "id": "7583" - }, - { "examine": "Your hellish pet cat!!", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7585", "name": "Wily hellcat", + "examine": "Your hellish pet cat!!", "archery_ticket_price": "0", - "id": "7585" + "durability": null }, { - "examine": "I can train on this", - "durability": null, + "id": "7586", "name": "Dummy", + "examine": "I can train on this", "archery_ticket_price": "0", - "id": "7586" + "durability": null }, { - "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, + "id": "7587", "name": "Coffin", - "archery_ticket_price": "0", - "id": "7587" - }, - { "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", "archery_ticket_price": "0", - "id": "7588" + "durability": null }, { + "id": "7588", + "name": "Coffin", "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", "archery_ticket_price": "0", - "id": "7589" + "durability": null }, { + "id": "7589", + "name": "Coffin", "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", "archery_ticket_price": "0", - "id": "7590" + "durability": null }, { + "id": "7590", + "name": "Coffin", "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", "archery_ticket_price": "0", - "id": "7591" + "durability": null }, { - "examine": "Aside from the braaaains on the lapel, it's still quite good.", - "durability": null, - "name": "Zombie shirt", + "id": "7591", + "name": "Coffin", + "examine": "Filled with items. Like a bank, but spookier!", "archery_ticket_price": "0", + "durability": null + }, + { "id": "7592", + "name": "Zombie shirt", + "examine": "Aside from the braaaains on the lapel, it's still quite good.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "4" }, { - "examine": "Good for a shamble about town.", - "durability": null, - "name": "Zombie trousers", - "archery_ticket_price": "0", "id": "7593", + "name": "Zombie trousers", + "examine": "Good for a shamble about town.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "7" }, { - "remove_head": "true", - "examine": "I look 40,000 years old in this...", - "durability": null, - "name": "Zombie mask", - "archery_ticket_price": "0", "id": "7594", + "name": "Zombie mask", + "examine": "I look 40,000 years old in this...", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true" }, { - "examine": "Smells pretty funky.", - "durability": null, - "name": "Zombie gloves", - "archery_ticket_price": "0", "id": "7595", + "name": "Zombie gloves", + "examine": "Smells pretty funky.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "9" }, { - "shop_price": "160", - "examine": "Thrilling.", - "durability": null, - "name": "Zombie boots", - "archery_ticket_price": "0", "id": "7596", - "equipment_slot": "10" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", + "name": "Zombie boots", + "examine": "Thrilling.", "archery_ticket_price": "0", - "id": "7597" + "durability": null, + "equipment_slot": "10", + "shop_price": "160" }, { - "durability": null, + "id": "7597", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7598" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7598", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7599" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7599", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7600" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7600", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7601" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7601", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7602" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7602", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7603" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7603", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7604" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7604", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7605" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7605", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7606" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7606", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7607" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7607", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7608" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7608", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7609" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7609", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7610" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7610", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7611" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7611", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7612" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7612", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7613" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7613", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7614" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7614", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7615" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7615", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7616" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7616", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7617" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "7617", "name": "Item", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7618" + "durability": null, + "tradeable": "true" }, { - "examine": "A silvery rod of mithril and silver with sapphire on top.", + "id": "7618", + "name": "Item", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "7620", "name": "Silvthrill rod", + "examine": "A silvery rod of mithril and silver with sapphire on top.", "archery_ticket_price": "0", - "id": "7620" + "durability": null }, { - "examine": "A bucket partially filled with rubble. / A bucket totally filled with rubble.", - "durability": null, + "id": "7622", "name": "Bucket of rubble", - "weight": "2", - "archery_ticket_price": "0", - "id": "7622" - }, - { "examine": "A bucket partially filled with rubble. / A bucket totally filled with rubble.", - "durability": null, - "name": "Bucket of rubble", - "weight": "2", "archery_ticket_price": "0", - "id": "7624" + "durability": null, + "weight": "2" }, { + "id": "7624", + "name": "Bucket of rubble", "examine": "A bucket partially filled with rubble. / A bucket totally filled with rubble.", - "durability": null, - "name": "Bucket of rubble", - "weight": "2", "archery_ticket_price": "0", - "id": "7626" + "durability": null, + "weight": "2" }, { - "destroy_message": "You can get another plaster fragment by exploring near the Inn in Burgh de Rott.", - "examine": "A fragment of plaster with some impressions on it.", + "id": "7626", + "name": "Bucket of rubble", + "examine": "A bucket partially filled with rubble. / A bucket totally filled with rubble.", + "archery_ticket_price": "0", "durability": null, + "weight": "2" + }, + { + "id": "7628", "name": "Plaster fragment", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A fragment of plaster with some impressions on it.", "archery_ticket_price": "0", - "id": "7628" + "destroy": "true", + "destroy_message": "You can get another plaster fragment by exploring near the Inn in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You can get another scroll by exploring near the Inn in Burgh de Rott.", - "examine": "An ancient tattered scroll.", - "durability": null, + "id": "7629", "name": "Dusty scroll", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient tattered scroll.", "archery_ticket_price": "0", - "id": "7629" + "destroy": "true", + "destroy_message": "You can get another scroll by exploring near the Inn in Burgh de Rott.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another temple key by talking to Drezel.", - "examine": "A key for the Temple Library. (In Aid of the Myreque)", - "durability": null, + "id": "7632", "name": "Temple library key", - "tradeable": "false", - "destroy": "true", + "examine": "A key for the Temple Library. (In Aid of the Myreque)", "archery_ticket_price": "0", - "id": "7632" + "destroy": "true", + "destroy_message": "You can get another temple key by talking to Drezel.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Ancient Book by searching the library in the temple on the Salve.", - "shop_price": "5000", - "examine": "The unholy book of a forgotten god.", - "durability": null, - "name": "Ancient book", - "weight": "1", - "archery_ticket_price": "0", "id": "7633", - "bonuses": "4,4,4,6,0,4,4,4,6,0,2,0,0,5,0" + "name": "Ancient book", + "examine": "The unholy book of a forgotten god.", + "archery_ticket_price": "0", + "bonuses": "4,4,4,6,0,4,4,4,6,0,2,0,0,5,0", + "destroy_message": "You can get another Ancient Book by searching the library in the temple on the Salve.", + "durability": null, + "shop_price": "5000", + "weight": "1" }, { - "destroy_message": "You can get another Crumbling tome by searching the library in the temple on the Salve.", - "examine": "An ancient history book.", - "durability": null, + "id": "7634", "name": "Battered tome", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient history book.", "archery_ticket_price": "0", - "id": "7634" + "destroy": "true", + "destroy_message": "You can get another Crumbling tome by searching the library in the temple on the Salve.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Leather book by searching the library in the temple on the Salve.", - "examine": "An ancient leather-bound tome.", - "durability": null, + "id": "7635", "name": "Leather book", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient leather-bound tome.", "archery_ticket_price": "0", - "id": "7635" + "destroy": "true", + "destroy_message": "You can get another Leather book by searching the library in the temple on the Salve.", + "durability": null, + "tradeable": "false" }, { - "examine": "Rod of Ivandis dust.", - "durability": null, + "id": "7636", "name": "Rod dust", + "examine": "Rod of Ivandis dust.", "archery_ticket_price": "0", - "id": "7636" + "durability": null }, { - "examine": "A silvery rod of mithril and silver with sapphire on top.", - "durability": null, - "name": "Silvthrill rod", - "archery_ticket_price": "0", "id": "7637", - "equipment_slot": "3" - }, - { - "examine": "A silvery rod of mithril and silver with sapphire on top.", - "durability": null, "name": "Silvthrill rod", + "examine": "A silvery rod of mithril and silver with sapphire on top.", "archery_ticket_price": "0", - "id": "7638" - }, - { - "turn90cw_anim": "1207", - "examine": "A fully charged rod.", - "walk_anim": "1205", - "has_special": "true", "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(10)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7639", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(9)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7640", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "destroy": "true", - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "defence_anim": "420", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(8)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7641", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(7)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7642", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(6)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7643", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(5)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7644", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(4)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7645", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(3)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7646", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "2! 2 charges left! Ha Ha Ha.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(2)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7647", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "name": "Rod of ivandis(1)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7648", - "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "destroy_message": "You can make another one of these by using clay on Ivandis' coffin.", - "examine": "A mould of the Rod of Ivandis.", - "durability": null, - "name": "Rod clay mould", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7649" - }, - { - "ge_buy_limit": "1000", - "examine": "It's ground up silver.", - "grand_exchange_price": "316", - "durability": null, - "name": "Silver dust", - "tradeable": "true", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "7650" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "316", - "durability": null, - "name": "Silver dust", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7651" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "96", - "examine": "An unfinished potion.", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7652" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "96", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7653" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "77", - "examine": "An unfinished potion.", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7654" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "77", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7655" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "58", - "examine": "An unfinished potion.", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7656" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "58", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7657" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "30", - "examine": "An unfinished potion.", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7658" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "30", - "durability": null, - "name": "Guthix balance(unf)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7659" - }, - { - "ge_buy_limit": "1000", - "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", - "grand_exchange_price": "140", - "durability": null, - "name": "Guthix balance(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7660" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "140", - "durability": null, - "name": "Guthix balance(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7661" - }, - { - "ge_buy_limit": "1000", - "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", - "grand_exchange_price": "168", - "durability": null, - "name": "Guthix balance(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7662" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "168", - "durability": null, - "name": "Guthix balance(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7663" - }, - { - "ge_buy_limit": "1000", - "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", - "grand_exchange_price": "132", - "durability": null, - "name": "Guthix balance(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7664" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "132", - "durability": null, - "name": "Guthix balance(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7665" - }, - { - "ge_buy_limit": "1000", - "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", - "grand_exchange_price": "66", - "durability": null, - "name": "Guthix balance(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7666" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "66", - "durability": null, - "name": "Guthix balance(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7667" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1663", - "examine": "A specially crafted hammer with strange markings on it.", - "walk_anim": "1663", - "turn90ccw_anim": "1663", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1663", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "1615", - "stand_anim": "1662", - "tradeable": "true", - "run_anim": "1664", - "archery_ticket_price": "0", - "id": "7668", - "stand_turn_anim": "823", - "bonuses": "-4,-4,35,-4,0,0,0,0,0,0,0,35,0,0,0", - "shop_price": "3000", - "durability": null, - "weight": "1.8", - "weapon_interface": "10", - "render_anim": "27", - "name": "Gadderhammer" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1615", - "durability": null, - "name": "Gadderhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7669" - }, - { - "turn90cw_anim": "3680", - "examine": "I think they look a bit silly.", - "walk_anim": "3680", - "durability": null, - "weight": "0.4", - "turn90ccw_anim": "3680", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "turn180_anim": "3680", - "render_anim": "1386", - "equipment_slot": "3", - "stand_anim": "3677", - "name": "Boxing gloves", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7671", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "turn90cw_anim": "3680", - "examine": "I think they look a bit silly.", - "walk_anim": "3680", - "durability": null, - "weight": "0.4", - "turn90ccw_anim": "3680", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "turn180_anim": "3680", - "render_anim": "1386", - "equipment_slot": "3", - "stand_anim": "3677", - "name": "Boxing gloves", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "7673", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "examine": "A less-than razor sharp sword.", - "durability": null, - "name": "Wooden sword", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "7675", - "weapon_interface": "5", - "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", - "render_anim": "2584", "equipment_slot": "3" }, { + "id": "7638", + "name": "Silvthrill rod", + "examine": "A silvery rod of mithril and silver with sapphire on top.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7639", + "name": "Rod of ivandis(10)", + "examine": "A fully charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7640", + "name": "Rod of ivandis(9)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7641", + "name": "Rod of ivandis(8)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "destroy": "true", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "7642", + "name": "Rod of ivandis(7)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7643", + "name": "Rod of ivandis(6)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7644", + "name": "Rod of ivandis(5)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7645", + "name": "Rod of ivandis(4)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7646", + "name": "Rod of ivandis(3)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7647", + "name": "Rod of ivandis(2)", + "examine": "2! 2 charges left! Ha Ha Ha.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7648", + "name": "Rod of ivandis(1)", + "examine": "A partialy charged rod.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "false", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "7649", + "name": "Rod clay mould", + "examine": "A mould of the Rod of Ivandis.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can make another one of these by using clay on Ivandis' coffin.", + "durability": null + }, + { + "id": "7650", + "name": "Silver dust", + "examine": "It's ground up silver.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.33" + }, + { + "id": "7651", + "name": "Silver dust", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7652", + "name": "Guthix balance(unf)", + "examine": "An unfinished potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7653", + "name": "Guthix balance(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7654", + "name": "Guthix balance(unf)", + "examine": "An unfinished potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7655", + "name": "Guthix balance(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7656", + "name": "Guthix balance(unf)", + "examine": "An unfinished potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7657", + "name": "Guthix balance(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7658", + "name": "Guthix balance(unf)", + "examine": "An unfinished potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7659", + "name": "Guthix balance(unf)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7660", + "name": "Guthix balance(4)", + "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7661", + "name": "Guthix balance(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7662", + "name": "Guthix balance(3)", + "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7663", + "name": "Guthix balance(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7664", + "name": "Guthix balance(2)", + "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7665", + "name": "Guthix balance(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7666", + "name": "Guthix balance(1)", + "examine": "A potion of harralander, red spiders eggs, garlic and silver dust.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7667", + "name": "Guthix balance(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7668", + "name": "Gadderhammer", + "examine": "A specially crafted hammer with strange markings on it.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_speed": "5", + "bonuses": "-4,-4,35,-4,0,0,0,0,0,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "27", + "run_anim": "1664", + "shop_price": "3000", + "stand_anim": "1662", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1663", + "turn90ccw_anim": "1663", + "turn90cw_anim": "1663", + "two_handed": "true", + "walk_anim": "1663", + "weapon_interface": "10", + "weight": "1.8" + }, + { + "id": "7669", + "name": "Gadderhammer", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7671", + "name": "Boxing gloves", + "examine": "I think they look a bit silly.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "1386", + "run_anim": "824", + "stand_anim": "3677", + "stand_turn_anim": "823", + "turn180_anim": "3680", + "turn90ccw_anim": "3680", + "turn90cw_anim": "3680", + "two_handed": "true", + "walk_anim": "3680", + "weapon_interface": "9", + "weight": "0.4" + }, + { + "id": "7673", + "name": "Boxing gloves", + "examine": "I think they look a bit silly.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "1386", + "run_anim": "824", + "stand_anim": "3677", + "stand_turn_anim": "823", + "turn180_anim": "3680", + "turn90ccw_anim": "3680", + "turn90cw_anim": "3680", + "two_handed": "true", + "walk_anim": "3680", + "weapon_interface": "9", + "weight": "0.4" + }, + { + "id": "7675", + "name": "Wooden sword", + "examine": "A less-than razor sharp sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "7676", "name": "Wooden shield", "archery_ticket_price": "0", - "id": "7676", + "durability": null, + "equip_audio": "2250", "equipment_slot": "5" }, { - "examine": "It knows where the treasure is.", - "durability": null, + "id": "7677", "name": "Treasure stone", - "weight": "1.8", + "examine": "It knows where the treasure is.", "archery_ticket_price": "0", - "id": "7677" + "durability": null, + "weight": "1.8" }, { - "shop_price": "160", - "examine": "You can use this to open the prize chest!", - "durability": null, + "id": "7678", "name": "Prize key", - "weight": "1", + "examine": "You can use this to open the prize chest!", "archery_ticket_price": "0", - "id": "7678" + "durability": null, + "shop_price": "160", + "weight": "1" }, { - "examine": "A good tool for bashing someone.", - "durability": null, - "name": "Pugel", - "archery_ticket_price": "0", - "two_handed": "true", "id": "7679", - "weapon_interface": "14", - "equipment_slot": "3" - }, - { - "durability": null, "name": "Pugel", - "tradeable": "true", + "examine": "A good tool for bashing someone.", "archery_ticket_price": "0", - "id": "7680" - }, - { - "examine": "Party Pete's Bumper Book Of Games", "durability": null, - "name": "Game book", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7681" - }, - { - "durability": null, - "name": "Hoop", - "archery_ticket_price": "0", - "id": "7682", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Hoop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7683" - }, - { - "durability": null, - "name": "Dart", - "archery_ticket_price": "0", - "attack_speed": "3", - "id": "7684", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7685" - }, - { - "durability": null, - "name": "Bow and arrow", - "archery_ticket_price": "0", - "id": "7686", - "weapon_interface": "1", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Bow and arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7687" - }, - { - "examine": "The kettle is empty.", - "durability": null, - "name": "Kettle", - "archery_ticket_price": "0", - "id": "7688" - }, - { - "durability": null, - "name": "Kettle", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7689" - }, - { - "examine": "It's full of cold water.", - "durability": null, - "name": "Full kettle", - "archery_ticket_price": "0", - "id": "7690" - }, - { - "examine": "It's full of boiling water.", - "durability": null, - "name": "Hot kettle", - "archery_ticket_price": "0", - "id": "7691" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7692" - }, - { - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7693" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7694" - }, - { - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7695" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7696" - }, - { - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7697" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7698" - }, - { - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7699" - }, - { - "examine": "Add boiling water to make a tea.", - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7700" - }, - { - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7701" - }, - { - "examine": "This teapot is empty.", - "durability": null, - "name": "Teapot", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7702" - }, - { - "durability": null, - "name": "Teapot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7703" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7704" - }, - { - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7705" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7706" - }, - { - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7707" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7708" - }, - { - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7709" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7710" - }, - { - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7711" - }, - { - "examine": "Add boiling water to make a tea.", - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7712" - }, - { - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7713" - }, - { - "examine": "This teapot is empty.", - "durability": null, - "name": "Teapot", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7714" - }, - { - "durability": null, - "name": "Teapot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7715" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7716" - }, - { - "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7717" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7718" - }, - { - "durability": null, - "name": "Pot of tea (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7719" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7720" - }, - { - "durability": null, - "name": "Pot of tea (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7721" - }, - { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7722" - }, - { - "durability": null, - "name": "Pot of tea (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7723" - }, - { - "examine": "Add boiling water to make a tea.", - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7724" - }, - { - "durability": null, - "name": "Teapot with leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7725" - }, - { - "examine": "This teapot is empty.", - "durability": null, - "name": "Teapot", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7726" - }, - { - "durability": null, - "name": "Teapot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7727" - }, - { - "shop_price": "1", - "examine": "An empty cup.", - "grand_exchange_price": "7", - "durability": null, - "name": "Empty cup", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7728" - }, - { - "durability": null, - "name": "Empty cup", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7729" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7730" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7731" - }, - { - "examine": "A porcelain cup.", - "durability": null, - "name": "Porcelain cup", - "archery_ticket_price": "0", - "id": "7732" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7733" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7734" - }, - { - "examine": "A porcelain cup.", - "durability": null, - "name": "Porcelain cup", - "archery_ticket_price": "0", - "id": "7735" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7736" - }, - { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", - "durability": null, - "name": "Cup of tea", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7737" - }, - { - "examine": "Mmm, how about a nice cup of tea?", - "durability": null, - "name": "Tea leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7738" - }, - { - "durability": null, - "name": "Tea leaves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7739" - }, - { - "examine": "A glass of frothy ale.", - "grand_exchange_price": "151", - "durability": null, - "name": "Beer", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7740" - }, - { - "durability": null, - "name": "Beer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7741" - }, - { - "shop_price": "1", - "examine": "I need to fill this with beer.", - "grand_exchange_price": "25", - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "7742" - }, - { - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7743" - }, - { - "shop_price": "3", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "131", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7744" - }, - { - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7745" - }, - { - "shop_price": "2", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "569", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7746" - }, - { - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7747" - }, - { - "shop_price": "2", - "examine": "A glass of bitter.", - "grand_exchange_price": "523", - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7748" - }, - { - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7749" - }, - { - "shop_price": "5", - "examine": "A foul smelling brew.", - "grand_exchange_price": "14", - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7750" - }, - { - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7751" - }, - { - "shop_price": "2", - "examine": "A glass of Cider", - "grand_exchange_price": "1539", - "durability": null, - "name": "Cider", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7752" - }, - { - "durability": null, - "name": "Cider", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7753" - }, - { - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "2371", - "durability": null, - "name": "Chef's delight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7754" - }, - { - "durability": null, - "name": "Chef's delight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7755" - }, - { - "durability": null, - "name": "Paintbrush", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7756", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Paintbrush", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7757" - }, - { - "examine": "A decent-enough weapon gone rusty.", - "durability": null, - "name": "Rusty sword", - "weight": "2", - "archery_ticket_price": "0", - "id": "7758", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "examine": "Nice bit of crafting.", - "grand_exchange_price": "3140", - "durability": null, - "name": "Toy soldier", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7759" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3140", - "durability": null, - "name": "Toy soldier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7760" - }, - { - "examine": "Nice bit of crafting.", - "grand_exchange_price": "3123", - "durability": null, - "name": "Toy soldier (wound)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7761" - }, - { - "durability": null, - "name": "Toy soldier (wound)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7762" - }, - { - "ge_buy_limit": "100", - "examine": "In inventory: Nice bit of crafting! When released: Nice bit of crafting that...", - "grand_exchange_price": "3236", - "durability": null, - "name": "Toy doll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7763" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3236", - "durability": null, - "name": "Toy doll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7764" - }, - { - "examine": "In inventory: Nice bit of crafting! When released: Nice bit of crafting that...", - "grand_exchange_price": "3289", - "durability": null, - "name": "Toy doll (wound)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7765" - }, - { - "durability": null, - "name": "Toy doll (wound)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7766" - }, - { - "shop_price": "1705", - "ge_buy_limit": "100", - "examine": "Nice bit of crafting!", - "grand_exchange_price": "3384", - "durability": null, - "name": "Toy mouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7767" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3384", - "durability": null, - "name": "Toy mouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7768" - }, - { - "shop_price": "1705", - "examine": "Nice bit of crafting!", - "grand_exchange_price": "3484", - "durability": null, - "name": "Toy mouse (wound)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7769" - }, - { - "durability": null, - "name": "Toy mouse (wound)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7770" - }, - { - "ge_buy_limit": "100", - "examine": "Inventory: Nice bit of crafting!As a follower: An amazing piece of crafting.", - "grand_exchange_price": "2930", - "durability": null, - "name": "Clockwork cat", - "tradeable": "true", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7771" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2930", - "durability": null, - "name": "Clockwork cat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7772" - }, - { - "examine": "I can use this to make a lyre.", - "durability": null, - "name": "Branch", - "weight": "1", - "archery_ticket_price": "0", - "id": "7773", - "equipment_slot": "3" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7774" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7775" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7776" - }, - { - "examine": "A long section of vine made up of lots of shorter sections.", - "durability": null, - "name": "Long vine", - "archery_ticket_price": "0", - "id": "7777" - }, - { - "examine": "A short section of vines.", - "durability": null, - "name": "Short vine", - "archery_ticket_price": "0", - "id": "7778" - }, - { - "examine": "A tome of learning that focuses on the Fishing skill.", - "durability": null, - "name": "Fishing tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7779" - }, - { - "examine": "A tome of learning that focuses on the Fishing skill.", - "durability": null, - "name": "Fishing tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7780" - }, - { - "examine": "A tome of learning that focuses on the Fishing skill.", - "durability": null, - "name": "Fishing tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7781" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7782" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7783" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7784" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7785" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7786" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7787" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7788" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7789" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7790" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Mining skill.", - "durability": null, - "name": "Mining tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7791" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Mining skill.", - "durability": null, - "name": "Mining tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7792" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Mining skill.", - "durability": null, - "name": "Mining tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7793" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7794" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7795" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7796" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Woodcutting skill.", - "durability": null, - "name": "Woodcutting tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7797" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Woodcutting skill.", - "durability": null, - "name": "Woodcutting tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7798" - }, - { - "shop_price": "160", - "examine": "A tome of learning that focuses on the Woodcutting skill.", - "durability": null, - "name": "Woodcutting tome", - "weight": "1", - "archery_ticket_price": "0", - "id": "7799" - }, - { - "examine": "A shell from a giant snail.", - "durability": null, - "name": "Snail shell", - "weight": "7", - "archery_ticket_price": "0", - "id": "7800" - }, - { - "ge_buy_limit": "10000", - "examine": "Scaly but not slimy!", - "grand_exchange_price": "1618", - "durability": null, - "name": "Snake hide", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7801" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1618", - "durability": null, - "name": "Snake hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7802" - }, - { - "shop_price": "100", - "examine": "A non-magical copy of the make-over mage's amulet.", - "durability": null, - "name": "Yin yang amulet", - "archery_ticket_price": "0", - "id": "7803", - "equipment_slot": "2" - }, - { - "turn90cw_anim": "1207", - "walk_anim": "1205", - "durability": null, - "turn90ccw_anim": "1208", - "two_handed": "true", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", "equipment_slot": "3", - "stand_anim": "813", - "name": "Picture", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "7804", - "stand_turn_anim": "1209", - "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0" + "two_handed": "true", + "weapon_interface": "14" }, { + "id": "7680", + "name": "Pugel", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7681", + "name": "Game book", + "examine": "Party Pete's Bumper Book Of Games", + "archery_ticket_price": "0", "durability": null, - "name": "Picture", "tradeable": "true", - "archery_ticket_price": "0", - "id": "7805" + "weight": "1" }, { - "attack_anims": "381,390,390,390", - "examine": "A heavy duty sword.", + "id": "7682", + "name": "Hoop", + "archery_ticket_price": "0", "durability": null, - "name": "Anger sword", + "equipment_slot": "3" + }, + { + "id": "7683", + "name": "Hoop", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7684", + "name": "Dart", "archery_ticket_price": "0", "attack_speed": "3", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3" + }, + { + "id": "7685", + "name": "Dart", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7686", + "name": "Bow and arrow", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "1" + }, + { + "id": "7687", + "name": "Bow and arrow", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7688", + "name": "Kettle", + "examine": "The kettle is empty.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7689", + "name": "Kettle", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7690", + "name": "Full kettle", + "examine": "It's full of cold water.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7691", + "name": "Hot kettle", + "examine": "It's full of boiling water.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7692", + "name": "Pot of tea (4)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7693", + "name": "Pot of tea (4)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7694", + "name": "Pot of tea (3)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7695", + "name": "Pot of tea (3)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7696", + "name": "Pot of tea (2)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7697", + "name": "Pot of tea (2)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7698", + "name": "Pot of tea (1)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7699", + "name": "Pot of tea (1)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7700", + "name": "Teapot with leaves", + "examine": "Add boiling water to make a tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7701", + "name": "Teapot with leaves", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7702", + "name": "Teapot", + "examine": "This teapot is empty.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "7703", + "name": "Teapot", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7704", + "name": "Pot of tea (4)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7705", + "name": "Pot of tea (4)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7706", + "name": "Pot of tea (3)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7707", + "name": "Pot of tea (3)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7708", + "name": "Pot of tea (2)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7709", + "name": "Pot of tea (2)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7710", + "name": "Pot of tea (1)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7711", + "name": "Pot of tea (1)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7712", + "name": "Teapot with leaves", + "examine": "Add boiling water to make a tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7713", + "name": "Teapot with leaves", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7714", + "name": "Teapot", + "examine": "This teapot is empty.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "7715", + "name": "Teapot", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7716", + "name": "Pot of tea (4)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7717", + "name": "Pot of tea (4)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7718", + "name": "Pot of tea (3)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7719", + "name": "Pot of tea (3)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7720", + "name": "Pot of tea (2)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7721", + "name": "Pot of tea (2)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7722", + "name": "Pot of tea (1)", + "examine": "I'd really like a nice cup of tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7723", + "name": "Pot of tea (1)", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7724", + "name": "Teapot with leaves", + "examine": "Add boiling water to make a tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1.5" + }, + { + "id": "7725", + "name": "Teapot with leaves", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7726", + "name": "Teapot", + "examine": "This teapot is empty.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "7727", + "name": "Teapot", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7728", + "name": "Empty cup", + "examine": "An empty cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "1", + "tradeable": "false" + }, + { + "id": "7729", + "name": "Empty cup", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7730", + "name": "Cup of tea", + "examine": "A nice cup of nettle tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7731", + "name": "Cup of tea", + "examine": "A milky cup of nettle tea.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7732", + "name": "Porcelain cup", + "examine": "A porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7733", + "name": "Cup of tea", + "examine": "Some nettle tea in a porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7734", + "name": "Cup of tea", + "examine": "Some milky nettle tea in a porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7735", + "name": "Porcelain cup", + "examine": "A porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7736", + "name": "Cup of tea", + "examine": "Some nettle tea in a porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7737", + "name": "Cup of tea", + "examine": "Some milky nettle tea in a porcelain cup.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "10", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "7738", + "name": "Tea leaves", + "examine": "Mmm, how about a nice cup of tea?", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7739", + "name": "Tea leaves", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7740", + "name": "Beer", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "7741", + "name": "Beer", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7742", + "name": "Beer glass", + "examine": "I need to fill this with beer.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "1", + "tradeable": "false", + "weight": "0.05" + }, + { + "id": "7743", + "name": "Beer glass", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7744", + "name": "Asgarnian ale", + "examine": "Probably the finest readily-available ale in Asgarnia.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "3", + "tradeable": "false" + }, + { + "id": "7745", + "name": "Asgarnian ale", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7746", + "name": "Greenman's ale", + "examine": "A glass of frothy ale.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "7747", + "name": "Greenman's ale", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7748", + "name": "Dragon bitter", + "examine": "A glass of bitter.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "2", + "tradeable": "false", + "weight": "1" + }, + { + "id": "7749", + "name": "Dragon bitter", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7750", + "name": "Moonlight mead", + "examine": "A foul smelling brew.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "7751", + "name": "Moonlight mead", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7752", + "name": "Cider", + "examine": "A glass of Cider", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "shop_price": "2", + "tradeable": "false", + "weight": "1" + }, + { + "id": "7753", + "name": "Cider", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7754", + "name": "Chef's delight", + "examine": "A fruity, full-bodied ale.", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7755", + "name": "Chef's delight", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "tradeable": "false" + }, + { + "id": "7756", + "name": "Paintbrush", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "7757", + "name": "Paintbrush", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7758", + "name": "Rusty sword", + "examine": "A decent-enough weapon gone rusty.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "2" + }, + { + "id": "7759", + "name": "Toy soldier", + "examine": "Nice bit of crafting.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7760", + "name": "Toy soldier", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7761", + "name": "Toy soldier (wound)", + "examine": "Nice bit of crafting.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "true" + }, + { + "id": "7762", + "name": "Toy soldier (wound)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7763", + "name": "Toy doll", + "examine": "Nice bit of crafting!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7764", + "name": "Toy doll", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7765", + "name": "Toy doll (wound)", + "examine": "Nice bit of crafting!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7766", + "name": "Toy doll (wound)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7767", + "name": "Toy mouse", + "examine": "Nice bit of crafting!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1705", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7768", + "name": "Toy mouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7769", + "name": "Toy mouse (wound)", + "examine": "Nice bit of crafting!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1705", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7770", + "name": "Toy mouse (wound)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7771", + "name": "Clockwork cat", + "examine": "Inventory: Nice bit of crafting!As a follower: An amazing piece of crafting.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7772", + "name": "Clockwork cat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7773", + "name": "Branch", + "examine": "I can use this to make a lyre.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1" + }, + { + "id": "7774", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7775", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7776", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7777", + "name": "Long vine", + "examine": "A long section of vine made up of lots of shorter sections.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7778", + "name": "Short vine", + "examine": "A short section of vines.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7779", + "name": "Fishing tome", + "examine": "A tome of learning that focuses on the Fishing skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7780", + "name": "Fishing tome", + "examine": "A tome of learning that focuses on the Fishing skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7781", + "name": "Fishing tome", + "examine": "A tome of learning that focuses on the Fishing skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7782", + "name": "Agility tome", + "examine": "A tome of learning that focuses on Agility.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7783", + "name": "Agility tome", + "examine": "A tome of learning that focuses on Agility.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7784", + "name": "Agility tome", + "examine": "A tome of learning that focuses on Agility.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7785", + "name": "Thieving tome", + "examine": "A tome of learning that focuses on the Thieving skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7786", + "name": "Thieving tome", + "examine": "A tome of learning that focuses on the Thieving skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7787", + "name": "Thieving tome", + "examine": "A tome of learning that focuses on the Thieving skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7788", + "name": "Slayer tome", + "examine": "A tome of learning which focuses on the Slayer skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7789", + "name": "Slayer tome", + "examine": "A tome of learning which focuses on the Slayer skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7790", + "name": "Slayer tome", + "examine": "A tome of learning which focuses on the Slayer skill.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7791", + "name": "Mining tome", + "examine": "A tome of learning that focuses on the Mining skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7792", + "name": "Mining tome", + "examine": "A tome of learning that focuses on the Mining skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7793", + "name": "Mining tome", + "examine": "A tome of learning that focuses on the Mining skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7794", + "name": "Firemaking tome", + "examine": "A tome of learning that focuses on Firemaking.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7795", + "name": "Firemaking tome", + "examine": "A tome of learning that focuses on Firemaking.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7796", + "name": "Firemaking tome", + "examine": "A tome of learning that focuses on Firemaking.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7797", + "name": "Woodcutting tome", + "examine": "A tome of learning that focuses on the Woodcutting skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7798", + "name": "Woodcutting tome", + "examine": "A tome of learning that focuses on the Woodcutting skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7799", + "name": "Woodcutting tome", + "examine": "A tome of learning that focuses on the Woodcutting skill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7800", + "name": "Snail shell", + "examine": "A shell from a giant snail.", + "archery_ticket_price": "0", + "durability": null, + "weight": "7" + }, + { + "id": "7801", + "name": "Snake hide", + "examine": "Scaly but not slimy!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7802", + "name": "Snake hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7803", + "name": "Yin yang amulet", + "examine": "A non-magical copy of the make-over mage's amulet.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "shop_price": "100" + }, + { + "id": "7804", + "name": "Picture", + "archery_ticket_price": "0", + "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3" + }, + { + "id": "7805", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "7806", - "weapon_interface": "5", + "name": "Anger sword", + "examine": "A heavy duty sword.", + "archery_ticket_price": "0", + "attack_anims": "381,390,390,390", + "attack_speed": "3", "bonuses": "20,20,20,0,0,0,2,1,0,0,0,5,0,0,0", "defence_anim": "397", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "5" }, { - "attack_anims": "395,395,395,395", - "examine": "A heavy duty axe.", - "durability": null, - "name": "Anger battleaxe", - "archery_ticket_price": "0", "id": "7807", - "weapon_interface": "2", + "name": "Anger battleaxe", + "examine": "A heavy duty axe.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", "bonuses": "20,20,20,0,0,0,0,0,0,-1,0,13,0,0,0", "defence_anim": "397", - "equipment_slot": "3" + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "weapon_interface": "2" }, { - "attack_anims": "390,390,381,390", - "examine": "A heavy duty mace.", - "durability": null, - "name": "Anger mace", - "archery_ticket_price": "0", "id": "7808", - "weapon_interface": "8", + "name": "Anger mace", + "examine": "A heavy duty mace.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", "bonuses": "20,20,20,0,0,0,0,0,0,0,0,5,1,0,0", "defence_anim": "397", - "equipment_slot": "3" - }, - { - "turn90cw_anim": "1207", - "examine": "A heavy duty spear.", - "walk_anim": "1205", "durability": null, - "turn90ccw_anim": "1208", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "2079", "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "stand_anim": "813", - "name": "Anger spear", - "run_anim": "1210", - "archery_ticket_price": "0", + "weapon_interface": "8" + }, + { "id": "7809", + "name": "Anger spear", + "examine": "A heavy duty spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "bonuses": "20,20,20,0,0,1,1,0,0,0,0,6,0,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "20,20,20,0,0,1,1,0,0,0,0,6,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14" }, { - "shop_price": "1", - "examine": "This wine clearly did not age well.", - "durability": null, + "id": "7810", "name": "Jug of vinegar", - "weight": "1", + "examine": "This wine clearly did not age well.", "archery_ticket_price": "0", - "id": "7810" - }, - { - "examine": "Well, this pot is certainly full of vinegar and no mistake.", "durability": null, - "name": "Pot of vinegar", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "7811" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Goblin skull", - "archery_ticket_price": "0", - "id": "7812" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7813" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Goblin skull", - "archery_ticket_price": "0", - "id": "7814" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Bear ribs", - "archery_ticket_price": "0", - "id": "7815" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7816" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Bear ribs", - "archery_ticket_price": "0", - "id": "7817" - }, - { - "examine": "Unpolished: This needs a good polish.", - "durability": null, - "name": "Ram skull", - "archery_ticket_price": "0", - "id": "7818" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7819" - }, - { - "examine": "Unpolished: This needs a good polish.", - "durability": null, - "name": "Ram skull", - "archery_ticket_price": "0", - "id": "7820" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Unicorn bone", - "archery_ticket_price": "0", - "id": "7821" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7822" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Unicorn bone", - "archery_ticket_price": "0", - "id": "7823" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Giant rat bone", - "archery_ticket_price": "0", - "id": "7824" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7825" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Giant rat bone", - "archery_ticket_price": "0", - "id": "7826" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Giant bat wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7827" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7828" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Giant bat wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7829" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Wolf bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7830" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7831" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Wolf bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7832" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Bat wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7833" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7834" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Bat wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7835" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rat bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7836" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7837" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rat bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7838" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Baby dragon bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7839" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7840" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Baby dragon bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7841" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ogre ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7842" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7843" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ogre ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7844" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7845" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7846" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7847" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7848" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7849" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7850" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Mogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7851" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7852" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Mogre bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7853" - }, - { - "examine": "This needs a good polish./ This belongs in a museum!", - "durability": null, - "name": "Monkey paw", - "archery_ticket_price": "0", - "id": "7854" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7855" - }, - { - "examine": "This needs a good polish./ This belongs in a museum!", - "durability": null, - "name": "Monkey paw", - "archery_ticket_price": "0", - "id": "7856" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Dagannoth ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7857" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7858" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Dagannoth ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7859" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Snake spine", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7860" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7861" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Snake spine", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7862" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zombie bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7863" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7864" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zombie bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7865" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Werewolf bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7866" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7867" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Werewolf bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7868" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Moss giant bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7869" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7870" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Moss giant bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7871" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Fire giant bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7872" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7873" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Fire giant bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7874" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ice giant ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7875" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7876" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ice giant ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7877" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Terrorbird wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7878" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7879" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Terrorbird wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7880" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ghoul bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7881" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7882" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ghoul bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7883" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Troll bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7884" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7885" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Troll bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7886" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Seagull wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7887" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7888" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Seagull wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7889" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Undead cow ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7890" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7891" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Undead cow ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7892" - }, - { - "shop_price": "160", - "examine": "Unpolished: This needs a good polish. Polished: This bone belongs in a museum!", - "durability": null, - "name": "Experiment bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7893" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7894" - }, - { - "shop_price": "160", - "examine": "Unpolished: This needs a good polish. Polished: This bone belongs in a museum!", - "durability": null, - "name": "Experiment bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7895" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rabbit bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7896" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7897" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rabbit bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7898" - }, - { - "examine": "Dirty:This bone needs a good polish.", - "durability": null, - "name": "Basilisk bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7899" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7900" - }, - { - "examine": "Dirty:This bone needs a good polish.", - "durability": null, - "name": "Basilisk bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7901" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Desert lizard bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7902" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7903" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Desert lizard bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7904" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Cave goblin skull", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7905" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7906" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Cave goblin skull", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7907" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Big frog leg", - "weight": "1", - "archery_ticket_price": "0", - "id": "7908" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7909" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Big frog leg", - "weight": "1", - "archery_ticket_price": "0", - "id": "7910" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Vulture wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7911" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7912" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Vulture wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7913" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jackal bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7914" - }, - { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7915" - }, - { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jackal bone", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7916" - }, - { - "destroy_message": "The ram skull helm looks like it will fall apart if you drop it. You would have to go to the Odd Old Man for a replacement.", - "examine": "Makes me feel baaad to the bone.", - "durability": null, - "name": "Ram skull helm", - "weight": "3", - "archery_ticket_price": "0", - "id": "7917", - "bonuses": "0,0,0,0,-2,19,21,16,0,19,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "The bonesack looks like it will fall apart if you drop it. You would have to go to the Odd Old Man for a replacement.", - "examine": "The Bonesack is a little old item that protects like leather.", - "durability": null, - "name": "Bonesack", - "weight": "9", - "archery_ticket_price": "0", - "id": "7918", - "bonuses": "0,0,0,0,0,4,4,4,4,4,4,0,0,0,0", - "equipment_slot": "1" - }, - { - "shop_price": "500", - "examine": "A very good vintage.", - "durability": null, - "name": "Bottle of wine", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7919" - }, - { - "durability": null, - "name": "Bottle of wine", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7920" - }, - { - "examine": "This one has clearly been taken down and passed around.", - "durability": null, - "name": "Empty wine bottle", - "weight": "1", - "archery_ticket_price": "0", - "id": "7921" - }, - { - "examine": "The money off voucher has expired.", - "durability": null, - "name": "Al kharid flyer", - "archery_ticket_price": "0", - "id": "7922" - }, - { - "examine": "A ring given to you by the Easter Bunny.", - "durability": null, - "name": "Easter ring", - "archery_ticket_price": "0", - "id": "7927" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7928" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7929" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7930" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7931" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7932" - }, - { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7933" - }, - { - "shop_price": "390", - "ge_buy_limit": "100", - "examine": "A field ration to help your wounds go away.", - "grand_exchange_price": "171", - "durability": null, - "name": "Field ration", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7934" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "171", - "durability": null, - "name": "Field ration", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7935" - }, - { "shop_price": "1", - "ge_buy_limit": "25000", - "examine": "An uncharged Rune Stone of extra capability.", - "grand_exchange_price": "180", - "durability": null, - "name": "Pure essence", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7936" + "weight": "1" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "109", - "durability": null, - "name": "Pure essence", - "tradeable": "true", + "id": "7811", + "name": "Pot of vinegar", + "examine": "Well, this pot is certainly full of vinegar and no mistake.", "archery_ticket_price": "0", - "id": "7937" + "durability": null, + "weight": "1.3" }, { - "ge_buy_limit": "5000", - "examine": "A word in your shell-like.", - "grand_exchange_price": "13600", - "durability": null, - "name": "Tortoise shell", - "tradeable": "true", - "weight": "6.8", + "id": "7812", + "name": "Goblin skull", + "examine": "This needs a good polish.", "archery_ticket_price": "0", - "id": "7939" + "durability": null }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "13600", - "durability": null, - "name": "Tortoise shell", - "tradeable": "true", + "id": "7813", + "name": "Bone in vinegar", + "examine": "There is a goblin bone in here.", "archery_ticket_price": "0", - "id": "7940" + "durability": null, + "weight": "1" }, { - "examine": "A sturdy sheet of iron.", - "durability": null, - "name": "Iron sheet", - "weight": "1.75", + "id": "7814", + "name": "Goblin skull", + "examine": "This belongs in a museum!", "archery_ticket_price": "0", - "id": "7941" + "durability": null }, { - "examine": "Perfect for storing. Not so good for eating.", - "durability": null, - "name": "Fresh monkfish", - "weight": "0.8", + "id": "7815", + "name": "Bear ribs", + "examine": "This needs a good polish.", "archery_ticket_price": "0", - "id": "7942" + "durability": null }, { - "examine": "Perfect for storing. Not so good for eating.", - "durability": null, - "name": "Fresh monkfish", - "weight": "0.8", + "id": "7816", + "name": "Bone in vinegar", + "examine": "There is a bear bone in here.", "archery_ticket_price": "0", - "id": "7943" + "durability": null, + "weight": "1" }, { - "ge_buy_limit": "20000", - "examine": "I should try cooking this./Freshly caught. Needs cooking. (Fresh Monkfish)", - "grand_exchange_price": "618", - "durability": null, - "name": "Raw monkfish", - "tradeable": "true", - "weight": "0.4", + "id": "7817", + "name": "Bear ribs", + "examine": "This belongs in a museum!", "archery_ticket_price": "0", - "id": "7944" + "durability": null }, { - "ge_buy_limit": "20000", - "grand_exchange_price": "618", - "durability": null, - "name": "Raw monkfish", - "tradeable": "true", + "id": "7818", + "name": "Ram skull", + "examine": "This needs a good polish.", "archery_ticket_price": "0", - "id": "7945" + "durability": null }, { - "ge_buy_limit": "10000", - "examine": "A tasty fish.", - "grand_exchange_price": "617", - "durability": null, - "name": "Monkfish", - "tradeable": "true", - "weight": "0.4", + "id": "7819", + "name": "Bone in vinegar", + "examine": "There is a ram bone in here.", "archery_ticket_price": "0", - "id": "7946" + "durability": null, + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "617", - "durability": null, - "name": "Monkfish", - "tradeable": "true", + "id": "7820", + "name": "Ram skull", + "examine": "This belongs in a museum!", "archery_ticket_price": "0", - "id": "7947" + "durability": null }, { - "durability": null, - "name": "Burnt monkfish", - "tradeable": "true", + "id": "7821", + "name": "Unicorn bone", + "examine": "This needs a good polish.", "archery_ticket_price": "0", - "id": "7949" + "durability": null }, { - "examine": "A highly portable army of skeletal magic.", - "durability": null, - "name": "Bone seeds", + "id": "7822", + "name": "Bone in vinegar", + "examine": "There is a unicorn bone in here.", "archery_ticket_price": "0", - "id": "7950" + "durability": null, + "weight": "1" }, { + "id": "7823", + "name": "Unicorn bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7824", + "name": "Giant rat bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7825", + "name": "Bone in vinegar", + "examine": "There is a giant rat bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7826", + "name": "Giant rat bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7827", + "name": "Giant bat wing", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7828", + "name": "Bone in vinegar", + "examine": "There is a giant bat bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7829", + "name": "Giant bat wing", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7830", + "name": "Wolf bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7831", + "name": "Bone in vinegar", + "examine": "There is a wolf bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7832", + "name": "Wolf bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7833", + "name": "Bat wing", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7834", + "name": "Bone in vinegar", + "examine": "There is a bat bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7835", + "name": "Bat wing", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7836", + "name": "Rat bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7837", + "name": "Bone in vinegar", + "examine": "There is a rat bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7838", + "name": "Rat bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7839", + "name": "Baby dragon bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7840", + "name": "Bone in vinegar", + "examine": "There is a baby blue dragon bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7841", + "name": "Baby dragon bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7842", + "name": "Ogre ribs", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7843", + "name": "Bone in vinegar", + "examine": "There is a ogre bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7844", + "name": "Ogre ribs", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7845", + "name": "Jogre bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7846", + "name": "Bone in vinegar", + "examine": "There is a jogre bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7847", + "name": "Jogre bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7848", + "name": "Zogre bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7849", + "name": "Bone in vinegar", + "examine": "There is a zogre bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7850", + "name": "Zogre bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7851", + "name": "Mogre bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7852", + "name": "Bone in vinegar", + "examine": "There is a mogre bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7853", + "name": "Mogre bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7854", + "name": "Monkey paw", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7855", + "name": "Bone in vinegar", + "examine": "There is a monkey bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7856", + "name": "Monkey paw", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7857", + "name": "Dagannoth ribs", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7858", + "name": "Bone in vinegar", + "examine": "There is a Dagannoth bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7859", + "name": "Dagannoth ribs", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7860", + "name": "Snake spine", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7861", + "name": "Bone in vinegar", + "examine": "There is a snake bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7862", + "name": "Snake spine", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7863", + "name": "Zombie bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7864", + "name": "Bone in vinegar", + "examine": "There is a zombie bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7865", + "name": "Zombie bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7866", + "name": "Werewolf bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7867", + "name": "Bone in vinegar", + "examine": "There is a werewolf bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7868", + "name": "Werewolf bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7869", + "name": "Moss giant bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7870", + "name": "Bone in vinegar", + "examine": "There is a moss giant bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7871", + "name": "Moss giant bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7872", + "name": "Fire giant bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7873", + "name": "Bone in vinegar", + "examine": "There is a fire giant bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7874", + "name": "Fire giant bone", + "examine": "This bone belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7875", + "name": "Ice giant ribs", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7876", + "name": "Bone in vinegar", + "examine": "There is an ice giant bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7877", + "name": "Ice giant ribs", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7878", + "name": "Terrorbird wing", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7879", + "name": "Bone in vinegar", + "examine": "There is a terrorbird bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7880", + "name": "Terrorbird wing", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7881", + "name": "Ghoul bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7882", + "name": "Bone in vinegar", + "examine": "There is a ghoul bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7883", + "name": "Ghoul bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7884", + "name": "Troll bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7885", + "name": "Bone in vinegar", + "examine": "There is a troll bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7886", + "name": "Troll bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7887", + "name": "Seagull wing", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7888", + "name": "Bone in vinegar", + "examine": "There is a seagull bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7889", + "name": "Seagull wing", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7890", + "name": "Undead cow ribs", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7891", + "name": "Bone in vinegar", + "examine": "There is an undead cow bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7892", + "name": "Undead cow ribs", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7893", + "name": "Experiment bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "160", - "examine": "A book taken from the desk of Herman Caranos.", + "weight": "0.9" + }, + { + "id": "7894", + "name": "Bone in vinegar", + "examine": "There is an experiment bone in here.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "7895", + "name": "Experiment bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "0.9" + }, + { + "id": "7896", + "name": "Rabbit bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7897", + "name": "Bone in vinegar", + "examine": "There is a rabbit bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7898", + "name": "Rabbit bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7899", + "name": "Basilisk bone", + "examine": "This bone needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7900", + "name": "Bone in vinegar", + "examine": "There is a basilisk bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7901", + "name": "Basilisk bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7902", + "name": "Desert lizard bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7903", + "name": "Bone in vinegar", + "examine": "There is a desert lizard bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7904", + "name": "Desert lizard bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7905", + "name": "Cave goblin skull", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7906", + "name": "Bone in vinegar", + "examine": "There is a cave goblin bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7907", + "name": "Cave goblin skull", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7908", + "name": "Big frog leg", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7909", + "name": "Bone in vinegar", + "examine": "There is a big frog bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7910", + "name": "Big frog leg", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7911", + "name": "Vulture wing", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7912", + "name": "Bone in vinegar", + "examine": "There is a vulture bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7913", + "name": "Vulture wing", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7914", + "name": "Jackal bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7915", + "name": "Bone in vinegar", + "examine": "There is a jackal bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7916", + "name": "Jackal bone", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7917", + "name": "Ram skull helm", + "examine": "Makes me feel baaad to the bone.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-2,19,21,16,0,19,6,0,0,0,0", + "destroy_message": "The ram skull helm looks like it will fall apart if you drop it. You would have to go to the Odd Old Man for a replacement.", + "durability": null, + "equipment_slot": "0", + "weight": "3" + }, + { + "id": "7918", + "name": "Bonesack", + "examine": "The Bonesack is a little old item that protects like leather.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,4,4,4,4,4,4,0,0,0,0", + "destroy_message": "The bonesack looks like it will fall apart if you drop it. You would have to go to the Odd Old Man for a replacement.", + "durability": null, + "equipment_slot": "1", + "weight": "9" + }, + { + "id": "7919", + "name": "Bottle of wine", + "examine": "A very good vintage.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "500", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7920", + "name": "Bottle of wine", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7921", + "name": "Empty wine bottle", + "examine": "This one has clearly been taken down and passed around.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7922", + "name": "Al kharid flyer", + "examine": "The money off voucher has expired.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7927", + "name": "Easter ring", + "examine": "A ring given to you by the Easter Bunny.", + "archery_ticket_price": "0", + "attack_audios": "", + "durability": null, + "equip_audio": "", + "equipment_slot": "12" + }, + { + "id": "7928", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7929", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7930", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7931", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7932", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7933", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7934", + "name": "Field ration", + "examine": "A field ration to help your wounds go away.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "390", + "tradeable": "true" + }, + { + "id": "7935", + "name": "Field ration", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7936", + "name": "Pure essence", + "examine": "An uncharged Rune Stone of extra capability.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "7937", + "name": "Pure essence", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "7939", + "name": "Tortoise shell", + "examine": "A word in your shell-like.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "7940", + "name": "Tortoise shell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "7941", + "name": "Iron sheet", + "examine": "A sturdy sheet of iron.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.75" + }, + { + "id": "7942", + "name": "Fresh monkfish", + "examine": "Perfect for storing. Not so good for eating.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.8" + }, + { + "id": "7943", + "name": "Fresh monkfish", + "examine": "Perfect for storing. Not so good for eating.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.8" + }, + { + "id": "7944", + "name": "Raw monkfish", + "examine": "I should try cooking this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "7945", + "name": "Raw monkfish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" + }, + { + "id": "7946", + "name": "Monkfish", + "examine": "A tasty fish.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "7947", + "name": "Monkfish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7949", + "name": "Burnt monkfish", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7950", + "name": "Bone seeds", + "examine": "A highly portable army of skeletal magic.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7951", "name": "Herman's book", - "tradeable": "false", - "destroy": "true", - "weight": "0.09", + "examine": "A book taken from the desk of Herman Caranos.", "archery_ticket_price": "0", - "id": "7951" + "destroy": "true", + "durability": null, + "shop_price": "160", + "tradeable": "false", + "weight": "0.09" }, { - "examine": "Useless without the head.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", "id": "7952", - "equipment_slot": "3" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", + "examine": "Useless without the head.", "archery_ticket_price": "0", - "id": "7953" + "durability": null, + "equipment_slot": "3", + "tradeable": "true" }, { + "id": "7953", + "name": "Picture", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "7955", "name": "Burnt shrimp", - "tradeable": "true", "archery_ticket_price": "0", - "id": "7955" + "durability": null, + "tradeable": "true" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "7956", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "7956" + "durability": null, + "tradeable": "true", + "weight": "5" }, { - "shop_price": "2", - "examine": "A mostly clean apron.", - "grand_exchange_price": "169", - "durability": null, + "id": "7957", "name": "White apron", - "tradeable": "true", - "weight": "0.45", + "examine": "A mostly clean apron.", "archery_ticket_price": "0", - "id": "7957" + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "0.45" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A prop for holding up a tunnel roof.", - "durability": null, + "id": "7958", "name": "Mining prop", - "tradeable": "false", - "destroy": "true", + "examine": "A prop for holding up a tunnel roof.", "archery_ticket_price": "0", - "id": "7958" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get your box back by talking to the guard by the dungeon entrance in Etceteria.", - "examine": "A box full of stolen Etceterian items.", - "durability": null, + "id": "7959", "name": "Heavy box", - "tradeable": "false", - "destroy": "true", + "examine": "A box full of stolen Etceterian items.", "archery_ticket_price": "0", - "id": "7959" + "destroy": "true", + "destroy_message": "You can get your box back by talking to the guard by the dungeon entrance in Etceteria.", + "durability": null, + "tradeable": "false" }, { - "examine": "It says 'To the dungeons' on the side.", - "durability": null, + "id": "7960", "name": "Empty box", + "examine": "It says 'To the dungeons' on the side.", "archery_ticket_price": "0", - "id": "7960" + "durability": null }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, + "id": "7961", "name": "Burnt diary", - "archery_ticket_price": "0", - "id": "7961" - }, - { "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", "archery_ticket_price": "0", - "id": "7962" + "durability": null }, { + "id": "7962", + "name": "Burnt diary", "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", "archery_ticket_price": "0", - "id": "7963" + "durability": null }, { + "id": "7963", + "name": "Burnt diary", "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", "archery_ticket_price": "0", - "id": "7964" + "durability": null }, { + "id": "7964", + "name": "Burnt diary", "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", "archery_ticket_price": "0", - "id": "7965" + "durability": null }, { - "examine": "A dwarf-made coal engine. It looks very sturdy.", - "durability": null, + "id": "7965", + "name": "Burnt diary", + "examine": "A diary with one/two/three/four/five page(s).", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7967", "name": "Engine", - "tradeable": "false", - "destroy": "true", + "examine": "A dwarf-made coal engine. It looks very sturdy.", "archery_ticket_price": "0", - "id": "7967" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A beam with a pulley attached.", - "durability": null, + "id": "7969", "name": "Pulley beam", + "examine": "A beam with a pulley attached.", "archery_ticket_price": "0", - "id": "7969" + "durability": null }, { - "examine": "A long beam with a pulley attached.", - "durability": null, + "id": "7970", "name": "Long pulley beam", + "examine": "A long beam with a pulley attached.", "archery_ticket_price": "0", - "id": "7970" + "durability": null }, { - "examine": "A very long beam with a pulley attached.", - "durability": null, + "id": "7971", "name": "Longer pulley beam", + "examine": "A very long beam with a pulley attached.", "archery_ticket_price": "0", - "id": "7971" + "durability": null }, { - "examine": "The manual for an AMCE Lift-In-A-Box.", - "durability": null, + "id": "7972", "name": "Lift manual", - "tradeable": "false", - "destroy": "true", + "examine": "The manual for an AMCE Lift-In-A-Box.", "archery_ticket_price": "0", - "id": "7972" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A wooden beam.", - "durability": null, + "id": "7973", "name": "Beam", + "examine": "A wooden beam.", "archery_ticket_price": "0", - "id": "7973" + "durability": null }, { - "durability": null, + "id": "7975", "name": "Crawling hand", + "examine": "I should get it stuffed!", "archery_ticket_price": "0", "attack_speed": "4", - "id": "7975" + "durability": null }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7976", "name": "Cockatrice head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I should get it stuffed!", "archery_ticket_price": "0", - "id": "7976" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7977", "name": "Basilisk head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I should get it stuffed!", "archery_ticket_price": "0", - "id": "7977" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7978", "name": "Kurask head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I should get it stuffed!", "archery_ticket_price": "0", - "id": "7978" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7979", "name": "Abyssal head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I should get it stuffed!", "archery_ticket_price": "0", - "id": "7979" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7980", "name": "Kbd heads", - "tradeable": "false", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7980" - }, - { "examine": "I should get it stuffed!", - "durability": null, - "name": "Kq head", "archery_ticket_price": "0", - "id": "7981" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "durability": null, + "id": "7981", + "name": "Kq head", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7982", "name": "Crawling hand", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", "attack_speed": "4", - "id": "7982" + "durability": null }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7983", "name": "Cockatrice head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", - "id": "7983" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7984", "name": "Basilisk head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", - "id": "7984" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7985", "name": "Kurask head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", - "id": "7985" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7986", "name": "Abyssal head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", - "id": "7986" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7987", "name": "Kbd heads", - "tradeable": "false", + "examine": "I could mount these on my wall!", + "archery_ticket_price": "0", "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7987" + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "7988", "name": "Kq head", + "examine": "I could mount this on my wall!", "archery_ticket_price": "0", - "id": "7988" + "durability": null }, { - "examine": "Whopper! I should get this stuffed!", - "durability": null, + "id": "7989", "name": "Big bass", - "weight": "6", + "examine": "Whopper! I should get this stuffed!", "archery_ticket_price": "0", - "id": "7989" + "durability": null, + "weight": "6" }, { - "examine": "Whopper! I should get this stuffed!", - "durability": null, + "id": "7990", "name": "Big bass", - "weight": "6", + "examine": "I should mount this on my wall!", "archery_ticket_price": "0", - "id": "7990" + "durability": null, + "weight": "6" }, { - "examine": "Whopper! I should get this stuffed!", - "durability": null, + "id": "7991", "name": "Big swordfish", - "archery_ticket_price": "0", - "id": "7991" - }, - { "examine": "Whopper! I should get this stuffed!", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7992", "name": "Big swordfish", + "examine": "I should mount this on my wall!", "archery_ticket_price": "0", - "id": "7992" + "durability": null }, { - "examine": "Its a monster! I should get this stuffed!", - "durability": null, + "id": "7993", "name": "Big shark", - "weight": "0.7", + "examine": "It's a monster! I should get this stuffed!", "archery_ticket_price": "0", - "id": "7993" + "durability": null, + "weight": "0.7" }, { - "examine": "Its a monster! I should get this stuffed!", - "durability": null, + "id": "7994", "name": "Big shark", - "weight": "0.7", + "examine": "I should mount this on my wall!", "archery_ticket_price": "0", - "id": "7994" + "durability": null, + "weight": "0.7" }, { - "examine": "A portrait of King Arthur.", - "durability": null, + "id": "7995", "name": "Arthur portrait", - "weight": "1", + "examine": "A portrait of King Arthur.", "archery_ticket_price": "0", - "id": "7995" + "durability": null, + "weight": "1" }, { - "shop_price": "1000", - "examine": "A portrait of Elena.", - "durability": null, + "id": "7996", "name": "Elena portrait", - "weight": "1", + "examine": "A portrait of Elena.", "archery_ticket_price": "0", - "id": "7996" + "durability": null, + "shop_price": "1000", + "weight": "1" }, { - "examine": "A painting of the staute of King Alvis of Keldagrim", - "durability": null, + "id": "7997", "name": "Keldagrim portrait", - "weight": "1", + "examine": "A painting of the staute of King Alvis of Keldagrim", "archery_ticket_price": "0", - "id": "7997" + "durability": null, + "weight": "1" }, { - "examine": "A portrait of Prince Brand and Princess Astrid of Miscellania.", - "durability": null, + "id": "7998", "name": "Misc. portrait", - "weight": "1", + "examine": "A portrait of Prince Brand and Princess Astrid of Miscellania.", "archery_ticket_price": "0", - "id": "7998" + "durability": null, + "weight": "1" }, { - "shop_price": "2000", - "examine": "The exotic land of the Elves.", - "durability": null, + "id": "8000", "name": "Isafdar painting", + "examine": "The exotic land of the Elves.", "archery_ticket_price": "0", - "id": "8000" + "durability": null, + "shop_price": "2000" }, { - "shop_price": "2000", - "examine": "The tropical coast of karamja.", - "durability": null, + "id": "8001", "name": "Karamja painting", + "examine": "The tropical coast of karamja.", "archery_ticket_price": "0", - "id": "8001" + "durability": null, + "shop_price": "2000" }, { - "shop_price": "2000", - "examine": "Oxtable's famous painting of the Lumbridge water mill.", - "durability": null, + "id": "8002", "name": "Lumbridge painting", - "weight": "1", + "examine": "Oxtable's famous painting of the Lumbridge water mill.", "archery_ticket_price": "0", - "id": "8002" - }, - { + "durability": null, "shop_price": "2000", - "examine": "A painting of the spooky forests of morytania.", - "durability": null, + "weight": "1" + }, + { + "id": "8003", "name": "Morytania painting", - "weight": "1", + "examine": "A painting of the spooky forests of morytania.", "archery_ticket_price": "0", - "id": "8003" + "durability": null, + "shop_price": "2000", + "weight": "1" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1341", - "durability": null, + "id": "8007", "name": "Varrock teleport", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8007" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1371", - "durability": null, + "id": "8008", "name": "Lumbridge teleport", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8008" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1414", - "durability": null, + "id": "8009", "name": "Falador teleport", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "8009" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1369", - "durability": null, + "id": "8010", "name": "Camelot teleport", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8010" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1511", - "durability": null, + "id": "8011", "name": "Ardougne teleport", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8011" - }, - { + "durability": null, + "ge_buy_limit": "1000", "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1025", - "durability": null, + "tradeable": "true" + }, + { + "id": "8012", "name": "Watchtower t'port", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8012" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1426", - "durability": null, + "id": "8013", "name": "Teleport to house", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8013" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "200", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "360", - "durability": null, + "id": "8014", "name": "Bones to bananas", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8014" - }, - { + "durability": null, "ge_buy_limit": "200", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "620", - "durability": null, + "tradeable": "true" + }, + { + "id": "8015", "name": "Bones to peaches", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8015" + "durability": null, + "ge_buy_limit": "200", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "343", - "durability": null, + "id": "8016", "name": "Enchant sapphire", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8016" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "409", - "durability": null, + "id": "8017", "name": "Enchant emerald", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8017" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "207", - "durability": null, + "id": "8018", "name": "Enchant ruby", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8018" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "326", - "durability": null, + "id": "8019", "name": "Enchant diamond", - "tradeable": "true", + "examine": "A tablet containing a magic spell.", "archery_ticket_price": "0", - "id": "8019" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "250", - "durability": null, + "id": "8020", "name": "Enchant dragonstn.", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8020" - }, - { - "ge_buy_limit": "1000", "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "31", - "durability": null, - "name": "Enchant onyx", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8021" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "grand_exchange_price": "1918", + "id": "8021", + "name": "Enchant onyx", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "8031", "name": "Wooden bed", "archery_ticket_price": "0", - "id": "8031" + "durability": null }, { - "grand_exchange_price": "1631", - "durability": null, + "id": "8032", "name": "Oak bed", "archery_ticket_price": "0", - "id": "8032" + "durability": null }, { - "grand_exchange_price": "1388", - "durability": null, + "id": "8033", "name": "Large oak bed", "archery_ticket_price": "0", - "id": "8033" + "durability": null }, { - "grand_exchange_price": "1376", - "durability": null, + "id": "8034", "name": "Teak bed", "archery_ticket_price": "0", - "id": "8034" + "durability": null }, { - "grand_exchange_price": "1996", - "durability": null, + "id": "8035", "name": "Large teak bed", "archery_ticket_price": "0", - "id": "8035" + "durability": null }, { - "grand_exchange_price": "1314", - "durability": null, + "id": "8036", "name": "4-poster", "archery_ticket_price": "0", - "id": "8036" + "durability": null }, { - "grand_exchange_price": "274779", - "durability": null, + "id": "8037", "name": "Gilded 4-poster", "archery_ticket_price": "0", - "id": "8037" + "durability": null }, { - "grand_exchange_price": "18", - "durability": null, + "id": "8038", "name": "Shoe box", "archery_ticket_price": "0", - "id": "8038" + "durability": null }, { - "grand_exchange_price": "42", - "durability": null, + "id": "8039", "name": "Oak drawers", "archery_ticket_price": "0", - "id": "8039" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8040", "name": "Oak wardrobe", "archery_ticket_price": "0", - "id": "8040" + "durability": null }, { - "grand_exchange_price": "126", - "durability": null, + "id": "8041", "name": "Teak drawers", "archery_ticket_price": "0", - "id": "8041" + "durability": null }, { - "grand_exchange_price": "12", - "durability": null, + "id": "8042", "name": "Teak wardrobe", "archery_ticket_price": "0", - "id": "8042" + "durability": null }, { - "grand_exchange_price": "102", - "durability": null, + "id": "8043", "name": "Mahogany 'drobe", "archery_ticket_price": "0", - "id": "8043" + "durability": null }, { - "grand_exchange_price": "139452", - "durability": null, + "id": "8044", "name": "Gilded wardrobe", "archery_ticket_price": "0", - "id": "8044" + "durability": null }, { - "grand_exchange_price": "510", - "durability": null, + "id": "8045", "name": "Shaving stand", "archery_ticket_price": "0", - "id": "8045" + "durability": null }, { - "grand_exchange_price": "449", - "durability": null, + "id": "8046", "name": "Oak shaving stand", "archery_ticket_price": "0", - "id": "8046" + "durability": null }, { - "grand_exchange_price": "147", - "durability": null, + "id": "8047", "name": "Oak dresser", "archery_ticket_price": "0", - "id": "8047" + "durability": null }, { - "grand_exchange_price": "478", - "durability": null, + "id": "8048", "name": "Teak dresser", "archery_ticket_price": "0", - "id": "8048" + "durability": null }, { - "grand_exchange_price": "456", - "durability": null, + "id": "8049", "name": "Fancy teak dresser", "archery_ticket_price": "0", - "id": "8049" + "durability": null }, { - "grand_exchange_price": "404", - "durability": null, + "id": "8050", "name": "Mahogany dresser", "archery_ticket_price": "0", - "id": "8050" + "durability": null }, { - "grand_exchange_price": "140594", - "durability": null, + "id": "8051", "name": "Gilded dresser", "archery_ticket_price": "0", - "id": "8051" + "durability": null }, { - "grand_exchange_price": "367", - "durability": null, + "id": "8052", "name": "Oak clock", "archery_ticket_price": "0", - "id": "8052" + "durability": null }, { - "grand_exchange_price": "552", - "durability": null, + "id": "8053", "name": "Teak clock", "archery_ticket_price": "0", - "id": "8053" + "durability": null }, { - "grand_exchange_price": "154310", - "durability": null, + "id": "8054", "name": "Gilded clock", "archery_ticket_price": "0", - "id": "8054" + "durability": null }, { - "examine": "When unanimated: A dusty old suit of armour. When animated: Aaargh, it's alive!", - "durability": null, + "id": "8085", "name": "Suit of armour", + "examine": "When unanimated: A dusty old suit of armour. When animated: Aaargh, it's alive!", "archery_ticket_price": "0", - "id": "8085" + "durability": null }, { - "grand_exchange_price": "13", - "durability": null, + "id": "8108", "name": "Wooden bench", "archery_ticket_price": "0", - "id": "8108" + "durability": null }, { - "grand_exchange_price": "131", - "durability": null, + "id": "8109", "name": "Oak bench", "archery_ticket_price": "0", - "id": "8109" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8110", "name": "Carved oak bench", "archery_ticket_price": "0", - "id": "8110" + "durability": null }, { - "grand_exchange_price": "17", - "durability": null, + "id": "8111", "name": "Teak dining bench", "archery_ticket_price": "0", - "id": "8111" + "durability": null }, { - "grand_exchange_price": "2", - "durability": null, + "id": "8112", "name": "Carved teak bench", "archery_ticket_price": "0", - "id": "8112" + "durability": null }, { - "grand_exchange_price": "145", - "durability": null, + "id": "8113", "name": "Mahogany bench", "archery_ticket_price": "0", - "id": "8113" + "durability": null }, { - "grand_exchange_price": "531526", - "durability": null, + "id": "8114", "name": "Gilded bench", "archery_ticket_price": "0", - "id": "8114" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8115", "name": "Wood dining table", "archery_ticket_price": "0", - "id": "8115" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8116", "name": "Oak dining table", "archery_ticket_price": "0", - "id": "8116" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8117", "name": "Carved oak table", "archery_ticket_price": "0", - "id": "8117" + "durability": null }, { - "grand_exchange_price": "2", - "durability": null, + "id": "8118", "name": "Teak table", "archery_ticket_price": "0", - "id": "8118" + "durability": null }, { - "grand_exchange_price": "249", - "durability": null, + "id": "8119", "name": "Carved teak table", "archery_ticket_price": "0", - "id": "8119" + "durability": null }, { - "grand_exchange_price": "497", - "durability": null, + "id": "8120", "name": "Mahogany table", "archery_ticket_price": "0", - "id": "8120" + "durability": null }, { - "grand_exchange_price": "394177", - "durability": null, + "id": "8121", "name": "Opulent table", "archery_ticket_price": "0", - "id": "8121" + "durability": null }, { - "shop_price": "24", - "examine": "A candle.", - "grand_exchange_price": "104", - "durability": null, + "id": "8128", "name": "Candles", - "tradeable": "true", + "examine": "A candle.", "archery_ticket_price": "0", - "id": "8128" + "durability": null, + "shop_price": "24", + "tradeable": "true" }, { - "durability": null, + "id": "8131", "name": "Skeleton guard", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8131" + "durability": null }, { - "examine": "He doesn't seem pleased to see me./Beware of the dog! (Construction)", - "durability": null, + "id": "8132", "name": "Guard dog", + "examine": "He doesn't seem pleased to see me./Beware of the dog! (Construction)", "archery_ticket_price": "0", - "id": "8132" + "durability": null }, { - "examine": "He doesn't look very welcoming.", - "durability": null, + "id": "8133", "name": "Hobgoblin guard", + "examine": "He doesn't look very welcoming.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8133" + "durability": null }, { - "examine": "Young but still dangerous.", - "durability": null, + "id": "8134", "name": "Baby red dragon", + "examine": "Young but still dangerous.", "archery_ticket_price": "0", - "id": "8134" + "durability": null }, { - "examine": "No spider could grow that big! It's unrealistic!", - "durability": null, + "id": "8135", "name": "Huge spider", + "examine": "No spider could grow that big! It's unrealistic!", "archery_ticket_price": "0", - "id": "8135" + "durability": null }, { - "examine": "(Level 122)", - "durability": null, + "id": "8137", "name": "Hellhound", + "examine": "(Level 122)", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8137" + "durability": null }, { - "examine": "I don't think insect repellent will work...", - "durability": null, + "id": "8139", "name": "Kalphite soldier", + "examine": "I don't think insect repellent will work...", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8139" + "durability": null }, { - "examine": "The Tok-Xil fires deadly spines out of its arm, projected by its own heat.", - "durability": null, + "id": "8140", "name": "Tok-xil", + "examine": "The Tok-Xil fires deadly spines out of its arm, projected by its own heat.", "archery_ticket_price": "0", - "id": "8140" + "durability": null }, { - "examine": "Its scales seem to be made of steel.", - "durability": null, + "id": "8142", "name": "Steel dragon", + "examine": "Its scales seem to be made of steel.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8142" + "durability": null }, { - "shop_price": "5000", - "examine": "You can plant this in your garden.", - "durability": null, + "id": "8203", "name": "Thorny hedge", - "tradeable": "true", + "examine": "You can plant this in your garden.", "archery_ticket_price": "0", - "id": "8203" + "durability": null, + "shop_price": "5000", + "tradeable": "true" }, { - "shop_price": "10000", - "examine": "You can plant this in your garden.", - "durability": null, + "id": "8204", "name": "Nice hedge", - "tradeable": "true", + "examine": "You can plant this in your garden.", "archery_ticket_price": "0", - "id": "8204" + "durability": null, + "shop_price": "10000", + "tradeable": "true" }, { - "shop_price": "15000", - "examine": "You can plant this in your garden.", - "durability": null, + "id": "8205", "name": "Small box hedge", - "tradeable": "true", + "examine": "You can plant this in your garden.", "archery_ticket_price": "0", - "id": "8205" + "durability": null, + "shop_price": "15000", + "tradeable": "true" }, { - "shop_price": "20000", - "examine": "You can plant this in your garden.", - "durability": null, + "id": "8206", "name": "Topiary hedge", - "tradeable": "true", - "weight": "10", + "examine": "You can plant this in your garden.", "archery_ticket_price": "0", - "id": "8206" + "durability": null, + "shop_price": "20000", + "tradeable": "true", + "weight": "10" }, { - "shop_price": "25000", - "examine": "You can plant this in your garden.", - "durability": null, + "id": "8207", "name": "Fancy hedge", - "weight": "10", - "archery_ticket_price": "0", - "id": "8207" - }, - { - "shop_price": "50000", "examine": "You can plant this in your garden.", + "archery_ticket_price": "0", "durability": null, + "shop_price": "25000", + "weight": "10" + }, + { + "id": "8208", "name": "Tall fancy hedge", + "examine": "You can plant this in your garden.", "archery_ticket_price": "0", - "id": "8208" + "durability": null, + "shop_price": "50000" }, { - "examine": "Some rosemary.", - "grand_exchange_price": "20", - "durability": null, + "id": "8210", "name": "Rosemary", - "tradeable": "true", + "examine": "Some rosemary.", "archery_ticket_price": "0", - "id": "8210" + "durability": null, + "tradeable": "true" }, { - "examine": "A bunch of marigolds.", - "grand_exchange_price": "3862", - "durability": null, + "id": "8214", "name": "Marigolds", - "tradeable": "true", + "examine": "A bunch of marigolds.", "archery_ticket_price": "0", - "id": "8214" + "durability": null, + "tradeable": "true" }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8239", "name": "Beer barrel", "archery_ticket_price": "0", - "id": "8239" + "durability": null }, { - "grand_exchange_price": "16860", - "durability": null, + "id": "8240", "name": "Cider barrel", "archery_ticket_price": "0", - "id": "8240" + "durability": null }, { - "shop_price": "3", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "131", - "durability": null, + "id": "8241", "name": "Asgarnian ale", - "tradeable": "true", + "examine": "Probably the finest readily-available ale in Asgarnia.", "archery_ticket_price": "0", - "id": "8241" + "durability": null, + "shop_price": "3", + "tradeable": "true" }, { - "shop_price": "2", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "569", - "durability": null, + "id": "8242", "name": "Greenman's ale", - "tradeable": "true", + "examine": "A glass of frothy ale.", "archery_ticket_price": "0", - "id": "8242" - }, - { + "durability": null, "shop_price": "2", - "examine": "A glass of bitter.", - "grand_exchange_price": "523", - "durability": null, + "tradeable": "true" + }, + { + "id": "8243", "name": "Dragon bitter", - "tradeable": "true", - "weight": "1", + "examine": "A glass of bitter.", "archery_ticket_price": "0", - "id": "8243" + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "1" }, { - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "2371", - "durability": null, + "id": "8244", "name": "Chef's delight", - "tradeable": "true", + "examine": "A fruity, full-bodied ale.", "archery_ticket_price": "0", - "id": "8244" + "durability": null, + "tradeable": "true" }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8246", "name": "Wood kitchen table", "archery_ticket_price": "0", - "id": "8246" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8247", "name": "Oak kitchen table", "archery_ticket_price": "0", - "id": "8247" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8248", "name": "Teak kitchen table", "archery_ticket_price": "0", - "id": "8248" + "durability": null }, { - "durability": null, + "id": "8260", "name": "Crawling hand", + "examine": "A trophy of a mighty slayer!", "archery_ticket_price": "0", "attack_speed": "4", - "id": "8260" + "durability": null }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8261", "name": "Cockatrice head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "A trophy of a mighty slayer!", "archery_ticket_price": "0", - "id": "8261" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8262", "name": "Basilisk head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "A trophy of a mighty slayer!", "archery_ticket_price": "0", - "id": "8262" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8263", "name": "Kurask head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "A trophy of a mighty slayer!", "archery_ticket_price": "0", - "id": "8263" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8264", "name": "Abyssal head", - "tradeable": "false", - "destroy": "true", - "weight": "10", + "examine": "A trophy of a mighty slayer!!", "archery_ticket_price": "0", - "id": "8264" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8265", "name": "Kbd heads", - "tradeable": "false", + "examine": "A trophy of a mighty dragon-slayer!", + "archery_ticket_price": "0", "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "8265" + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "I should get it stuffed!", - "durability": null, + "id": "8266", "name": "Kq head", + "examine": "A trophy of a mighty kalphite slayer!", "archery_ticket_price": "0", - "id": "8266" + "durability": null }, { - "durability": null, - "name": "Runite armour", - "archery_ticket_price": "0", "id": "8272", + "name": "Runite armour", "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null }, { - "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", - "durability": null, - "name": "Silverlight", - "weight": "1.8", - "archery_ticket_price": "0", - "attack_speed": "5", "id": "8279", - "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0" - }, - { - "examine": "This used to belong to King Arthur.", - "durability": null, - "name": "Excalibur", - "weight": "2.2", + "name": "Silverlight", + "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", "archery_ticket_price": "0", "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0", + "durability": null, + "weight": "1.8" + }, + { "id": "8280", - "bonuses": "20,29,-2,0,0,0,3,2,1,0,0,25,0,0,0" - }, - { - "examine": "The magical sword Silverlight, enhanced with the blood of Agrith-Naar.", - "durability": null, - "name": "Darklight", - "weight": "1.8", + "name": "Excalibur", + "examine": "This used to belong to King Arthur.", "archery_ticket_price": "0", "attack_speed": "5", + "bonuses": "20,29,-2,0,0,0,3,2,1,0,0,25,0,0,0", + "durability": null, + "weight": "2.2" + }, + { "id": "8281", - "bonuses": "10,16,-2,0,0,0,3,2,2,0,0,13,0,0,0" + "name": "Darklight", + "examine": "The magical sword Silverlight, enhanced with the blood of Agrith-Naar.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "10,16,-2,0,0,0,3,2,2,0,0,13,0,0,0", + "durability": null, + "weight": "1.8" }, { - "examine": "This provides partial protection from dragon-breath attacks.", - "grand_exchange_price": "305", - "durability": null, - "name": "Anti-dragon shield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8282", - "bonuses": "0,0,0,0,0,7,9,8,2,8,0,0,0,0,0" - }, - { - "examine": "A very powerful dragonstone amulet.", - "grand_exchange_price": "1", + "name": "Anti-dragon shield", + "examine": "This provides partial protection from dragon-breath attacks.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,7,9,8,2,8,0,0,0,0,0", "durability": null, - "name": "Amulet of glory", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "5.4" + }, + { "id": "8283", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,0,3,0" + "name": "Amulet of glory", + "examine": "A very powerful dragonstone amulet.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,0,3,0", + "durability": null, + "tradeable": "true" }, { - "shop_price": "450", - "examine": "The cape worn by members of the Legends Guild.", - "durability": null, - "name": "Cape of legends", - "weight": "1.8", - "archery_ticket_price": "0", "id": "8284", - "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0" + "name": "Cape of legends", + "examine": "The cape worn by members of the Legends Guild.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,7,7,7,7,7,7,0,0,0,0", + "durability": null, + "shop_price": "450", + "weight": "1.8" }, { - "examine": "Legendary King of the Britons.", - "durability": null, + "id": "8285", "name": "King arthur", + "examine": "Legendary King of the Britons.", "archery_ticket_price": "0", - "id": "8285" + "durability": null }, { - "examine": "She looks concerned.", - "durability": null, + "id": "8286", "name": "Elena", + "examine": "She looks concerned.", "archery_ticket_price": "0", - "id": "8286" + "durability": null }, { - "examine": "It's full of pent-up aggression.", - "durability": null, + "id": "8305", "name": "Rocnar", + "examine": "It's full of pent-up aggression.", "archery_ticket_price": "0", - "id": "8305" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8309", "name": "Crude wooden chair", "archery_ticket_price": "0", - "id": "8309" + "durability": null }, { - "grand_exchange_price": "50", - "durability": null, + "id": "8310", "name": "Wooden chair", "archery_ticket_price": "0", - "id": "8310" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8311", "name": "Rocking chair", "archery_ticket_price": "0", - "id": "8311" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8312", "name": "Oak chair", "archery_ticket_price": "0", - "id": "8312" + "durability": null }, { - "grand_exchange_price": "2", - "durability": null, + "id": "8313", "name": "Oak armchair", "archery_ticket_price": "0", - "id": "8313" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8314", "name": "Teak armchair", "archery_ticket_price": "0", - "id": "8314" + "durability": null }, { - "grand_exchange_price": "8", - "durability": null, + "id": "8315", "name": "Mahogany armchair", "archery_ticket_price": "0", - "id": "8315" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8319", "name": "Wooden bookcase", "archery_ticket_price": "0", - "id": "8319" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "8320", "name": "Oak bookcase", "archery_ticket_price": "0", - "id": "8320" + "durability": null }, { - "grand_exchange_price": "52", - "durability": null, + "id": "8321", "name": "Mahogany b'kcase", "archery_ticket_price": "0", - "id": "8321" + "durability": null }, { + "id": "8417", + "name": "Bagged dead tree", + "examine": "You can plant this in your garden.", + "archery_ticket_price": "0", + "durability": null, "requirements": "{5,22}", "shop_price": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8418", + "name": "Bagged dead tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8419", + "name": "Bagged nice tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged dead tree", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8417" - }, - { "durability": null, - "name": "Bagged dead tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8418" - }, - { "requirements": "{10,22}", "shop_price": "2000", + "tradeable": "true" + }, + { + "id": "8420", + "name": "Bagged nice tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8421", + "name": "Bagged oak tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged nice tree", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8419" - }, - { "durability": null, - "name": "Bagged nice tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8420" - }, - { "requirements": "{15,22}", + "tradeable": "true" + }, + { + "id": "8422", + "name": "Bagged oak tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8423", + "name": "Bagged willow tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged oak tree", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8421" - }, - { "durability": null, - "name": "Bagged oak tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8422" - }, - { "requirements": "{22,30}", "shop_price": "10000", + "tradeable": "true" + }, + { + "id": "8424", + "name": "Bagged willow tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8425", + "name": "Bagged maple tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged willow tree", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8423" - }, - { "durability": null, - "name": "Bagged willow tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8424" - }, - { "requirements": "{22,45}", "shop_price": "15000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8426", + "name": "Bagged maple tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8427", + "name": "Bagged yew tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged maple tree", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8425" - }, - { "durability": null, - "name": "Bagged maple tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8426" - }, - { "requirements": "{22,60}", "shop_price": "20000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8428", + "name": "Bagged yew tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8429", + "name": "Bagged magic tree", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged yew tree", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8427" - }, - { "durability": null, - "name": "Bagged yew tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8428" - }, - { "requirements": "{22,75}", "shop_price": "50000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8430", + "name": "Bagged magic tree", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8431", + "name": "Bagged plant 1", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged magic tree", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8429" - }, - { "durability": null, - "name": "Bagged magic tree", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8430" - }, - { + "ge_buy_limit": "5000", "shop_price": "1000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8432", + "name": "Bagged plant 1", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "8433", + "name": "Bagged plant 2", "examine": "You can plant this in your garden.", - "grand_exchange_price": "1345", - "durability": null, - "name": "Bagged plant 1", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8431" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1345", "durability": null, - "name": "Bagged plant 1", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8432" - }, - { "requirements": "{6,22}", "shop_price": "5000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8434", + "name": "Bagged plant 2", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8435", + "name": "Bagged plant 3", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged plant 2", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8433" - }, - { "durability": null, - "name": "Bagged plant 2", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8434" - }, - { "requirements": "{12,22}", "shop_price": "10000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8436", + "name": "Bagged plant 3", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8437", + "name": "Thorny hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged plant 3", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8435" - }, - { "durability": null, - "name": "Bagged plant 3", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8436" - }, - { "requirements": "{22,56}", "shop_price": "5000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8438", + "name": "Thorny hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8439", + "name": "Nice hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Thorny hedge", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8437" - }, - { "durability": null, - "name": "Thorny hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8438" - }, - { "requirements": "{22,60}", "shop_price": "10000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8440", + "name": "Nice hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8441", + "name": "Small box hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Nice hedge", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8439" - }, - { "durability": null, - "name": "Nice hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8440" - }, - { "requirements": "{22,64}", "shop_price": "15000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8442", + "name": "Small box hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8443", + "name": "Topiary hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Small box hedge", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8441" - }, - { "durability": null, - "name": "Small box hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8442" - }, - { "requirements": "{22,68}", "shop_price": "20000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8444", + "name": "Topiary hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8445", + "name": "Fancy hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Topiary hedge", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8443" - }, - { "durability": null, - "name": "Topiary hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8444" - }, - { "requirements": "{22,72}", "shop_price": "25000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8446", + "name": "Fancy hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8447", + "name": "Tall fancy hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Fancy hedge", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8445" - }, - { "durability": null, - "name": "Fancy hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8446" - }, - { "requirements": "{22,76}", "shop_price": "50000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8448", + "name": "Tall fancy hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8449", + "name": "Tall box hedge", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Tall fancy hedge", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8447" - }, - { "durability": null, - "name": "Tall fancy hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8448" - }, - { "requirements": "{22,80}", "shop_price": "100000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8450", + "name": "Tall box hedge", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8451", + "name": "Bagged rosemary", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Tall box hedge", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8449" - }, - { "durability": null, - "name": "Tall box hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8450" - }, - { "requirements": "{22,66}", "shop_price": "5000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8452", + "name": "Bagged rosemary", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8453", + "name": "Bagged daffodils", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged rosemary", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8451" - }, - { "durability": null, - "name": "Bagged rosemary", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8452" - }, - { "requirements": "{22,71}", "shop_price": "10000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8454", + "name": "Bagged daffodils", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8455", + "name": "Bagged bluebells", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged daffodils", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8453" - }, - { "durability": null, - "name": "Bagged daffodils", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8454" - }, - { "requirements": "{22,76}", "shop_price": "15000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8456", + "name": "Bagged bluebells", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8457", + "name": "Bagged sunflower", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged bluebells", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8455" - }, - { "durability": null, - "name": "Bagged bluebells", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8456" - }, - { "requirements": "{22,66}", "shop_price": "5000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8458", + "name": "Bagged sunflower", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8459", + "name": "Bagged marigolds", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged sunflower", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8457" - }, - { "durability": null, - "name": "Bagged sunflower", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8458" - }, - { "requirements": "{22,71}", "shop_price": "10000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8460", + "name": "Bagged marigolds", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8461", + "name": "Bagged roses", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged marigolds", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "10", - "id": "8459" - }, - { "durability": null, - "name": "Bagged marigolds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8460" - }, - { "requirements": "{22,76}", "shop_price": "15000", - "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged roses", "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "8461" + "weight": "10" }, { - "durability": null, + "id": "8462", "name": "Bagged roses", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8462" + "durability": null, + "tradeable": "true" }, { - "shop_price": "160", - "examine": "How to build a house.", - "durability": null, + "id": "8463", "name": "Construction guide", - "tradeable": "true", - "weight": "1", + "examine": "How to build a house.", "archery_ticket_price": "0", - "id": "8463" + "durability": null, + "shop_price": "160", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8464", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8466", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8468", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8470", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8472", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8474", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8476", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8478", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8480", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8482", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8484", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8486", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8488", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8490", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Rune heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8492", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, "name": "Rune heraldic helm", - "weight": "2.7", + "examine": "The colours represent crest.", "archery_ticket_price": "0", - "id": "8494", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8494", + "name": "Rune heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "weight": "2.7" + }, + { + "id": "8496", + "name": "Crude wooden chair", "examine": "A ready-to-assemble crude chair.", - "durability": null, - "name": "Crude wooden chair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8496" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8497", "name": "Crude wooden chair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8497" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "53", + "id": "8498", + "name": "Wooden chair", "examine": "A ready-to-assemble wooden chair.", - "durability": null, - "name": "Wooden chair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8498" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "53", - "durability": null, + "id": "8499", "name": "Wooden chair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8499" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8500", + "name": "Rocking chair", "examine": "A ready-to-assemble rocking chair.", - "durability": null, - "name": "Rocking chair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8500" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8501", "name": "Rocking chair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8501" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", + "id": "8502", + "name": "Oak chair", "examine": "A ready-to-assemble oak chair.", - "durability": null, - "name": "Oak chair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8502" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, + "id": "8503", "name": "Oak chair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8503" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8504", + "name": "Oak armchair", "examine": "A ready-to-assemble oak armchair.", - "durability": null, - "name": "Oak armchair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8504" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8505", "name": "Oak armchair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8505" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8506", + "name": "Teak armchair", "examine": "A ready-to-assemble teak armchair.", - "durability": null, - "name": "Teak armchair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8506" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8507", "name": "Teak armchair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8507" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", + "id": "8508", + "name": "Mahogany armchair", "examine": "A ready-to-assemble mahogany armchair.", - "durability": null, - "name": "Mahogany armchair", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8508" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, + "id": "8509", "name": "Mahogany armchair", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8509" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8510", + "name": "Wooden bookcase", "examine": "A ready-to-assemble wooden bookcase.", - "durability": null, - "name": "Wooden bookcase", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8510" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8511", "name": "Wooden bookcase", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8511" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8512", + "name": "Oak bookcase", "examine": "A ready-to-assemble oak bookcase.", - "durability": null, - "name": "Oak bookcase", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8512" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8513", "name": "Oak bookcase", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8513" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "46", + "id": "8514", + "name": "Mahogany b'kcase", "examine": "A ready-to-assemble mahogany bookcase.", - "durability": null, - "name": "Mahogany b'kcase", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8514" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "46", - "durability": null, + "id": "8515", "name": "Mahogany b'kcase", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8515" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8516", + "name": "Beer barrel", "examine": "A ready-to-assemble beer barrel.", - "durability": null, - "name": "Beer barrel", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8516" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8517", "name": "Beer barrel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8517" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "16900", + "id": "8518", + "name": "Cider barrel", "examine": "A ready-to-assemble cider barrel.", - "durability": null, - "name": "Cider barrel", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8518" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "16900", - "durability": null, + "id": "8519", "name": "Cider barrel", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8519" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "3", - "ge_buy_limit": "100", + "id": "8520", + "name": "Asgarnian ale", "examine": "A ready-to-assemble barrel of Asgarnian Ale.", - "grand_exchange_price": "4586", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8520" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4586", - "durability": null, + "id": "8521", "name": "Asgarnian ale", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8521" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "100", + "id": "8522", + "name": "Greenman's ale", "examine": "A ready-to-assemble barrel of Greenman's Ale.", - "grand_exchange_price": "8875", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8522" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "8875", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8523" - }, - { "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8523", + "name": "Greenman's ale", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8524", + "name": "Dragon bitter", "examine": "A ready-to-assemble barrel of Dragon Bitter.", - "grand_exchange_price": "10300", - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8524" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "10300", - "durability": null, + "id": "8525", "name": "Dragon bitter", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8525" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "8526", + "name": "Chef's delight", "examine": "A ready-to-assemble barrel of Chef's Delight.", - "grand_exchange_price": "48800", - "durability": null, - "name": "Chef's delight", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8526" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "48800", - "durability": null, + "id": "8527", "name": "Chef's delight", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8527" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8528", + "name": "Wood kitchen table", "examine": "A ready-to-assemble wooden kitchen table.", - "durability": null, - "name": "Wood kitchen table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8528" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8529", "name": "Wood kitchen table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8529" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8530", + "name": "Oak kitchen table", "examine": "A ready-to-assemble oak kitchen table.", - "durability": null, - "name": "Oak kitchen table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8530" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8531", "name": "Oak kitchen table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8531" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8532", + "name": "Teak kitchen table", "examine": "A ready-to-assemble teak kitchen table.", - "durability": null, - "name": "Teak kitchen table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8532" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Teak kitchen table", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8533" + "weight": "1" }, { + "id": "8533", + "name": "Teak kitchen table", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8535", "name": "Oak lectern", "archery_ticket_price": "0", - "id": "8535" + "durability": null }, { - "durability": null, + "id": "8537", "name": "Eagle lectern", "archery_ticket_price": "0", - "id": "8537" + "durability": null }, { - "durability": null, + "id": "8539", "name": "Demon lectern", "archery_ticket_price": "0", - "id": "8539" + "durability": null }, { - "durability": null, + "id": "8541", "name": "Teak eagle lectern", "archery_ticket_price": "0", - "id": "8541" + "durability": null }, { - "durability": null, + "id": "8543", "name": "Teak demon lectern", "archery_ticket_price": "0", - "id": "8543" + "durability": null }, { - "durability": null, + "id": "8545", "name": "Mahogany eagle", "archery_ticket_price": "0", - "id": "8545" + "durability": null }, { - "durability": null, + "id": "8547", "name": "Mahogany demon", "archery_ticket_price": "0", - "id": "8547" + "durability": null }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8548", + "name": "Wood dining table", "examine": "A ready-to-assemble wooden dining table.", - "durability": null, - "name": "Wood dining table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8548" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8549", "name": "Wood dining table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8549" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8550", + "name": "Oak dining table", "examine": "A ready-to-assemble oak dining table.", - "durability": null, - "name": "Oak dining table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8550" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8551", "name": "Oak dining table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8551" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8552", + "name": "Carved oak table", "examine": "A ready-to-assemble carved oak dining table.", - "durability": null, - "name": "Carved oak table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8552" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8553", "name": "Carved oak table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8553" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", + "id": "8554", + "name": "Teak table", "examine": "A ready-to-assemble teak dining table.", - "durability": null, - "name": "Teak table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8554" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, + "id": "8555", "name": "Teak table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8555" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "245", + "id": "8556", + "name": "Carved teak table", "examine": "A ready-to-assemble carved teak dining table.", - "durability": null, - "name": "Carved teak table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8556" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "245", - "durability": null, + "id": "8557", "name": "Carved teak table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8557" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "575", + "id": "8558", + "name": "Mahogany table", "examine": "A ready-to-assemble mahogany dining table.", - "durability": null, - "name": "Mahogany table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8558" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "575", - "durability": null, + "id": "8559", "name": "Mahogany table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8559" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "379100", + "id": "8560", + "name": "Opulent table", "examine": "A ready-to-assemble opulent dining table.", - "durability": null, - "name": "Opulent table", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8560" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "379100", - "durability": null, + "id": "8561", "name": "Opulent table", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8561" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "15", + "id": "8562", + "name": "Wooden bench", "examine": "A ready-to-assemble wooden dining bench.", - "durability": null, - "name": "Wooden bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8562" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "15", - "durability": null, + "id": "8563", "name": "Wooden bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8563" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137", + "id": "8564", + "name": "Oak bench", "examine": "A ready-to-assemble oak dining bench.", - "durability": null, - "name": "Oak bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8564" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137", - "durability": null, + "id": "8565", "name": "Oak bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8565" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8566", + "name": "Carved oak bench", "examine": "A ready-to-assemble carved oak dining bench.", - "durability": null, - "name": "Carved oak bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8566" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8567", "name": "Carved oak bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8567" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", + "id": "8568", + "name": "Teak dining bench", "examine": "A ready-to-assemble teak dining bench.", - "durability": null, - "name": "Teak dining bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8568" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, + "id": "8569", "name": "Teak dining bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8569" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", + "id": "8570", + "name": "Carved teak bench", "examine": "A ready-to-assemble carved teak dining bench.", - "durability": null, - "name": "Carved teak bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8570" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, + "id": "8571", "name": "Carved teak bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8571" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "126", + "id": "8572", + "name": "Mahogany bench", "examine": "A ready-to-assemble mahogany dining bench.", - "durability": null, - "name": "Mahogany bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8572" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "126", - "durability": null, + "id": "8573", "name": "Mahogany bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8573" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "517600", + "id": "8574", + "name": "Gilded bench", "examine": "A ready-to-assemble gilded mahogany dining bench.", - "durability": null, - "name": "Gilded bench", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8574" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "517600", - "durability": null, + "id": "8575", "name": "Gilded bench", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8575" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1974", + "id": "8576", + "name": "Wooden bed", "examine": "A ready-to-assemble wooden bed.", - "durability": null, - "name": "Wooden bed", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8576" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1974", - "durability": null, + "id": "8577", "name": "Wooden bed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8577" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1775", + "id": "8578", + "name": "Oak bed", "examine": "A ready-to-assemble oak bed.", - "durability": null, - "name": "Oak bed", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8578" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1775", - "durability": null, + "id": "8579", "name": "Oak bed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8579" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1433", + "id": "8580", + "name": "Large oak bed", "examine": "A ready-to-assemble large oak bed.", - "durability": null, - "name": "Large oak bed", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8580" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1433", - "durability": null, + "id": "8581", "name": "Large oak bed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8581" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1442", + "id": "8582", + "name": "Teak bed", "examine": "A ready-to-assemble teak bed.", - "durability": null, - "name": "Teak bed", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8582" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1442", - "durability": null, + "id": "8583", "name": "Teak bed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8583" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2030", + "id": "8584", + "name": "Large teak bed", "examine": "A ready-to-assemble large teak bed.", - "durability": null, - "name": "Large teak bed", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8584" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2030", - "durability": null, + "id": "8585", "name": "Large teak bed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8585" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1401", + "id": "8586", + "name": "4-poster", "examine": "A ready-to-assemble four-poster bed.", - "durability": null, - "name": "4-poster", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8586" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1401", - "durability": null, + "id": "8587", "name": "4-poster", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8587" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "286700", + "id": "8588", + "name": "Gilded 4-poster", "examine": "A ready-to-assemble gilded four-poster bed.", - "durability": null, - "name": "Gilded 4-poster", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8588" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "286700", - "durability": null, + "id": "8589", "name": "Gilded 4-poster", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8589" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "384", + "id": "8590", + "name": "Oak clock", "examine": "A ready-to-assemble oak clock.", - "durability": null, - "name": "Oak clock", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8590" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "384", - "durability": null, + "id": "8591", "name": "Oak clock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8591" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "595", + "id": "8592", + "name": "Teak clock", "examine": "A ready-to-assemble teak clock.", - "durability": null, - "name": "Teak clock", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8592" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "595", - "durability": null, + "id": "8593", "name": "Teak clock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8593" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "156100", + "id": "8594", + "name": "Gilded clock", "examine": "A ready-to-assemble gilded mahogany clock.", - "durability": null, - "name": "Gilded clock", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8594" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "156100", - "durability": null, + "id": "8595", "name": "Gilded clock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8595" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "510", + "id": "8596", + "name": "Shaving stand", "examine": "A ready-to-assemble shaving stand.", - "durability": null, - "name": "Shaving stand", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8596" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "510", - "durability": null, + "id": "8597", "name": "Shaving stand", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8597" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", + "id": "8598", + "name": "Oak shaving stand", "examine": "A ready-to-assemble oak shaving stand.", - "durability": null, - "name": "Oak shaving stand", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8598" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", - "durability": null, + "id": "8599", "name": "Oak shaving stand", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8599" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "147", + "id": "8600", + "name": "Oak dresser", "examine": "A ready-to-assemble oak dresser.", - "durability": null, - "name": "Oak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8600" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "147", - "durability": null, + "id": "8601", "name": "Oak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8601" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "478", + "id": "8602", + "name": "Teak dresser", "examine": "A ready-to-assemble teak dresser.", - "durability": null, - "name": "Teak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8602" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "478", - "durability": null, + "id": "8603", "name": "Teak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8603" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", + "id": "8604", + "name": "Fancy teak dresser", "examine": "A ready-to-assemble fancy teak dresser.", - "durability": null, - "name": "Fancy teak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8604" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", - "durability": null, + "id": "8605", "name": "Fancy teak dresser", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8605" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "399", + "id": "8606", + "name": "Mahogany dresser", "examine": "A ready-to-assemble mahogany dresser.", - "durability": null, - "name": "Mahogany dresser", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8606" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "399", - "durability": null, + "id": "8607", "name": "Mahogany dresser", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8607" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "138800", + "id": "8608", + "name": "Gilded dresser", "examine": "A ready-to-assemble gilded dresser.", - "durability": null, - "name": "Gilded dresser", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8608" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "138800", - "durability": null, + "id": "8609", "name": "Gilded dresser", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8609" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "21", + "id": "8610", + "name": "Shoe box", "examine": "A ready-to-assemble shoe box.", - "durability": null, - "name": "Shoe box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8610" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "21", - "durability": null, + "id": "8611", "name": "Shoe box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8611" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "47", + "id": "8612", + "name": "Oak drawers", "examine": "A ready-to-assemble oak chest of drawers.", - "durability": null, - "name": "Oak drawers", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8612" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "47", - "durability": null, + "id": "8613", "name": "Oak drawers", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8613" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "8614", + "name": "Oak wardrobe", "examine": "A ready-to-assemble oak bedroom wardrobe.", - "durability": null, - "name": "Oak wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8614" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8615", "name": "Oak wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8615" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "132", + "id": "8616", + "name": "Teak drawers", "examine": "A ready-to-assemble teak chest of drawers.", - "durability": null, - "name": "Teak drawers", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8616" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "132", - "durability": null, + "id": "8617", "name": "Teak drawers", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8617" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "13", + "id": "8618", + "name": "Teak wardrobe", "examine": "A ready-to-assemble teak bedroom wardrobe.", - "durability": null, - "name": "Teak wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8618" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, + "id": "8619", "name": "Teak wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8619" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "98", + "id": "8620", + "name": "Mahogany 'drobe", "examine": "A ready-to-assemble mahogany bedroom wardrobe.", - "durability": null, - "name": "Mahogany 'drobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8620" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "98", - "durability": null, + "id": "8621", "name": "Mahogany 'drobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8621" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137700", + "id": "8622", + "name": "Gilded wardrobe", "examine": "A ready-to-assemble gilded mahogany bedroom wardrobe.", - "durability": null, - "name": "Gilded wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "8622" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "137700", - "durability": null, - "name": "Gilded wardrobe", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8623" + "weight": "1" }, { + "id": "8623", + "name": "Gilded wardrobe", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8625", "name": "Crystal ball", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8625" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8627", "name": "Elemental sphere", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8627" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8629", "name": "Crystal of power", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8629" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8631", "name": "Globe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8631" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8633", "name": "Ornamental globe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8633" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8635", "name": "Lunar globe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8635" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8637", "name": "Celestial globe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8637" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8639", "name": "Armillary sphere", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8639" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8641", "name": "Small orrery", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8641" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8643", "name": "Large orrery", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8643" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8645", "name": "Wooden telescope", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8645" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8647", "name": "Teak telescope", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8647" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8649", "name": "Mahogany 'scope", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8649" + "durability": null, + "tradeable": "true" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8650", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8652", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8654", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8656", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8658", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8660", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8662", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8664", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8666", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8668", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8670", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8672", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8674", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8676", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Banner", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8678", - "stand_turn_anim": "1426" - }, - { - "turn90cw_anim": "1424", - "walk_anim": "1422", - "durability": null, - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", "run_anim": "1427", - "archery_ticket_price": "0", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" + }, + { "id": "8680", - "stand_turn_anim": "1426" + "name": "Banner", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8682", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8684", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8686", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8688", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8690", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8692", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8694", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8696", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8698", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8700", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8702", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8704", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8706", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8708", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, - "name": "Steel heraldic helm", - "weight": "2.7", - "archery_ticket_price": "0", "id": "8710", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "The colours represent crest.", - "durability": null, "name": "Steel heraldic helm", - "weight": "2.7", + "examine": "The colours represent crest.", "archery_ticket_price": "0", - "id": "8712", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", + "id": "8712", + "name": "Steel heraldic helm", + "examine": "The colours represent crest.", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { "id": "8714", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8716", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8718", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8720", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8722", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8724", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8726", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8728", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8730", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8732", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8734", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8736", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8738", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8740", + "name": "Rune kiteshield", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, - "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" - }, - { - "lendable": "true", - "examine": "A large metal shield.", - "grand_exchange_price": "32583", - "durability": null, "name": "Rune kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "8744", + "examine": "A large metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", + "id": "8744", + "name": "Rune kiteshield", "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", + "absorb": "3,0,7", "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { "id": "8746", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8748", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8750", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8752", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8754", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8756", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8758", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8760", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8762", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8764", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8766", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8768", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8770", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "8772", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, - "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" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "grand_exchange_price": "346", - "durability": null, "name": "Steel kiteshield", - "tradeable": "true", - "weight": "5.4", + "examine": "A large metal shield.", "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" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" }, { - "ge_buy_limit": "10000", + "id": "8776", + "name": "Steel kiteshield", + "examine": "A large metal shield.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "8778", + "name": "Oak plank", "examine": "A plank of sturdy oak.", - "grand_exchange_price": "660", - "durability": null, - "name": "Oak plank", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "8778" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "660", - "durability": null, + "id": "8779", "name": "Oak plank", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8779" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "500", - "ge_buy_limit": "10000", + "id": "8780", + "name": "Teak plank", "examine": "A plank of fine teak.", - "grand_exchange_price": "959", - "durability": null, - "name": "Teak plank", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "8780" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "500", + "tradeable": "true", + "weight": "0.8" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "959", - "durability": null, + "id": "8781", "name": "Teak plank", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8781" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "1500", - "ge_buy_limit": "10000", + "id": "8782", + "name": "Mahogany plank", "examine": "A plank of expensive mahogany.", - "grand_exchange_price": "2286", - "durability": null, - "name": "Mahogany plank", - "tradeable": "true", - "weight": "0.8", "archery_ticket_price": "0", - "id": "8782" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "2286", - "durability": null, + "shop_price": "1500", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "8783", "name": "Mahogany plank", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8783" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "130000", - "ge_buy_limit": "100", + "id": "8784", + "name": "Gold leaf", "examine": "A very delicate sheet of gold.", - "grand_exchange_price": "133800", - "durability": null, - "name": "Gold leaf", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8784" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "130000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "133800", - "durability": null, + "id": "8785", "name": "Gold leaf", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8785" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "325000", - "ge_buy_limit": "100", + "id": "8786", + "name": "Marble block", "examine": "A beautifully carved marble block.", - "grand_exchange_price": "324800", - "durability": null, - "name": "Marble block", - "tradeable": "true", - "weight": "13.6", "archery_ticket_price": "0", - "id": "8786" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "325000", + "tradeable": "true", + "weight": "13.6" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "324800", - "durability": null, + "id": "8787", "name": "Marble block", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8787" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "975000", - "ge_buy_limit": "100", + "id": "8788", + "name": "Magic stone", "examine": "A magic stone to make high-level furniture.", - "grand_exchange_price": "980300", - "durability": null, - "name": "Magic stone", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8788" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "975000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "980300", - "durability": null, + "id": "8789", "name": "Magic stone", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8789" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "650", - "ge_buy_limit": "100", + "id": "8790", + "name": "Bolt of cloth", "examine": "A bolt of ordinary cloth.", - "grand_exchange_price": "1090", - "durability": null, - "name": "Bolt of cloth", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8790" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "1090", - "durability": null, - "name": "Bolt of cloth", + "shop_price": "650", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8791" + "weight": "1" }, { - "ge_buy_limit": "5000", + "id": "8791", + "name": "Bolt of cloth", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8792", + "name": "Clockwork", "examine": "A clockwork mechanism.", - "grand_exchange_price": "1630", - "durability": null, - "name": "Clockwork", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "8792" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "1630", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "8793", "name": "Clockwork", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8793" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "13", - "ge_buy_limit": "100", - "examine": "Good for cutting wood.", - "grand_exchange_price": "64", - "durability": null, - "name": "Saw", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "8794", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "64", - "durability": null, "name": "Saw", - "tradeable": "true", + "examine": "Good for cutting wood.", "archery_ticket_price": "0", - "id": "8795" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "shop_price": "13", + "tradeable": "true", + "weight": "1" }, { + "id": "8795", + "name": "Saw", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8836", "name": "Mahogany logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8836" + "durability": null, + "tradeable": "true" }, { - "shop_price": "1", - "ge_buy_limit": "1000", + "id": "8837", + "name": "Timber beam", "examine": "A hefty beam of timber, perfect for building temples.", - "grand_exchange_price": "29", - "durability": null, - "name": "Timber beam", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8837" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "29", - "durability": null, - "name": "Timber beam", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8838" + "shop_price": "1", + "tradeable": "true" }, { + "id": "8838", + "name": "Timber beam", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "8839", + "name": "Void knight top", + "examine": "Torso armour from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,45,45,45,45,45,45,0,0,0,0", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", "shop_price": "250", - "examine": "Torso armour from the order of the Void Knights.", - "durability": null, - "name": "Void knight top", "tradeable": "false", - "weight": "6.5", - "archery_ticket_price": "0", - "id": "8839", - "bonuses": "0,0,0,0,0,45,45,45,45,45,45,0,0,0,0", - "equipment_slot": "4" + "weight": "6.5" }, { - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "250", - "examine": "Leg armour of the order of the Void Knights.", - "durability": null, - "name": "Void knight robe", - "tradeable": "false", - "weight": "8", - "archery_ticket_price": "0", "id": "8840", + "name": "Void knight robe", + "examine": "Leg armour of the order of the Void Knights.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,30,30,30,30,30,15,0,0,0,0", - "equipment_slot": "7" - }, - { + "durability": null, + "equipment_slot": "7", "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", "shop_price": "250", - "examine": "A mace used by the order of the Void Knights.", - "durability": null, - "weight": "1", - "attack_speed": "5", - "weapon_interface": "1", - "render_anim": "2553", - "equipment_slot": "3", - "attack_anims": "401,401,401,401", - "name": "Void knight mace", "tradeable": "false", - "archery_ticket_price": "0", - "id": "8841", - "bonuses": "22,0,41,8,0,2,2,2,2,2,0,38,6,0,0" + "weight": "8" }, { + "id": "8841", + "name": "Void knight mace", + "examine": "A mace used by the order of the Void Knights.", + "archery_ticket_price": "0", + "attack_anims": "401,401,401,401", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "22,0,41,8,0,2,2,2,2,2,0,38,6,0,0", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "render_anim": "2553", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "250", + "tradeable": "false", + "weapon_interface": "1", + "weight": "1" + }, + { + "id": "8842", + "name": "Void knight gloves", + "examine": "Gloves as used by the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", + "durability": null, + "equipment_slot": "9", "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", "shop_price": "150", - "examine": "Gloves as used by the order of the Void Knights.", - "durability": null, - "name": "Void knight gloves", "tradeable": "false", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "8842", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", - "equipment_slot": "9" + "weight": "0.5" }, { - "examine": "A defensive weapon.", - "durability": null, - "name": "Bronze defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8844", + "name": "Bronze defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "3,2,1,-3,-2,3,2,1,-3,-2,1,0,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "weight": "0.4" }, { - "examine": "A defensive weapon.", - "durability": null, - "name": "Iron defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8845", + "name": "Iron defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "5,4,3,-3,-2,5,4,3,-3,-2,2,0,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "weight": "0.4" }, { - "requirements": "{0,5}-{1,5}", - "examine": "A defensive weapon.", - "durability": null, - "name": "Steel defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8846", + "name": "Steel defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "7,6,5,-3,-2,7,6,5,-3,-2,3,1,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "requirements": "{0,5}-{1,5}", + "weight": "0.4" }, { - "requirements": "{0,10}-{1,10}", - "examine": "A defensive weapon.", - "durability": null, - "name": "Black defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8847", + "name": "Black defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "9,8,7,-3,-2,9,8,7,-3,-2,4,2,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "requirements": "{0,10}-{1,10}", + "weight": "0.4" }, { - "requirements": "{0,20}-{1,20}", - "examine": "A defensive weapon.", - "durability": null, - "name": "Mithril defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8848", + "name": "Mithril defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "10,9,8,-3,-2,10,9,8,-3,-2,5,3,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "requirements": "{0,20}-{1,20}", + "weight": "0.4" }, { - "requirements": "{0,30}-{1,30}", - "examine": "A defensive weapon.", - "durability": null, - "name": "Adamant defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8849", + "name": "Adamant defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "13,12,11,-3,-2,13,12,11,-3,-2,6,4,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "requirements": "{0,30}-{1,30}", + "weight": "0.4" }, { - "requirements": "{0,40}-{1,40}", - "examine": "A defensive weapon.", - "durability": null, - "name": "Rune defender", - "weight": "0.4", - "archery_ticket_price": "0", "id": "8850", + "name": "Rune defender", + "examine": "A defensive weapon.", + "archery_ticket_price": "0", "bonuses": "20,19,18,-3,-2,20,19,18,-3,-2,8,5,0,0,0", "defence_anim": "4177", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2238", + "equipment_slot": "5", + "requirements": "{0,40}-{1,40}", + "weight": "0.4" }, { - "examine": "Warrior Guild Token.", - "durability": null, + "id": "8851", "name": "Warrior guild token", + "examine": "Warrior Guild Token.", "archery_ticket_price": "0", - "id": "8851" + "durability": null }, { - "examine": "Large, round, heavy shield.", - "durability": null, - "name": "Defensive shield", - "weight": "3.6", - "archery_ticket_price": "0", - "two_handed": "false", "id": "8856", + "name": "Defensive shield", + "examine": "Large, round, heavy shield.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,8,9,7,0,8,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "two_handed": "false", + "weight": "3.6" }, { - "examine": "Just landed 18lb shot.", - "durability": null, + "id": "8858", "name": "18lb shot", - "destroy": "true", + "examine": "Just landed 18lb shot.", "archery_ticket_price": "0", - "id": "8858" + "destroy": "true", + "durability": null }, { - "examine": "Just landed 22lb shot.", - "durability": null, + "id": "8859", "name": "22lb shot", - "destroy": "true", + "examine": "Just landed 22lb shot.", "archery_ticket_price": "0", - "id": "8859" + "destroy": "true", + "durability": null }, { - "examine": "To put on your head.", - "durability": null, - "name": "One barrel", - "destroy": "true", - "weight": "4.5", - "archery_ticket_price": "0", "id": "8860", - "equipment_slot": "0" + "name": "One barrel", + "examine": "To put on your head.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "weight": "4.5" }, { - "examine": "To put on your head.", - "durability": null, - "name": "Two barrels", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", "id": "8861", - "equipment_slot": "0" + "name": "Two barrels", + "examine": "To put on your head.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "weight": "9" }, { - "examine": "To put on your head.", - "durability": null, - "name": "Three barrels", - "destroy": "true", - "weight": "13.5", - "archery_ticket_price": "0", "id": "8862", - "equipment_slot": "0" + "name": "Three barrels", + "examine": "To put on your head.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "weight": "13.5" }, { - "examine": "To put on your head.", - "durability": null, - "name": "Four barrels", - "destroy": "true", - "weight": "18.1", - "archery_ticket_price": "0", "id": "8863", - "equipment_slot": "0" - }, - { + "name": "Four barrels", "examine": "To put on your head.", - "durability": null, - "name": "Five barrels", - "destroy": "true", - "weight": "22.6", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "weight": "18.1" + }, + { "id": "8864", - "equipment_slot": "0" - }, - { - "examine": "A heap of finely ground ashes.", - "durability": null, - "name": "Ground ashes", + "name": "Five barrels", + "examine": "To put on your head.", "archery_ticket_price": "0", - "id": "8865" - }, - { - "examine": "A key made of solid iron.", - "durability": null, - "name": "Iron key", - "archery_ticket_price": "0", - "id": "8869" - }, - { - "examine": "See article", - "durability": null, - "name": "Zanik", - "archery_ticket_price": "0", - "id": "8870" - }, - { - "turn90cw_anim": "4194", - "examine": "It's got Zanik in it.", - "walk_anim": "4194", - "durability": null, "destroy": "true", - "weight": "32", - "turn90ccw_anim": "4194", - "turn180_anim": "4194", - "render_anim": "822", - "equipment_slot": "3", - "stand_anim": "4193", - "name": "Crate with zanik", - "tradeable": "false", - "run_anim": "4194", + "durability": null, + "equipment_slot": "0", + "weight": "22.6" + }, + { + "id": "8865", + "name": "Ground ashes", + "examine": "A heap of finely ground ashes.", "archery_ticket_price": "0", + "durability": null + }, + { + "id": "8869", + "name": "Iron key", + "examine": "A key made of solid iron.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "8870", + "name": "Zanik", + "examine": "See article", + "archery_ticket_price": "0", + "durability": null + }, + { "id": "8871", - "stand_turn_anim": "4194" + "name": "Crate with zanik", + "examine": "It's got Zanik in it.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "render_anim": "822", + "run_anim": "4194", + "stand_anim": "4193", + "stand_turn_anim": "4194", + "tradeable": "false", + "turn180_anim": "4194", + "turn90ccw_anim": "4194", + "turn90cw_anim": "4194", + "walk_anim": "4194", + "weight": "32" }, { - "shop_price": "2000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,400,400", - "grand_exchange_price": "2855", - "attack_audios": "2517,2517,2500,2517", - "name": "Bone dagger", - "tradeable": "true", - "archery_ticket_price": "0", "id": "8872", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2855", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Bone dagger", - "tradeable": "true", + "examine": "A powerful dagger.", "archery_ticket_price": "0", - "id": "8873" + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "render_anim": "2584", + "shop_price": "2000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" }, { - "shop_price": "2000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,400,400", - "grand_exchange_price": "3033", - "attack_audios": "2517,2517,2500,2517", - "name": "Bone dagger (p)", - "tradeable": "true", + "id": "8873", + "name": "Bone dagger", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "8874", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3033", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Bone dagger (p)", - "tradeable": "true", + "examine": "A powerful dagger.", "archery_ticket_price": "0", - "id": "8875" + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "render_anim": "2584", + "shop_price": "2000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" }, { - "shop_price": "2000", - "ge_buy_limit": "100", - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "400,400,400,400", - "grand_exchange_price": "3523", - "attack_audios": "2517,2517,2500,2517", - "name": "Bone dagger (p+)", - "tradeable": "true", + "id": "8875", + "name": "Bone dagger (p)", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "8876", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3523", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Bone dagger (p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8877" - }, - { - "shop_price": "2000", - "ge_buy_limit": "100", "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "400,400,400,400", - "grand_exchange_price": "8337", "attack_audios": "2517,2517,2500,2517", - "name": "Bone dagger (p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8878", - "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8337", - "examine": "Swap this note at any bank for the equivalent item.", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "defence_anim": "378", "durability": null, - "name": "Bone dagger (p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8879" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "This fires crossbow bolts.", - "walk_anim": "4226", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "4227", - "defence_anim": "424", + "equip_audio": "2248", "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "1159", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "8880", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,42,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,28}", - "shop_price": "2000", - "durability": null, - "weight": "2.2", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Dorgeshuun c'bow" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1159", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dorgeshuun c'bow", + "has_special": "true", + "render_anim": "2584", + "shop_price": "2000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8881" + "weapon_interface": "5", + "weight": "0.4" }, { + "id": "8877", + "name": "Bone dagger (p+)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8878", + "name": "Bone dagger (p++)", + "examine": "A powerful dagger.", + "archery_ticket_price": "0", + "attack_anims": "400,400,400,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "5,3,-4,1,0,0,0,0,1,0,0,4,0,0,0", + "defence_anim": "378", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "render_anim": "2584", + "shop_price": "2000", + "tradeable": "true", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "8879", + "name": "Bone dagger (p++)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8880", + "name": "Dorgeshuun c'bow", + "examine": "This fires crossbow bolts.", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,42,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "render_anim": "175", + "requirements": "{4,28}", + "run_anim": "4228", + "shop_price": "2000", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "2.2" + }, + { + "id": "8881", + "name": "Dorgeshuun c'bow", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8882", + "name": "Bone bolts", + "examine": "Good if you have a bone crossbow!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,28}", "shop_price": "4", - "ge_buy_limit": "10000", - "examine": "Good if you have a bone crossbow!", - "grand_exchange_price": "12", - "durability": null, - "name": "Bone bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8882", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" + "tradeable": "true" }, { - "examine": "See article", - "durability": null, + "id": "8887", "name": "Zanik", + "examine": "See article", "archery_ticket_price": "0", - "id": "8887" + "durability": null }, { - "bankable": "false", - "examine": "Lovely money!", - "durability": null, + "id": "8890", "name": "Coins", + "examine": "Lovely money!", "archery_ticket_price": "0", - "id": "8890" + "bankable": "false", + "durability": null }, { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "durability": null, + "id": "8900", "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", "archery_ticket_price": "0", - "id": "8900" + "durability": null }, { - "requirements": "{1,20}-{2,10}", - "ge_buy_limit": "10", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "989400", - "durability": null, - "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" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "989400", - "durability": null, "name": "Black mask (10)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8902" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (9)", - "tradeable": "true", - "weight": "10", + "id": "8902", + "name": "Black mask (10)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "8903", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (9)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8904" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (8)", - "tradeable": "true", - "weight": "10", + "id": "8904", + "name": "Black mask (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8905", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (8)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8906" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (7)", - "tradeable": "true", - "weight": "10", + "id": "8906", + "name": "Black mask (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8907", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (7)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8908" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (6)", - "tradeable": "true", - "weight": "10", + "id": "8908", + "name": "Black mask (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8909", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (6)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8910" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (5)", - "tradeable": "true", - "weight": "10", + "id": "8910", + "name": "Black mask (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8911", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (5)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8912" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (4)", - "tradeable": "true", - "weight": "10", + "id": "8912", + "name": "Black mask (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8913", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (4)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8914" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (3)", - "tradeable": "true", - "weight": "10", + "id": "8914", + "name": "Black mask (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8915", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (3)", - "tradeable": "true", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8916" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{1,20}-{2,10}", - "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (2)", - "tradeable": "true", - "weight": "10", + "id": "8916", + "name": "Black mask (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8917", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, "name": "Black mask (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8918" - }, - { - "requirements": "{1,20}-{2,10}", "examine": "A magic cave horror mask.", - "grand_exchange_price": "583700", - "durability": null, - "name": "Black mask (1)", - "tradeable": "true", - "weight": "10", "archery_ticket_price": "0", - "id": "8919", "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" - }, - { "durability": null, - "name": "Black mask (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8920" - }, - { + "equipment_slot": "0", "requirements": "{1,20}-{2,10}", - "ge_buy_limit": "10", - "examine": "An inert-seeming cave horror mask.", - "grand_exchange_price": "956100", - "durability": null, - "name": "Black mask", "tradeable": "true", - "weight": "10", + "weight": "10" + }, + { + "id": "8918", + "name": "Black mask (2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8919", + "name": "Black mask (1)", + "examine": "A magic cave horror mask.", "archery_ticket_price": "0", - "id": "8921", "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "956100", + "id": "8920", + "name": "Black mask (1)", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "8921", "name": "Black mask", - "tradeable": "true", + "examine": "An inert-seeming cave horror mask.", "archery_ticket_price": "0", - "id": "8922" + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "requirements": "{1,20}-{2,10}", + "tradeable": "true", + "weight": "10" }, { - "requirements": "{18,35}", - "shop_price": "900", - "examine": "A stick on a string... pure style.", - "durability": null, - "name": "Witchwood icon", + "id": "8922", + "name": "Black mask", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "8923", + "name": "Witchwood icon", + "examine": "A stick on a string... pure style.", + "archery_ticket_price": "0", "bonuses": "0,0,0,1,0,0,0,0,0,0,0,0,1,0,0", - "equipment_slot": "2" + "durability": null, + "equipment_slot": "2", + "requirements": "{18,35}", + "shop_price": "900" }, { - "remove_head": "true", - "examine": "A white bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", "id": "8924", - "equipment_slot": "0" + "name": "Bandana and eyepatch", + "examine": "A white bandana and a patch for the right eye.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "remove_head": "true", - "examine": "A red bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", "id": "8925", - "equipment_slot": "0" + "name": "Bandana and eyepatch", + "examine": "A red bandana and a patch for the right eye.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "remove_head": "true", - "examine": "A blue bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", "id": "8926", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "A brown bandana and a patch for the right eye.", - "durability": null, "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", + "examine": "A blue bandana and a patch for the right eye.", "archery_ticket_price": "0", - "id": "8927", - "equipment_slot": "0" - }, - { - "examine": "A pirate hat and a patch for the right eye.", "durability": null, - "name": "Hat and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "8928", - "equipment_slot": "0" - }, - { - "shop_price": "800", - "examine": "Tied together so they don't come apart.", - "durability": null, - "name": "Crabclaw and hook", - "weight": "1", - "archery_ticket_price": "0", - "id": "8929", - "bonuses": "0,0,0,0,0,3,5,3,0,0,0,1,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Crude wooden pipe section.", - "durability": null, - "name": "Pipe section", - "weight": "1", - "archery_ticket_price": "0", - "id": "8930" - }, - { - "durability": null, - "name": "Pipe section", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8931" - }, - { - "examine": "Repairs made with this will be patchy at best.", - "durability": null, - "name": "Lumber patch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8932" - }, - { - "durability": null, - "name": "Lumber patch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8933" - }, - { - "examine": "Slimy logs from the scrapey tree.", - "durability": null, - "name": "Scrapey tree logs", - "archery_ticket_price": "0", - "id": "8934" - }, - { - "durability": null, - "name": "Scrapey tree logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8935" - }, - { - "examine": "Very blue.", - "durability": null, - "name": "Blue flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8936" - }, - { - "durability": null, - "name": "Blue flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8937" - }, - { - "examine": "Very red.", - "durability": null, - "name": "Red flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8938" - }, - { - "durability": null, - "name": "Red flowers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8939" - }, - { - "examine": "Bluuuuuuuue Monkeeeeeeey!", - "durability": null, - "name": "Blue monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8943" - }, - { - "examine": "Bluuuuuuuue Monkeeeeeeey!", - "durability": null, - "name": "Blue monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8944" - }, - { - "examine": "Bluuuuuuuue Monkeeeeeeey!", - "durability": null, - "name": "Blue monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8945" - }, - { - "examine": "A well red monkey.", - "durability": null, - "name": "Red monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8946" - }, - { - "examine": "A well red monkey.", - "durability": null, - "name": "Red monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8947" - }, - { - "examine": "A well red monkey.", - "durability": null, - "name": "Red monkey", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8948" - }, - { + "equipment_slot": "0", "remove_head": "true", - "examine": "Essential pirate wear.", - "durability": null, - "name": "Pirate bandana", "tradeable": "true", - "weight": "0.1", + "weight": "0.1" + }, + { + "id": "8927", + "name": "Bandana and eyepatch", + "examine": "A brown bandana and a patch for the right eye.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "8928", + "name": "Hat and eyepatch", + "examine": "A pirate hat and a patch for the right eye.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "8929", + "name": "Crabclaw and hook", + "examine": "Tied together so they don't come apart.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,3,5,3,0,0,0,1,0,0,0", + "durability": null, + "equipment_slot": "9", + "shop_price": "800", + "weight": "1" + }, + { + "id": "8930", + "name": "Pipe section", + "examine": "Crude wooden pipe section.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "8931", + "name": "Pipe section", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8932", + "name": "Lumber patch", + "examine": "Repairs made with this will be patchy at best.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8933", + "name": "Lumber patch", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8934", + "name": "Scrapey tree logs", + "examine": "Slimy logs from the scrapey tree.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "8935", + "name": "Scrapey tree logs", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8936", + "name": "Blue flowers", + "examine": "Very blue.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8937", + "name": "Blue flowers", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8938", + "name": "Red flowers", + "examine": "Very red.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8939", + "name": "Red flowers", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8943", + "name": "Blue monkey", + "examine": "Bluuuuuuuue Monkeeeeeeey!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8944", + "name": "Blue monkey", + "examine": "Bluuuuuuuue Monkeeeeeeey!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8945", + "name": "Blue monkey", + "examine": "Bluuuuuuuue Monkeeeeeeey!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8946", + "name": "Red monkey", + "examine": "A well red monkey.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8947", + "name": "Red monkey", + "examine": "A well red monkey.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "8948", + "name": "Red monkey", + "examine": "A well red monkey.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8949", - "equipment_slot": "0" + "name": "Pirate bandana", + "examine": "Essential pirate wear.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "0.1" }, { - "examine": "Shiver me timbers!", - "grand_exchange_price": "83200", - "durability": null, - "name": "Pirate hat", - "tradeable": "true", - "archery_ticket_price": "0", "id": "8950", - "equipment_slot": "0" + "name": "Pirate hat", + "examine": "Shiver me timbers!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "examine": "Piratical currency.", - "durability": null, + "id": "8951", "name": "Pieces of eight", + "examine": "Piratical currency.", "archery_ticket_price": "0", - "id": "8951" + "durability": null }, { - "remove_sleeves": "true", - "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Blue naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "8952", - "equipment_slot": "4" - }, - { + "name": "Blue naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Green naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8953", - "equipment_slot": "4" - }, - { + "name": "Green naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Red naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8954", - "equipment_slot": "4" - }, - { + "name": "Red naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Brown naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8955", - "equipment_slot": "4" - }, - { + "name": "Brown naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Black naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8956", - "equipment_slot": "4" - }, - { + "name": "Black naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Purple naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8957", - "equipment_slot": "4" - }, - { + "name": "Purple naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Grey naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "8958", - "equipment_slot": "4" + "name": "Grey naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "100", + "tradeable": "false" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Blue tricorn hat", - "archery_ticket_price": "0", "id": "8959", - "equipment_slot": "0" + "name": "Blue tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Green tricorn hat", - "archery_ticket_price": "0", "id": "8960", - "equipment_slot": "0" + "name": "Green tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Red tricorn hat", - "archery_ticket_price": "0", "id": "8961", - "equipment_slot": "0" + "name": "Red tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Brown tricorn hat", - "archery_ticket_price": "0", "id": "8962", - "equipment_slot": "0" + "name": "Brown tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Black tricorn hat", - "archery_ticket_price": "0", "id": "8963", - "equipment_slot": "0" + "name": "Black tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Purple tricorn hat", - "archery_ticket_price": "0", "id": "8964", - "equipment_slot": "0" - }, - { - "shop_price": "50", + "name": "Purple tricorn hat", "examine": "I could never look square in this.", - "durability": null, - "name": "Grey tricorn hat", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" + }, + { "id": "8965", - "equipment_slot": "0" + "name": "Grey tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "shop_price": "50" }, { - "shop_price": "200", - "turn90cw_anim": "1424", - "examine": "The flag of The Cutthroat.", - "walk_anim": "1422", - "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Cutthroat flag", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8966", - "stand_turn_anim": "1426" - }, - { + "name": "Cutthroat flag", + "examine": "The flag of The Cutthroat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", "shop_price": "200", - "turn90cw_anim": "1424", - "examine": "The flag of The Guilded Smile.", - "walk_anim": "1422", - "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "attack_speed": "2", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", "stand_anim": "1421", - "name": "Guilded smile flag", - "run_anim": "1427", - "archery_ticket_price": "0", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" + }, + { "id": "8967", - "stand_turn_anim": "1426" + "name": "Guilded smile flag", + "examine": "The flag of The Guilded Smile.", + "archery_ticket_price": "0", + "attack_speed": "2", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "shop_price": "200", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" }, { - "shop_price": "300", - "turn90cw_anim": "1424", - "examine": "The flag of The Bronze Fist.", - "walk_anim": "1422", - "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Bronze fist flag", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8968", - "stand_turn_anim": "1426" + "name": "Bronze fist flag", + "examine": "The flag of The Bronze Fist.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "shop_price": "300", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" }, { - "shop_price": "400", - "turn90cw_anim": "1424", - "examine": "The flag of The Lucky Shot.", - "walk_anim": "1422", - "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Lucky shot flag", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8969", - "stand_turn_anim": "1426" + "name": "Lucky shot flag", + "examine": "The flag of The Lucky Shot.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "shop_price": "400", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" }, { - "shop_price": "500", - "turn90cw_anim": "1424", - "examine": "The flag of The Treasure Trove.", - "walk_anim": "1422", - "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", - "equipment_slot": "3", - "stand_anim": "1421", - "name": "Treasure flag", - "run_anim": "1427", - "archery_ticket_price": "0", "id": "8970", - "stand_turn_anim": "1426" - }, - { - "shop_price": "600", - "turn90cw_anim": "1424", - "examine": "The flag of The Phasmatys Pride.", - "walk_anim": "1422", + "name": "Treasure flag", + "examine": "The flag of The Treasure Trove.", + "archery_ticket_price": "0", "durability": null, - "weight": "6", - "turn90ccw_anim": "1425", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "1423", - "render_anim": "131", "equipment_slot": "3", - "stand_anim": "1421", - "name": "Phasmatys flag", + "render_anim": "131", "run_anim": "1427", - "archery_ticket_price": "0", + "shop_price": "500", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" + }, + { "id": "8971", - "stand_turn_anim": "1426" + "name": "Phasmatys flag", + "examine": "The flag of The Phasmatys Pride.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "131", + "run_anim": "1427", + "shop_price": "600", + "stand_anim": "1421", + "stand_turn_anim": "1426", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "14", + "weight": "6" }, { + "id": "8972", + "name": "Bowl of red water", "examine": "A Bowl of red water.", - "durability": null, - "name": "Bowl of red water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8972" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8973", "name": "Bowl of red water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8973" + "durability": null, + "tradeable": "true" }, { + "id": "8974", + "name": "Bowl of blue water", "examine": "A Bowl of blue water.", - "durability": null, - "name": "Bowl of blue water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8974" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8975", "name": "Bowl of blue water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8975" + "durability": null, + "tradeable": "true" }, { - "examine": "Monkeys seem to like throwing these.", - "durability": null, + "id": "8976", "name": "Bitternut", + "examine": "Monkeys seem to like throwing these.", "archery_ticket_price": "0", - "id": "8976" + "durability": null }, { + "id": "8977", + "name": "Scrapey bark", "examine": "Greasy bark from the scrapey tree.", - "durability": null, - "name": "Scrapey bark", - "weight": "1.1", "archery_ticket_price": "0", - "id": "8977" + "durability": null, + "weight": "1.1" }, { - "durability": null, + "id": "8978", "name": "Scrapey bark", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8978" + "durability": null, + "tradeable": "true" }, { + "id": "8979", + "name": "Bridge section", "examine": "Caution; not for use over troubled water.", - "durability": null, - "name": "Bridge section", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8979" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8980", "name": "Bridge section", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8980" + "durability": null, + "tradeable": "true" }, { + "id": "8981", + "name": "Sweetgrubs", "examine": "Better than sea slugs.", - "durability": null, - "name": "Sweetgrubs", "archery_ticket_price": "0", - "id": "8981" + "durability": null }, { - "durability": null, + "id": "8982", "name": "Sweetgrubs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8982" + "durability": null, + "tradeable": "true" }, { - "shop_price": "2", - "examine": "It's an empty bucket.", - "grand_exchange_price": "50", - "durability": null, + "id": "8986", "name": "Bucket", - "tradeable": "true", - "weight": "1", + "examine": "It's an empty bucket.", "archery_ticket_price": "0", - "id": "8986" + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "1" }, { - "durability": null, + "id": "8987", "name": "Torch", "archery_ticket_price": "0", - "id": "8987", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "5", - "examine": "Apparently good for brewing.", - "durability": null, + "id": "8988", "name": "The stuff", - "weight": "0.5", + "examine": "Apparently good for brewing.", "archery_ticket_price": "0", - "id": "8988" + "durability": null, + "shop_price": "5", + "weight": "0.5" }, { - "examine": "A how-to of brewing and arson.", - "durability": null, + "id": "8989", "name": "Brewin' guide", - "archery_ticket_price": "0", - "id": "8989" - }, - { "examine": "A how-to of brewing and arson.", - "durability": null, - "name": "Brewin' guide", "archery_ticket_price": "0", - "id": "8990" + "durability": null }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Blue navy slacks", + "id": "8990", + "name": "Brewin' guide", + "examine": "A how-to of brewing and arson.", "archery_ticket_price": "0", + "durability": null + }, + { "id": "8991", - "equipment_slot": "7" + "name": "Blue navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Green navy slacks", - "archery_ticket_price": "0", "id": "8992", - "equipment_slot": "7" + "name": "Green navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Red navy slacks", - "archery_ticket_price": "0", "id": "8993", - "equipment_slot": "7" + "name": "Red navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Brown navy slacks", - "archery_ticket_price": "0", "id": "8994", - "equipment_slot": "7" + "name": "Brown navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Black navy slacks", - "archery_ticket_price": "0", "id": "8995", - "equipment_slot": "7" + "name": "Black navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Purple navy slacks", - "archery_ticket_price": "0", "id": "8996", - "equipment_slot": "7" - }, - { - "shop_price": "100", + "name": "Purple navy slacks", "examine": "Not for slackers", - "durability": null, - "name": "Grey navy slacks", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" + }, + { "id": "8997", - "equipment_slot": "7" - }, - { - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", + "name": "Grey navy slacks", + "examine": "Not for slackers", "archery_ticket_price": "0", - "id": "8998" + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "durability": null, + "id": "8998", "name": "Bandana and eyepatch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8999" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "8999", "name": "Bandana and eyepatch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9000" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9000", "name": "Bandana and eyepatch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9001" + "durability": null, + "tradeable": "true" }, { + "id": "9001", + "name": "Bandana and eyepatch", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "9002", "name": "Hat and eyepatch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9002" + "durability": null, + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "100", + "id": "9003", + "name": "Security book", "examine": "WARNING: Contains information which could make your account secure!", - "grand_exchange_price": "100", - "durability": null, - "name": "Security book", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9003" - }, - { + "durability": null, "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "9004", + "name": "Stronghold notes", "examine": "Information regarding the Stronghold of Security.", - "grand_exchange_price": "16", - "durability": null, - "name": "Stronghold notes", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9004" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "destroy_message": "You can get another pair of Fancy Boots from the Stronghold of Security", - "examine": "Very nice boots from the Stronghold of Security.", - "durability": null, - "name": "Fancy boots", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "9005", - "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "destroy_message": "You can get another pair of Fighting Boots from the Stronghold of Security.", + "name": "Fancy boots", "examine": "Very nice boots from the Stronghold of Security.", - "durability": null, - "name": "Fighting boots", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "9006", "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", - "equipment_slot": "10" + "destroy": "true", + "destroy_message": "You can get another pair of Fancy Boots from the Stronghold of Security", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "tradeable": "false" }, { - "examine": "Ooooh spooky!", + "id": "9006", + "name": "Fighting boots", + "examine": "Very nice boots from the Stronghold of Security.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another pair of Fighting Boots from the Stronghold of Security.", "durability": null, + "equipment_slot": "10", + "tradeable": "false" + }, + { + "id": "9007", "name": "Right skull half", - "tradeable": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "9007" - }, - { "examine": "Ooooh spooky!", + "archery_ticket_price": "0", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "9008", "name": "Left skull half", - "tradeable": "false", - "destroy": "true", - "weight": "4", + "examine": "Ooooh spooky!", "archery_ticket_price": "0", - "id": "9008" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "4" }, { - "examine": "Seems to be for use with a staff or sceptre of some sort.", - "durability": null, + "id": "9009", "name": "Strange skull", - "tradeable": "false", - "weight": "1.2", + "examine": "Seems to be for use with a staff or sceptre of some sort.", "archery_ticket_price": "0", - "id": "9009" + "durability": null, + "tradeable": "false", + "weight": "1.2" }, { - "examine": "Top half of a broken sceptre.", - "durability": null, + "id": "9010", "name": "Top of sceptre", - "tradeable": "false", - "weight": "2", + "examine": "Top half of a broken sceptre.", "archery_ticket_price": "0", - "id": "9010" + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "examine": "Bottom half of a broken sceptre.", - "durability": null, + "id": "9011", "name": "Bottom of sceptre", - "tradeable": "false", - "weight": "1.8", + "examine": "Bottom half of a broken sceptre.", "archery_ticket_price": "0", - "id": "9011" + "durability": null, + "tradeable": "false", + "weight": "1.8" }, { - "examine": "Sceptre with runes on it, seems to be missing something.", - "durability": null, + "id": "9012", "name": "Runed sceptre", - "tradeable": "false", - "weight": "3.5", + "examine": "Sceptre with runes on it, seems to be missing something.", "archery_ticket_price": "0", - "id": "9012" + "durability": null, + "tradeable": "false", + "weight": "3.5" }, { - "turn90cw_anim": "1207", - "examine": "A fragile magical Sceptre.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You can obtain another Sceptre by collecting all four pieces from the Stronghold of Security.", - "stand_anim": "813", - "name": "Skull sceptre", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "9013", + "name": "Skull sceptre", + "examine": "A fragile magical Sceptre.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", + "destroy": "true", + "destroy_message": "You can obtain another Sceptre by collecting all four pieces from the Stronghold of Security.", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "1.8" }, { - "durability": null, + "id": "9014", "name": "Security book", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9014" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9015", "name": "Stronghold notes", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9015" + "durability": null, + "tradeable": "true" }, { - "examine": "Oversized nail clippings.", - "durability": null, + "id": "9016", "name": "Gorak claws", + "examine": "Oversized nail clippings.", "archery_ticket_price": "0", - "id": "9016" + "durability": null }, { - "examine": "A flower with magical properties.", - "durability": null, + "id": "9017", "name": "Star flower", + "examine": "A flower with magical properties.", "archery_ticket_price": "0", - "id": "9017" + "durability": null }, { - "examine": "Ground-down gorak claws.", - "durability": null, + "id": "9018", "name": "Gorak claw powder", - "weight": "1", + "examine": "Ground-down gorak claws.", "archery_ticket_price": "0", - "id": "9018" + "durability": null, + "weight": "1" }, { - "examine": "Contains the Fairy Queen's magical essence.", - "durability": null, - "name": "Queen's secateurs", - "archery_ticket_price": "0", "id": "9020", + "name": "Queen's secateurs", + "examine": "Contains the Fairy Queen's magical essence.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "Maybe I can find another in Fairy Nuff's grotto.", - "examine": "A scroll that says she's a healer, that's Fairy Nuff.", - "durability": null, + "id": "9025", "name": "Nuff's certificate", - "tradeable": "false", - "destroy": "true", + "examine": "A scroll that says she's a healer, that's Fairy Nuff.", "archery_ticket_price": "0", - "id": "9025" + "destroy": "true", + "destroy_message": "Maybe I can find another in Fairy Nuff's grotto.", + "durability": null, + "tradeable": "false" }, { - "ge_buy_limit": "100", + "id": "9026", + "name": "Ivory comb", "examine": "Simon Templeton at the Agility Pyramid will give me 50 coins for this.", - "grand_exchange_price": "187", - "durability": null, - "name": "Ivory comb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9026" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "durability": null, + "id": "9027", "name": "Ivory comb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9027" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9028", + "name": "Golden scarab", "examine": "Simon Templeton at the Agility Pyramid will give me 1000 coins for this.", - "grand_exchange_price": "513", - "durability": null, - "name": "Golden scarab", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9028" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "513", - "durability": null, + "id": "9029", "name": "Golden scarab", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9029" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9030", + "name": "Stone scarab", "examine": "Little ornament in the shape of a scarab.", - "grand_exchange_price": "168", - "durability": null, - "name": "Stone scarab", - "tradeable": "true", - "weight": "0.07", "archery_ticket_price": "0", - "id": "9030" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.07" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "168", - "durability": null, + "id": "9031", "name": "Stone scarab", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9031" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9032", + "name": "Pottery scarab", "examine": "A small pottery scarab.", - "grand_exchange_price": "74", - "durability": null, - "name": "Pottery scarab", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9032" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "74", - "durability": null, + "id": "9033", "name": "Pottery scarab", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9033" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9034", + "name": "Golden statuette", "examine": "A small golden statuette.", - "grand_exchange_price": "740", - "durability": null, - "name": "Golden statuette", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9034" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "740", - "durability": null, + "id": "9035", "name": "Golden statuette", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9035" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9036", + "name": "Pottery statuette", "examine": "A small pottery statuette.", - "grand_exchange_price": "97", - "durability": null, - "name": "Pottery statuette", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "9036" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "97", - "durability": null, + "id": "9037", "name": "Pottery statuette", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9037" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9038", + "name": "Stone statuette", "examine": "A small stone statuette.", - "grand_exchange_price": "193", - "durability": null, - "name": "Stone statuette", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9038" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "193", - "durability": null, + "id": "9039", "name": "Stone statuette", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9039" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9040", + "name": "Gold seal", "examine": "Simon Templeton at the Agility Arena will give me 750 coins for this.", - "grand_exchange_price": "379", - "durability": null, - "name": "Gold seal", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9040" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "379", - "durability": null, + "id": "9041", "name": "Gold seal", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9041" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9042", + "name": "Stone seal", "examine": "Simon Templeton at the Agility Pyramid will give me 150 coins for this.", - "grand_exchange_price": "147", - "durability": null, - "name": "Stone seal", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9042" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "147", - "durability": null, + "tradeable": "true" + }, + { + "id": "9043", "name": "Stone seal", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9043" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,30}-{6,30}", - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "This sceptre is fully charged.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1000000", - "stand_anim": "813", - "tradeable": "true", - "name": "Pharaoh's sceptre", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "9044", - "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1000000", - "durability": null, "name": "Pharaoh's sceptre", - "tradeable": "true", + "examine": "This sceptre is fully charged.", "archery_ticket_price": "0", - "id": "9045" - }, - { - "requirements": "{0,30}-{6,30}", - "turn90cw_anim": "1207", - "examine": "This sceptre has two charges left.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2", - "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0", + "destroy": "true", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "27", - "stand_anim": "813", - "tradeable": "true", - "name": "Pharaoh's sceptre", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" + }, + { + "id": "9045", + "name": "Pharaoh's sceptre", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "9046", - "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0" - }, - { - "durability": null, "name": "Pharaoh's sceptre", - "tradeable": "true", + "examine": "This sceptre has two charges left.", "archery_ticket_price": "0", - "id": "9047" - }, - { - "requirements": "{0,30}-{6,30}", - "turn90cw_anim": "1207", - "examine": "This sceptre has one charge left.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2", - "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0", + "destroy": "true", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "27", - "stand_anim": "813", - "tradeable": "true", - "name": "Pharaoh's sceptre", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" + }, + { + "id": "9047", + "name": "Pharaoh's sceptre", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "9048", - "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0" - }, - { - "durability": null, "name": "Pharaoh's sceptre", - "tradeable": "true", + "examine": "This sceptre has one charge left.", "archery_ticket_price": "0", - "id": "9049" - }, - { - "requirements": "{0,30}-{6,30}", - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "This sceptre has no charges left.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "2", - "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "3", - "turn180_anim": "1206", - "render_anim": "28", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0", + "destroy": "true", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "985800", - "stand_anim": "813", - "tradeable": "true", - "name": "Pharaoh's sceptre", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "9050", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0" + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "985800", - "durability": null, + "id": "9049", "name": "Pharaoh's sceptre", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9051" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9050", + "name": "Pharaoh's sceptre", + "examine": "This sceptre has no charges left.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "3", + "weight": "2" + }, + { + "id": "9051", + "name": "Pharaoh's sceptre", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "9052", + "name": "Locust meat", "examine": "Delicious and nutritious. Well, nutritious anyway.", - "grand_exchange_price": "68", - "durability": null, - "name": "Locust meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9052" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "68", - "durability": null, + "tradeable": "true" + }, + { + "id": "9053", "name": "Locust meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9053" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Red goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "9054", - "equipment_slot": "4" + "name": "Red goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Black goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "9055", - "equipment_slot": "4" + "name": "Black goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Yellow goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "9056", - "equipment_slot": "4" + "name": "Yellow goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Green goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "9057", - "equipment_slot": "4" + "name": "Green goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Purple goblin mail", - "weight": "3", - "archery_ticket_price": "0", "id": "9058", - "equipment_slot": "4" - }, - { + "name": "Purple goblin mail", "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Pink goblin mail", - "weight": "3", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" + }, + { "id": "9059", - "equipment_slot": "4" + "name": "Pink goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "A mystical lantern casting a green beam.", - "durability": null, + "id": "9064", "name": "Emerald lantern", - "weight": "1.3", + "examine": "A mystical lantern casting a green beam.", "archery_ticket_price": "0", - "id": "9064" + "durability": null, + "weight": "1.3" }, { - "examine": "A mystical lantern casting a green beam.", - "durability": null, - "name": "Emerald lantern", - "weight": "1.3", - "archery_ticket_price": "0", "id": "9065", - "equipment_slot": "5" + "name": "Emerald lantern", + "examine": "A mystical lantern casting a green beam.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "1.3" }, { - "examine": "A roughly circular disc of glass.", - "durability": null, + "id": "9066", "name": "Emerald lens", + "examine": "A roughly circular disc of glass.", "archery_ticket_price": "0", - "id": "9066" + "durability": null }, { - "examine": "A log of my thoughts...", - "durability": null, + "id": "9067", "name": "Dream log", - "weight": "1", + "examine": "A log of my thoughts...", "archery_ticket_price": "0", - "id": "9067" + "durability": null, + "weight": "1" }, { - "shop_price": "1000", - "examine": "Mystical headgear.", - "durability": null, - "name": "Moonclan helm", - "archery_ticket_price": "0", "id": "9068", - "bonuses": "0,0,0,3,-5,3,3,3,3,-5,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "shop_price": "1000", - "examine": "A mystical hat.", - "durability": null, - "name": "Moonclan hat", - "weight": "1", + "name": "Moonclan helm", + "examine": "Mystical headgear.", "archery_ticket_price": "0", - "id": "9069", "bonuses": "0,0,0,3,-5,3,3,3,3,-5,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "shop_price": "1000" }, { + "id": "9069", + "name": "Moonclan hat", + "examine": "A mystical hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-5,3,3,3,3,-5,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "shop_price": "1000", + "weight": "1" + }, + { + "id": "9070", + "name": "Moonclan armour", + "examine": "Provides good protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,-10,5,5,5,5,-10,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "1000", - "examine": "Provides good protection.", - "durability": null, - "name": "Moonclan armour", - "weight": "4", - "archery_ticket_price": "0", - "id": "9070", - "bonuses": "0,0,0,5,-10,5,5,5,5,-10,5,0,0,0,0", - "equipment_slot": "4" + "weight": "4" }, { - "shop_price": "1000", - "examine": "This should protect my legs.", - "durability": null, - "name": "Moonclan skirt", - "weight": "3", - "archery_ticket_price": "0", "id": "9071", + "name": "Moonclan skirt", + "examine": "This should protect my legs.", + "archery_ticket_price": "0", "bonuses": "0,0,0,5,-7,5,5,5,5,-7,0,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "shop_price": "1000", + "weight": "3" }, { - "shop_price": "900", - "examine": "These should keep my hands safe.", - "durability": null, - "name": "Moonclan gloves", - "archery_ticket_price": "0", "id": "9072", + "name": "Moonclan gloves", + "examine": "These should keep my hands safe.", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,-5,2,2,2,2,-5,2,0,0,0,0", - "equipment_slot": "9" + "durability": null, + "equipment_slot": "9", + "shop_price": "900" }, { - "shop_price": "900", - "examine": "Groovy foot protection.", - "durability": null, - "name": "Moonclan boots", - "archery_ticket_price": "0", "id": "9073", + "name": "Moonclan boots", + "examine": "Groovy foot protection.", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,-5,2,2,2,2,-5,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "shop_price": "900" }, { - "shop_price": "200", - "examine": "A mystical cape.", - "durability": null, - "name": "Moonclan cape", - "archery_ticket_price": "0", "id": "9074", + "name": "Moonclan cape", + "examine": "A mystical cape.", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,-2,0,1,1,2,-2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "shop_price": "200" }, { - "shop_price": "220", - "ge_buy_limit": "25000", - "examine": "Used for Lunar Spells", - "grand_exchange_price": "231", - "durability": null, + "id": "9075", "name": "Astral rune", - "tradeable": "true", + "examine": "Used for Lunar Spells", "archery_ticket_price": "0", - "id": "9075" + "durability": null, + "ge_buy_limit": "25000", + "shop_price": "220", + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This needs refining", - "durability": null, + "id": "9076", "name": "Lunar ore", - "weight": "7", + "examine": "This needs refining", "archery_ticket_price": "0", - "id": "9076" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "7" }, { - "destroy_message": "You'll have to mine another lunar ore and smelt it.", - "examine": "It's a bar of magic metal.", - "durability": null, + "id": "9077", "name": "Lunar bar", - "tradeable": "false", - "destroy": "true", + "examine": "It's a bar of magic metal.", "archery_ticket_price": "0", - "id": "9077" + "destroy": "true", + "destroy_message": "You'll have to mine another lunar ore and smelt it.", + "durability": null, + "tradeable": "false" }, { - "examine": "A book of Moonclan history.", - "durability": null, + "id": "9078", "name": "Moonclan manual", - "weight": "1", + "examine": "A book of Moonclan history.", "archery_ticket_price": "0", - "id": "9078" + "durability": null, + "weight": "1" }, { - "examine": "The tooth, the whole tooth, and nothing but the tooth.", - "durability": null, + "id": "9079", "name": "Suqah tooth", + "examine": "The tooth, the whole tooth, and nothing but the tooth.", "archery_ticket_price": "0", - "id": "9079" + "durability": null }, { - "examine": "An untanned piece of suqah hide.", - "durability": null, + "id": "9080", "name": "Suqah hide", + "examine": "An untanned piece of suqah hide.", "archery_ticket_price": "0", - "id": "9080" + "durability": null }, { - "examine": "A piece of Suqah hide that has been expertly tanned into leather.", - "durability": null, + "id": "9081", "name": "Suqah leather", - "weight": "3", + "examine": "A piece of Suqah hide that has been expertly tanned into leather.", "archery_ticket_price": "0", - "id": "9081" + "durability": null, + "weight": "3" }, { - "examine": "A ground Suqah tooth.", - "durability": null, + "id": "9082", "name": "Ground tooth", + "examine": "A ground Suqah tooth.", "archery_ticket_price": "0", - "id": "9082" + "durability": null }, { - "destroy_message": "Brundt the Chieftain will probably have another lying around somewhere anyway...", - "examine": "A seal of passage issued by Brundt the Chieftain of the Fremennik.", - "durability": null, - "name": "Seal of passage", - "tradeable": "false", - "destroy": "true", - "weight": "4.5", - "archery_ticket_price": "0", "id": "9083", - "equipment_slot": "2" + "name": "Seal of passage", + "examine": "A seal of passage issued by Brundt the Chieftain of the Fremennik.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Brundt the Chieftain will probably have another lying around somewhere anyway...", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "4.5" }, { - "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", + "name": "Lunar staff", + "examine": "A Moonclan staff.", + "archery_ticket_price": "0", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "3,2,16,13,0,2,3,2,13,1,0,15,3,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,65}", + "run_anim": "1210", + "shop_price": "30000", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "3,2,16,13,0,2,3,2,13,1,0,15,3,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "destroy_message": "You'll have to get another from the Oneiromancer.", - "examine": "A vessel for holding liquid.", - "durability": null, + "id": "9085", "name": "Empty vial", - "destroy": "true", + "examine": "A vessel for holding liquid.", "archery_ticket_price": "0", - "id": "9085" + "destroy": "true", + "destroy_message": "You'll have to get another from the Oneiromancer.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "10", - "examine": "A glass vial containing water.", - "grand_exchange_price": "21", - "durability": null, + "id": "9086", "name": "Vial of water", + "examine": "A glass vial containing water.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "10", "tradeable": "true", - "weight": "0.02", - "archery_ticket_price": "0", - "id": "9086" + "weight": "0.02" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A vessel for dreaming while awake!", - "durability": null, + "id": "9087", "name": "Waking sleep vial", + "examine": "A vessel for dreaming while awake!", "archery_ticket_price": "0", - "id": "9087" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A vessel with water and Guam inside.", - "durability": null, + "id": "9088", "name": "Guam vial", + "examine": "A vessel with water and Guam inside.", "archery_ticket_price": "0", - "id": "9088" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A vessel with water and Marrentill inside.", - "durability": null, + "id": "9089", "name": "Marr vial", + "examine": "A vessel with water and Marrentill inside.", "archery_ticket_price": "0", - "id": "9089" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A vessel with water, guam and marrentill inside.", - "durability": null, + "id": "9090", "name": "Guam-marr vial", + "examine": "A vessel with water, guam and marrentill inside.", "archery_ticket_price": "0", - "id": "9090" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "requirements": "{6,65}", - "turn90cw_anim": "1207", - "examine": "A staff enchanted by air.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "1", - "turn90ccw_anim": "1208", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Lunar staff - pt1", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "9091", + "name": "Lunar staff - pt1", + "examine": "A staff enchanted by air.", + "archery_ticket_price": "0", + "attack_audios": "2555,0,0,0", + "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,65}", + "run_anim": "1210", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1" }, { - "requirements": "{6,65}", - "turn90cw_anim": "1207", - "examine": "A staff enchanted by air and fire.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "1", - "turn90ccw_anim": "1208", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Lunar staff - pt2", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "9092", - "stand_turn_anim": "1209", - "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0" - }, - { - "requirements": "{6,65}", - "turn90cw_anim": "1207", - "examine": "A staff enchanted by air, fire and water.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "weight": "1", - "turn90ccw_anim": "1208", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "813", + "name": "Lunar staff - pt2", + "examine": "A staff enchanted by air and fire.", + "archery_ticket_price": "0", "attack_audios": "2555,0,0,0", - "name": "Lunar staff - pt3", - "tradeable": "false", + "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,65}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "9093", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1" }, { - "examine": "Small bits of wood from the first magic tree!", + "id": "9093", + "name": "Lunar staff - pt3", + "examine": "A staff enchanted by air, fire and water.", + "archery_ticket_price": "0", + "attack_audios": "2555,0,0,0", + "bonuses": "-1,-1,10,10,0,2,3,1,10,0,0,10,0,0,0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,65}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "1" + }, + { + "id": "9094", "name": "Kindling", + "examine": "Small bits of wood from the first magic tree!", "archery_ticket_price": "0", - "id": "9094" + "durability": null }, { - "examine": "Magic wood soaked with a potion of waking sleep. Groovy.", - "durability": null, + "id": "9095", "name": "Soaked kindling", + "examine": "Magic wood soaked with a potion of waking sleep. Groovy.", "archery_ticket_price": "0", - "id": "9095" + "durability": null }, { + "id": "9096", + "name": "Lunar helm", + "examine": "A mystical helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,40}-{6,65}", "shop_price": "15000", - "examine": "A mystical helmet.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "absorb": "3,1,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar helm", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9096", - "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0" + "weight": "0.9" }, { + "id": "9097", + "name": "Lunar torso", + "examine": "Provides good protection.", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,10,-10,34,22,40,12,0,35,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", "requirements": "{1,40}-{6,65}", "shop_price": "120000", - "examine": "Provides good protection.", - "durability": null, - "destroy": "true", - "weight": "4.5", - "absorb": "6,3,0", - "equipment_slot": "4", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "remove_sleeves": "true", - "name": "Lunar torso", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9097", - "bonuses": "0,0,0,10,-10,34,22,40,12,0,35,0,0,0,0" + "weight": "4.5" }, { + "id": "9098", + "name": "Lunar legs", + "examine": "These should protect my legs.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,-7,20,19,23,9,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "7", "requirements": "{1,40}-{6,65}", "shop_price": "80000", - "examine": "These should protect my legs.", - "durability": null, - "destroy": "true", - "weight": "3.65", - "absorb": "4,2,0", - "equipment_slot": "7", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar legs", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9098", - "bonuses": "0,0,0,7,-7,20,19,23,9,0,20,0,0,0,0" + "weight": "3.65" }, { - "requirements": "{1,40}-{6,65}", - "shop_price": "10000", - "examine": "These should keep my hands safe.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "equipment_slot": "9", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar gloves", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9099", - "bonuses": "0,0,0,4,-1,2,1,1,2,0,1,0,0,0,0" - }, - { + "name": "Lunar gloves", + "examine": "These should keep my hands safe.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,-1,2,1,1,2,0,1,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "9", "requirements": "{1,40}-{6,65}", "shop_price": "10000", - "examine": "Mystical foot protection.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "equipment_slot": "10", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar boots", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9100", - "bonuses": "0,0,0,2,-1,1,2,2,2,0,2,0,0,0,0" + "weight": "0.9" }, { + "id": "9100", + "name": "Lunar boots", + "examine": "Mystical foot protection.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,-1,1,2,2,2,0,2,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "10", + "requirements": "{1,40}-{6,65}", + "shop_price": "10000", + "tradeable": "false", + "weight": "0.9" + }, + { + "id": "9101", + "name": "Lunar cape", + "examine": "Oooo pretty!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "1", "requirements": "{1,40}-{6,65}", "shop_price": "12000", - "examine": "Oooo pretty!", - "durability": null, - "destroy": "true", - "weight": "0.4", - "equipment_slot": "1", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar cape", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9101", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" + "weight": "0.4" }, { + "id": "9102", + "name": "Lunar amulet", + "examine": "Awesome.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "2", "requirements": "{1,40}-{6,65}", "shop_price": "4000", - "examine": "Awesome.", - "durability": null, - "name": "Lunar amulet", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9102", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "2" + "tradeable": "false" }, { - "destroy_message": "The Oneiromancer might be able to help you get another.", - "examine": "I'll be the talk of the town with this... maybe.", - "durability": null, + "id": "9103", "name": "A special tiara", - "tradeable": "false", - "destroy": "true", + "examine": "I'll be the talk of the town with this... maybe.", "archery_ticket_price": "0", - "id": "9103" + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "tradeable": "false" }, { + "id": "9104", + "name": "Lunar ring", + "examine": "A mysterious ring that can fill the wearer with magical power.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "12", "requirements": "{1,40}-{6,65}", "shop_price": "2000", - "examine": "A mysterious ring that can fill the wearer with magical power.", - "durability": null, - "name": "Lunar ring", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9104", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", - "equipment_slot": "12" + "tradeable": "false" }, { - "durability": null, + "id": "9106", "name": "Astral tiara", "archery_ticket_price": "0", - "id": "9106", + "durability": null, "equipment_slot": "0" }, { - "requirements": "{4,16}", - "examine": "Blurite crossbow bolts.", - "durability": null, - "name": "Blurite bolts", - "archery_ticket_price": "0", "id": "9139", + "name": "Blurite bolts", + "examine": "Blurite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{4,16}" }, { + "id": "9140", + "name": "Iron bolts", + "examine": "Iron crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,26}", "shop_price": "60", - "ge_buy_limit": "25000", - "examine": "Iron crossbow bolts.", - "grand_exchange_price": "41", - "durability": null, - "name": "Iron bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9140", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", - "equipment_slot": "13" + "tradeable": "true" }, { + "id": "9141", + "name": "Steel bolts", + "examine": "Steel crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,31}", "shop_price": "150", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "grand_exchange_price": "90", - "durability": null, - "name": "Steel bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9141", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64", - "equipment_slot": "13" + "tradeable": "true" }, { + "id": "9142", + "name": "Mithril bolts", + "examine": "Mithril crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,36}", "shop_price": "32", - "ge_buy_limit": "10000", - "examine": "Mithril crossbow bolts.", - "grand_exchange_price": "108", - "durability": null, - "name": "Mithril bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9142", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", - "equipment_slot": "13" + "tradeable": "true" }, { + "id": "9143", + "name": "Adamant bolts", + "examine": "Adamantite crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,46}", "shop_price": "23", - "ge_buy_limit": "10000", - "examine": "Adamantite crossbow bolts.", - "grand_exchange_price": "210", - "durability": null, - "name": "Adamant bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9143", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "grand_exchange_price": "627", - "durability": null, - "name": "Rune bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9144", + "name": "Rune bolts", + "examine": "Runite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,61}", + "tradeable": "true" }, { + "id": "9145", + "name": "Silver bolts", + "examine": "Silver crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,26}", "shop_price": "6", - "ge_buy_limit": "10000", - "examine": "Silver crossbow bolts.", - "grand_exchange_price": "8", - "durability": null, - "name": "Silver bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9145", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", - "equipment_slot": "13" + "tradeable": "true" }, { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "A bronze crossbow.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "29", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", "id": "9174", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,0", - "shop_price": "92", - "durability": null, - "weight": "4", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Bronze crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "29", - "durability": null, "name": "Bronze crossbow", - "tradeable": "true", + "examine": "A bronze crossbow.", "archery_ticket_price": "0", - "id": "9175" + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "500", + "render_anim": "175", + "run_anim": "4228", + "shop_price": "92", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "4" }, { - "requirements": "{4,16}", - "turn90cw_anim": "821", - "examine": "A blurite crossbow.", - "walk_anim": "4226", - "durability": null, - "weight": "4", - "turn90ccw_anim": "822", - "attack_speed": "6", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "name": "Blurite crossbow", - "run_anim": "4228", + "id": "9175", + "name": "Bronze crossbow", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { "id": "9176", + "name": "Blurite crossbow", + "examine": "A blurite crossbow.", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,30,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "175", + "requirements": "{4,16}", + "run_anim": "4228", + "stand_anim": "4591", "stand_turn_anim": "823", - "bonuses": "0,0,0,0,30,0,0,0,0,0,0,0,0,0,0" + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "4" }, { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "An iron crossbow.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "20", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", "id": "9177", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,42,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,26}", - "shop_price": "157", - "durability": null, - "weight": "4", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Iron crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "20", - "durability": null, "name": "Iron crossbow", - "tradeable": "true", + "examine": "An iron crossbow.", "archery_ticket_price": "0", - "id": "9178" - }, - { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "A steel crossbow.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "99", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "9179", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,54,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,31}", - "durability": null, - "weight": "5", - "weapon_interface": "17", - "render_anim": "175", "attack_audios": "2700,0,0,0", - "name": "Steel crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "99", - "durability": null, - "name": "Steel crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9180" - }, - { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "A mithril crossbow", - "walk_anim": "4226", - "turn90ccw_anim": "822", "attack_speed": "6", - "turn180_anim": "4227", + "bonuses": "0,0,0,0,42,0,0,0,0,0,0,0,0,0,0", "defence_anim": "424", + "durability": null, + "equip_audio": "2244", "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "355", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "9181", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,66,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,36}", - "durability": null, - "weight": "6", - "weapon_interface": "17", + "ge_buy_limit": "500", "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Mith crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "355", - "durability": null, - "name": "Mith crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9182" - }, - { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "An adamantite crossbow.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "905", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "9183", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,78,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,46}", - "shop_price": "2244", - "durability": null, - "weight": "6", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Adamant crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "905", - "durability": null, - "name": "Adamant crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9184" - }, - { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "A runite crossbow.", - "walk_anim": "4226", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "9706", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "9185", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,61}", - "shop_price": "16200", - "durability": null, - "weight": "6", - "weapon_interface": "17", - "render_anim": "175", - "lendable": "true", - "attack_audios": "2700,0,0,0", - "name": "Rune crossbow" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "9706", - "durability": null, - "name": "Rune crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9186" - }, - { - "ge_buy_limit": "10000", - "examine": "Jade bolt tips.", - "grand_exchange_price": "2", - "durability": null, - "name": "Jade bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9187" - }, - { - "ge_buy_limit": "10000", - "examine": "Red Topaz bolt tips.", - "grand_exchange_price": "2", - "durability": null, - "name": "Topaz bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9188" - }, - { - "ge_buy_limit": "10000", - "examine": "Sapphire bolt tips.", - "grand_exchange_price": "4", - "durability": null, - "name": "Sapphire bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9189" - }, - { - "ge_buy_limit": "10000", - "examine": "Emerald bolt tips.", - "grand_exchange_price": "84", - "durability": null, - "name": "Emerald bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9190" - }, - { - "ge_buy_limit": "10000", - "examine": "Ruby bolt tips.", - "grand_exchange_price": "159", - "durability": null, - "name": "Ruby bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9191" - }, - { - "ge_buy_limit": "10000", - "examine": "Diamond bolt tips.", - "grand_exchange_price": "1289", - "durability": null, - "name": "Diamond bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9192" - }, - { - "ge_buy_limit": "10000", - "examine": "Dragonstone bolt tips.", - "grand_exchange_price": "2477", - "durability": null, - "name": "Dragon bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9193" - }, - { - "shop_price": "13500", - "ge_buy_limit": "10000", - "examine": "Onyx bolt tips.", - "grand_exchange_price": "8096", - "tokkul_price": "1500", - "durability": null, - "name": "Onyx bolt tips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9194" - }, - { - "ge_buy_limit": "25000", - "examine": "Enchanted Opal tipped Bronze Crossbow Bolts.", - "grand_exchange_price": "10", - "durability": null, - "name": "Opal bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9236", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,14", - "equipment_slot": "13" - }, - { - "requirements": "{4,15}", - "examine": "Enchanted Jade tipped Blurite Crossbow Bolts.", - "durability": null, - "name": "Jade bolts (e)", - "archery_ticket_price": "0", - "id": "9237", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,30", - "equipment_slot": "13" - }, - { "requirements": "{4,26}", - "ge_buy_limit": "25000", - "examine": "Enchanted Pearl tipped Iron Crossbow Bolts.", - "grand_exchange_price": "36", - "durability": null, - "name": "Pearl bolts (e)", + "run_anim": "4228", + "shop_price": "157", + "stand_anim": "4591", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9238", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,48", - "equipment_slot": "13" + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "4" }, { + "id": "9178", + "name": "Iron crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9179", + "name": "Steel crossbow", + "examine": "A steel crossbow.", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,54,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "500", + "render_anim": "175", "requirements": "{4,31}", - "ge_buy_limit": "25000", - "examine": "Enchanted Red Topaz tipped Steel Crossbow Bolts.", - "grand_exchange_price": "44", - "durability": null, - "name": "Topaz bolts (e)", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9239", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,66", - "equipment_slot": "13" + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "5" }, { + "id": "9180", + "name": "Steel crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9181", + "name": "Mith crossbow", + "examine": "A mithril crossbow", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,66,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "500", + "render_anim": "175", + "requirements": "{4,36}", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "6" + }, + { + "id": "9182", + "name": "Mith crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9183", + "name": "Adamant crossbow", + "examine": "An adamantite crossbow.", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,78,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "500", + "render_anim": "175", + "requirements": "{4,46}", + "run_anim": "4228", + "shop_price": "2244", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "6" + }, + { + "id": "9184", + "name": "Adamant crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9185", + "name": "Rune crossbow", + "examine": "A runite crossbow.", + "archery_ticket_price": "0", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "500", + "lendable": "true", + "render_anim": "175", + "requirements": "{4,61}", + "run_anim": "4228", + "shop_price": "16200", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "6" + }, + { + "id": "9186", + "name": "Rune crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9187", + "name": "Jade bolt tips", + "examine": "Jade bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9188", + "name": "Topaz bolt tips", + "examine": "Red Topaz bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9189", + "name": "Sapphire bolt tips", + "examine": "Sapphire bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9190", + "name": "Emerald bolt tips", + "examine": "Emerald bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9191", + "name": "Ruby bolt tips", + "examine": "Ruby bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9192", + "name": "Diamond bolt tips", + "examine": "Diamond bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9193", + "name": "Dragon bolt tips", + "examine": "Dragonstone bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9194", + "name": "Onyx bolt tips", + "examine": "Onyx bolt tips.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "13500", + "tokkul_price": "1500", + "tradeable": "true" + }, + { + "id": "9236", + "name": "Opal bolts (e)", + "examine": "Enchanted Opal tipped Bronze Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,14", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "9237", + "name": "Jade bolts (e)", + "examine": "Enchanted Jade tipped Blurite Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,30", + "durability": null, + "equipment_slot": "13", + "requirements": "{4,15}" + }, + { + "id": "9238", + "name": "Pearl bolts (e)", + "examine": "Enchanted Pearl tipped Iron Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,48", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,26}", + "tradeable": "true" + }, + { + "id": "9239", + "name": "Topaz bolts (e)", + "examine": "Enchanted Red Topaz tipped Steel Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,66", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,31}", + "tradeable": "true" + }, + { + "id": "9240", + "name": "Sapphire bolts (e)", + "examine": "Enchanted Sapphire tipped Mithril Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,83", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,36}", "shop_price": "103", - "ge_buy_limit": "25000", - "examine": "Enchanted Sapphire tipped Mithril Crossbow Bolts.", - "grand_exchange_price": "95", - "durability": null, - "name": "Sapphire bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9240", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,83", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Enchanted Emerald tipped Mithril Crossbow Bolts.", - "grand_exchange_price": "304", - "durability": null, - "name": "Emerald bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9241", + "name": "Emerald bolts (e)", + "examine": "Enchanted Emerald tipped Mithril Crossbow Bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,85", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,36}", + "tradeable": "true" }, { - "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Enchanted Ruby tipped Adamantite Crossbow Bolts.", - "grand_exchange_price": "450", - "durability": null, - "name": "Ruby bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9242", + "name": "Ruby bolts (e)", + "examine": "Enchanted Ruby tipped Adamantite Crossbow Bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,103", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Enchanted Diamond tipped Adamantite Crossbow Bolts.", - "grand_exchange_price": "1631", - "durability": null, - "name": "Diamond bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9243", + "name": "Diamond bolts (e)", + "examine": "Enchanted Diamond tipped Adamantite Crossbow Bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,105", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,46}", + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "25000", - "examine": "Enchanted Dragonstone tipped Runite Crossbow Bolts.", - "grand_exchange_price": "3217", - "durability": null, - "name": "Dragon bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9244", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,117", - "equipment_slot": "13" - }, - { - "requirements": "{4,61}", - "ge_buy_limit": "25000", - "examine": "Enchanted Onyx tipped runite Crossbow Bolts.", - "grand_exchange_price": "8870", - "durability": null, - "name": "Onyx bolts (e)", - "tradeable": "true", + "name": "Dragon bolts (e)", + "examine": "Enchanted Dragonstone tipped Runite Crossbow Bolts.", "archery_ticket_price": "0", - "id": "9245", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,120", - "equipment_slot": "13" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,117", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,61}", + "tradeable": "true" }, { - "requirements": "{4,16}", + "id": "9245", + "name": "Onyx bolts (e)", + "examine": "Enchanted Onyx tipped runite Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,120", "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,61}", + "tradeable": "true" + }, + { + "id": "9286", "name": "Blurite bolts(p)", "archery_ticket_price": "0", - "id": "9286", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{4,16}" }, { + "id": "9287", + "name": "Iron bolts (p)", + "examine": "Iron crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,26}", "shop_price": "60", - "ge_buy_limit": "25000", - "examine": "Iron crossbow bolts.", - "grand_exchange_price": "88", - "durability": null, - "name": "Iron bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9287", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "grand_exchange_price": "132", - "durability": null, - "name": "Steel bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9288", + "name": "Steel bolts (p)", + "examine": "Steel crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,31}", + "tradeable": "true" }, { + "id": "9289", + "name": "Mithril bolts (p)", + "examine": "Mithril crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,36}", "shop_price": "32", - "ge_buy_limit": "10000", - "examine": "Mithril crossbow bolts.", - "grand_exchange_price": "146", - "durability": null, - "name": "Mithril bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9289", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", - "equipment_slot": "13" + "tradeable": "true" }, { + "id": "9290", + "name": "Adamant bolts (p)", + "examine": "Adamantite crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,46}", "shop_price": "23", - "ge_buy_limit": "10000", - "examine": "Adamantite crossbow bolts.", - "grand_exchange_price": "302", - "durability": null, - "name": "Adamant bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9290", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "grand_exchange_price": "734", - "durability": null, - "name": "Runite bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9291", + "name": "Runite bolts (p)", + "examine": "Runite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,61}", + "tradeable": "true" }, { + "id": "9292", + "name": "Silver bolts (p)", + "examine": "Silver crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,26}", "shop_price": "6", - "ge_buy_limit": "10000", - "examine": "Silver crossbow bolts.", - "grand_exchange_price": "34", - "durability": null, - "name": "Silver bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9292", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,16}", - "durability": null, + "id": "9293", "name": "Blurite bolts(p+)", "archery_ticket_price": "0", - "id": "9293", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{4,16}" }, { + "id": "9294", + "name": "Iron bolts(p+)", + "examine": "Iron crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,26}", "shop_price": "60", - "ge_buy_limit": "25000", - "examine": "Iron crossbow bolts.", - "grand_exchange_price": "185", - "durability": null, - "name": "Iron bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9294", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "grand_exchange_price": "215", - "durability": null, - "name": "Steel bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9295", + "name": "Steel bolts(p+)", + "examine": "Steel crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,31}", + "tradeable": "true" }, { + "id": "9296", + "name": "Mithril bolts(p+)", + "examine": "Mithril crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,36}", "shop_price": "32", - "ge_buy_limit": "10000", - "examine": "Mithril crossbow bolts.", - "grand_exchange_price": "296", - "durability": null, - "name": "Mithril bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9296", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,46}", - "ge_buy_limit": "10000", - "grand_exchange_price": "472", - "durability": null, + "id": "9297", "name": "Adamant bolts(p+)", "archery_ticket_price": "0", - "id": "9297", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", - "equipment_slot": "13" - }, - { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "grand_exchange_price": "1130", "durability": null, - "name": "Runite bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9298", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", - "equipment_slot": "13" + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,46}" }, { + "id": "9298", + "name": "Runite bolts(p+)", + "examine": "Runite crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,61}", + "tradeable": "true" + }, + { + "id": "9299", + "name": "Silver bolts(p+)", + "examine": "Silver crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,26}", "shop_price": "6", - "ge_buy_limit": "10000", - "examine": "Silver crossbow bolts.", - "grand_exchange_price": "129", - "durability": null, - "name": "Silver bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9299", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,16}", - "durability": null, + "id": "9300", "name": "Blurite bolts(p++)", "archery_ticket_price": "0", - "id": "9300", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{4,16}" }, { + "id": "9301", + "name": "Iron bolts(p++)", + "examine": "Iron crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,26}", "shop_price": "60", - "ge_buy_limit": "25000", - "examine": "Iron crossbow bolts.", - "grand_exchange_price": "1136", - "durability": null, - "name": "Iron bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9301", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,46", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "grand_exchange_price": "1088", - "durability": null, - "name": "Steel bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9302", + "name": "Steel bolts(p++)", + "examine": "Steel crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,31}", + "tradeable": "true" }, { + "id": "9303", + "name": "Mithril bolts(p++)", + "examine": "Mithril crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,36}", "shop_price": "32", - "ge_buy_limit": "10000", - "examine": "Mithril crossbow bolts.", - "grand_exchange_price": "903", - "durability": null, - "name": "Mithril bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9303", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,82", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,46}", - "ge_buy_limit": "10000", - "grand_exchange_price": "1043", - "durability": null, + "id": "9304", "name": "Adamant bolts(p++)", "archery_ticket_price": "0", - "id": "9304", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", - "equipment_slot": "13" - }, - { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "grand_exchange_price": "1581", "durability": null, - "name": "Runite bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9305", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", - "equipment_slot": "13" + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,46}" }, { + "id": "9305", + "name": "Runite bolts(p++)", + "examine": "Runite crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,61}", + "tradeable": "true" + }, + { + "id": "9306", + "name": "Silver bolts(p++)", + "examine": "Silver crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", "requirements": "{4,26}", "shop_price": "6", - "ge_buy_limit": "10000", - "examine": "Silver crossbow bolts.", - "grand_exchange_price": "1211", - "durability": null, - "name": "Silver bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9306", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,36", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,16}", - "examine": "Jade tipped blurite bolts.", - "durability": null, - "name": "Jade bolts", - "archery_ticket_price": "0", "id": "9335", + "name": "Jade bolts", + "examine": "Jade tipped blurite bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,30", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "requirements": "{4,16}" }, { + "id": "9336", + "name": "Topaz bolts", + "examine": "Red Topaz tipped Steel Crossbow Bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,66", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,31}", "shop_price": "62", - "ge_buy_limit": "25000", - "examine": "Red Topaz tipped Steel Crossbow Bolts.", - "grand_exchange_price": "60", - "durability": null, - "name": "Topaz bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9336", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,66", - "equipment_slot": "13" + "tradeable": "true" }, { - "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Sapphire tipped Mithril crossbow bolts.", - "grand_exchange_price": "89", - "durability": null, - "name": "Sapphire bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9337", + "name": "Sapphire bolts", + "examine": "Sapphire tipped Mithril crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,83", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Emerald tipped Mithril crossbow bolts.", - "grand_exchange_price": "177", - "durability": null, - "name": "Emerald bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9338", + "name": "Emerald bolts", + "examine": "Emerald tipped Mithril crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,85", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,36}", + "tradeable": "true" }, { - "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Ruby tipped Adamantite crossbow bolts.", - "grand_exchange_price": "408", - "durability": null, - "name": "Ruby bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9339", + "name": "Ruby bolts", + "examine": "Ruby tipped Adamantite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,103", - "equipment_slot": "13" - }, - { + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Diamond tipped Adamantite crossbow bolts.", - "grand_exchange_price": "1545", - "durability": null, - "name": "Diamond bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9340", + "name": "Diamond bolts", + "examine": "Diamond tipped Adamantite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,105", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,46}", + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "25000", - "examine": "Dragonstone tipped Runite crossbow bolts.", - "grand_exchange_price": "3201", - "durability": null, - "name": "Dragon bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9341", + "name": "Dragon bolts", + "examine": "Dragonstone tipped Runite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,117", - "equipment_slot": "13" - }, - { - "requirements": "{4,61}", + "durability": null, + "equipment_slot": "13", "ge_buy_limit": "25000", - "examine": "Onyx tipped Runite crossbow bolts.", - "grand_exchange_price": "8752", - "durability": null, - "name": "Onyx bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{4,61}", + "tradeable": "true" + }, + { "id": "9342", + "name": "Onyx bolts", + "examine": "Onyx tipped Runite crossbow bolts.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,120", - "equipment_slot": "13" + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", + "requirements": "{4,61}", + "tradeable": "true" }, { - "shop_price": "1", - "ge_buy_limit": "10000", - "examine": "Unfeathered bronze crossbow bolts.", - "grand_exchange_price": "3", - "durability": null, + "id": "9375", "name": "Bronze bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered bronze crossbow bolts.", "archery_ticket_price": "0", - "id": "9375" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "1", + "tradeable": "true" }, { - "examine": "Unfeathered blurite crossbow bolts.", - "durability": null, + "id": "9376", "name": "Blurite bolts (unf)", + "examine": "Unfeathered blurite crossbow bolts.", "archery_ticket_price": "0", - "id": "9376" + "durability": null }, { - "shop_price": "2", - "ge_buy_limit": "10000", - "examine": "Unfeathered iron crossbow bolts.", - "grand_exchange_price": "14", - "durability": null, + "id": "9377", "name": "Iron bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered iron crossbow bolts.", "archery_ticket_price": "0", - "id": "9377" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "2", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Unfeathered steel crossbow bolts.", - "grand_exchange_price": "61", - "durability": null, + "id": "9378", "name": "Steel bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered steel crossbow bolts.", "archery_ticket_price": "0", - "id": "9378" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Unfeathered mithril crossbow bolts.", - "grand_exchange_price": "115", - "durability": null, + "id": "9379", "name": "Mithril bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered mithril crossbow bolts.", "archery_ticket_price": "0", - "id": "9379" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Unfeathered adamantite crossbow bolts.", - "grand_exchange_price": "266", - "durability": null, + "id": "9380", "name": "Adamant bolts(unf)", - "tradeable": "true", + "examine": "Unfeathered adamantite crossbow bolts.", "archery_ticket_price": "0", - "id": "9380" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Unfeathered runite crossbow bolts", - "grand_exchange_price": "1235", - "durability": null, + "id": "9381", "name": "Runite bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered runite crossbow bolts", "archery_ticket_price": "0", - "id": "9381" - }, - { + "durability": null, "ge_buy_limit": "10000", - "examine": "Unfeathered silver crossbow bolts.", - "grand_exchange_price": "2", - "durability": null, + "tradeable": "true" + }, + { + "id": "9382", "name": "Silver bolts (unf)", - "tradeable": "true", + "examine": "Unfeathered silver crossbow bolts.", "archery_ticket_price": "0", - "id": "9382" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "A mithril grapple tipped bolt with a rope.", - "grand_exchange_price": "1447", - "durability": null, + "id": "9415", "name": "Grapple", - "tradeable": "true", + "examine": "A mithril grapple tipped bolt with a rope.", "archery_ticket_price": "0", - "id": "9415" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9416", + "name": "Mith grapple tip", "examine": "A mithril grapple tip.", - "grand_exchange_price": "461", - "durability": null, - "name": "Mith grapple tip", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9416" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "461", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "9417", "name": "Mith grapple tip", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9417" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A mithril grapple tipped bolt with a rope.", - "grand_exchange_price": "858", - "durability": null, + "id": "9418", "name": "Mith grapple", - "tradeable": "true", + "examine": "A mithril grapple tipped bolt with a rope.", "archery_ticket_price": "0", - "id": "9418" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "A mithril grapple tipped bolt with a rope.", - "grand_exchange_price": "1470", - "durability": null, - "name": "Mith grapple", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9419", - "equipment_slot": "13" + "name": "Mith grapple", + "examine": "A mithril grapple tipped bolt with a rope.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "9420", + "name": "Bronze limbs", + "examine": "A pair of bronze crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", "requirements": "{9,9}", "shop_price": "20", - "ge_buy_limit": "5000", - "examine": "A pair of bronze crossbow limbs.", - "grand_exchange_price": "49", - "durability": null, - "name": "Bronze limbs", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9420", - "equipment_slot": "5" + "weight": "1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "49", - "durability": null, + "id": "9421", "name": "Bronze limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9421" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{24,9}", - "examine": "A pair of blurite crossbow limbs.", - "durability": null, - "name": "Blurite limbs", - "weight": "1", - "archery_ticket_price": "0", "id": "9422", - "equipment_slot": "5" + "name": "Blurite limbs", + "examine": "A pair of blurite crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "requirements": "{24,9}", + "weight": "1" }, { + "id": "9423", + "name": "Iron limbs", + "examine": "A pair of iron crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", "requirements": "{9,39}", "shop_price": "200", - "ge_buy_limit": "5000", - "examine": "A pair of iron crossbow limbs.", - "grand_exchange_price": "34", - "durability": null, - "name": "Iron limbs", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9423", - "equipment_slot": "5" + "weight": "1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "34", - "durability": null, + "id": "9424", "name": "Iron limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9424" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "9425", + "name": "Steel limbs", + "examine": "A pair of steel crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", "requirements": "{9,46}", "shop_price": "900", - "ge_buy_limit": "5000", - "examine": "A pair of steel crossbow limbs.", - "grand_exchange_price": "97", - "durability": null, - "name": "Steel limbs", "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "9425", - "equipment_slot": "5" + "weight": "2" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "durability": null, + "id": "9426", "name": "Steel limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9426" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "9427", + "name": "Mithril limbs", + "examine": "A pair of mithril crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", "requirements": "{9,54}", "shop_price": "650", - "ge_buy_limit": "5000", - "examine": "A pair of mithril crossbow limbs.", - "grand_exchange_price": "347", - "durability": null, - "name": "Mithril limbs", "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "9427", - "equipment_slot": "5" + "weight": "2" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "347", - "durability": null, + "id": "9428", "name": "Mithril limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9428" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "9429", + "name": "Adamantite limbs", + "examine": "A pair of adamantite crossbow limbs.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", "requirements": "{9,61}", "shop_price": "4800", - "ge_buy_limit": "5000", - "examine": "A pair of adamantite crossbow limbs.", - "grand_exchange_price": "1434", - "durability": null, - "name": "Adamantite limbs", "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "9429", - "equipment_slot": "5" + "weight": "3" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1434", - "durability": null, + "id": "9430", "name": "Adamantite limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9430" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{9,69}", - "ge_buy_limit": "5000", - "examine": "A pair of runite crossbow limbs.", - "grand_exchange_price": "9441", - "durability": null, - "name": "Runite limbs", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", "id": "9431", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "9441", - "durability": null, "name": "Runite limbs", - "tradeable": "true", + "examine": "A pair of runite crossbow limbs.", "archery_ticket_price": "0", - "id": "9432" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "5000", + "requirements": "{9,69}", + "tradeable": "true", + "weight": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "1500", - "examine": "A pouch for storing crossbow bolts.", + "id": "9432", + "name": "Runite limbs", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "9433", "name": "Bolt pouch", - "tradeable": "false", + "examine": "A pouch for storing crossbow bolts.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9433" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "1500", + "tradeable": "false", + "weight": "1" }, { - "shop_price": "25", - "ge_buy_limit": "100", + "id": "9434", + "name": "Bolt mould", "examine": "A mould for creating silver crossbow bolts.", - "grand_exchange_price": "409", - "durability": null, - "name": "Bolt mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "9434" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "409", - "durability": null, + "shop_price": "25", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "9435", "name": "Bolt mould", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9435" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9436", + "name": "Sinew", "examine": "I can use this to make a crossbow string.", - "grand_exchange_price": "196", - "durability": null, - "name": "Sinew", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9436" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "196", - "durability": null, + "id": "9437", "name": "Sinew", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9437" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9438", + "name": "Crossbow string", "examine": "A string for a crossbow.", - "grand_exchange_price": "203", - "durability": null, - "name": "Crossbow string", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9438" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "203", - "durability": null, - "name": "Crossbow string", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9439" + "tradeable": "true" }, { + "id": "9439", + "name": "Crossbow string", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9440", + "name": "Wooden stock", + "examine": "A wooden crossbow stock", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{9,9}", "shop_price": "8", - "ge_buy_limit": "1000", - "examine": "A wooden crossbow stock", - "grand_exchange_price": "1", - "durability": null, - "name": "Wooden stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9440", - "equipment_slot": "3" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "1", - "durability": null, + "id": "9441", "name": "Wooden stock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9441" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "requirements": "{24,9}", - "ge_buy_limit": "1000", - "examine": "An oak crossbow stock.", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak stock", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", "id": "9442", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1", - "durability": null, "name": "Oak stock", - "tradeable": "true", + "examine": "An oak crossbow stock.", "archery_ticket_price": "0", - "id": "9443" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{24,9}", + "tradeable": "true", + "weight": "3" }, { + "id": "9443", + "name": "Oak stock", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9444", + "name": "Willow stock", + "examine": "A willow crossbow stock.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{9,39}", "shop_price": "21", - "ge_buy_limit": "1000", - "examine": "A willow crossbow stock.", - "grand_exchange_price": "3", - "durability": null, - "name": "Willow stock", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9444", - "equipment_slot": "3" + "weight": "1" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "3", - "durability": null, + "id": "9445", "name": "Willow stock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9445" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { + "id": "9446", + "name": "Teak stock", + "examine": "A teak crossbow stock.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", "requirements": "{9,46}", "shop_price": "231", - "ge_buy_limit": "1000", - "examine": "A teak crossbow stock.", - "grand_exchange_price": "6", - "durability": null, - "name": "Teak stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9446", - "equipment_slot": "3" + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "6", - "durability": null, + "id": "9447", "name": "Teak stock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9447" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "requirements": "{9,54}", - "ge_buy_limit": "1000", - "examine": "A maple crossbow stock.", - "grand_exchange_price": "7", - "durability": null, - "name": "Maple stock", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9448", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "7", - "durability": null, "name": "Maple stock", - "tradeable": "true", + "examine": "A maple crossbow stock.", "archery_ticket_price": "0", - "id": "9449" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{9,54}", + "tradeable": "true" }, { - "requirements": "{9,61}", - "ge_buy_limit": "1000", - "examine": "A Mahogany crossbow stock.", - "grand_exchange_price": "23", - "durability": null, - "name": "Mahogany stock", - "tradeable": "true", - "weight": "3", + "id": "9449", + "name": "Maple stock", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "9450", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "23", - "durability": null, "name": "Mahogany stock", - "tradeable": "true", + "examine": "A Mahogany crossbow stock.", "archery_ticket_price": "0", - "id": "9451" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{9,61}", + "tradeable": "true", + "weight": "3" }, { - "requirements": "{9,69}", - "ge_buy_limit": "1000", - "examine": "A yew crossbow stock.", - "grand_exchange_price": "37", - "durability": null, - "name": "Yew stock", - "tradeable": "true", - "weight": "3", + "id": "9451", + "name": "Mahogany stock", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { "id": "9452", - "equipment_slot": "3" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "37", - "durability": null, "name": "Yew stock", - "tradeable": "true", + "examine": "A yew crossbow stock.", "archery_ticket_price": "0", - "id": "9453" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "1000", + "requirements": "{9,69}", + "tradeable": "true", + "weight": "3" }, { - "shop_price": "27", - "ge_buy_limit": "500", + "id": "9453", + "name": "Yew stock", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9454", + "name": "Bronze c'bow (u)", "examine": "An unstrung bronze crossbow.", - "grand_exchange_price": "25", - "durability": null, - "name": "Bronze c'bow (u)", - "tradeable": "true", - "weight": "8", "archery_ticket_price": "0", - "id": "9454" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "25", - "durability": null, - "name": "Bronze c'bow (u)", + "shop_price": "27", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9455" + "weight": "8" }, { - "shop_price": "27", - "examine": "An unstrung blurite crossbow.", + "id": "9455", + "name": "Bronze c'bow (u)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9456", "name": "Blurite c'bow (u)", - "weight": "8", + "examine": "An unstrung blurite crossbow.", "archery_ticket_price": "0", - "id": "9456" + "durability": null, + "shop_price": "27", + "weight": "8" }, { - "ge_buy_limit": "500", + "id": "9457", + "name": "Iron c'bow (u)", "examine": "An unstrung iron crossbow.", - "grand_exchange_price": "26", - "durability": null, - "name": "Iron c'bow (u)", - "tradeable": "true", - "weight": "8", "archery_ticket_price": "0", - "id": "9457" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true", + "weight": "8" }, { - "ge_buy_limit": "500", - "grand_exchange_price": "26", - "durability": null, + "id": "9458", "name": "Iron c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9458" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "shop_price": "27", - "ge_buy_limit": "500", + "id": "9459", + "name": "Steel c'bow (u)", "examine": "An unstrung steel crossbow.", - "grand_exchange_price": "114", - "durability": null, - "name": "Steel c'bow (u)", - "tradeable": "true", - "weight": "8", "archery_ticket_price": "0", - "id": "9459" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "114", - "durability": null, - "name": "Steel c'bow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9460" - }, - { "shop_price": "27", + "tradeable": "true", + "weight": "8" + }, + { + "id": "9460", + "name": "Steel c'bow (u)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "9461", + "name": "Mithril c'bow (u)", "examine": "An unstrung mithril crossbow.", - "grand_exchange_price": "330", - "durability": null, - "name": "Mithril c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9461" + "durability": null, + "ge_buy_limit": "500", + "shop_price": "27", + "tradeable": "true" }, { - "ge_buy_limit": "500", - "grand_exchange_price": "330", - "durability": null, + "id": "9462", "name": "Mithril c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9462" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "shop_price": "693", - "ge_buy_limit": "500", + "id": "9463", + "name": "Adamant c'bow (u)", "examine": "An unstrung adamantite crossbow.", - "grand_exchange_price": "1007", - "durability": null, - "name": "Adamant c'bow (u)", - "tradeable": "true", - "weight": "8", "archery_ticket_price": "0", - "id": "9463" + "durability": null, + "ge_buy_limit": "500", + "shop_price": "693", + "tradeable": "true", + "weight": "8" }, { - "ge_buy_limit": "500", - "grand_exchange_price": "1007", - "durability": null, + "id": "9464", "name": "Adamant c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9464" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "shop_price": "4000", - "ge_buy_limit": "500", + "id": "9465", + "name": "Runite c'bow (u)", "examine": "An unstrung runite crossbow.", - "grand_exchange_price": "9493", - "durability": null, - "name": "Runite c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9465" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "9493", - "durability": null, + "shop_price": "4000", + "tradeable": "true" + }, + { + "id": "9466", "name": "Runite c'bow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9466" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "examine": "It's a bar of blurite.", - "durability": null, + "id": "9467", "name": "Blurite bar", - "weight": "1.8", + "examine": "It's a bar of blurite.", "archery_ticket_price": "0", - "id": "9467" + "durability": null, + "weight": "1.8" }, { - "examine": "What is left over when a log is made into a plank.", - "durability": null, + "id": "9468", "name": "Sawdust", - "weight": "0.5", + "examine": "What is left over when a log is made into a plank.", "archery_ticket_price": "0", - "id": "9468" + "durability": null, + "weight": "0.5" }, { - "ge_buy_limit": "100", - "examine": "A seed pod of the Grand Tree.", - "grand_exchange_price": "16200", - "durability": null, + "id": "9469", "name": "Grand seed pod", - "tradeable": "true", + "examine": "A seed pod of the Grand Tree.", "archery_ticket_price": "0", - "id": "9469" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "10", - "examine": "A scarf. You feel your upper lip stiffening.", - "grand_exchange_price": "1900000", - "durability": null, - "name": "Gnome scarf", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "9470", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1900000", - "durability": null, "name": "Gnome scarf", - "tradeable": "true", + "examine": "A scarf. You feel your upper lip stiffening.", "archery_ticket_price": "0", - "id": "9471" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "10", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "10", - "examine": "Tally Ho!", - "grand_exchange_price": "95800", - "durability": null, - "name": "Gnome goggles", - "tradeable": "true", + "id": "9471", + "name": "Gnome scarf", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "9472", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "95800", - "durability": null, "name": "Gnome goggles", - "tradeable": "true", + "examine": "Tally Ho!", "archery_ticket_price": "0", - "id": "9473" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "tradeable": "true" }, { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "id": "9473", + "name": "Gnome goggles", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "9474", "name": "Reward token", - "tradeable": "false", - "destroy": "true", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", "archery_ticket_price": "0", - "id": "9474" + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" }, { - "ge_buy_limit": "100", + "id": "9475", + "name": "Mint cake", "examine": "It looks very minty.", - "grand_exchange_price": "675", - "durability": null, - "name": "Mint cake", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "9475" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "675", - "durability": null, + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "9476", "name": "Mint cake", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9476" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "You can check on your delivery details here", - "durability": null, + "id": "9477", "name": "Aluft aloft box", + "examine": "You can check on your delivery details here", "archery_ticket_price": "0", - "id": "9477" + "durability": null }, { - "shop_price": "1", + "id": "9478", + "name": "Half made batta", "examine": "This cheese & tomato batta needs baking (and garnishing with equa leaves).", - "durability": null, - "name": "Half made batta", "archery_ticket_price": "0", - "id": "9478" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9479", + "name": "Unfinished batta", "examine": "This cheese & tomato batta needs garnishing with equa leaves.", - "durability": null, - "name": "Unfinished batta", "archery_ticket_price": "0", - "id": "9479" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9480", + "name": "Half made batta", "examine": "This fruit batta needs baking (and garnishing with gnome spices).", - "durability": null, - "name": "Half made batta", "archery_ticket_price": "0", - "id": "9480" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9481", + "name": "Unfinished batta", "examine": "This fruit batta needs garnishing with gnome spices.", - "durability": null, - "name": "Unfinished batta", "archery_ticket_price": "0", - "id": "9481" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9482", + "name": "Half made batta", "examine": "This toad batta needs baking.", - "durability": null, - "name": "Half made batta", "archery_ticket_price": "0", - "id": "9482" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9483", + "name": "Half made batta", "examine": "This veggie batta needs baking (and garnishing with equa leaves).", - "durability": null, - "name": "Half made batta", "archery_ticket_price": "0", - "id": "9483" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", + "id": "9484", + "name": "Unfinished batta", "examine": "This veggie batta needs garnishing with equa leaves.", - "durability": null, - "name": "Unfinished batta", "archery_ticket_price": "0", - "id": "9484" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", - "examine": "This worm batta needs baking (and garnishing with equa leaves).", - "durability": null, + "id": "9485", "name": "Half made batta", + "examine": "This worm batta needs baking (and garnishing with equa leaves).", "archery_ticket_price": "0", - "id": "9485" + "durability": null, + "shop_price": "1" }, { - "shop_price": "1", - "examine": "This worm batta needs garnishing with equa leaves.", - "durability": null, + "id": "9486", "name": "Unfinished batta", + "examine": "This worm batta needs garnishing with equa leaves.", "archery_ticket_price": "0", - "id": "9486" + "durability": null, + "shop_price": "1" }, { - "shop_price": "28", + "id": "9487", + "name": "Wizard blizzard", "examine": "This looks like a strange mix.", - "grand_exchange_price": "740", - "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9487" - }, - { "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9488" - }, - { "shop_price": "28", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9488", + "name": "Wizard blizzard", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9489", + "name": "Wizard blizzard", "examine": "This looks like a strange mix.", - "grand_exchange_price": "740", - "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9489" + "durability": null, + "shop_price": "28", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "28", + "id": "9508", + "name": "Wizard blizzard", "examine": "This looks like a strange mix.", - "grand_exchange_price": "740", - "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9508" - }, - { "durability": null, - "name": "Wizard blizzard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9509" - }, - { "shop_price": "28", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9509", + "name": "Wizard blizzard", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9510", + "name": "Short green guy", "examine": "A Short Green Guy... looks good.", - "grand_exchange_price": "954", - "durability": null, - "name": "Short green guy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9510" - }, - { "durability": null, - "name": "Short green guy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9511" - }, - { "shop_price": "28", + "tradeable": "true" + }, + { + "id": "9511", + "name": "Short green guy", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9512", + "name": "Pineapple punch", "examine": "A fresh healthy fruit mix.", - "grand_exchange_price": "287", - "durability": null, - "name": "Pineapple punch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9512" - }, - { "durability": null, - "name": "Pineapple punch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9513" - }, - { "shop_price": "28", + "tradeable": "true" + }, + { + "id": "9513", + "name": "Pineapple punch", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9514", + "name": "Fruit blast", "examine": "A cool refreshing fruit mix.", - "grand_exchange_price": "2276", - "durability": null, - "name": "Fruit blast", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9514" - }, - { "durability": null, - "name": "Fruit blast", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9515" - }, - { "shop_price": "28", + "tradeable": "true" + }, + { + "id": "9515", + "name": "Fruit blast", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9516", + "name": "Drunk dragon", "examine": "A warm creamy alcoholic beverage", - "grand_exchange_price": "577", - "durability": null, - "name": "Drunk dragon", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9516" - }, - { "durability": null, - "name": "Drunk dragon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9517" - }, - { "shop_price": "28", + "tradeable": "true" + }, + { + "id": "9517", + "name": "Drunk dragon", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9518", + "name": "Choc saturday", "examine": "A warm creamy alcoholic beverage", - "grand_exchange_price": "281", - "durability": null, - "name": "Choc saturday", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9518" + "durability": null, + "shop_price": "28", + "tradeable": "true", + "weight": "1" }, { - "durability": null, + "id": "9519", "name": "Choc saturday", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9519" + "durability": null, + "tradeable": "true" }, { - "shop_price": "30", + "id": "9520", + "name": "Blurberry special", "examine": "Looks good... smells strong.", - "grand_exchange_price": "27", + "archery_ticket_price": "0", "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "9521", "name": "Blurberry special", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9520" - }, - { "durability": null, - "name": "Blurberry special", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9521" + "tradeable": "true" }, { - "shop_price": "1", + "id": "9522", + "name": "Batta tin", "examine": "A deep tin used to make gnome battas in.", - "durability": null, - "name": "Batta tin", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "9522" - }, - { "durability": null, - "name": "Batta tin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9523" - }, - { "shop_price": "1", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "9523", + "name": "Batta tin", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9524", + "name": "Batta tin", "examine": "A deep tin used to make gnome battas in.", - "durability": null, - "name": "Batta tin", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", - "id": "9524" + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.1" }, { - "durability": null, + "id": "9525", "name": "Batta tin", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9525" + "durability": null, + "tradeable": "true" }, { + "id": "9527", + "name": "Fruit batta", "examine": "It actually smells quite good.", - "grand_exchange_price": "310", - "durability": null, - "name": "Fruit batta", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9527" + "durability": null, + "tradeable": "true", + "weight": "0.2" }, { - "durability": null, + "id": "9528", "name": "Fruit batta", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9528" + "durability": null, + "tradeable": "true" }, { + "id": "9529", + "name": "Toad batta", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "78", - "examine": "It actually smells quite good.", - "grand_exchange_price": "280", - "durability": null, + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "9530", "name": "Toad batta", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9529" - }, - { "durability": null, - "name": "Toad batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9530" + "tradeable": "true" }, { + "id": "9531", + "name": "Worm batta", "examine": "It actually smells quite good.", - "grand_exchange_price": "148", - "durability": null, - "name": "Worm batta", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9531" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9532", "name": "Worm batta", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9532" + "durability": null, + "tradeable": "true" }, { + "id": "9533", + "name": "Vegetable batta", "examine": "Well... It looks healthy.", - "grand_exchange_price": "240", - "durability": null, - "name": "Vegetable batta", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9533" + "durability": null, + "tradeable": "true", + "weight": "0.2" }, { - "durability": null, + "id": "9534", "name": "Vegetable batta", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9534" + "durability": null, + "tradeable": "true" }, { + "id": "9535", + "name": "Cheese+tom batta", "examine": "This smells really good.", - "grand_exchange_price": "266", - "durability": null, - "name": "Cheese+tom batta", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9535" + "durability": null, + "tradeable": "true", + "weight": "0.2" }, { - "durability": null, + "id": "9536", "name": "Cheese+tom batta", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9536" + "durability": null, + "tradeable": "true" }, { + "id": "9538", + "name": "Toad crunchies", "examine": "It actually smells quite good.", - "grand_exchange_price": "2606", - "durability": null, - "name": "Toad crunchies", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9538" + "durability": null, + "tradeable": "true", + "weight": "0.2" }, { - "durability": null, + "id": "9539", "name": "Toad crunchies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9539" + "durability": null, + "tradeable": "true" }, { - "shop_price": "70", + "id": "9540", + "name": "Spicy crunchies", "examine": "Yum...smells spicy.", - "grand_exchange_price": "99", - "durability": null, - "name": "Spicy crunchies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9540" - }, - { "durability": null, - "name": "Spicy crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9541" - }, - { - "shop_price": "80", - "examine": "It actually smells quite good.", - "grand_exchange_price": "255", - "durability": null, - "name": "Worm crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9542" - }, - { - "durability": null, - "name": "Worm crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9543" - }, - { "shop_price": "70", + "tradeable": "true" + }, + { + "id": "9541", + "name": "Spicy crunchies", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9542", + "name": "Worm crunchies", + "examine": "It actually smells quite good.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "80", + "tradeable": "true" + }, + { + "id": "9543", + "name": "Worm crunchies", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9544", + "name": "Chocchip crunchies", "examine": "Yum... smells good.", - "grand_exchange_price": "889", - "durability": null, - "name": "Chocchip crunchies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9544" + "durability": null, + "shop_price": "70", + "tradeable": "true" }, { - "durability": null, + "id": "9545", "name": "Chocchip crunchies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9545" + "durability": null, + "tradeable": "true" }, { + "id": "9547", + "name": "Worm hole", "examine": "It actually smells quite good.", - "grand_exchange_price": "475", - "durability": null, - "name": "Worm hole", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9547" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9548", "name": "Worm hole", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9548" + "durability": null, + "tradeable": "true" }, { + "id": "9549", + "name": "Veg ball", "examine": "This looks pretty healthy.", - "grand_exchange_price": "514", - "durability": null, - "name": "Veg ball", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9549" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9550", "name": "Veg ball", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9550" + "durability": null, + "tradeable": "true" }, { + "id": "9551", + "name": "Tangled toads' legs", "examine": "It actually smells quite good.", - "grand_exchange_price": "2650", - "durability": null, - "name": "Tangled toads' legs", - "tradeable": "true", - "weight": "0.3", "archery_ticket_price": "0", - "id": "9551" + "durability": null, + "tradeable": "true", + "weight": "0.3" }, { - "durability": null, + "id": "9552", "name": "Tangled toads' legs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9552" + "durability": null, + "tradeable": "true" }, { - "shop_price": "450", + "id": "9553", + "name": "Chocolate bomb", "examine": "Full of creamy, chocolately goodness.", - "grand_exchange_price": "4704", - "durability": null, - "name": "Chocolate bomb", - "tradeable": "true", - "weight": "0.3", "archery_ticket_price": "0", - "id": "9553" + "durability": null, + "shop_price": "450", + "tradeable": "true", + "weight": "0.3" }, { - "durability": null, + "id": "9554", "name": "Chocolate bomb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9554" + "durability": null, + "tradeable": "true" }, { + "id": "9558", + "name": "Half made bowl", "examine": "This unfinished choco bomb needs baking, and final ingredients", - "durability": null, - "name": "Half made bowl", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9558" + "durability": null, + "weight": "0.2" }, { + "id": "9559", + "name": "Half made bowl", "examine": "This unfinished tangled toad legs bowl needs baking, and final ingredients", - "durability": null, - "name": "Half made bowl", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9559" + "durability": null, + "weight": "0.2" }, { + "id": "9560", + "name": "Unfinished bowl", "examine": "This unfinished choc bomb needs cream and chocolate dust.", - "durability": null, - "name": "Unfinished bowl", "archery_ticket_price": "0", - "id": "9560" + "durability": null }, { + "id": "9561", + "name": "Half made bowl", "examine": "This unfinished veggie ball needs baking, and final ingredients", - "durability": null, - "name": "Half made bowl", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9561" + "durability": null, + "weight": "0.2" }, { + "id": "9562", + "name": "Unfinished bowl", "examine": "This unfinished veggie ball needs equa leaves.", - "durability": null, - "name": "Unfinished bowl", "archery_ticket_price": "0", - "id": "9562" + "durability": null }, { - "examine": "This unfinished worm hole needs baking, and final ingredients", - "durability": null, + "id": "9563", "name": "Half made bowl", - "weight": "0.2", + "examine": "This unfinished worm hole needs baking, and final ingredients", "archery_ticket_price": "0", - "id": "9563" + "durability": null, + "weight": "0.2" }, { - "examine": "This unfinished worm hole needs equa leaves.", - "durability": null, + "id": "9564", "name": "Unfinished bowl", + "examine": "This unfinished worm hole needs equa leaves.", "archery_ticket_price": "0", - "id": "9564" + "durability": null }, { - "durability": null, + "id": "9565", "name": "Cocktail shaker", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9565" + "durability": null, + "tradeable": "true" }, { - "examine": "This wizzard blizzard needs pouring, a lime slice, and pineapple chunks.", - "durability": null, + "id": "9566", "name": "Mixed blizzard", + "examine": "This wizzard blizzard needs pouring, a lime slice, and pineapple chunks.", "archery_ticket_price": "0", - "id": "9566" + "durability": null }, { - "examine": "This short green guy cocktail needs pouring, a lime slice and equa leaves.", - "durability": null, + "id": "9567", "name": "Mixed sgg", + "examine": "This short green guy cocktail needs pouring, a lime slice and equa leaves.", "archery_ticket_price": "0", - "id": "9567" + "durability": null }, { - "examine": "This fruit blast cocktail needs pouring and a lemon slice.", - "durability": null, + "id": "9568", "name": "Mixed blast", + "examine": "This fruit blast cocktail needs pouring and a lemon slice.", "archery_ticket_price": "0", - "id": "9568" + "durability": null }, { - "examine": "This pineapple punch needs pouring, lime and pineapple chunks, and a orange slice.", - "durability": null, + "id": "9569", "name": "Mixed punch", + "examine": "This pineapple punch needs pouring, lime and pineapple chunks, and a orange slice.", "archery_ticket_price": "0", - "id": "9569" + "durability": null }, { - "examine": "This blurberry special needs pouring, orange and lemon chunks, a lime slice and equa leaves.", - "durability": null, + "id": "9570", "name": "Mixed blurberry special", + "examine": "This blurberry special needs pouring, orange and lemon chunks, a lime slice and equa leaves.", "archery_ticket_price": "0", - "id": "9570" + "durability": null }, { + "id": "9577", + "name": "Half made crunchy", "examine": "This choco chip crunchy needs baking and garnishing with chocolate dust.", - "durability": null, - "name": "Half made crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9577" + "durability": null, + "tradeable": "true" }, { + "id": "9578", + "name": "Unfinished crunchy", "examine": "This choco chip crunchy needs garnishing with chocolate dust.", - "durability": null, - "name": "Unfinished crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9578" + "durability": null, + "tradeable": "true" }, { + "id": "9579", + "name": "Half made crunchy", "examine": "This spicy crunchy needs baking and garnishing with gnome spices.", - "durability": null, - "name": "Half made crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9579" + "durability": null, + "tradeable": "true" }, { + "id": "9580", + "name": "Unfinished crunchy", "examine": "This spicy crunchy needs garnishing with gnome spices.", - "durability": null, - "name": "Unfinished crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9580" + "durability": null, + "tradeable": "true" }, { + "id": "9581", + "name": "Half made crunchy", "examine": "This toad crunchy needs baking and garnishing with equa leaves.", - "durability": null, - "name": "Half made crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9581" + "durability": null, + "tradeable": "true" }, { + "id": "9582", + "name": "Unfinished crunchy", "examine": "This toad crunchy needs garnishing with equa leaves.", - "durability": null, - "name": "Unfinished crunchy", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9582" + "durability": null, + "tradeable": "true" }, { - "examine": "This worm crunchy needs baking and garnishing with gnome spices.", - "durability": null, + "id": "9583", "name": "Half made crunchy", - "tradeable": "true", + "examine": "This worm crunchy needs baking and garnishing with gnome spices.", "archery_ticket_price": "0", - "id": "9583" + "durability": null, + "tradeable": "true" }, { - "examine": "This worm crunchy needs garnishing with gnome spices.", - "durability": null, + "id": "9584", "name": "Unfinished crunchy", - "tradeable": "true", + "examine": "This worm crunchy needs garnishing with gnome spices.", "archery_ticket_price": "0", - "id": "9584" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9585", "name": "Batta tin", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9585" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9586", "name": "Crunchy tray", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9586" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9587", "name": "Gnomebowl mould", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9587" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9588", "name": "Raw crunchies", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9588" + "durability": null, + "tradeable": "true" }, { + "id": "9589", + "name": "Dossier", + "examine": "A dossier containing info on the Black Knight plot.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "Speak to Sir Amik Varze at the beginning of the Black Knight's Fortress Quest.", - "examine": "A dossier containing info on the Black Knight plot.", "durability": null, - "name": "Dossier", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9589" + "tradeable": "false" }, { - "examine": "A dossier containing info on the Black Knight plot.", - "durability": null, + "id": "9590", "name": "Dossier", - "tradeable": "false", - "destroy": "true", + "examine": "A dossier containing info on the Black Knight plot.", "archery_ticket_price": "0", - "id": "9590" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Glue made from tree sap and ground mud runes.", - "durability": null, + "id": "9592", "name": "Magic glue", - "weight": "3", + "examine": "Glue made from tree sap and ground mud runes.", "archery_ticket_price": "0", - "id": "9592" + "durability": null, + "weight": "3" }, { - "examine": "This doesn't look like it will do anything interesting.", - "durability": null, + "id": "9593", "name": "Weird gloop", - "tradeable": "false", - "destroy": "true", - "weight": "2.5", + "examine": "This doesn't look like it will do anything interesting.", "archery_ticket_price": "0", - "id": "9593" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "2.5" }, { - "examine": "Mud runes ground into a powder.", - "durability": null, + "id": "9594", "name": "Ground mud runes", - "tradeable": "false", - "destroy": "true", - "weight": "0.1", + "examine": "Mud runes ground into a powder.", "archery_ticket_price": "0", - "id": "9594" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "0.1" }, { - "examine": "A red circular crystalline disc.", - "durability": null, + "id": "9597", "name": "Red circle", + "examine": "A red circular crystalline disc.", "archery_ticket_price": "0", - "id": "9597" + "durability": null }, { - "examine": "A red triangular crystalline disc.", - "durability": null, + "id": "9598", "name": "Red triangle", + "examine": "A red triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9598" + "durability": null }, { - "examine": "A red square-shaped crystalline disc.", - "durability": null, + "id": "9599", "name": "Red square", + "examine": "A red square-shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9599" + "durability": null }, { - "examine": "A red pentagon shaped crystalline disc.", - "durability": null, + "id": "9600", "name": "Red pentagon", + "examine": "A red pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9600" + "durability": null }, { - "examine": "An orange circular crystalline disc.", - "durability": null, + "id": "9601", "name": "Orange circle", + "examine": "An orange circular crystalline disc.", "archery_ticket_price": "0", - "id": "9601" + "durability": null }, { - "examine": "An orange triangular crystalline disc.", - "durability": null, + "id": "9602", "name": "Orange triangle", + "examine": "An orange triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9602" + "durability": null }, { - "examine": "An orange square-shaped crystalline disc.", - "durability": null, + "id": "9603", "name": "Orange square", + "examine": "An orange square-shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9603" + "durability": null }, { - "examine": "An orange pentagon shaped crystalline disc.", - "durability": null, + "id": "9604", "name": "Orange pentagon", + "examine": "An orange pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9604" + "durability": null }, { - "examine": "A yellow circular crystalline disc.", - "durability": null, + "id": "9605", "name": "Yellow circle", + "examine": "A yellow circular crystalline disc.", "archery_ticket_price": "0", - "id": "9605" + "durability": null }, { - "examine": "A yellow triangular crystalline disc.", - "durability": null, + "id": "9606", "name": "Yellow triangle", + "examine": "A yellow triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9606" + "durability": null }, { - "examine": "A yellow square-shaped crystalline disc.", - "durability": null, + "id": "9607", "name": "Yellow square", + "examine": "A yellow square-shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9607" + "durability": null }, { - "examine": "A yellow pentagon shaped crystalline disc.", - "durability": null, + "id": "9608", "name": "Yellow pentagon", + "examine": "A yellow pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9608" + "durability": null }, { - "examine": "A green circular crystalline disc.", - "durability": null, + "id": "9609", "name": "Green circle", + "examine": "A green circular crystalline disc.", "archery_ticket_price": "0", - "id": "9609" + "durability": null }, { - "examine": "A green triangular crystalline disc.", - "durability": null, + "id": "9610", "name": "Green triangle", + "examine": "A green triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9610" + "durability": null }, { - "examine": "A green square shaped crystalline disc.", - "durability": null, + "id": "9611", "name": "Green square", + "examine": "A green square shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9611" + "durability": null }, { - "examine": "A green pentagon shaped crystalline disc.", - "durability": null, + "id": "9612", "name": "Green pentagon", + "examine": "A green pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9612" + "durability": null }, { - "examine": "A blue circular crystalline disc.", - "durability": null, + "id": "9613", "name": "Blue circle", + "examine": "A blue circular crystalline disc.", "archery_ticket_price": "0", - "id": "9613" + "durability": null }, { - "examine": "A blue triangular crystalline disc.", - "durability": null, + "id": "9614", "name": "Blue triangle", + "examine": "A blue triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9614" + "durability": null }, { - "examine": "A blue square shaped crystalline disc.", - "durability": null, + "id": "9615", "name": "Blue square", + "examine": "A blue square shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9615" + "durability": null }, { - "examine": "A blue pentagon shaped crystalline disc.", - "durability": null, + "id": "9616", "name": "Blue pentagon", + "examine": "A blue pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9616" + "durability": null }, { - "examine": "An indigo circular crystalline disc.", - "durability": null, + "id": "9617", "name": "Indigo circle", + "examine": "An indigo circular crystalline disc.", "archery_ticket_price": "0", - "id": "9617" + "durability": null }, { - "examine": "An indigo triangular crystalline disc.", - "durability": null, + "id": "9618", "name": "Indigo triangle", + "examine": "An indigo triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9618" + "durability": null }, { - "examine": "An indigo square shaped crystalline disc.", - "durability": null, + "id": "9619", "name": "Indigo square", + "examine": "An indigo square shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9619" + "durability": null }, { - "examine": "An indigo pentagon shaped crystalline disc.", - "durability": null, + "id": "9620", "name": "Indigo pentagon", + "examine": "An indigo pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9620" + "durability": null }, { - "examine": "A violet circular crystalline disc.", - "durability": null, + "id": "9621", "name": "Violet circle", + "examine": "A violet circular crystalline disc.", "archery_ticket_price": "0", - "id": "9621" + "durability": null }, { - "examine": "A violet triangular crystalline disc.", - "durability": null, + "id": "9622", "name": "Violet triangle", + "examine": "A violet triangular crystalline disc.", "archery_ticket_price": "0", - "id": "9622" + "durability": null }, { - "examine": "A violet square-shaped crystalline disc.", - "durability": null, + "id": "9623", "name": "Violet square", + "examine": "A violet square-shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9623" + "durability": null }, { - "examine": "A violet pentagon shaped crystalline disc.", - "durability": null, + "id": "9624", "name": "Violet pentagon", + "examine": "A violet pentagon shaped crystalline disc.", "archery_ticket_price": "0", - "id": "9624" + "durability": null }, { - "destroy_message": "Maybe Brimstail will have another...", - "examine": "A magical saw.", - "durability": null, - "name": "Crystal saw", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "9625", - "equipment_slot": "3" - }, - { - "destroy_message": "Maybe Brimstail will have another...", - "examine": "This small crystal seed looks grey and dead.", - "durability": null, - "name": "Small crystal seed", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "name": "Crystal saw", + "examine": "A magical saw.", "archery_ticket_price": "0", - "id": "9626" + "destroy": "true", + "destroy_message": "Maybe Brimstail will have another...", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" }, { + "id": "9626", + "name": "Small crystal seed", + "examine": "This small crystal seed looks grey and dead.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Maybe Brimstail will have another...", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "9629", + "name": "Tyras helm", + "examine": "As used by King Tyras personal guard.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{1,5}", "shop_price": "1265", - "ge_buy_limit": "100", - "examine": "As used by King Tyras personal guard.", - "durability": null, - "weight": "0.9", - "equipment_slot": "0", - "grand_exchange_price": "1406", - "name": "Tyras helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9629", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0" + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1406", - "durability": null, + "id": "9630", "name": "Tyras helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9630" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "This needs refining.", - "durability": null, + "id": "9632", "name": "Daeyalt ore", - "tradeable": "false", - "destroy": "true", - "weight": "2", + "examine": "This needs refining.", "archery_ticket_price": "0", - "id": "9632" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "shop_price": "635", - "ge_buy_limit": "100", - "examine": "Dressing like the Vyrewatch could be a useful disguise in the ghetto of Meiyerditch.", - "durability": null, - "weight": "0.9", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "2160", - "name": "Vyrewatch top", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9634", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2160", - "durability": null, "name": "Vyrewatch top", - "tradeable": "true", + "examine": "Dressing like the Vyrewatch could be a useful disguise in the ghetto of Meiyerditch.", "archery_ticket_price": "0", - "id": "9635" - }, - { - "shop_price": "635", - "ge_buy_limit": "100", - "examine": "Dress like a powerful Vyrewatch!", - "grand_exchange_price": "1204", - "durability": null, - "name": "Vyrewatch legs", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "9636", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1204", "durability": null, - "name": "Vyrewatch legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9637" - }, - { - "shop_price": "635", - "ge_buy_limit": "100", - "examine": "Dress like a powerful Vyrewatch!", - "grand_exchange_price": "2874", - "durability": null, - "name": "Vyrewatch shoes", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "9638", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2874", - "durability": null, - "name": "Vyrewatch shoes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9639" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", - "durability": null, - "weight": "0.9", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "588", - "name": "Citizen top", + "shop_price": "635", "tradeable": "true", + "weight": "0.9" + }, + { + "id": "9635", + "name": "Vyrewatch top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9636", + "name": "Vyrewatch legs", + "examine": "Dress like a powerful Vyrewatch!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "635", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "9637", + "name": "Vyrewatch legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9638", + "name": "Vyrewatch shoes", + "examine": "Dress like a powerful Vyrewatch!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "635", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "9639", + "name": "Vyrewatch shoes", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "9640", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "588", - "durability": null, "name": "Citizen top", - "tradeable": "true", + "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", "archery_ticket_price": "0", - "id": "9641" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "6", + "tradeable": "true", + "weight": "0.9" }, { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", - "grand_exchange_price": "491", - "durability": null, - "name": "Citizen trousers", - "tradeable": "true", - "weight": "0.9", + "id": "9641", + "name": "Citizen top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "9642", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "491", - "durability": null, "name": "Citizen trousers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9643" - }, - { - "shop_price": "6", - "ge_buy_limit": "100", "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", - "grand_exchange_price": "458", - "durability": null, - "name": "Citizen shoes", - "tradeable": "true", - "weight": "0.9", "archery_ticket_price": "0", - "id": "9644", "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "458", + "shop_price": "6", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "9643", + "name": "Citizen trousers", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9644", "name": "Citizen shoes", - "tradeable": "true", + "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", "archery_ticket_price": "0", - "id": "9645" + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "6", + "tradeable": "true", + "weight": "0.9" }, { - "destroy_message": "You can get another sketch by using charcoal with papyrus, when on the wall near Castle Drakyn.", - "examine": "Northern approach of the castle.", + "id": "9645", + "name": "Citizen shoes", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9646", "name": "Castle sketch 1", - "tradeable": "false", - "destroy": "true", + "examine": "Northern approach of the castle.", "archery_ticket_price": "0", - "id": "9646" + "destroy": "true", + "destroy_message": "You can get another sketch by using charcoal with papyrus, when on the wall near Castle Drakyn.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another sketch by using charcoal with papyrus, when on the wall near Castle Drakyn.", - "examine": "Western approach of the castle.", - "durability": null, + "id": "9647", "name": "Castle sketch 2", - "tradeable": "false", - "destroy": "true", + "examine": "Western approach of the castle.", "archery_ticket_price": "0", - "id": "9647" - }, - { + "destroy": "true", "destroy_message": "You can get another sketch by using charcoal with papyrus, when on the wall near Castle Drakyn.", - "examine": "Southern approach of the castle.", "durability": null, + "tradeable": "false" + }, + { + "id": "9648", "name": "Castle sketch 3", - "tradeable": "false", - "destroy": "true", + "examine": "Southern approach of the castle.", "archery_ticket_price": "0", - "id": "9648" + "destroy": "true", + "destroy_message": "You can get another sketch by using charcoal with papyrus, when on the wall near Castle Drakyn.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A key to some large, strange door.", - "durability": null, + "id": "9651", "name": "Large ornate key", - "tradeable": "false", - "destroy": "true", + "examine": "A key to some large, strange door.", "archery_ticket_price": "0", - "id": "9651" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Unknown You can reclaim this item from the place you found it.", - "examine": "A book called Haemalchemy Volume 1.", - "durability": null, + "id": "9652", "name": "Haemalchemy", - "tradeable": "false", - "destroy": "true", + "examine": "A book called Haemalchemy Volume 1.", "archery_ticket_price": "0", - "id": "9652" + "destroy": "true", + "destroy_message": "Unknown You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Safalaan gave you this message; he would probably write out another one for you.", - "examine": "A sealed message from Safalaan to Valiaf.", - "durability": null, + "id": "9653", "name": "Sealed message", - "tradeable": "false", - "destroy": "true", + "examine": "A sealed message from Safalaan to Valiaf.", "archery_ticket_price": "0", - "id": "9653" + "destroy": "true", + "destroy_message": "Safalaan gave you this message; he would probably write out another one for you.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A key to the Witch's house's front door. (Witch's House)", - "durability": null, + "id": "9654", "name": "Door key", + "examine": "A key to the Witch's house's front door. (Witch's House)", "archery_ticket_price": "0", - "id": "9654" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You found this ladder top on a wall section in the north of Meiyerditch; you can get another one there.", - "examine": "The top part of a ladder.", - "durability": null, + "id": "9655", "name": "Ladder top", - "tradeable": "false", - "destroy": "true", + "examine": "The top part of a ladder.", "archery_ticket_price": "0", - "id": "9655" + "destroy": "true", + "destroy_message": "You found this ladder top on a wall section in the north of Meiyerditch; you can get another one there.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", - "examine": "A tome of experience that awards 2000 XP per chapter.", - "durability": null, + "id": "9656", "name": "Tome of xp (3)", - "tradeable": "false", - "destroy": "true", + "examine": "A tome of experience that awards 2000 XP per chapter.", "archery_ticket_price": "0", - "id": "9656" + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", - "examine": "A tome of experience that awards 2000 XP per chapter.", - "durability": null, + "id": "9657", "name": "Tome of xp (2)", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9657" - }, - { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", "examine": "A tome of experience that awards 2000 XP per chapter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", "durability": null, + "tradeable": "false" + }, + { + "id": "9658", "name": "Tome of xp (1)", - "tradeable": "false", - "destroy": "true", + "examine": "A tome of experience that awards 2000 XP per chapter.", "archery_ticket_price": "0", - "id": "9658" + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "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?", - "grand_exchange_price": "74", - "durability": null, + "id": "9659", "name": "Bucket of water", - "tradeable": "true", - "weight": "3", + "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?", "archery_ticket_price": "0", - "id": "9659" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "2", - "examine": "It's an empty bucket.", - "grand_exchange_price": "50", "durability": null, - "name": "Bucket", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9660" + "weight": "3" }, { - "destroy_message": "Safalaan gave you this key as a reward for helping out in Meiyerditch, you can get another key from him.", - "examine": "A key allowing a shortcut into the Sanguinesti region. (Darkness of Hallowvale)", + "id": "9660", + "name": "Bucket", + "examine": "It's an empty bucket.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9662", "name": "Shortcut key", - "tradeable": "false", - "destroy": "true", + "examine": "A key allowing a shortcut into the Sanguinesti region. (Darkness of Hallowvale)", "archery_ticket_price": "0", - "id": "9662" + "destroy": "true", + "destroy_message": "Safalaan gave you this key as a reward for helping out in Meiyerditch, you can get another key from him.", + "durability": null, + "tradeable": "false" }, { - "durability": null, + "id": "9665", "name": "Torch", "archery_ticket_price": "0", - "id": "9665", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "25000", - "ge_buy_limit": "100", + "id": "9666", + "name": "Pros'yte harness m", "examine": "Unknown edit", - "grand_exchange_price": "27500", - "durability": null, - "name": "Pros'yte harness m", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "9666" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "27500", - "durability": null, - "name": "Pros'yte harness m", + "shop_price": "25000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9667" + "weight": "2.2" }, { + "id": "9667", + "name": "Pros'yte harness m", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9668", + "name": "Initiate harness m", + "examine": "Unknown edit", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "20000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "9669", + "name": "Initiate harness m", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9670", + "name": "Pros'yte harness f", "examine": "Unknown edit", - "grand_exchange_price": "22700", - "durability": null, - "name": "Initiate harness m", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "9668" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "22700", - "durability": null, - "name": "Initiate harness m", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9669" - }, - { "shop_price": "25000", - "ge_buy_limit": "100", - "examine": "Unknown edit", - "grand_exchange_price": "27900", - "durability": null, - "name": "Pros'yte harness f", "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "9670" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "27900", - "durability": null, + "id": "9671", "name": "Pros'yte harness f", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9671" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "9672", + "name": "Proselyte sallet", + "examine": "A Proselyte Temple Knight's helm.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,4,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,30}-{5,20}", "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A Proselyte Temple Knight's helm.", - "durability": null, - "weight": "2", - "absorb": "1,0,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "4619", - "name": "Proselyte sallet", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9672", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,4,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4619", - "durability": null, + "id": "9673", "name": "Proselyte sallet", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9673" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "9674", + "name": "Proselyte hauberk", + "examine": "A Proselyte Temple Knight's armour.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,8,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", "requirements": "{1,30}-{5,20}", "shop_price": "12000", - "ge_buy_limit": "100", - "examine": "A Proselyte Temple Knight's armour.", - "durability": null, - "weight": "8.6", - "absorb": "2,0,4", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "7076", - "name": "Proselyte hauberk", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9674", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,8,0,0" + "weight": "8.6" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "7076", - "durability": null, + "id": "9675", "name": "Proselyte hauberk", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9675" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,30}-{5,20}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "A Proselyte Temple Knight's leg armour", - "durability": null, - "weight": "7.7", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "5923", - "name": "Proselyte cuisse", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9676", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5923", - "durability": null, "name": "Proselyte cuisse", - "tradeable": "true", + "examine": "A Proselyte Temple Knight's leg armour", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "9677" - }, - { + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,5,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{1,30}-{5,20}", "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "A Proselyte Temple Knight's leg armour.", - "durability": null, - "weight": "9", - "absorb": "1,0,3", - "equipment_slot": "7", - "grand_exchange_price": "5730", - "name": "Proselyte tasset", "tradeable": "true", + "weight": "7.7" + }, + { + "id": "9677", + "name": "Proselyte cuisse", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "9678", - "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,5,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "5730", - "durability": null, "name": "Proselyte tasset", - "tradeable": "true", + "examine": "A Proselyte Temple Knight's leg armour.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "9679" + "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,5,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,30}-{5,20}", + "shop_price": "10000", + "tradeable": "true", + "weight": "9" }, { - "examine": "A rendered down baby sea slug.", + "id": "9679", + "name": "Proselyte tasset", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9680", "name": "Sea slug glue", + "examine": "A rendered down baby sea slug.", "archery_ticket_price": "0", - "id": "9680" + "durability": null }, { - "destroy_message": "As a security precaution, your CommOrb will automatically self", - "examine": "A Temple Knight Communication Orb. Top Secret!", - "durability": null, + "id": "9681", "name": "Commorb v2", + "examine": "A Temple Knight Communication Orb. Top Secret!", "archery_ticket_price": "0", - "id": "9681" + "destroy_message": "As a security precaution, your CommOrb will automatically self", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A copy of the mysterious glyphs.", - "durability": null, + "id": "9682", "name": "Door transcription", + "examine": "A copy of the mysterious glyphs.", "archery_ticket_price": "0", - "id": "9682" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "Dead sea slug, very sticky.", - "durability": null, + "id": "9683", "name": "Dead sea slug", - "tradeable": "false", - "destroy": "true", + "examine": "Dead sea slug, very sticky.", "archery_ticket_price": "0", - "id": "9683" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get this page from Mayor Hobb's Desk.", - "examine": "A page from Maledict's holy book.", - "durability": null, + "id": "9684", "name": "Page 1", - "tradeable": "false", - "destroy": "true", - "weight": "0.1", + "examine": "A page from Maledict's holy book.", "archery_ticket_price": "0", - "id": "9684" + "destroy": "true", + "destroy_message": "You can get this page from Mayor Hobb's Desk.", + "durability": null, + "tradeable": "false", + "weight": "0.1" }, { - "destroy_message": "You can get this page from Ezekial Lovecraft.", - "examine": "A page from Maledict's holy book.", - "durability": null, + "id": "9685", "name": "Page 2", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9685" - }, - { - "destroy_message": "You will have to get the torn page from Col. O' Niall.", "examine": "A page from Maledict's holy book.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get this page from Ezekial Lovecraft.", "durability": null, + "tradeable": "false" + }, + { + "id": "9686", "name": "Page 3", - "tradeable": "false", - "destroy": "true", + "examine": "A page from Maledict's holy book.", "archery_ticket_price": "0", - "id": "9686" + "destroy": "true", + "destroy_message": "You will have to get the torn page from Col. O' Niall.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A piece of torn page.", - "durability": null, + "id": "9687", "name": "Fragment 1", + "examine": "A piece of torn page.", "archery_ticket_price": "0", - "id": "9687" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A piece of torn page.", - "durability": null, + "id": "9688", "name": "Fragment 2", - "archery_ticket_price": "0", - "id": "9688" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", "examine": "A piece of torn page.", - "durability": null, + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "9689", "name": "Fragment 3", + "examine": "A piece of torn page.", "archery_ticket_price": "0", - "id": "9689" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "Are you sure you wish to destroy this?", - "examine": "A blank water rune.", - "durability": null, + "id": "9690", "name": "Blank water rune", - "tradeable": "false", - "destroy": "true", + "examine": "A blank water rune.", "archery_ticket_price": "0", - "id": "9690" + "destroy": "true", + "destroy_message": "Are you sure you wish to destroy this?", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "6", - "durability": null, + "id": "9691", "name": "Water rune", - "tradeable": "true", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "9691" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "destroy_message": "Are you sure you wish to destroy this?", - "examine": "A blank air rune.", - "durability": null, + "id": "9692", "name": "Blank air rune", - "tradeable": "false", - "destroy": "true", + "examine": "A blank air rune.", "archery_ticket_price": "0", - "id": "9692" + "destroy": "true", + "destroy_message": "Are you sure you wish to destroy this?", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "9", - "durability": null, + "id": "9693", "name": "Air rune", - "tradeable": "true", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "9693" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "destroy_message": "Are you sure you wish to destroy this?", - "examine": "A blank earth rune.", - "durability": null, + "id": "9694", "name": "Blank earth rune", - "tradeable": "false", - "destroy": "true", + "examine": "A blank earth rune.", "archery_ticket_price": "0", - "id": "9694" + "destroy": "true", + "destroy_message": "Are you sure you wish to destroy this?", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "17", - "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "7", - "durability": null, + "id": "9695", "name": "Earth rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9695" - }, - { - "destroy_message": "Are you sure you wish to destroy this?", - "examine": "A blank mind rune.", - "durability": null, - "name": "Blank mind rune", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9696" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "17", - "examine": "Used for basic missile spells.", - "grand_exchange_price": "6", - "durability": null, - "name": "Mind rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9697" - }, - { - "destroy_message": "Are you sure you wish to destroy this?", - "examine": "A blank fire rune.", - "durability": null, - "name": "Blank fire rune", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9698" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "17", "examine": "One of the 4 basic elemental Runes.", - "grand_exchange_price": "16", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "9696", + "name": "Blank mind rune", + "examine": "A blank mind rune.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you wish to destroy this?", + "durability": null, + "tradeable": "false" + }, + { + "id": "9697", + "name": "Mind rune", + "examine": "Used for basic missile spells.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "17", + "tradeable": "true" + }, + { + "id": "9698", + "name": "Blank fire rune", + "examine": "A blank fire rune.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you wish to destroy this?", + "durability": null, + "tradeable": "false" + }, + { + "id": "9699", "name": "Fire rune", - "tradeable": "true", + "examine": "One of the 4 basic elemental Runes.", "archery_ticket_price": "0", - "id": "9699" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "17", + "tradeable": "true" }, { - "durability": null, + "id": "9701", "name": "Torch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9701" + "durability": null, + "tradeable": "true" }, { - "examine": "A big, bad troll.", - "durability": null, + "id": "9702", "name": "Stick", + "examine": "A big, bad troll.", "archery_ticket_price": "0", "attack_speed": "6", - "id": "9702", - "weapon_interface": "5", - "equipment_slot": "3" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "5" }, { - "shop_price": "35", - "examine": "Basic training sword.", - "durability": null, - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "name": "Training sword", - "archery_ticket_price": "0", "id": "9703", - "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0" - }, - { - "examine": "Made of flimsy painted wood.", - "durability": null, - "name": "Training shield", - "weight": "5.4", + "name": "Training sword", + "examine": "Basic training sword.", "archery_ticket_price": "0", - "id": "9704", - "bonuses": "0,0,0,0,0,4,5,3,1,4,0,0,0,0,0", - "equipment_slot": "5" + "attack_anims": "381,390,390,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "4", + "bonuses": "4,3,-2,0,0,0,2,1,0,0,0,5,0,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "2584", + "shop_price": "35", + "weapon_interface": "5", + "weight": "1.8" }, { - "examine": "Light and flexible, good for a beginner.", + "id": "9704", + "name": "Training shield", + "examine": "Made of flimsy painted wood.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,4,5,3,1,4,0,0,0,0,0", "durability": null, - "destroy": "true", - "weight": "1.3", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", - "defence_anim": "424", - "equipment_slot": "3", + "equipment_slot": "5", + "weight": "5.4" + }, + { + "id": "9705", + "name": "Training bow", + "examine": "Light and flexible, good for a beginner.", + "archery_ticket_price": "0", "attack_anims": "426,426,426,426", "attack_audios": "2700,0,0,0", - "name": "Training bow", + "attack_speed": "4", + "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "destroy": "true", + "durability": null, + "equipment_slot": "3", "tradeable": "false", - "archery_ticket_price": "0", - "id": "9705", - "bonuses": "0,0,0,0,8,0,0,0,0,0,0,0,0,0,0" + "two_handed": "true", + "weapon_interface": "16", + "weight": "1.3" }, { - "examine": "Standard training arrows.", - "durability": null, - "name": "Training arrows", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "9706", + "name": "Training arrows", + "examine": "Standard training arrows.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7", - "equipment_slot": "13" + "destroy": "true", + "durability": null, + "equipment_slot": "13", + "tradeable": "false" }, { - "examine": "Book of the elemental shield.", - "durability": null, + "id": "9715", "name": "Slashed book", - "weight": "0.1", + "examine": "Book of the elemental shield.", "archery_ticket_price": "0", - "id": "9715" + "durability": null, + "weight": "0.1" }, { - "examine": "Book of the elemental helm.", - "durability": null, + "id": "9717", "name": "Beaten book", - "weight": "0.1", + "examine": "Book of the elemental helm.", "archery_ticket_price": "0", - "id": "9717" + "durability": null, + "weight": "0.1" }, { - "examine": "On the subject of lava dippers.", - "durability": null, + "id": "9718", "name": "Crane schematic", + "examine": "On the subject of lava dippers.", "archery_ticket_price": "0", - "id": "9718" + "durability": null }, { - "examine": "A scroll with a lever schematic drawn on it.", - "durability": null, + "id": "9719", "name": "Lever schematic", + "examine": "A scroll with a lever schematic drawn on it.", "archery_ticket_price": "0", - "id": "9719" + "durability": null }, { - "examine": "A crane claw.", - "durability": null, + "id": "9720", "name": "Crane claw", - "weight": "1", + "examine": "A crane claw.", "archery_ticket_price": "0", - "id": "9720" + "durability": null, + "weight": "1" }, { - "examine": "A pipe that belongs in a cannon stand.", - "durability": null, + "id": "9721", + "name": "Scroll", + "examine": "A scroll with some writing on it." + }, + { + "id": "9722", + "name": "Key", + "examine": "Quite a small key." + }, + { + "id": "9723", "name": "Pipe", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", + "examine": "A pipe that belongs in a cannon stand.", "archery_ticket_price": "0", - "id": "9723" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "0.2" }, { - "examine": "A large cog.", - "durability": null, + "id": "9724", "name": "Large cog", - "weight": "1", + "examine": "A large cog.", "archery_ticket_price": "0", - "id": "9724" + "durability": null, + "weight": "1" }, { - "examine": "A medium cog.", - "durability": null, + "id": "9725", "name": "Medium cog", - "weight": "1", + "examine": "A medium cog.", "archery_ticket_price": "0", - "id": "9725" + "durability": null, + "weight": "1" }, { - "examine": "A small cog.", - "durability": null, + "id": "9726", "name": "Small cog", - "weight": "1", + "examine": "A small cog.", "archery_ticket_price": "0", - "id": "9726" + "durability": null, + "weight": "1" }, { - "examine": "A primed elemental ingot.", - "durability": null, + "id": "9727", "name": "Primed bar", - "weight": "1.8", + "examine": "A primed elemental ingot.", "archery_ticket_price": "0", - "id": "9727" + "durability": null, + "weight": "1.8" }, { - "examine": "An elemental mind ingot.", - "durability": null, + "id": "9728", "name": "Elemental mind bar", - "weight": "1", + "examine": "An elemental mind ingot.", "archery_ticket_price": "0", - "id": "9728" + "durability": null, + "weight": "1" }, { - "ge_buy_limit": "100", - "examine": "A helmet made in the Elemental Workshop.", - "grand_exchange_price": "196", - "durability": null, - "name": "Elemental helmet", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", "id": "9729", - "bonuses": "0,0,0,0,0,0,0,0,4,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "196", - "durability": null, "name": "Elemental helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9730" - }, - { - "ge_buy_limit": "100", - "examine": "A shield made in the Elemental Workshop.", - "grand_exchange_price": "8351", - "durability": null, - "name": "Mind shield", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "9731", - "bonuses": "0,0,0,0,0,0,0,0,9,0,25,0,0,0,0", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8351", - "durability": null, - "name": "Mind shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9732" - }, - { - "shop_price": "1200", - "ge_buy_limit": "100", "examine": "A helmet made in the Elemental Workshop.", - "grand_exchange_price": "1589", - "durability": null, - "name": "Mind helmet", - "tradeable": "true", - "weight": "0.1", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "9730", + "name": "Elemental helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9731", + "name": "Mind shield", + "examine": "A shield made in the Elemental Workshop.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,9,0,25,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "9732", + "name": "Mind shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "9733", - "bonuses": "0,0,0,0,0,0,0,0,6,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1589", - "durability": null, "name": "Mind helmet", - "tradeable": "true", + "examine": "A helmet made in the Elemental Workshop.", "archery_ticket_price": "0", - "id": "9734" + "bonuses": "0,0,0,0,0,0,0,0,6,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "shop_price": "1200", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "10000", + "id": "9734", + "name": "Mind helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9735", + "name": "Desert goat horn", "examine": "Not much good for blowing.", - "grand_exchange_price": "902", - "durability": null, - "name": "Desert goat horn", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "9735" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "10000", + "id": "9736", + "name": "Goat horn dust", "examine": "Finely ground desert goat horn.", - "grand_exchange_price": "1082", - "durability": null, - "name": "Goat horn dust", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "9736" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "1082", - "durability": null, + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "9737", "name": "Goat horn dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9737" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "durability": null, + "id": "9738", "name": "Desert goat horn", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9738" + "durability": null, + "tradeable": "true" }, { - "shop_price": "207", - "ge_buy_limit": "100", + "id": "9739", + "name": "Combat potion(4)", "examine": "4 doses of combat potion.", - "grand_exchange_price": "45", - "durability": null, - "name": "Combat potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9739" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "45", - "durability": null, - "name": "Combat potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9740" - }, - { "shop_price": "207", + "tradeable": "true" + }, + { + "id": "9740", + "name": "Combat potion(4)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9741", + "name": "Combat potion(3)", "examine": "3 doses of combat potion.", - "grand_exchange_price": "25", - "durability": null, - "name": "Combat potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9741" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "25", - "durability": null, - "name": "Combat potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9742" - }, - { "shop_price": "207", + "tradeable": "true" + }, + { + "id": "9742", + "name": "Combat potion(3)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9743", + "name": "Combat potion(2)", "examine": "2 doses of combat potion.", - "grand_exchange_price": "19", - "durability": null, - "name": "Combat potion(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9743" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, - "name": "Combat potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9744" - }, - { "shop_price": "207", + "tradeable": "true" + }, + { + "id": "9744", + "name": "Combat potion(2)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9745", + "name": "Combat potion(1)", "examine": "1 dose of combat potion.", - "grand_exchange_price": "72", - "durability": null, - "name": "Combat potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9745" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "72", - "durability": null, - "name": "Combat potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9746" + "shop_price": "207", + "tradeable": "true" }, { - "requirements": "{0,99}", - "shop_price": "99000", - "examine": "The cape worn by masters of attack.", - "durability": null, - "name": "Attack cape", - "tradeable": "false", - "weight": "0.4", + "id": "9746", + "name": "Combat potion(1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "9747", + "name": "Attack cape", + "examine": "The cape worn by masters of attack.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{0,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{0,99}", - "durability": null, - "name": "Attack cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9748", + "name": "Attack cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{0,99}", + "tradeable": "false" }, { + "id": "9749", + "name": "Attack hood", + "examine": "Attack skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{0,99}", "shop_price": "99000", - "examine": "Attack skillcape hood.", - "durability": null, - "name": "Attack hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9749", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{2,99}", - "shop_price": "99000", - "examine": "The cape only worn by the strongest people.", - "durability": null, - "name": "Strength cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9750", + "name": "Strength cape", + "examine": "The cape only worn by the strongest people.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{2,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{2,99}", - "durability": null, - "name": "Strength cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9751", + "name": "Strength cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{2,99}", + "tradeable": "false" }, { + "id": "9752", + "name": "Strength hood", + "examine": "Strength skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{2,99}", "shop_price": "99000", - "examine": "Strength skillcape hood.", - "durability": null, - "name": "Strength hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9752", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{1,99}", - "shop_price": "99000", - "examine": "The cape worn by masters of the art of Defence.", - "durability": null, - "name": "Defence cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9753", + "name": "Defence cape", + "examine": "The cape worn by masters of the art of Defence.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{1,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{1,99}", - "durability": null, - "name": "Defence cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9754", + "name": "Defence cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{1,99}", + "tradeable": "false" }, { + "id": "9755", + "name": "Defence hood", + "examine": "Defence skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,99}", "shop_price": "99000", - "examine": "Defence skillcape hood.", - "durability": null, - "name": "Defence hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9755", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{4,99}", - "shop_price": "99000", - "examine": "The cape worn by master archers.", - "durability": null, - "name": "Ranging cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9756", + "name": "Ranging cape", + "examine": "The cape worn by master archers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{4,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{4,99}", - "durability": null, - "name": "Ranging cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9757", + "name": "Ranging cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{4,99}", + "tradeable": "false" }, { + "id": "9758", + "name": "Ranging hood", + "examine": "Range skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{4,99}", "shop_price": "99000", - "examine": "Range skillcape hood.", - "durability": null, - "name": "Ranging hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9758", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{5,99}", - "shop_price": "99000", - "examine": "The cape worn by the most pious of heroes.", - "durability": null, - "name": "Prayer cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9759", + "name": "Prayer cape", + "examine": "The cape worn by the most pious of heroes.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{5,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{5,99}", - "durability": null, - "name": "Prayer cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9760", + "name": "Prayer cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{5,99}", + "tradeable": "false" }, { + "id": "9761", + "name": "Prayer hood", + "examine": "Prayer skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{5,99}", "shop_price": "99000", - "examine": "Prayer skillcape hood.", - "durability": null, - "name": "Prayer hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9761", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{6,99}", - "shop_price": "99000", - "examine": "The cape worn by the most powerful mages.", - "durability": null, - "name": "Magic cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9762", + "name": "Magic cape", + "examine": "The cape worn by the most powerful mages.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{6,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{6,99}", - "durability": null, - "name": "Magic cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9763", + "name": "Magic cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{6,99}", + "tradeable": "false" }, { + "id": "9764", + "name": "Magic hood", + "examine": "Magic skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{6,99}", "shop_price": "99000", - "examine": "Magic skillcape hood.", - "durability": null, - "name": "Magic hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9764", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{20,99}", - "shop_price": "99000", - "examine": "The cape worn by master runecrafters.", - "durability": null, - "name": "Runecraft cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9765", + "name": "Runecraft cape", + "examine": "The cape worn by master runecrafters.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{20,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{20,99}", - "durability": null, - "name": "Runecraft cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9766", + "name": "Runecraft cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{20,99}", + "tradeable": "false" }, { + "id": "9767", + "name": "Runecrafting hood", + "examine": "Runecrafting skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{20,99}", "shop_price": "99000", - "examine": "Runecrafting skillcape hood.", - "durability": null, - "name": "Runecrafting hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9767", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{3,99}", - "shop_price": "99000", - "examine": "The cape worn by well-constituted adventurers.", - "durability": null, - "name": "Hitpoints cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9768", + "name": "Hitpoints cape", + "examine": "The cape worn by well-constituted adventurers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{3,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{3,99}", - "durability": null, - "name": "Hitpoints cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9769", + "name": "Hitpoints cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{3,99}", + "tradeable": "false" }, { + "id": "9770", + "name": "Hitpoints hood", + "examine": "Constitution skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{3,99}", "shop_price": "99000", - "examine": "Constitution skillcape hood.", - "durability": null, - "name": "Hitpoints hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9770", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{16,99}", - "shop_price": "99000", - "examine": "The cape worn by the most agile of heroes.", - "durability": null, - "name": "Agility cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9771", + "name": "Agility cape", + "examine": "The cape worn by the most agile of heroes.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{16,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{16,99}", - "durability": null, - "name": "Agility cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9772", + "name": "Agility cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{16,99}", + "tradeable": "false" }, { + "id": "9773", + "name": "Agility hood", + "examine": "Agility skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{16,99}", "shop_price": "99000", - "examine": "Agility skillcape hood.", - "durability": null, - "name": "Agility hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9773", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{15,99}", - "shop_price": "99000", - "examine": "The cape worn by the most skilled at the art of herblore.", - "durability": null, - "name": "Herblore cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9774", + "name": "Herblore cape", + "examine": "The cape worn by the most skilled at the art of herblore.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{15,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{15,99}", - "durability": null, - "name": "Herblore cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9775", + "name": "Herblore cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{15,99}", + "tradeable": "false" }, { + "id": "9776", + "name": "Herblore hood", + "examine": "Herblore skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{15,99}", "shop_price": "99000", - "examine": "Herblore skillcape hood.", - "durability": null, - "name": "Herblore hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9776", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{17,99}", - "shop_price": "99000", - "examine": "The cape worn by master thieves.", - "durability": null, - "name": "Thieving cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9777", + "name": "Thieving cape", + "examine": "The cape worn by master thieves.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{17,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{17,99}", - "durability": null, - "name": "Thieving cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9778", + "name": "Thieving cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{17,99}", + "tradeable": "false" }, { + "id": "9779", + "name": "Thieving hood", + "examine": "Thieving skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{17,99}", "shop_price": "99000", - "examine": "Thieving skillcape hood.", - "durability": null, - "name": "Thieving hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9779", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{12,99}", - "shop_price": "99000", - "examine": "The cape worn by master craftworkers.", - "durability": null, - "name": "Crafting cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9780", + "name": "Crafting cape", + "examine": "The cape worn by master craftworkers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{12,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{12,99}", - "durability": null, - "name": "Crafting cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9781", + "name": "Crafting cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{12,99}", + "tradeable": "false" }, { + "id": "9782", + "name": "Crafting hood", + "examine": "Crafting skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{12,99}", "shop_price": "99000", - "examine": "Crafting skillcape hood.", - "durability": null, - "name": "Crafting hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9782", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{9,99}", - "shop_price": "99000", - "examine": "The cape worn by the best of fletchers.", - "durability": null, - "name": "Fletching cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9783", + "name": "Fletching cape", + "examine": "The cape worn by the best of fletchers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{9,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{9,99}", - "durability": null, - "name": "Fletching cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9784", + "name": "Fletching cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{9,99}", + "tradeable": "false" }, { + "id": "9785", + "name": "Fletching hood", + "examine": "Fletching skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{9,99}", "shop_price": "99000", - "examine": "Fletching skillcape hood.", - "durability": null, - "name": "Fletching hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9785", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{18,99}", - "shop_price": "99000", - "examine": "The cape worn by slayer masters.", - "durability": null, - "name": "Slayer cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9786", + "name": "Slayer cape", + "examine": "The cape worn by slayer masters.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{18,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{18,99}", - "durability": null, - "name": "Slayer cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9787", + "name": "Slayer cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{18,99}", + "tradeable": "false" }, { + "id": "9788", + "name": "Slayer hood", + "examine": "Slayer skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{18,99}", "shop_price": "99000", - "examine": "Slayer skillcape hood.", - "durability": null, - "name": "Slayer hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9788", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{22,99}", - "shop_price": "99000", - "examine": "The cape worn by master builders.", - "durability": null, - "name": "Construct. cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9789", + "name": "Construct. cape", + "examine": "The cape worn by master builders.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{22,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{22,99}", - "durability": null, - "name": "Construct. cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9790", + "name": "Construct. cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{22,99}", + "tradeable": "false" }, { + "id": "9791", + "name": "Construct. hood", + "examine": "Construction skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{22,99}", "shop_price": "99000", - "examine": "Construction skillcape hood.", - "durability": null, - "name": "Construct. hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9791", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{14,99}", - "shop_price": "99000", - "examine": "The cape worn by the most skilled miners.", - "durability": null, - "name": "Mining cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9792", + "name": "Mining cape", + "examine": "The cape worn by the most skilled miners.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{14,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{14,99}", - "durability": null, - "name": "Mining cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9793", + "name": "Mining cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{14,99}", + "tradeable": "false" }, { + "id": "9794", + "name": "Mining hood", + "examine": "Mining skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{14,99}", "shop_price": "99000", - "examine": "Mining skillcape hood.", - "durability": null, - "name": "Mining hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9794", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{13,99}", - "shop_price": "99000", - "examine": "The cape worn by master smiths.", - "durability": null, - "name": "Smithing cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9795", + "name": "Smithing cape", + "examine": "The cape worn by master smiths.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{13,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{13,99}", - "durability": null, - "name": "Smithing cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9796", + "name": "Smithing cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{13,99}", + "tradeable": "false" }, { + "id": "9797", + "name": "Smithing hood", + "examine": "Smithing skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{13,99}", "shop_price": "99000", - "examine": "Smithing skillcape hood.", - "durability": null, - "name": "Smithing hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9797", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{10,99}", - "shop_price": "99000", - "examine": "The cape worn by the best fishermen.", - "durability": null, - "name": "Fishing cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9798", + "name": "Fishing cape", + "examine": "The cape worn by the best fishermen.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{10,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{10,99}", - "durability": null, - "name": "Fishing cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9799", + "name": "Fishing cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{10,99}", + "tradeable": "false" }, { + "id": "9800", + "name": "Fishing hood", + "examine": "Fishing skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{10,99}", "shop_price": "99000", - "examine": "Fishing skillcape hood.", - "durability": null, - "name": "Fishing hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9800", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{7,99}", - "shop_price": "99000", - "examine": "The cape worn by the world's best chefs.", - "durability": null, - "name": "Cooking cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9801", + "name": "Cooking cape", + "examine": "The cape worn by the world's best chefs.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{7,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{7,99}", - "durability": null, - "name": "Cooking cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9802", + "name": "Cooking cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{7,99}", + "tradeable": "false" }, { + "id": "9803", + "name": "Cooking hood", + "examine": "Cooking skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{7,99}", "shop_price": "99000", - "examine": "Cooking skillcape hood.", - "durability": null, - "name": "Cooking hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9803", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{11,99}", - "shop_price": "99000", - "examine": "The cape worn by master firelighters.", - "durability": null, - "name": "Firemaking cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9804", + "name": "Firemaking cape", + "examine": "The cape worn by master firelighters.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{11,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{11,99}", - "durability": null, - "name": "Firemaking cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9805", + "name": "Firemaking cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{11,99}", + "tradeable": "false" }, { + "id": "9806", + "name": "Firemaking hood", + "examine": "Firemaking skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{11,99}", "shop_price": "99000", - "examine": "Firemaking skillcape hood.", - "durability": null, - "name": "Firemaking hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9806", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{8,99}", - "shop_price": "99000", - "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", - "durability": null, - "name": "Woodcutting cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9807", + "name": "Woodcutting cape", + "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{8,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{8,99}", - "durability": null, - "name": "Woodcut. cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9808", + "name": "Woodcut. cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{8,99}", + "tradeable": "false" }, { + "id": "9809", + "name": "Woodcutting hood", + "examine": "Woodcutting skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{8,99}", "shop_price": "99000", - "examine": "Woodcutting skillcape hood.", - "durability": null, - "name": "Woodcutting hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9809", - "equipment_slot": "0" + "weight": "0.4" }, { - "requirements": "{19,99}", - "shop_price": "99000", - "examine": "The cape worn by master farmers.", - "durability": null, - "name": "Farming cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9810", + "name": "Farming cape", + "examine": "The cape worn by master farmers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{19,99}", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "requirements": "{19,99}", - "durability": null, - "name": "Farming cape(t)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "9811", + "name": "Farming cape(t)", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{19,99}", + "tradeable": "false" }, { + "id": "9812", + "name": "Farming hood", + "examine": "Farming skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{19,99}", "shop_price": "99000", - "examine": "Farming skillcape hood.", - "durability": null, - "name": "Farming hood", "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9812", - "equipment_slot": "0" + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by only the most experienced adventurers.", - "durability": null, - "name": "Quest point cape", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9813", + "name": "Quest point cape", + "examine": "The cape worn by only the most experienced adventurers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "remove_head": "true", - "shop_price": "99000", - "examine": "Quest skillcape hood.", - "durability": null, - "name": "Quest point hood", - "tradeable": "false", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9814", - "equipment_slot": "0" + "name": "Quest point hood", + "examine": "Quest skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "99000", + "tradeable": "false", + "weight": "0.4" }, { - "examine": "A woolly bobble hat.", - "durability": null, - "name": "Bobble hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.25", - "archery_ticket_price": "0", "id": "9815", - "equipment_slot": "0" + "name": "Bobble hat", + "examine": "A woolly bobble hat.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.25" }, { - "examine": "A woolly scarf.", - "durability": null, - "name": "Bobble scarf", - "weight": "0.25", - "archery_ticket_price": "0", "id": "9816", - "equipment_slot": "2" + "name": "Bobble scarf", + "examine": "A woolly scarf.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.25" }, { - "grand_exchange_price": "1", - "durability": null, + "id": "9817", "name": "Oak cape rack", "archery_ticket_price": "0", - "id": "9817" + "durability": null }, { - "grand_exchange_price": "88", - "durability": null, + "id": "9818", "name": "Teak cape rack", "archery_ticket_price": "0", - "id": "9818" + "durability": null }, { - "grand_exchange_price": "120", - "durability": null, + "id": "9819", "name": "M'gany cape rack", "archery_ticket_price": "0", - "id": "9819" + "durability": null }, { - "grand_exchange_price": "136025", - "durability": null, + "id": "9820", "name": "Gilded cape rack", "archery_ticket_price": "0", - "id": "9820" + "durability": null }, { - "grand_exchange_price": "328598", - "durability": null, + "id": "9821", "name": "Marble cape rack", "archery_ticket_price": "0", - "id": "9821" + "durability": null }, { - "grand_exchange_price": "979747", - "durability": null, + "id": "9822", "name": "Magical cape rack", "archery_ticket_price": "0", - "id": "9822" + "durability": null }, { - "grand_exchange_price": "59", - "durability": null, + "id": "9823", "name": "Oak costume box", "archery_ticket_price": "0", - "id": "9823" + "durability": null }, { - "grand_exchange_price": "122", - "durability": null, + "id": "9824", "name": "Teak costume box", "archery_ticket_price": "0", - "id": "9824" + "durability": null }, { - "grand_exchange_price": "59", - "durability": null, + "id": "9825", "name": "Mahogany cos box", "archery_ticket_price": "0", - "id": "9825" + "durability": null }, { - "grand_exchange_price": "15", - "durability": null, + "id": "9826", "name": "Oak armour case", "archery_ticket_price": "0", - "id": "9826" + "durability": null }, { - "grand_exchange_price": "27", - "durability": null, + "id": "9827", "name": "Teak armour case", "archery_ticket_price": "0", - "id": "9827" + "durability": null }, { - "grand_exchange_price": "90", - "durability": null, + "id": "9828", "name": "M'gany arm'r case", "archery_ticket_price": "0", - "id": "9828" + "durability": null }, { - "grand_exchange_price": "103", - "durability": null, + "id": "9829", "name": "Oak magic wardrobe", "archery_ticket_price": "0", - "id": "9829" + "durability": null }, { - "grand_exchange_price": "1", - "durability": null, + "id": "9830", "name": "Carved oak magic wardrobe", "archery_ticket_price": "0", - "id": "9830" + "durability": null }, { - "grand_exchange_price": "127", - "durability": null, + "id": "9831", "name": "Teak magic wardrobe", "archery_ticket_price": "0", - "id": "9831" + "durability": null }, { - "grand_exchange_price": "13", - "durability": null, + "id": "9832", "name": "Carved teak magic wardrobe", "archery_ticket_price": "0", - "id": "9832" + "durability": null }, { - "grand_exchange_price": "135", - "durability": null, + "id": "9833", "name": "Mahogany magic wardrobe", "archery_ticket_price": "0", - "id": "9833" + "durability": null }, { - "grand_exchange_price": "137267", - "durability": null, + "id": "9834", "name": "Gilded magic wardrobe", "archery_ticket_price": "0", - "id": "9834" + "durability": null }, { - "grand_exchange_price": "341567", - "durability": null, + "id": "9835", "name": "Marble magic wardrobe", "archery_ticket_price": "0", - "id": "9835" + "durability": null }, { - "grand_exchange_price": "21", - "durability": null, + "id": "9836", "name": "Oak toy box", "archery_ticket_price": "0", - "id": "9836" + "durability": null }, { - "grand_exchange_price": "151", - "durability": null, + "id": "9837", "name": "Teak toy box", "archery_ticket_price": "0", - "id": "9837" + "durability": null }, { - "grand_exchange_price": "20", - "durability": null, + "id": "9838", "name": "Mahogany toy box", "archery_ticket_price": "0", - "id": "9838" + "durability": null }, { - "grand_exchange_price": "21", - "durability": null, + "id": "9839", "name": "Oak treasure chest", "archery_ticket_price": "0", - "id": "9839" + "durability": null }, { - "grand_exchange_price": "157", - "durability": null, + "id": "9840", "name": "Teak treas' chest", "archery_ticket_price": "0", - "id": "9840" + "durability": null }, { - "grand_exchange_price": "39", - "durability": null, + "id": "9841", "name": "M'gany treas' chest", "archery_ticket_price": "0", - "id": "9841" + "durability": null }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3", + "id": "9843", + "name": "Oak cape rack", "examine": "A ready-to-assemble oak cape rack.", - "durability": null, - "name": "Oak cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9843" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "92", + "id": "9844", + "name": "Teak cape rack", "examine": "A ready-to-assemble teak cape rack..", - "durability": null, - "name": "Teak cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9844" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "111", + "id": "9845", + "name": "M'gany cape rack", "examine": "A ready-to-assemble mahogany cape rack.", - "durability": null, - "name": "M'gany cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9845" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "130900", + "id": "9846", + "name": "Gilded cape rack", "examine": "A ready-to-assemble gilded mahogany rack.", - "durability": null, - "name": "Gilded cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9846" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "329500", + "id": "9847", + "name": "Marble cape rack", "examine": "A ready-to-assemble marble cape rack.", - "durability": null, - "name": "Marble cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9847" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1000000", + "id": "9848", + "name": "Magical cape rack", "examine": "A ready-to-assemble magic cape rack.", - "durability": null, - "name": "Magical cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9848" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "25", + "id": "9849", + "name": "Oak toy box", "examine": "A ready-to-assemble oak toy box.", - "durability": null, - "name": "Oak toy box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9849" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "165", + "id": "9850", + "name": "Teak toy box", "examine": "A ready-to-assemble teak toy box.", - "durability": null, - "name": "Teak toy box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9850" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "20", + "id": "9851", + "name": "Mahogany toy box", "examine": "A ready-to-assemble mahogany toy box.", - "durability": null, - "name": "Mahogany toy box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9851" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "111", - "examine": "A ready-to-assemble oak magic wardrobe.", - "durability": null, + "id": "9852", "name": "Oak magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "1", - "id": "9852" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", "examine": "A ready-to-assemble oak magic wardrobe.", - "durability": null, - "name": "Carved oak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9853" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { + "id": "9853", + "name": "Carved oak magic wardrobe", + "examine": "A ready-to-assemble oak magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "133", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9854", + "name": "Teak magic wardrobe", "examine": "A ready-to-assemble teak magic wardrobe.", - "durability": null, - "name": "Teak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9854" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "15", + "id": "9855", + "name": "Carved teak magic wardrobe", "examine": "A ready-to-assemble carved teak magic wardrobe.", - "durability": null, - "name": "Carved teak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9855" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "129", + "id": "9856", + "name": "Mahogany magic wardrobe", "examine": "A ready-to-assemble mahogany magic wardrobe.", - "durability": null, - "name": "Mahogany magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9856" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137700", + "id": "9857", + "name": "Gilded magic wardrobe", "examine": "A ready-to-assemble gilded mahogany magic wardrobe.", - "durability": null, - "name": "Gilded magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9857" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "335800", + "id": "9858", + "name": "Marble magic wardrobe", "examine": "A ready-to-assemble marble magic wardrobe.", - "durability": null, - "name": "Marble magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9858" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "17", + "id": "9859", + "name": "Oak armour case", "examine": "A ready-to-assemble oak armour case.", - "durability": null, - "name": "Oak armour case", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9859" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "29", + "id": "9860", + "name": "Teak armour case", "examine": "A ready-to-assemble teak armour case.", - "durability": null, - "name": "Teak armour case", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9860" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "88", + "id": "9861", + "name": "M'gany arm'r case", "examine": "A ready-to-assemble mahogany armour case.", - "durability": null, - "name": "M'gany arm'r case", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9861" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "24", + "id": "9862", + "name": "Oak treasure chest", "examine": "A ready-to-assemble oak treasure chest.", - "durability": null, - "name": "Oak treasure chest", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9862" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "164", + "id": "9863", + "name": "Teak treas' chest", "examine": "A ready-to-assemble teak treasure chest.", - "durability": null, - "name": "Teak treas' chest", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9863" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "41", + "id": "9864", + "name": "M'gany treas' chest", "examine": "A ready-to-assemble mahogany treasure chest.", - "durability": null, - "name": "M'gany treas' chest", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9864" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "62", - "durability": null, + "id": "9865", "name": "Oak costume box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9865" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "135", - "durability": null, + "id": "9866", "name": "Teak costume box", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "1", - "id": "9866" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "60", - "durability": null, - "name": "Mahogany cos box", "tradeable": "true", - "archery_ticket_price": "0", - "weight": "1", - "id": "9867" + "weight": "1" }, { + "id": "9867", + "name": "Mahogany cos box", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9868", "name": "Oak cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9868" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9869", "name": "Teak cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9869" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9870", "name": "M'gany cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9870" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9871", "name": "Gilded cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9871" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9872", "name": "Marble cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9872" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9873", "name": "Magical cape rack", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9873" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9874", "name": "Oak toy box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9874" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9875", "name": "Teak toy box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9875" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9876", "name": "Mahogany toy box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9876" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9877", "name": "Oak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9877" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9878", "name": "Carved oak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9878" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9879", "name": "Teak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9879" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9880", "name": "Carved teak magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9880" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9881", "name": "Mahogany magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9881" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9882", "name": "Gilded magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9882" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9883", "name": "Marble magic wardrobe", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9883" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9884", "name": "Oak armour case", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9884" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9885", "name": "Teak armour case", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9885" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9886", "name": "M'gany arm'r case", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9886" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9887", "name": "Oak treasure chest", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9887" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9888", "name": "Teak treas' chest", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9888" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9889", "name": "M'gany treas' chest", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9889" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9890", "name": "Oak costume box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9890" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9891", "name": "Teak costume box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9891" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "9892", "name": "Mahogany cos box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9892" + "durability": null, + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A lump that at some point might have been a gout tuber.", - "durability": null, + "id": "9901", "name": "Goutweedy lump", - "tradeable": "false", - "destroy": "true", + "examine": "A lump that at some point might have been a gout tuber.", "archery_ticket_price": "0", - "id": "9901" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pile of gout tubers suitable for use in mountainous terrain.", - "durability": null, + "id": "9902", "name": "Hardy gout tubers", - "tradeable": "false", - "destroy": "true", + "examine": "A pile of gout tubers suitable for use in mountainous terrain.", "archery_ticket_price": "0", - "id": "9902" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "My Arm will probably have a spare copy.", - "examine": "Farmer Gricoller's Farming Manual.", - "durability": null, + "id": "9903", "name": "Farming manual", - "tradeable": "false", - "destroy": "true", + "examine": "Farmer Gricoller's Farming Manual.", "archery_ticket_price": "0", - "id": "9903" + "destroy": "true", + "destroy_message": "My Arm will probably have a spare copy.", + "durability": null, + "tradeable": "false" }, { - "examine": "A book for sailors.", - "durability": null, + "id": "9904", "name": "Sailing book", + "examine": "A book for sailors.", "archery_ticket_price": "0", - "id": "9904" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9906", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9907", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9908", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9909", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9910", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, - "name": "Ghost buster 500", - "archery_ticket_price": "0", "id": "9911", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "See article", - "durability": null, "name": "Ghost buster 500", + "examine": "See article", "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3" + }, + { "id": "9912", + "name": "Ghost buster 500", + "examine": "See article", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A white destabiliser for use in a ghost buster.", - "durability": null, - "name": "White destabiliser", - "archery_ticket_price": "0", "id": "9913", + "name": "White destabiliser", + "examine": "A white destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A red destabiliser for use in a ghost buster.", - "durability": null, - "name": "Red destabiliser", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "9914", - "equipment_slot": "5" + "name": "Red destabiliser", + "examine": "A red destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "5", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A blue destabiliser for use in a ghost buster.", - "durability": null, - "name": "Blue destabiliser", - "archery_ticket_price": "0", "id": "9915", + "name": "Blue destabiliser", + "examine": "A blue destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A green destabiliser for use in a ghost buster.", - "durability": null, - "name": "Green destabiliser", - "archery_ticket_price": "0", "id": "9916", + "name": "Green destabiliser", + "examine": "A green destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A yellow destabiliser for use in a ghost buster.", - "durability": null, - "name": "Yellow destabiliser", - "archery_ticket_price": "0", "id": "9917", + "name": "Yellow destabiliser", + "examine": "A yellow destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A black destabiliser for use in a ghost buster.", - "durability": null, - "name": "Black destabiliser", - "archery_ticket_price": "0", "id": "9918", + "name": "Black destabiliser", + "examine": "A black destabiliser for use in a ghost buster.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, "equipment_slot": "5" }, { - "examine": "An angry-looking tree root.", - "durability": null, + "id": "9919", "name": "Evil root", + "examine": "An angry-looking tree root.", "archery_ticket_price": "0", - "id": "9919" + "durability": null }, { - "remove_head": "true", - "destroy_message": "Players may get another from Diango in Draynor Village.", - "examine": "Better not light it!", - "durability": null, - "name": "Jack lantern mask", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "9920", + "name": "Jack lantern mask", + "examine": "Better not light it!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Players may get another from Diango in Draynor Village.", + "durability": null, + "equip_audio": "3227", + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Skeleton feet.", - "durability": null, - "name": "Skeleton boots", - "weight": "2", - "archery_ticket_price": "0", "id": "9921", - "equipment_slot": "10" + "name": "Skeleton boots", + "examine": "Skeleton feet.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "10", + "weight": "2" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some skeletal gloves.", - "durability": null, - "name": "Skeleton gloves", - "weight": "2", - "archery_ticket_price": "0", "id": "9922", - "equipment_slot": "9" + "name": "Skeleton gloves", + "examine": "Some skeletal gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "weight": "2" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Does my pelvis look big in this?", - "durability": null, - "name": "Skeleton leggings", - "weight": "2", - "archery_ticket_price": "0", "id": "9923", - "equipment_slot": "7" + "name": "Skeleton leggings", + "examine": "Does my pelvis look big in this?", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "7", + "weight": "2" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "remove_sleeves": "true", - "examine": "The shirt of a fully body skeleton costume.", - "durability": null, - "name": "Skeleton shirt", - "weight": "2", - "archery_ticket_price": "0", "id": "9924", - "equipment_slot": "4" - }, - { - "remove_head": "true", + "name": "Skeleton shirt", + "examine": "The shirt of a fully body skeleton costume.", + "archery_ticket_price": "0", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A scary skeleton mask.", "durability": null, - "name": "Skeleton mask", - "weight": "2", - "archery_ticket_price": "0", - "id": "9925", - "equipment_slot": "0" - }, - { - "destroy_message": "You'll have to buy another one from Auguste on Entrana.", - "shop_price": "30000", - "examine": "This sapling is ready to be replanted in a tree patch.", - "durability": null, - "name": "Auguste's sapling", - "tradeable": "false", - "destroy": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "9932" - }, - { - "examine": "An incomplete origami balloon.", - "durability": null, - "name": "Balloon structure", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9933" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Origami balloon", - "archery_ticket_price": "0", - "id": "9934" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Yellow balloon", - "archery_ticket_price": "0", - "id": "9935" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Blue balloon", - "archery_ticket_price": "0", - "id": "9936" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Red balloon", - "archery_ticket_price": "0", - "id": "9937" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Orange balloon", - "archery_ticket_price": "0", - "id": "9938" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Green balloon", - "archery_ticket_price": "0", - "id": "9939" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Purple balloon", - "archery_ticket_price": "0", - "id": "9940" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Pink balloon", - "archery_ticket_price": "0", - "id": "9941" - }, - { - "examine": "An origami balloon.", - "durability": null, - "name": "Black balloon", - "archery_ticket_price": "0", - "id": "9942" - }, - { - "examine": "A bag full of sand.", - "durability": null, - "name": "Sandbag", - "weight": "1", - "archery_ticket_price": "0", - "id": "9943", - "equipment_slot": "3" - }, - { - "destroy_message": "You'll have to visit Auguste on Entrana to get a new jacket.", + "equipment_slot": "4", "remove_sleeves": "true", - "examine": "For all your flying needs.", - "durability": null, - "name": "Bomber jacket", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "9944", - "equipment_slot": "4" + "weight": "2" }, { - "remove_head": "true", - "destroy_message": "You'll have to visit Auguste on Entrana to get a new cap.", - "examine": "The red baron would be jealous.", - "durability": null, - "name": "Bomber cap", - "tradeable": "false", - "destroy": "true", + "id": "9925", + "name": "Skeleton mask", + "examine": "A scary skeleton mask.", "archery_ticket_price": "0", - "id": "9945", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "For better flying vision.", - "durability": null, - "name": "Cap and goggles", - "archery_ticket_price": "0", - "id": "9946", - "equipment_slot": "0" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A red coloured disk that's seen better days.", "durability": null, - "name": "Old red disk", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9947" + "equipment_slot": "0", + "remove_head": "true", + "weight": "2" }, { + "id": "9932", + "name": "Auguste's sapling", + "examine": "This sapling is ready to be replanted in a tree patch.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to buy another one from Auguste on Entrana.", + "durability": null, + "shop_price": "30000", + "tradeable": "false", + "weight": "0.9" + }, + { + "id": "9933", + "name": "Balloon structure", + "examine": "An incomplete origami balloon.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "9934", + "name": "Origami balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9935", + "name": "Yellow balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9936", + "name": "Blue balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9937", + "name": "Red balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9938", + "name": "Orange balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9939", + "name": "Green balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9940", + "name": "Purple balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9941", + "name": "Pink balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9942", + "name": "Black balloon", + "examine": "An origami balloon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9943", + "name": "Sandbag", + "examine": "A bag full of sand.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1" + }, + { + "id": "9944", + "name": "Bomber jacket", + "examine": "For all your flying needs.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to visit Auguste on Entrana to get a new jacket.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "1" + }, + { + "id": "9945", + "name": "Bomber cap", + "examine": "The red baron would be jealous.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to visit Auguste on Entrana to get a new cap.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false" + }, + { + "id": "9946", + "name": "Cap and goggles", + "examine": "For better flying vision.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true" + }, + { + "id": "9947", + "name": "Old red disk", + "examine": "A red coloured disk that's seen better days.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "9948", + "name": "Hunter cape", + "examine": "The cape worn by master hunters.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{21,99}", "shop_price": "99000", - "examine": "The cape worn by master hunters.", - "durability": null, - "name": "Hunter cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9948", - "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { - "requirements": "{21,99}", - "durability": null, + "id": "9949", "name": "Hunter cape(t)", "archery_ticket_price": "0", - "id": "9949", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{21,99}" }, { + "id": "9950", + "name": "Hunter hood", + "examine": "Hunter skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{21,99}", "shop_price": "99000", - "examine": "Hunter skillcape hood.", - "durability": null, - "name": "Hunter hood", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "9950", - "equipment_slot": "0" + "weight": "0.4" }, { - "durability": null, + "id": "9952", "name": "Imp", "archery_ticket_price": "0", "attack_speed": "4", - "id": "9952" + "durability": null }, { - "examine": "A little bigger than usual...", - "durability": null, + "id": "9953", "name": "Kebbit", - "archery_ticket_price": "0", - "id": "9953" - }, - { "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9954" + "durability": null }, { + "id": "9954", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9955" + "durability": null }, { + "id": "9955", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9956" + "durability": null }, { + "id": "9956", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9957" + "durability": null }, { + "id": "9957", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9958" + "durability": null }, { + "id": "9958", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9959" + "durability": null }, { + "id": "9959", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9960" + "durability": null }, { + "id": "9960", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9961" + "durability": null }, { + "id": "9961", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9962" + "durability": null }, { + "id": "9962", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9963" + "durability": null }, { + "id": "9963", + "name": "Kebbit", "examine": "A little bigger than usual...", - "durability": null, - "name": "Kebbit", "archery_ticket_price": "0", - "id": "9964" + "durability": null }, { - "examine": "It appears to be protecting the nest.", - "durability": null, + "id": "9964", + "name": "Kebbit", + "examine": "A little bigger than usual...", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9974", "name": "Giant eagle", + "examine": "It appears to be protecting the nest.", "archery_ticket_price": "0", - "id": "9974" + "durability": null }, { - "examine": "Aww, how cute. ", - "durability": null, + "id": "9975", "name": "Rabbit", + "examine": "Aww, how cute. ", "archery_ticket_price": "0", - "id": "9975" + "durability": null }, { - "examine": "Even more volatile than its vegetarian counterpart.", - "grand_exchange_price": "796", - "durability": null, + "id": "9977", "name": "Red chinchompa", - "tradeable": "true", - "destroy": "true", + "examine": "Even more volatile than its vegetarian counterpart.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "9977", - "bonuses": "0,0,0,0,70,0,0,0,0,0,0,0,15,0,0" + "bonuses": "0,0,0,0,70,0,0,0,0,0,0,0,15,0,0", + "destroy": "true", + "durability": null, + "tradeable": "true" }, { - "shop_price": "63", - "ge_buy_limit": "10000", + "id": "9978", + "name": "Raw bird meat", "examine": "This certainly needs cooking!", - "grand_exchange_price": "204", - "durability": null, - "name": "Raw bird meat", - "tradeable": "true", - "weight": "0.15", "archery_ticket_price": "0", - "id": "9978" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "204", - "durability": null, - "name": "Raw bird meat", + "shop_price": "63", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9979" + "weight": "0.15" }, { - "ge_buy_limit": "1000", + "id": "9979", + "name": "Raw bird meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "9980", + "name": "Roast bird meat", "examine": "A nicely roasted bird.", - "grand_exchange_price": "18", - "durability": null, - "name": "Roast bird meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9980" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "18", - "durability": null, - "name": "Roast bird meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9981" + "tradeable": "true" }, { + "id": "9981", + "name": "Roast bird meat", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9983", "name": "Burnt bird meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9983" + "durability": null, + "tradeable": "true" }, { - "shop_price": "63", - "ge_buy_limit": "1000", + "id": "9984", + "name": "Skewered bird meat", "examine": "This certainly needs cooking!", - "grand_exchange_price": "130", - "durability": null, - "name": "Skewered bird meat", - "tradeable": "true", - "weight": "0.15", "archery_ticket_price": "0", - "id": "9984" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "63", + "tradeable": "true", + "weight": "0.15" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "130", - "durability": null, + "id": "9985", "name": "Skewered bird meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9985" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9986", + "name": "Raw beast meat", "examine": "A slab of raw beast meat.", - "grand_exchange_price": "16", - "durability": null, - "name": "Raw beast meat", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "9986" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "16", - "durability": null, + "id": "9987", "name": "Raw beast meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9987" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9988", + "name": "Roast beast meat", "examine": "A delicious looking slab of roast beast.", - "grand_exchange_price": "42", - "durability": null, - "name": "Roast beast meat", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "9988" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "42", - "durability": null, - "name": "Roast beast meat", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9989" + "weight": "2" }, { + "id": "9989", + "name": "Roast beast meat", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9991", "name": "Burnt beast meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9991" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9992", + "name": "Skewered beast", "examine": "Beast meat on a spit.", - "grand_exchange_price": "97", - "durability": null, - "name": "Skewered beast", - "tradeable": "true", - "weight": "3.5", "archery_ticket_price": "0", - "id": "9992" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "3.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "97", - "durability": null, + "id": "9993", "name": "Skewered beast", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9993" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9994", + "name": "Spicy tomato", "examine": "A bowl of chopped tomatoes with an extra kick.", - "grand_exchange_price": "324", - "durability": null, - "name": "Spicy tomato", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "9994" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "324", - "durability": null, + "id": "9995", "name": "Spicy tomato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9995" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "9996", + "name": "Spicy minced meat", "examine": "A bowl of chopped beef with an extra kick.", - "grand_exchange_price": "263", - "durability": null, - "name": "Spicy minced meat", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "9996" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "263", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "9997", "name": "Spicy minced meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9997" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "9998", + "name": "Hunter potion(4)", "examine": "4 doses of hunter potion.", - "grand_exchange_price": "566", - "durability": null, - "name": "Hunter potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9998" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "566", - "durability": null, + "id": "9999", "name": "Hunter potion(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9999" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10000", + "name": "Hunter potion(3)", "examine": "3 doses of hunter potion.", - "grand_exchange_price": "229", - "durability": null, - "name": "Hunter potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10000" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "229", - "durability": null, + "id": "10001", "name": "Hunter potion(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10001" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10002", + "name": "Hunter potion(2)", "examine": "2 doses of hunter potion.", - "grand_exchange_price": "159", - "durability": null, - "name": "Hunter potion(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10002" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "159", - "durability": null, + "id": "10003", "name": "Hunter potion(2)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10003" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10004", + "name": "Hunter potion(1)", "examine": "1 dose of hunter potion.", - "grand_exchange_price": "148", - "durability": null, - "name": "Hunter potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10004" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "148", - "durability": null, + "id": "10005", "name": "Hunter potion(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10005" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "100", + "id": "10006", + "name": "Bird snare", "examine": "A simple bird catcher./There's something caught in it.", - "grand_exchange_price": "2", - "durability": null, - "name": "Bird snare", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10006" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, - "name": "Bird snare", + "shop_price": "6", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10007" + "weight": "0.4" }, { + "id": "10007", + "name": "Bird snare", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10008", + "name": "Box trap", + "examine": "If a creature goes inside, then the box should slam shut.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{21,27}", "shop_price": "38", - "ge_buy_limit": "100", - "examine": "If a creature goes inside, then the box should slam shut.", - "grand_exchange_price": "308", - "durability": null, - "name": "Box trap", "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10008" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "308", - "durability": null, + "id": "10009", "name": "Box trap", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10009" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{21,15}", - "shop_price": "24", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "For catching butterflies...", - "walk_anim": "1205", - "durability": null, - "weight": "0.2", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "weapon_interface": "10", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1", - "stand_anim": "813", - "name": "Butterfly net", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "10010", - "stand_turn_anim": "1209" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, "name": "Butterfly net", - "tradeable": "true", + "examine": "For catching butterflies...", "archery_ticket_price": "0", - "id": "10011" + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "defence_anim": "403", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{21,15}", + "run_anim": "1210", + "shop_price": "24", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "10", + "weight": "0.2" }, { + "id": "10011", + "name": "Butterfly net", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10012", + "name": "Butterfly jar", + "examine": "It's got little holes at the top.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{21,15}", "shop_price": "1", - "ge_buy_limit": "100", - "examine": "It's got little holes at the top.", - "grand_exchange_price": "156", - "durability": null, - "name": "Butterfly jar", "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.02", - "id": "10012" + "weight": "0.02" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "156", - "durability": null, + "id": "10013", "name": "Butterfly jar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10013" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "143", + "id": "10014", + "name": "Black warlock", "examine": "There's a black warlock butterfly in here.", - "durability": null, - "name": "Black warlock", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.02", - "id": "10014" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "143", - "durability": null, + "id": "10015", "name": "Black warlock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10015" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "227", + "id": "10016", + "name": "Snowy knight", "examine": "There's a snowy knight butterfly in here.", - "durability": null, - "name": "Snowy knight", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.02", - "id": "10016" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "227", - "durability": null, + "id": "10017", "name": "Snowy knight", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10017" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "180", + "id": "10018", + "name": "Sapphire glacialis", "examine": "There's a sapphire glacialis butterfly in here.", - "durability": null, - "name": "Sapphire glacialis", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.02", - "id": "10018" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "180", - "durability": null, + "id": "10019", "name": "Sapphire glacialis", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10019" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1270", + "id": "10020", + "name": "Ruby harvest", "examine": "There's a ruby harvest butterfly in here.", - "durability": null, - "name": "Ruby harvest", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "0.02", - "id": "10020" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "1270", - "durability": null, - "name": "Ruby harvest", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10021" + "weight": "0.02" }, { - "shop_price": "500", - "examine": "A very large, single leather glove", - "durability": null, - "name": "Falconer's glove", - "tradeable": "false", - "destroy": "true", - "weight": "1.8", + "id": "10021", + "name": "Ruby harvest", "archery_ticket_price": "0", - "attack_speed": "4", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "10023", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "500", - "turn90cw_anim": "5166", - "examine": "A very large, single leather glove", - "walk_anim": "5164", - "durability": null, - "destroy": "true", - "weight": "1.8", - "turn90ccw_anim": "5167", - "attack_speed": "4", - "turn180_anim": "5165", - "render_anim": "1283", - "equipment_slot": "3", - "stand_anim": "5160", "name": "Falconer's glove", - "tradeable": "false", - "run_anim": "5168", + "examine": "A very large, single leather glove", "archery_ticket_price": "0", - "id": "10024", - "stand_turn_anim": "5161", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0" + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "shop_price": "500", + "tradeable": "false", + "weight": "1.8" }, { + "id": "10024", + "name": "Falconer's glove", + "examine": "A very large, single leather glove", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "render_anim": "1283", + "run_anim": "5168", + "shop_price": "500", + "stand_anim": "5160", + "stand_turn_anim": "5161", + "tradeable": "false", + "turn180_anim": "5165", + "turn90ccw_anim": "5167", + "turn90cw_anim": "5166", + "walk_anim": "5164", + "weight": "1.8" + }, + { + "id": "10025", + "name": "Magic box", + "examine": "A magical catching box.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{21,71}", "shop_price": "720", - "ge_buy_limit": "100", - "examine": "A magical catching box.", - "grand_exchange_price": "784", - "durability": null, - "name": "Magic box", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10025" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "784", - "durability": null, + "id": "10026", "name": "Magic box", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10026" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "720", - "examine": "A magical catching box.", - "grand_exchange_price": "743", - "durability": null, + "id": "10027", "name": "Imp-in-a-box(2)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10027" - }, - { - "shop_price": "720", "examine": "A magical catching box.", - "grand_exchange_price": "743", - "durability": null, - "name": "Imp-in-a-box(1)", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "10028" + "durability": null, + "shop_price": "720", + "tradeable": "true", + "weight": "1" }, { + "id": "10028", + "name": "Imp-in-a-box(1)", + "examine": "A magical catching box.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "720", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10029", + "name": "Teasing stick", + "examine": "Reduces the risk of hand severage when poking large, vicious carnivores.", + "archery_ticket_price": "0", + "bonuses": "7,8,0,-4,0,-1,1,2,0,0,0,8,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", "requirements": "{21,31}", "shop_price": "1", - "ge_buy_limit": "100", - "examine": "Reduces the risk of hand severage when poking large, vicious carnivores.", - "durability": null, - "weight": "1", + "tradeable": "true", "weapon_interface": "6", - "equipment_slot": "3", - "grand_exchange_price": "1", - "name": "Teasing stick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10029", - "bonuses": "7,8,0,-4,0,-1,1,2,0,0,0,8,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "id": "10030", + "name": "Teasing stick", + "archery_ticket_price": "0", "durability": null, - "name": "Teasing stick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10030" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "10031", + "name": "Rabbit snare", + "examine": "The snare will tighten around animals passing through.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{21,27}", "shop_price": "18", - "ge_buy_limit": "100", - "examine": "The snare will tighten around animals passing through.", - "grand_exchange_price": "1", - "durability": null, - "name": "Rabbit snare", "tradeable": "true", - "weight": "0.02", - "archery_ticket_price": "0", - "id": "10031" + "weight": "0.02" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "10032", "name": "Rabbit snare", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10032" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{4,45}", - "ge_buy_limit": "20000", - "turn90cw_anim": "3177", - "walk_anim": "3177", - "durability": null, - "turn90ccw_anim": "3177", - "attack_speed": "4", - "weapon_interface": "19", - "turn180_anim": "3177", - "render_anim": "234", - "equipment_slot": "3", - "grand_exchange_price": "973", - "stand_anim": "3175", - "name": "Chinchompa", - "run_anim": "3178", - "archery_ticket_price": "0", "id": "10033", - "stand_turn_anim": "3177", - "bonuses": "0,0,0,0,45,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{4,55}", - "ge_buy_limit": "20000", - "turn90cw_anim": "3177", - "examine": "Even more volatile than its vegetarian counterpart.", - "walk_anim": "3177", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "3177", + "name": "Chinchompa", + "archery_ticket_price": "0", "attack_speed": "4", - "weapon_interface": "19", - "turn180_anim": "3177", - "render_anim": "234", + "bonuses": "0,0,0,0,45,0,0,0,0,0,0,0,0,0,0", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "1382", - "stand_anim": "3175", - "name": "Red chinchompa", - "tradeable": "true", + "ge_buy_limit": "20000", + "render_anim": "234", + "requirements": "{4,45}", "run_anim": "3178", - "archery_ticket_price": "0", - "id": "10034", + "stand_anim": "3175", "stand_turn_anim": "3177", - "bonuses": "0,0,0,0,70,0,0,0,0,0,0,0,0,0,15" + "turn180_anim": "3177", + "turn90ccw_anim": "3177", + "turn90cw_anim": "3177", + "walk_anim": "3177", + "weapon_interface": "19" }, { - "requirements": "{21,52}", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in polar areas.", - "grand_exchange_price": "449", - "durability": null, - "name": "Kyatt legs", - "tradeable": "true", - "weight": "0.2", + "id": "10034", + "name": "Red chinchompa", + "examine": "Even more volatile than its vegetarian counterpart.", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,70,0,0,0,0,0,0,0,0,0,15", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "20000", + "render_anim": "234", + "requirements": "{4,55}", + "run_anim": "3178", + "stand_anim": "3175", + "stand_turn_anim": "3177", + "tradeable": "true", + "turn180_anim": "3177", + "turn90ccw_anim": "3177", + "turn90cw_anim": "3177", + "walk_anim": "3177", + "weapon_interface": "19" + }, + { "id": "10035", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,10,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "449", - "durability": null, "name": "Kyatt legs", - "tradeable": "true", + "examine": "These should make me harder to spot in polar areas.", "archery_ticket_price": "0", - "id": "10036" + "bonuses": "0,0,0,0,-7,11,10,10,0,10,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{21,52}", + "tradeable": "true", + "weight": "0.2" }, { - "requirements": "{21,52}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "272", - "name": "Kyatt top", - "tradeable": "true", + "id": "10036", + "name": "Kyatt legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10037", - "bonuses": "0,0,0,0,0,10,15,19,0,12,20,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "272", - "durability": null, "name": "Kyatt top", - "tradeable": "true", + "examine": "This should make me harder to spot in polar areas.", "archery_ticket_price": "0", - "id": "10038" + "bonuses": "0,0,0,0,0,10,15,19,0,12,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{21,52}", + "tradeable": "true", + "weight": "0.2" }, { + "id": "10038", + "name": "Kyatt top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10039", + "name": "Kyatt hat", + "examine": "This should make me harder to spot in polar areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-1,4,5,3,0,4,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{21,52}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "0", - "grand_exchange_price": "8338", - "name": "Kyatt hat", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10039", - "bonuses": "0,0,0,0,-1,4,5,3,0,4,3,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8338", - "durability": null, + "id": "10040", "name": "Kyatt hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10040" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{21,28}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in woodland and jungle areas.", - "grand_exchange_price": "2889", - "durability": null, - "name": "Larupia legs", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10041", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,5,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2889", - "durability": null, "name": "Larupia legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10042" - }, - { - "requirements": "{21,28}", - "ge_buy_limit": "100", "examine": "This should make me harder to spot in woodland and jungle areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "3232", - "name": "Larupia top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10043", - "bonuses": "0,0,0,0,0,10,15,19,0,12,10,0,0,0,0" - }, - { + "bonuses": "0,0,0,0,-7,11,10,10,0,10,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "3232", - "durability": null, - "name": "Larupia top", + "requirements": "{21,28}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10044" + "weight": "0.2" }, { + "id": "10042", + "name": "Larupia legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10043", + "name": "Larupia top", + "examine": "This should make me harder to spot in woodland and jungle areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,10,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{21,28}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10044", + "name": "Larupia top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10045", + "name": "Larupia hat", + "examine": "This should make me harder to spot in woodland and jungle areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-1,4,5,3,0,4,5,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{21,28}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in woodland and jungle areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "0", - "grand_exchange_price": "7237", - "name": "Larupia hat", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10045", - "bonuses": "0,0,0,0,-1,4,5,3,0,4,5,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "7237", - "durability": null, + "id": "10046", "name": "Larupia hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10046" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "10047", + "name": "Graahk legs", + "examine": "These should make me harder to spot in desert areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-7,11,10,10,0,10,10,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{21,38}", "shop_price": "150", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in desert areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "7", - "grand_exchange_price": "266", - "name": "Graahk legs", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10047", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,10,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "266", - "durability": null, + "id": "10048", "name": "Graahk legs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10048" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "10049", + "name": "Graahk top", + "examine": "This should make me harder to spot in desert areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,15,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", "requirements": "{21,38}", "shop_price": "300", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10050", + "name": "Graahk top", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10051", + "name": "Graahk headdress", "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "252", - "name": "Graahk top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10049", - "bonuses": "0,0,0,0,0,10,15,19,0,12,15,0,0,0,0" - }, - { + "bonuses": "0,0,0,0,-1,4,5,3,0,4,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", "ge_buy_limit": "100", - "grand_exchange_price": "252", - "durability": null, - "name": "Graahk top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10050" - }, - { "remove_head": "true", "requirements": "{21,38}", "shop_price": "1", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "0", - "grand_exchange_price": "8445", - "name": "Graahk headdress", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10051", - "bonuses": "0,0,0,0,-1,4,5,3,0,4,3,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8445", - "durability": null, + "id": "10052", "name": "Graahk headdress", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10052" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in wooded areas.", - "grand_exchange_price": "3527", - "durability": null, - "name": "Wood camo top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10053", - "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3527", - "durability": null, "name": "Wood camo top", - "tradeable": "true", + "examine": "This should make me harder to spot in wooded areas.", "archery_ticket_price": "0", - "id": "10054" - }, - { - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in wooded areas.", - "grand_exchange_price": "3228", - "durability": null, - "name": "Wood camo legs", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10055", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3228", - "durability": null, - "name": "Wood camo legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10056" - }, - { - "requirements": "{21,4}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in jungle areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "315", - "name": "Jungle camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10057", - "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "315", - "durability": null, - "name": "Jungle camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10058" - }, - { - "requirements": "{21,4}", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in jungle areas.", - "grand_exchange_price": "462", - "durability": null, - "name": "Jungle camo legs", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10059", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "462", - "durability": null, - "name": "Jungle camo legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10060" - }, - { - "requirements": "{21,10}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "541", - "name": "Desert camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10061", - "bonuses": "0,0,0,0,0,10,15,19,0,12,10,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "541", - "durability": null, - "name": "Desert camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10062" - }, - { - "requirements": "{21,10}", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in desert areas.", - "grand_exchange_price": "910", - "durability": null, - "name": "Desert camo legs", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10063", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "910", - "durability": null, - "name": "Desert camo legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10064" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", - "grand_exchange_price": "7990", - "durability": null, - "name": "Polar camo top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10065", "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "7990", - "durability": null, - "name": "Polar camo top", - "tradeable": "true", + "id": "10054", + "name": "Wood camo top", "archery_ticket_price": "0", - "id": "10066" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in polar areas", - "grand_exchange_price": "8389", - "durability": null, - "name": "Polar camo legs", - "tradeable": "true", - "weight": "0.2", + "id": "10055", + "name": "Wood camo legs", + "examine": "These should make me harder to spot in wooded areas.", "archery_ticket_price": "0", - "id": "10067", "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8389", + "id": "10056", + "name": "Wood camo legs", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10057", + "name": "Jungle camo top", + "examine": "This should make me harder to spot in jungle areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{21,4}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10058", + "name": "Jungle camo top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10059", + "name": "Jungle camo legs", + "examine": "These should make me harder to spot in jungle areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{21,4}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10060", + "name": "Jungle camo legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10061", + "name": "Desert camo top", + "examine": "This should make me harder to spot in desert areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,10,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{21,10}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10062", + "name": "Desert camo top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10063", + "name": "Desert camo legs", + "examine": "These should make me harder to spot in desert areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{21,10}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10064", + "name": "Desert camo legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10065", + "name": "Polar camo top", + "examine": "This should make me harder to spot in polar areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10066", + "name": "Polar camo top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10067", "name": "Polar camo legs", - "tradeable": "true", + "examine": "These should make me harder to spot in polar areas", "archery_ticket_price": "0", - "id": "10068" + "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.2" }, { - "requirements": "{21,40}", - "ge_buy_limit": "100", - "examine": "A surprisingly aerodynamic cape.", - "grand_exchange_price": "1140", - "durability": null, - "name": "Spotted cape", - "tradeable": "true", - "weight": "-2.2", + "id": "10068", + "name": "Polar camo legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10069", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1140", - "durability": null, "name": "Spotted cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10070" - }, - { - "requirements": "{21,66}", - "ge_buy_limit": "100", - "examine": "A really surprisingly aerodynamic cape.", - "grand_exchange_price": "1262", - "durability": null, - "name": "Spottier cape", - "tradeable": "true", - "weight": "-4.5", - "archery_ticket_price": "0", - "id": "10071", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1262", - "durability": null, - "name": "Spottier cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10072" - }, - { - "requirements": "{21,40}", "examine": "A surprisingly aerodynamic cape.", - "grand_exchange_price": "951", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "requirements": "{21,40}", + "tradeable": "true", + "weight": "-2.2" + }, + { + "id": "10070", "name": "Spotted cape", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10073", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{21,66}", - "examine": "A really surprisingly aerodynamic cape.", - "grand_exchange_price": "881", - "durability": null, + "id": "10071", "name": "Spottier cape", - "tradeable": "true", - "weight": "0.4", + "examine": "A really surprisingly aerodynamic cape.", "archery_ticket_price": "0", - "id": "10074", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "requirements": "{21,66}", + "tradeable": "true", + "weight": "-4.5" }, { - "requirements": "{21,54}", - "ge_buy_limit": "100", - "examine": "Made from dark kebbit fur, these are perfect for tasks of a stealthier nature.", - "grand_exchange_price": "1063", - "durability": null, - "name": "Gloves of silence", - "tradeable": "true", - "weight": "0.2", + "id": "10072", + "name": "Spottier cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10073", + "name": "Spotted cape", + "examine": "A surprisingly aerodynamic cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "requirements": "{21,40}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10074", + "name": "Spottier cape", + "examine": "A really surprisingly aerodynamic cape.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "requirements": "{21,66}", + "tradeable": "true", + "weight": "0.4" + }, + { "id": "10075", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1063", - "durability": null, "name": "Gloves of silence", - "tradeable": "true", + "examine": "Made from dark kebbit fur, these are perfect for tasks of a stealthier nature.", "archery_ticket_price": "0", - "id": "10076" + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{21,54}", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "100", - "examine": "Attack at your own risk.", - "grand_exchange_price": "80", - "durability": null, - "name": "Spiky vambraces", - "tradeable": "true", - "weight": "1", + "id": "10076", + "name": "Gloves of silence", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10077", - "bonuses": "0,0,0,0,4,2,2,1,0,0,1,2,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "80", - "durability": null, "name": "Spiky vambraces", - "tradeable": "true", + "examine": "Attack at your own risk.", "archery_ticket_price": "0", - "id": "10078" + "bonuses": "0,0,0,0,4,2,2,1,0,0,1,2,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "requirements": "{4,40}", - "ge_buy_limit": "100", - "examine": "Made from 100% real dragonhide. Now with added spikiness.", - "grand_exchange_price": "1353", - "durability": null, - "name": "Green spiky vambs", - "tradeable": "true", - "weight": "1", + "id": "10078", + "name": "Spiky vambraces", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10079", - "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,2,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1353", - "durability": null, "name": "Green spiky vambs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10080" - }, - { - "requirements": "{4,50}", - "ge_buy_limit": "100", "examine": "Made from 100% real dragonhide. Now with added spikiness.", - "grand_exchange_price": "1773", - "durability": null, - "name": "Blue spiky vambs", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,2,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10080", + "name": "Green spiky vambs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10081", - "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,2,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1773", - "durability": null, "name": "Blue spiky vambs", - "tradeable": "true", + "examine": "Made from 100% real dragonhide. Now with added spikiness.", "archery_ticket_price": "0", - "id": "10082" + "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,2,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{4,50}", + "tradeable": "true" }, { - "requirements": "{4,60}", - "ge_buy_limit": "100", - "examine": "Vambraces made from 100% real dragonhide. Now with added spikes.", - "grand_exchange_price": "1978", - "durability": null, - "name": "Red spiky vambs", - "tradeable": "true", - "weight": "1", + "id": "10082", + "name": "Blue spiky vambs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10083", - "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,2,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1978", - "durability": null, "name": "Red spiky vambs", - "tradeable": "true", + "examine": "Vambraces made from 100% real dragonhide. Now with added spikes.", "archery_ticket_price": "0", - "id": "10084" + "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,2,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{4,60}", + "tradeable": "true", + "weight": "1" }, { - "requirements": "{4,70}", - "ge_buy_limit": "100", - "examine": "Vambraces made from 100% real dragonhide. Now with added spikiness.", - "grand_exchange_price": "2727", - "durability": null, - "name": "Black spiky vambs", - "tradeable": "true", - "weight": "1", + "id": "10084", + "name": "Red spiky vambs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10085", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,2,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2727", - "durability": null, "name": "Black spiky vambs", - "tradeable": "true", + "examine": "Vambraces made from 100% real dragonhide. Now with added spikiness.", "archery_ticket_price": "0", - "id": "10086" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,2,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{4,70}", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "5000", - "examine": "Attractive to other birds and hunters alike.", - "grand_exchange_price": "20", + "id": "10086", + "name": "Black spiky vambs", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10087", "name": "Stripy feather", - "tradeable": "true", + "examine": "Attractive to other birds and hunters alike.", "archery_ticket_price": "0", - "id": "10087" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "3", - "ge_buy_limit": "5000", - "examine": "A vivid red feather.", - "grand_exchange_price": "23", - "durability": null, + "id": "10088", "name": "Red feather", - "tradeable": "true", + "examine": "A vivid red feather.", "archery_ticket_price": "0", - "id": "10088" + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "3", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "A cool blue feather.", - "grand_exchange_price": "27", - "durability": null, + "id": "10089", "name": "Blue feather", - "tradeable": "true", + "examine": "A cool blue feather.", "archery_ticket_price": "0", - "id": "10089" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "A bright yellow feather.", - "grand_exchange_price": "36", - "durability": null, + "id": "10090", "name": "Yellow feather", - "tradeable": "true", + "examine": "A bright yellow feather.", "archery_ticket_price": "0", - "id": "10090" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "5000", - "examine": "A fairly plain feather.", - "grand_exchange_price": "25", - "durability": null, + "id": "10091", "name": "Orange feather", - "tradeable": "true", + "examine": "A fairly plain feather.", "archery_ticket_price": "0", - "id": "10091" + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "6", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10093", + "name": "Tatty larupia fur", "examine": "It's a shabby-looking larupia fur.", - "grand_exchange_price": "1195", - "durability": null, - "name": "Tatty larupia fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10093" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1195", - "durability": null, + "id": "10094", "name": "Tatty larupia fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10094" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10095", + "name": "Larupia fur", "examine": "It's a perfect-looking larupia fur.", - "grand_exchange_price": "4986", - "durability": null, - "name": "Larupia fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10095" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4986", - "durability": null, + "id": "10096", "name": "Larupia fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10096" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10097", + "name": "Tatty graahk fur", "examine": "It's a shabby-looking graahk fur.", - "grand_exchange_price": "28", - "durability": null, - "name": "Tatty graahk fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10097" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "28", - "durability": null, + "id": "10098", "name": "Tatty graahk fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10098" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10099", + "name": "Graahk fur", "examine": "It's a perfect-looking graahk fur.", - "grand_exchange_price": "7574", - "durability": null, - "name": "Graahk fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10099" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "7574", - "durability": null, + "id": "10100", "name": "Graahk fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10100" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10101", + "name": "Tatty kyatt fur", "examine": "It's a shabby-looking kyatt fur.", - "grand_exchange_price": "23", - "durability": null, - "name": "Tatty kyatt fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10101" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "23", - "durability": null, + "id": "10102", "name": "Tatty kyatt fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10102" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "8000", - "ge_buy_limit": "100", + "id": "10103", + "name": "Kyatt fur", "examine": "It's a perfect-looking kyatt fur.", - "grand_exchange_price": "11100", - "durability": null, - "name": "Kyatt fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10103" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "11100", - "durability": null, + "shop_price": "8000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10104", "name": "Kyatt fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10104" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "These bone spikes are both very tough and very sharp.", - "grand_exchange_price": "154", - "durability": null, + "id": "10105", "name": "Kebbit spike", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10105" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "154", - "durability": null, - "name": "Kebbit spike", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10106" - }, - { - "ge_buy_limit": "5000", "examine": "These bone spikes are both very tough and very sharp.", - "grand_exchange_price": "1619", - "durability": null, - "name": "Long kebbit spike", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10107" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1619", - "durability": null, - "name": "Long kebbit spike", - "tradeable": "true", + "id": "10106", + "name": "Kebbit spike", "archery_ticket_price": "0", - "id": "10108" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "10107", + "name": "Long kebbit spike", + "examine": "These bone spikes are both very tough and very sharp.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10108", + "name": "Long kebbit spike", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10109", + "name": "Kebbit teeth", "examine": "A kebbit-sized set of dentures.", - "grand_exchange_price": "894", - "durability": null, - "name": "Kebbit teeth", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10109" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "894", - "durability": null, + "id": "10110", "name": "Kebbit teeth", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10110" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", + "id": "10111", + "name": "Kebbit teeth dust", "examine": "Previously a kebbit-sized set of dentures.", - "grand_exchange_price": "947", - "durability": null, - "name": "Kebbit teeth dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10111" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "947", - "durability": null, + "id": "10112", "name": "Kebbit teeth dust", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10112" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", + "id": "10113", + "name": "Kebbit claws", "examine": "More menacing when attached to the owner.", - "grand_exchange_price": "55", - "durability": null, - "name": "Kebbit claws", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10113" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "55", - "durability": null, + "tradeable": "true" + }, + { + "id": "10114", "name": "Kebbit claws", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10114" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10115", + "name": "Dark kebbit fur", "examine": "Sleek, silent, and furry.", - "grand_exchange_price": "92", - "durability": null, - "name": "Dark kebbit fur", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "10115" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "92", - "durability": null, - "name": "Dark kebbit fur", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10116" + "weight": "1" }, { - "shop_price": "12", - "ge_buy_limit": "175", + "id": "10116", + "name": "Dark kebbit fur", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10117", + "name": "Polar kebbit fur", "examine": "A thick fur for a cold climate.", - "grand_exchange_price": "3862", - "durability": null, - "name": "Polar kebbit fur", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10117" - }, - { + "durability": null, "ge_buy_limit": "175", - "grand_exchange_price": "3862", - "durability": null, + "shop_price": "12", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10118", "name": "Polar kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10118" + "durability": null, + "ge_buy_limit": "175", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10119", + "name": "Feldip weasel fur", "examine": "Not actually from a weasel, but it is, at least, furry.", - "grand_exchange_price": "3", - "durability": null, - "name": "Feldip weasel fur", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10119" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3", - "durability": null, + "id": "10120", "name": "Feldip weasel fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10120" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10121", + "name": "Common kebbit fur", "examine": "Common fur from a common kebbit.", - "grand_exchange_price": "1555", - "durability": null, - "name": "Common kebbit fur", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10121" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1555", - "durability": null, + "id": "10122", "name": "Common kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10122" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10123", + "name": "Desert devil fur", "examine": "Sandy coloured kebbit fur.", - "grand_exchange_price": "95", - "durability": null, - "name": "Desert devil fur", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "10123" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.4" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "95", - "durability": null, + "id": "10124", "name": "Desert devil fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10124" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10125", + "name": "Spotted kebbit fur", "examine": "Maybe this is why people think furry dice make you go faster.", - "grand_exchange_price": "8", - "durability": null, - "name": "Spotted kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10125" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, + "id": "10126", "name": "Spotted kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10126" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "10127", + "name": "Dashing kebbit fur", "examine": "Nature's equivalent of go-faster stripes.", - "grand_exchange_price": "69", - "durability": null, - "name": "Dashing kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10127" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "69", - "durability": null, + "id": "10128", "name": "Dashing kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10128" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{10,35}", - "ge_buy_limit": "100", - "examine": "A mighty Hunter weapon. One previous owner.", - "durability": null, - "weight": "1.3", - "attack_speed": "5", - "weapon_interface": "5", - "equipment_slot": "3", - "grand_exchange_price": "19", - "name": "Barb-tail harpoon", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10129", - "bonuses": "9,4,-4,0,0,0,0,0,0,0,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, "name": "Barb-tail harpoon", - "tradeable": "true", + "examine": "A mighty Hunter weapon. One previous owner.", "archery_ticket_price": "0", - "id": "10130" + "attack_speed": "5", + "bonuses": "9,4,-4,0,0,0,0,0,0,0,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{10,35}", + "tradeable": "true", + "weapon_interface": "5", + "weight": "1.3" }, { - "requirements": "{21,24}", - "ge_buy_limit": "100", - "examine": "Not so lucky for the rabbit.", - "grand_exchange_price": "789", - "durability": null, - "name": "Strung rabbit foot", - "tradeable": "true", + "id": "10130", + "name": "Barb-tail harpoon", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10132", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "789", - "durability": null, "name": "Strung rabbit foot", - "tradeable": "true", + "examine": "Not so lucky for the rabbit.", "archery_ticket_price": "0", - "id": "10133" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "requirements": "{21,24}", + "tradeable": "true" }, { + "id": "10133", + "name": "Strung rabbit foot", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10134", + "name": "Rabbit foot", "examine": "This could be put on a string and worn for luck.", - "grand_exchange_price": "543", - "durability": null, - "name": "Rabbit foot", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10134" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "543", - "durability": null, - "name": "Rabbit foot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10135" + "tradeable": "true" }, { - "shop_price": "48", - "ge_buy_limit": "10000", + "id": "10135", + "name": "Rabbit foot", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10136", + "name": "Rainbow fish", "examine": "Well, this would certainly add some colour to a meal.", - "grand_exchange_price": "99", - "durability": null, - "name": "Rainbow fish", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "10136" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "99", - "durability": null, - "name": "Rainbow fish", + "shop_price": "48", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10137" + "weight": "1" }, { - "ge_buy_limit": "20000", + "id": "10137", + "name": "Rainbow fish", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "10138", + "name": "Raw rainbow fish", "examine": "A colourful fish, attracted to colourful flies.", - "grand_exchange_price": "236", - "durability": null, - "name": "Raw rainbow fish", - "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", - "id": "10138" - }, - { + "durability": null, "ge_buy_limit": "20000", - "grand_exchange_price": "236", - "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "10139", "name": "Raw rainbow fish", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10139" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "durability": null, + "id": "10141", "name": "Burnt rainbow fish", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10141" + "durability": null, + "tradeable": "true" }, { - "requirements": "{0,30}-{4,30}-{6,30}", - "ge_buy_limit": "10000", - "examine": "A thick, foul-smelling, tar-like substance with a green tinge", - "grand_exchange_price": "28", - "durability": null, - "name": "Guam tar", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10142", + "name": "Guam tar", + "examine": "A thick, foul-smelling, tar-like substance with a green tinge", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", - "equipment_slot": "13" - }, - { - "requirements": "{0,50}-{4,50}-{6,50}", + "durability": null, + "equipment_slot": "13", "ge_buy_limit": "10000", - "examine": "A thick, foul-smelling, tar-like substance with an orange tinge.", - "grand_exchange_price": "12", - "durability": null, - "name": "Marrentill tar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10143", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", - "equipment_slot": "13" - }, - { - "requirements": "{0,60}-{4,60}-{6,60}", - "ge_buy_limit": "10000", - "examine": "A thick, foul-smelling, tar-like substance with a red tinge.", - "grand_exchange_price": "28", - "durability": null, - "name": "Tarromin tar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10144", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", - "equipment_slot": "13" - }, - { - "requirements": "{0,70}-{4,70}-{6,70}", - "ge_buy_limit": "10000", - "examine": "A dark, thick, foul-smelling, tar-like substance.", - "grand_exchange_price": "47", - "durability": null, - "name": "Harralander tar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10145", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49", - "equipment_slot": "13" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "5245", - "examine": "Slightly slimy, but kind of cute.", - "walk_anim": "5245", - "turn90ccw_anim": "5245", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "5245", - "equipment_slot": "3", - "grand_exchange_price": "6", - "stand_anim": "5246", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "10146", - "stand_turn_anim": "823", - "bonuses": "0,19,0,0,29,0,0,0,0,0,0,31,0,0,0", - "requirements": "{0,50}-{4,50}-{6,50}", - "shop_price": "127", - "durability": null, - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "equip_audio": "732", - "render_anim": "1277", - "attack_audios": "740,735,736,0", - "name": "Orange salamander" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "5245", - "examine": "Slightly slimy but certainly striking.", - "walk_anim": "5245", - "turn90ccw_anim": "5245", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "5245", - "equipment_slot": "3", - "grand_exchange_price": "27", - "stand_anim": "5246", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "10147", - "stand_turn_anim": "823", - "bonuses": "0,37,0,0,47,0,0,0,0,0,0,49,0,0,0", - "requirements": "{0,60}-{4,60}-{6,60}", - "shop_price": "190", - "durability": null, - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "equip_audio": "732", - "render_anim": "1277", - "attack_audios": "740,735,736,0", - "name": "Red salamander" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "5245", - "examine": "Slightly slimy and somewhat menacing.", - "walk_anim": "5245", - "turn90ccw_anim": "5245", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "5245", - "equipment_slot": "3", - "grand_exchange_price": "124", - "stand_anim": "5246", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "10148", - "stand_turn_anim": "823", - "bonuses": "0,59,0,0,69,0,0,0,0,0,0,71,0,0,0", - "requirements": "{0,70}-{4,70}-{6,70}", - "durability": null, - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "equip_audio": "732", - "render_anim": "1277", - "attack_audios": "740,735,736,0", - "name": "Black salamander" - }, - { - "ge_buy_limit": "2000", - "turn90cw_anim": "5245", - "examine": "A very slimy and generally disgusting green lizard.", - "walk_anim": "5245", - "turn90ccw_anim": "5245", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "5245", - "equipment_slot": "3", - "grand_exchange_price": "1802", - "stand_anim": "5246", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "10149", - "stand_turn_anim": "823", - "bonuses": "0,10,0,0,20,0,0,0,0,0,0,22,0,0,0", "requirements": "{0,30}-{4,30}-{6,30}", - "durability": null, - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "equip_audio": "732", - "render_anim": "1277", - "attack_audios": "740,735,736,0", - "name": "Swamp lizard" + "tradeable": "true" }, { - "shop_price": "4", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A noose on a stick.", - "walk_anim": "5250", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "14", - "turn180_anim": "5251", - "render_anim": "1279", - "equipment_slot": "3", - "grand_exchange_price": "1", - "stand_anim": "5254", - "name": "Noose wand", - "tradeable": "true", - "run_anim": "5253", + "id": "10143", + "name": "Marrentill tar", + "examine": "A thick, foul-smelling, tar-like substance with an orange tinge.", "archery_ticket_price": "0", - "id": "10150", - "stand_turn_anim": "5252" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{0,50}-{4,50}-{6,50}", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Noose wand", - "tradeable": "true", + "id": "10144", + "name": "Tarromin tar", + "examine": "A thick, foul-smelling, tar-like substance with a red tinge.", "archery_ticket_price": "0", - "id": "10151" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{0,60}-{4,60}-{6,60}", + "tradeable": "true" }, { - "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", + "id": "10145", + "name": "Harralander tar", + "examine": "A dark, thick, foul-smelling, tar-like substance.", "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,0,0,0,0,0,0,0,0,0,0,49", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{0,70}-{4,70}-{6,70}", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2496", - "durability": null, - "name": "Hunters' crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10157" - }, - { - "requirements": "{4,50}", - "ge_buy_limit": "1000", - "examine": "Bolts made from the spikes of a prickly kebbit.", - "grand_exchange_price": "28", - "durability": null, - "name": "Kebbit bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10158", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" - }, - { - "requirements": "{4,50}", - "ge_buy_limit": "1000", - "examine": "Bolts made from the spikes of a razor-backed kebbit.", - "grand_exchange_price": "352", - "durability": null, - "name": "Long kebbit bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10159", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,38", - "equipment_slot": "13" - }, - { - "durability": null, + "id": "10146", "name": "Orange salamander", - "tradeable": "true", + "examine": "Slightly slimy, but kind of cute.", "archery_ticket_price": "0", - "id": "10160" + "attack_audios": "740,735,736,0", + "attack_speed": "5", + "bonuses": "0,19,0,0,29,0,0,0,0,0,0,31,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "732", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "render_anim": "1277", + "requirements": "{0,50}-{4,50}-{6,50}", + "run_anim": "824", + "shop_price": "127", + "stand_anim": "5246", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "5245", + "turn90ccw_anim": "5245", + "turn90cw_anim": "5245", + "two_handed": "true", + "walk_anim": "5245", + "weapon_interface": "21", + "weight": "4" }, { - "durability": null, + "id": "10147", "name": "Red salamander", - "tradeable": "true", + "examine": "Slightly slimy but certainly striking.", "archery_ticket_price": "0", - "id": "10161" + "attack_audios": "740,735,736,0", + "attack_speed": "5", + "bonuses": "0,37,0,0,47,0,0,0,0,0,0,49,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "732", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "render_anim": "1277", + "requirements": "{0,60}-{4,60}-{6,60}", + "run_anim": "824", + "shop_price": "190", + "stand_anim": "5246", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "5245", + "turn90ccw_anim": "5245", + "turn90cw_anim": "5245", + "two_handed": "true", + "walk_anim": "5245", + "weapon_interface": "21", + "weight": "4" }, { - "durability": null, + "id": "10148", "name": "Black salamander", - "tradeable": "true", + "examine": "Slightly slimy and somewhat menacing.", "archery_ticket_price": "0", - "id": "10162" + "attack_audios": "740,735,736,0", + "attack_speed": "5", + "bonuses": "0,59,0,0,69,0,0,0,0,0,0,71,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "732", + "equipment_slot": "3", + "ge_buy_limit": "1000", + "render_anim": "1277", + "requirements": "{0,70}-{4,70}-{6,70}", + "run_anim": "824", + "stand_anim": "5246", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "5245", + "turn90ccw_anim": "5245", + "turn90cw_anim": "5245", + "two_handed": "true", + "walk_anim": "5245", + "weapon_interface": "21", + "weight": "4" }, { - "durability": null, + "id": "10149", "name": "Swamp lizard", + "examine": "A very slimy and generally disgusting green lizard.", + "archery_ticket_price": "0", + "attack_audios": "740,735,736,0", + "attack_speed": "5", + "bonuses": "0,10,0,0,20,0,0,0,0,0,0,22,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "732", + "equipment_slot": "3", + "ge_buy_limit": "2000", + "render_anim": "1277", + "requirements": "{0,30}-{4,30}-{6,30}", + "run_anim": "824", + "stand_anim": "5246", + "stand_turn_anim": "823", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10163" + "turn180_anim": "5245", + "turn90ccw_anim": "5245", + "turn90cw_anim": "5245", + "two_handed": "true", + "walk_anim": "5245", + "weapon_interface": "21", + "weight": "4" }, { - "examine": "Probably a bit too big for fly fishing.", + "id": "10150", + "name": "Noose wand", + "examine": "A noose on a stick.", + "archery_ticket_price": "0", + "attack_speed": "4", "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "1279", + "run_anim": "5253", + "shop_price": "4", + "stand_anim": "5254", + "stand_turn_anim": "5252", + "tradeable": "true", + "turn180_anim": "5251", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "5250", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "10151", + "name": "Noose wand", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10156", + "name": "Hunters' crossbow", + "examine": "A weapon made of bone and wood.", + "archery_ticket_price": "0", + "attack_audios": "2700,0,0,0", + "attack_speed": "4", + "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "175", + "requirements": "{4,50}", + "run_anim": "4228", + "shop_price": "1300", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "5" + }, + { + "id": "10157", + "name": "Hunters' crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10158", + "name": "Kebbit bolts", + "examine": "Bolts made from the spikes of a prickly kebbit.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,50}", + "tradeable": "true" + }, + { + "id": "10159", + "name": "Long kebbit bolts", + "examine": "Bolts made from the spikes of a razor-backed kebbit.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,38", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "1000", + "requirements": "{4,50}", + "tradeable": "true" + }, + { + "id": "10160", + "name": "Orange salamander", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10161", + "name": "Red salamander", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10162", + "name": "Black salamander", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10163", + "name": "Swamp lizard", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10167", "name": "Eagle feather", + "examine": "Probably a bit too big for fly fishing.", "archery_ticket_price": "0", - "id": "10167" + "durability": null }, { - "examine": "A cape made from giant eagle feathers.", - "durability": null, - "name": "Eagle cape", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10171", - "equipment_slot": "1" + "name": "Eagle cape", + "examine": "A cape made from giant eagle feathers.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "1", + "weight": "0.2" }, { - "examine": "A cheaply made fake beak.", - "durability": null, - "name": "Fake beak", - "archery_ticket_price": "0", "id": "10172", + "name": "Fake beak", + "examine": "A cheaply made fake beak.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "0" }, { - "examine": "The title reads 'William Oddity's Guide to the Avian.", - "durability": null, + "id": "10173", "name": "Bird book", + "examine": "The title reads 'William Oddity's Guide to the Avian.", "archery_ticket_price": "0", - "id": "10173" + "durability": null }, { - "destroy_message": "You can obtain another metal feather from the camp", - "examine": "A small feather made out of some sort of metal. The detail is exquisite.", - "durability": null, + "id": "10174", "name": "Metal feather", - "tradeable": "false", - "destroy": "true", + "examine": "A small feather made out of some sort of metal. The detail is exquisite.", "archery_ticket_price": "0", - "id": "10174" + "destroy": "true", + "destroy_message": "You can obtain another metal feather from the camp", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another by inspecting the opening", - "examine": "An intricate feather crafted from a silver-coloured metal of some sort.", - "durability": null, + "id": "10176", "name": "Silver feather", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "An intricate feather crafted from a silver-coloured metal of some sort.", "archery_ticket_price": "0", - "id": "10176" + "destroy": "true", + "destroy_message": "You can get another by inspecting the opening", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You can get another by taking from pedestal.", - "examine": "An intricate feather crafted from a bronze-coloured metal of some sort.", - "durability": null, + "id": "10177", "name": "Bronze feather", - "tradeable": "false", + "examine": "An intricate feather crafted from a bronze-coloured metal of some sort.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "10177" + "destroy_message": "You can get another by taking from pedestal.", + "durability": null, + "tradeable": "false" }, { - "examine": "\"It looks like bird seed, but it's not quite right somehow.", - "durability": null, + "id": "10178", "name": "Odd bird seed", + "examine": "\"It looks like bird seed, but it's not quite right somehow.", "archery_ticket_price": "0", - "id": "10178" + "durability": null }, { - "examine": "There's a feather glued to the front. \"Property of Arthur Artimus\".", - "durability": null, + "id": "10179", "name": "Feathered journal", + "examine": "There's a feather glued to the front. \"Property of Arthur Artimus\".", "archery_ticket_price": "0", - "id": "10179" + "durability": null }, { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, + "id": "10180", "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10180" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "10181" - }, - { - "shop_price": "5000", "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", "archery_ticket_price": "0", - "id": "10182" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "10183" - }, - { "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10184" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10181", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10185" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10182", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10186" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10183", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10187" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10184", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10188" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10185", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10189" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10186", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10190" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10187", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10191" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10188", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10192" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10189", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10193" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10190", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10194" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10191", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10195" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10192", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10196" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10193", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10197" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10194", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10198" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10195", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10199" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10196", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10200" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10197", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10201" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10198", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10202" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10199", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10203" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10200", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10204" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10201", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10205" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10202", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10206" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10203", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10207" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10204", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10208" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10205", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10209" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10206", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10210" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10207", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10211" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10208", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10212" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10209", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10213" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10210", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10214" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10211", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10215" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10212", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10216" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10213", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10217" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10214", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10218" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10215", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10219" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10216", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10220" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10217", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10221" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10218", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10222" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10219", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10223" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10220", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10224" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10221", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10225" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10222", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10226" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10223", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10227" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10224", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10228" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10225", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10229" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10226", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10230" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10227", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10231" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10228", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10232" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10229", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10233" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10230", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10234" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10231", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10235" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10232", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10236" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10233", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10237" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10234", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10238" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10235", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10239" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10236", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10240" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10237", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10241" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10238", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10242" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10239", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10243" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10240", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10244" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10241", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10245" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10242", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10246" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10243", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10247" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10244", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10248" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10245", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10249" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10246", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10250" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10247", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10251" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10248", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10252" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10249", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10253" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10250", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10254" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10251", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10255" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10252", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10256" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10253", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10257" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10254", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10258" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10255", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10259" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10256", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10260" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10257", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10261" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10258", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10262" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10259", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10263" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10260", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10264" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10261", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10265" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10262", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10266" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10263", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10267" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10264", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10268" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10265", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10269" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10266", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10270" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10267", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10271" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10268", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10272" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10269", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10273" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10270", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10274" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10271", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10275" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10272", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10276" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10273", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10277" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10274", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10278" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10275", "name": "Casket", - "tradeable": "true", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10279" + "durability": null, + "tradeable": "true", + "weight": "5" }, { - "ge_buy_limit": "2", - "turn90cw_anim": "1207", - "examine": "A powerful bow made from willow.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "136", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "10276", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "10277", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "10278", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "10279", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { "id": "10280", - "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,22,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,20}", - "durability": null, - "weight": "1.8", - "weapon_interface": "16", - "render_anim": "28", - "lendable": "true", - "attack_audios": "2700,0,0,0", - "name": "Willow comp bow" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "136", - "durability": null, "name": "Willow comp bow", - "tradeable": "true", + "examine": "A powerful bow made from willow.", "archery_ticket_price": "0", - "id": "10281" + "attack_audios": "2700,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,22,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "lendable": "true", + "render_anim": "28", + "requirements": "{4,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "16", + "weight": "1.8" }, { - "ge_buy_limit": "2", - "turn90cw_anim": "1207", - "examine": "A powerful bow made from yew wood.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "876", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "10281", + "name": "Willow comp bow", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10282", - "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,49,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,40}", - "durability": null, - "weight": "1.8", - "weapon_interface": "16", - "render_anim": "28", - "lendable": "true", - "attack_audios": "2700,0,0,0", - "name": "Yew comp bow" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "876", - "durability": null, "name": "Yew comp bow", - "tradeable": "true", + "examine": "A powerful bow made from yew wood.", "archery_ticket_price": "0", - "id": "10283" - }, - { - "ge_buy_limit": "2", - "turn90cw_anim": "1207", - "examine": "A powerful bow made from magic wood.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "1268", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "10284", - "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,71,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,50}", - "durability": null, - "weight": "1.3", - "weapon_interface": "16", - "render_anim": "28", - "lendable": "true", "attack_audios": "2700,0,0,0", - "name": "Magic comp bow" + "attack_speed": "5", + "bonuses": "0,0,0,0,49,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "lendable": "true", + "render_anim": "28", + "requirements": "{4,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "16", + "weight": "1.8" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "1268", + "id": "10283", + "name": "Yew comp bow", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10284", "name": "Magic comp bow", - "tradeable": "true", + "examine": "A powerful bow made from magic wood.", "archery_ticket_price": "0", - "id": "10285" + "attack_audios": "2700,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,71,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "requirements": "{4,50}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "16", + "weight": "1.3" }, { - "remove_head": "true", - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "23600", - "name": "Rune helm (h1)", - "tradeable": "true", + "id": "10285", + "name": "Magic comp bow", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10286", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "23600", - "durability": null, "name": "Rune helm (h1)", - "tradeable": "true", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "10287" - }, - { + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "weight": "3", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "20800", - "name": "Rune helm (h2)", - "tradeable": "true", + "tradeable": "true" + }, + { + "id": "10287", + "name": "Rune helm (h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10288", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "20800", - "durability": null, "name": "Rune helm (h2)", - "tradeable": "true", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "10289" - }, - { + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "weight": "4", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "20800", - "name": "Rune helm (h3)", "tradeable": "true", + "weight": "3" + }, + { + "id": "10289", + "name": "Rune helm (h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10290", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "20800", - "durability": null, "name": "Rune helm (h3)", - "tradeable": "true", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "10291" - }, - { + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "21100", - "name": "Rune helm (h4)", "tradeable": "true", + "weight": "4" + }, + { + "id": "10291", + "name": "Rune helm (h3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10292", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "21100", - "durability": null, "name": "Rune helm (h4)", - "tradeable": "true", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "10293" - }, - { + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "weight": "2", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "24900", - "name": "Rune helm (h5)", "tradeable": "true", + "weight": "2" + }, + { + "id": "10293", + "name": "Rune helm (h4)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10294", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "24900", - "durability": null, "name": "Rune helm (h5)", - "tradeable": "true", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", - "id": "10295" + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" }, { - "remove_head": "true", - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "31500", - "name": "Adamant helm (h1)", - "tradeable": "true", + "id": "10295", + "name": "Rune helm (h5)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10296", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "31500", - "durability": null, "name": "Adamant helm (h1)", - "tradeable": "true", + "examine": "An adamant helmet with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "10297" - }, - { + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "10100", - "name": "Adamant helm (h2)", "tradeable": "true", + "weight": "3" + }, + { + "id": "10297", + "name": "Adamant helm (h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10298", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "10100", - "durability": null, "name": "Adamant helm (h2)", - "tradeable": "true", + "examine": "An adamant helmet with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "10299" - }, - { + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "8797", - "name": "Adamant helm (h3)", "tradeable": "true", + "weight": "3" + }, + { + "id": "10299", + "name": "Adamant helm (h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10300", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "8797", - "durability": null, "name": "Adamant helm (h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10301" - }, - { - "remove_head": "true", - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "An Adamant helmet with a heraldic design.", - "durability": null, - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "10900", - "name": "Adamant helm (h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10302", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "10900", - "durability": null, - "name": "Adamant helm (h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10303" - }, - { - "remove_head": "true", - "requirements": "{1,30}", - "ge_buy_limit": "2", "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "weight": "3", "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "17100", - "name": "Adamant helm (h5)", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10301", + "name": "Adamant helm (h3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10302", + "name": "Adamant helm (h4)", + "examine": "An Adamant helmet with a heraldic design.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10303", + "name": "Adamant helm (h4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10304", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "17100", - "durability": null, "name": "Adamant helm (h5)", - "tradeable": "true", + "examine": "An adamant helmet with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", - "id": "10305" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" }, { - "remove_head": "true", - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "20400", - "name": "Black helm (h1)", - "tradeable": "true", + "id": "10305", + "name": "Adamant helm (h5)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10306", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "20400", - "durability": null, "name": "Black helm (h1)", - "tradeable": "true", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", - "id": "10307" - }, - { + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "7033", - "name": "Black helm (h2)", "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10307", + "name": "Black helm (h1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10308", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7033", - "durability": null, "name": "Black helm (h2)", - "tradeable": "true", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", - "id": "10309" - }, - { + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "weight": "1.8", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7574", - "name": "Black helm (h3)", "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10309", + "name": "Black helm (h2)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10310", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7574", - "durability": null, "name": "Black helm (h3)", - "tradeable": "true", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", - "id": "10311" - }, - { + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "10100", - "name": "Black helm (h4)", "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10311", + "name": "Black helm (h3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10312", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "10100", - "durability": null, "name": "Black helm (h4)", - "tradeable": "true", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", - "id": "10313" - }, - { + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "10400", - "name": "Black helm (h5)", "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10313", + "name": "Black helm (h4)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10314", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "10400", - "durability": null, "name": "Black helm (h5)", - "tradeable": "true", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", - "id": "10315" + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,4,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" }, { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "See table", - "grand_exchange_price": "8196", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "weight": "2", + "id": "10315", + "name": "Black helm (h5)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10316", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "8196", - "durability": null, "name": "Bob shirt", - "tradeable": "true", + "examine": "Bob says: 'Never give your password out to anyone.'", "archery_ticket_price": "0", - "id": "10317" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "See table", - "grand_exchange_price": "14700", - "durability": null, - "name": "Bob shirt", "tradeable": "true", - "weight": "2", + "weight": "2" + }, + { + "id": "10317", + "name": "Bob shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10318", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "14700", - "durability": null, "name": "Bob shirt", - "tradeable": "true", + "examine": "Bob says: 'Always check the second trade screen.'", "archery_ticket_price": "0", - "id": "10319" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "See table", - "grand_exchange_price": "8403", - "durability": null, - "name": "Bob shirt", "tradeable": "true", - "weight": "2", + "weight": "2" + }, + { + "id": "10319", + "name": "Bob shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10320", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "8403", - "durability": null, "name": "Bob shirt", - "tradeable": "true", + "examine": "Bob says: 'Never trade in the wilderness!'", "archery_ticket_price": "0", - "id": "10321" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "See table", - "grand_exchange_price": "9230", - "durability": null, - "name": "Bob shirt", "tradeable": "true", - "weight": "2", + "weight": "2" + }, + { + "id": "10321", + "name": "Bob shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10322", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "9230", - "durability": null, "name": "Bob shirt", - "tradeable": "true", + "examine": "Bob says: 'A bank pin will keep your items secure.'", "archery_ticket_price": "0", - "id": "10323" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "See table", - "grand_exchange_price": "8197", - "durability": null, - "name": "Bob shirt", "tradeable": "true", - "weight": "2", + "weight": "2" + }, + { + "id": "10323", + "name": "Bob shirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10324", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "8197", - "durability": null, "name": "Bob shirt", - "tradeable": "true", + "examine": "Bob says: 'Keep your computer keylogger free and virus scanned.'", "archery_ticket_price": "0", - "id": "10325" + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "112", + "id": "10325", + "name": "Bob shirt", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10326", "name": "Purple firelighter", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10326" - }, - { - "ge_buy_limit": "100", "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "167", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10327", "name": "White firelighter", - "tradeable": "true", + "examine": "Makes firelighting a lot easier.", "archery_ticket_price": "0", - "id": "10327" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "A number of chemical covered wooden logs.", - "durability": null, + "id": "10328", "name": "White logs", - "archery_ticket_price": "0", - "id": "10328" - }, - { "examine": "A number of chemical covered wooden logs.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10329", "name": "Purple logs", + "examine": "A number of chemical covered wooden logs.", "archery_ticket_price": "0", - "id": "10329" + "durability": null }, { - "requirements": "{1,45}-{4,65}", - "ge_buy_limit": "2", - "examine": "Ancient range protection crafted from white dragonhide.", - "durability": null, - "weight": "4", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "33900000", - "name": "3rd age range top", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10330", - "bonuses": "0,0,0,-15,30,55,47,60,60,55,52,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "33900000", - "durability": null, "name": "3rd age range top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10331" - }, - { - "requirements": "{1,45}-{4,65}", - "ge_buy_limit": "2", - "examine": "Fabulously ancient range protection crafted from white dragonhide.", - "durability": null, - "weight": "6", - "absorb": "0,4,2", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "20800000", - "name": "3rd age range legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10332", - "bonuses": "0,0,0,-10,17,31,25,33,30,31,25,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "20800000", - "durability": null, - "name": "3rd age range legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10333" - }, - { - "remove_head": "true", - "requirements": "{1,45}-{4,65}", - "ge_buy_limit": "5000", "examine": "Ancient range protection crafted from white dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "6100000", - "name": "3rd age range coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10334", - "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "6100000", - "durability": null, - "name": "3rd age range coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10335" - }, - { - "requirements": "{1,45}-{4,65}", - "ge_buy_limit": "2", - "examine": "Fabulously ancient range protection crafted from white dragonhide.", - "durability": null, - "weight": "1", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "7100000", - "name": "3rd age vambraces", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10336", - "bonuses": "0,0,0,-10,11,6,5,7,9,0,5,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7100000", - "durability": null, - "name": "3rd age vambraces", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10337" - }, - { - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "weight": "2.5", - "absorb": "4,2,0", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "66500000", - "name": "3rd age robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10338", - "bonuses": "0,0,0,24,0,0,0,0,24,0,24,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "66500000", - "durability": null, - "name": "3rd age robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10339" - }, - { - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "weight": "1", - "absorb": "4,2,0", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "31500000", - "name": "3rd age robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10340", - "bonuses": "0,0,0,19,0,0,0,0,19,0,20,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "31500000", - "durability": null, - "name": "3rd age robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10341" - }, - { - "remove_head": "true", - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "absorb": "2,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "21000000", - "name": "3rd age mage hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10342", - "bonuses": "0,0,0,8,0,0,0,0,8,0,12,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "21000000", - "durability": null, - "name": "3rd age mage hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10343" - }, - { - "lendable": "true", - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "grand_exchange_price": "20100000", - "durability": null, - "name": "3rd age amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10344", - "bonuses": "0,0,0,15,0,0,0,0,10,0,0,0,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "20100000", - "durability": null, - "name": "3rd age amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10345" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "2", - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "weight": "2", - "absorb": "3,0,6", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "92800000", - "name": "3rd age platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10346", - "bonuses": "0,0,0,-25,-2,78,76,83,-5,75,25,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "92800000", - "durability": null, - "name": "3rd age platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10347" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "2", - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "weight": "9", - "absorb": "4,0,9", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "86200000", - "name": "3rd age platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10348", - "bonuses": "0,0,0,-20,0,96,108,113,-4,97,52,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "86200000", - "durability": null, - "name": "3rd age platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10349" - }, - { - "remove_head": "true", - "requirements": "{1,65}", - "ge_buy_limit": "2", - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "weight": "1", - "absorb": "2,0,4", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "41600000", - "name": "3rd age full helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10350", - "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "41600000", - "durability": null, - "name": "3rd age full helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10351" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "2", - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "weight": "2", - "absorb": "5,0,11", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "65100000", - "name": "3rd age kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10352", - "bonuses": "0,0,0,-10,-4,63,65,61,-3,63,60,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "65100000", - "durability": null, - "name": "3rd age kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10353" - }, - { - "ge_buy_limit": "100", - "examine": "A dragonstone amulet with 4 magic charges.", - "grand_exchange_price": "233100", - "durability": null, - "name": "Amulet of glory(t4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10354", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "233100", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(t4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10355" - }, - { - "examine": "A dragonstone amulet with 3 magic charges.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(t3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10356", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(t3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10357" - }, - { - "examine": "A dragonstone amulet with 2 magic charges.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(t2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10358", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(t2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10359" - }, - { - "examine": "A dragonstone amulet with 1 magic charge.", - "grand_exchange_price": "1", - "durability": null, - "name": "Amulet of glory(t1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10360", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(t1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10361" - }, - { - "ge_buy_limit": "100", - "examine": "A very powerful dragonstone amulet with a nice trim.", - "grand_exchange_price": "223000", - "durability": null, - "name": "Amulet of glory(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10362", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "223000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Amulet of glory(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10363" - }, - { - "ge_buy_limit": "2", - "examine": "An enchanted ruby amulet with a nice trim.", - "grand_exchange_price": "819400", - "durability": null, - "name": "Strength amulet(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10364", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,10,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "819400", - "durability": null, - "name": "Strength amulet(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10365" - }, - { - "ge_buy_limit": "5000", - "examine": "An enchanted sapphire amulet of magic.", - "grand_exchange_price": "128700", - "durability": null, - "name": "Amulet of magic(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10366", - "bonuses": "0,0,0,10,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "128700", - "durability": null, - "name": "Amulet of magic(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10367" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Zamorak blessed dragonhide vambraces.", - "grand_exchange_price": "116200", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "116200", - "durability": null, - "name": "Zamorak bracers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10369" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Zamorak blessed dragonhide body armour.", - "durability": null, - "weight": "6", "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,55,47,60,60,55,52,0,0,0,0", + "durability": null, "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "155200", - "name": "Zamorak d'hide", + "requirements": "{1,45}-{4,65}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10370", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" + "weight": "4" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "155200", - "durability": null, - "name": "Zamorak d'hide", - "tradeable": "true", + "id": "10331", + "name": "3rd age range top", "archery_ticket_price": "0", - "id": "10371" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{4,70}", - "ge_buy_limit": "2", - "examine": "Zamorak blessed dragonhide chaps.", - "durability": null, - "weight": "5", + "id": "10332", + "name": "3rd age range legs", + "examine": "Fabulously ancient range protection crafted from white dragonhide.", "absorb": "0,4,2", - "equipment_slot": "7", - "grand_exchange_price": "35100", - "name": "Zamorak chaps", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10372", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "35100", + "bonuses": "0,0,0,-10,17,31,25,33,30,31,25,0,0,0,0", "durability": null, - "name": "Zamorak chaps", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,45}-{4,65}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10373" + "weight": "6" }, { + "id": "10333", + "name": "3rd age range legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10334", + "name": "3rd age range coif", + "examine": "Ancient range protection crafted from white dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "5000", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,45}-{4,65}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10335", + "name": "3rd age range coif", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10336", + "name": "3rd age vambraces", + "examine": "Fabulously ancient range protection crafted from white dragonhide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,6,5,7,9,0,5,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,45}-{4,65}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10337", + "name": "3rd age vambraces", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10338", + "name": "3rd age robe top", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,24,0,0,0,0,24,0,24,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,30}-{6,65}", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "10339", + "name": "3rd age robe top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10340", + "name": "3rd age robe", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,19,0,0,0,0,19,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,30}-{6,65}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10341", + "name": "3rd age robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10342", + "name": "3rd age mage hat", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,8,0,0,0,0,8,0,12,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,30}-{6,65}", + "tradeable": "true" + }, + { + "id": "10343", + "name": "3rd age mage hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10344", + "name": "3rd age amulet", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,15,0,0,0,0,10,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,30}-{6,65}", + "tradeable": "true" + }, + { + "id": "10345", + "name": "3rd age amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10346", + "name": "3rd age platelegs", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-25,-2,78,76,83,-5,75,25,0,0,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10347", + "name": "3rd age platelegs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10348", + "name": "3rd age platebody", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-20,0,96,108,113,-4,97,52,0,0,0,0", + "durability": null, + "equip_audio": "2234", + "equipment_slot": "4", + "ge_buy_limit": "2", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "9" + }, + { + "id": "10349", + "name": "3rd age platebody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10350", + "name": "3rd age full helmet", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10351", + "name": "3rd age full helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10352", + "name": "3rd age kiteshield", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-4,63,65,61,-3,63,60,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10353", + "name": "3rd age kiteshield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10354", + "name": "Amulet of glory(t4)", + "examine": "A dragonstone amulet with 4 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10355", + "name": "Amulet of glory(t4)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10356", + "name": "Amulet of glory(t3)", + "examine": "A dragonstone amulet with 3 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "10357", + "name": "Amulet of glory(t3)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10358", + "name": "Amulet of glory(t2)", + "examine": "A dragonstone amulet with 2 magic charges.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "10359", + "name": "Amulet of glory(t2)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10360", + "name": "Amulet of glory(t1)", + "examine": "A dragonstone amulet with 1 magic charge.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "10361", + "name": "Amulet of glory(t1)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10362", + "name": "Amulet of glory(t)", + "examine": "A very powerful dragonstone amulet with a nice trim.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10363", + "name": "Amulet of glory(t)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10364", + "name": "Strength amulet(t)", + "examine": "An enchanted ruby amulet with a nice trim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,10,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10365", + "name": "Strength amulet(t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10366", + "name": "Amulet of magic(t)", + "examine": "An enchanted sapphire amulet of magic.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,10,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10367", + "name": "Amulet of magic(t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10368", + "name": "Zamorak bracers", + "examine": "Zamorak blessed dragonhide vambraces.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10369", + "name": "Zamorak bracers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10370", + "name": "Zamorak d'hide", + "examine": "Zamorak blessed dragonhide body armour.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "10371", + "name": "Zamorak d'hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10372", + "name": "Zamorak chaps", + "examine": "Zamorak blessed dragonhide chaps.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "10373", + "name": "Zamorak chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10374", + "name": "Zamorak coif", + "examine": "Zamorak blessed dragonhide coif.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Zamorak blessed dragonhide coif.", - "durability": null, - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "35900", - "name": "Zamorak coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10374", - "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0" + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "35900", - "durability": null, + "id": "10375", "name": "Zamorak coif", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10375" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide vambraces.", - "grand_exchange_price": "3181", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3181", - "durability": null, "name": "Guthix bracers", - "tradeable": "true", + "examine": "Guthix blessed dragonhide vambraces.", "archery_ticket_price": "0", - "id": "10377" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "1" }, { + "id": "10377", + "name": "Guthix bracers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10378", + "name": "Guthix dragonhide", + "examine": "Guthix blessed dragonhide body armour.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", "requirements": "{1,40}-{4,70}", "shop_price": "13", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide body armour.", - "durability": null, - "weight": "6", - "absorb": "0,6,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "39800", - "name": "Guthix dragonhide", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10378", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" + "weight": "6" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "39800", - "durability": null, + "id": "10379", "name": "Guthix dragonhide", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10379" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{4,70}", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide chaps.", - "durability": null, - "weight": "5", - "absorb": "0,4,2", - "equipment_slot": "7", - "grand_exchange_price": "7346", - "name": "Guthix chaps", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10380", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7346", - "durability": null, "name": "Guthix chaps", - "tradeable": "true", + "examine": "Guthix blessed dragonhide chaps.", + "absorb": "0,4,2", "archery_ticket_price": "0", - "id": "10381" + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "5" }, { + "id": "10381", + "name": "Guthix chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10382", + "name": "Guthix coif", + "examine": "Guthix blessed dragonhide coif.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide coif.", - "durability": null, - "weight": "0.85", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "27500", - "name": "Guthix coif", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10382", - "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0" + "weight": "0.85" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "27500", - "durability": null, + "id": "10383", "name": "Guthix coif", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10383" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,70}-{4,40}", - "ge_buy_limit": "2", - "examine": "Saradomin blessed dragonhide vambraces.", - "grand_exchange_price": "70800", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "70800", - "durability": null, "name": "Saradomin bracers", - "tradeable": "true", + "examine": "Saradomin blessed dragonhide vambraces.", "archery_ticket_price": "0", - "id": "10385" + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "9", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "1" }, { + "id": "10385", + "name": "Saradomin bracers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10386", + "name": "Saradomin d'hide", + "examine": "Saradomin blessed dragonhide body armour.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", "requirements": "{1,40}-{4,70}", "shop_price": "13000", - "ge_buy_limit": "2", - "examine": "Saradomin blessed dragonhide body armour.", - "durability": null, - "destroy": "true", - "weight": "6", - "absorb": "0,6,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "164100", - "name": "Saradomin d'hide", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10386", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" + "weight": "6" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "164100", - "durability": null, + "id": "10387", "name": "Saradomin d'hide", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10387" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{4,70}", - "ge_buy_limit": "2", - "examine": "Saradomin blessed dragonhide chaps.", - "grand_exchange_price": "36600", - "durability": null, - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "36600", - "durability": null, "name": "Saradomin chaps", - "tradeable": "true", + "examine": "Saradomin blessed dragonhide chaps.", + "absorb": "0,4,2", "archery_ticket_price": "0", - "id": "10389" + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", + "durability": null, + "equip_audio": "2241", + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true" }, { + "id": "10389", + "name": "Saradomin chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10390", + "name": "Saradomin coif", + "examine": "Saradomin blessed dragonhide coif.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "2", "remove_head": "true", "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Saradomin blessed dragonhide coif.", - "durability": null, - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "28000", - "name": "Saradomin coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10390", - "bonuses": "0,0,0,-1,7,4,7,10,4,8,8,0,0,0,0" + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "28000", - "durability": null, + "id": "10391", "name": "Saradomin coif", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10391" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "A big 'do about nothing.", - "grand_exchange_price": "45400", - "durability": null, - "name": "A powdered wig", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10392", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "45400", - "durability": null, "name": "A powdered wig", - "tradeable": "true", + "examine": "A big 'do about nothing.", "archery_ticket_price": "0", - "id": "10393" - }, - { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "These'll help me stay alive.", - "grand_exchange_price": "448900", "durability": null, - "name": "Flared trousers", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10394", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "448900", - "durability": null, - "name": "Flared trousers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10395" - }, - { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Alas, someone has slashed my pantaloons.", - "grand_exchange_price": "9863", - "durability": null, - "name": "Pantaloons", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10396", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "9863", - "durability": null, - "name": "Pantaloons", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10397" - }, - { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "A cap for wearing whil...zzzzzzzzz", - "grand_exchange_price": "276400", - "durability": null, - "name": "Sleeping cap", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10398", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "276400", - "durability": null, - "name": "Sleeping cap", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10399" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant mans' black shirt.", - "grand_exchange_price": "921500", - "durability": null, - "name": "Black ele' shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10400", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "921500", - "durability": null, - "name": "Black ele' shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10401" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant black pair of pantaloons.", - "grand_exchange_price": "270700", - "durability": null, - "name": "Black ele' legs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10402", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "270700", - "durability": null, - "name": "Black ele' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10403" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant men's red shirt.", - "grand_exchange_price": "118600", - "durability": null, - "name": "Red ele' shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10404", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "118600", - "durability": null, - "name": "Red ele' shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10405" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant red pair of pantaloons.", - "grand_exchange_price": "39400", - "durability": null, - "name": "Red ele' legs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10406", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "39400", - "durability": null, - "name": "Red ele' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10407" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant mans' blue shirt.", - "grand_exchange_price": "134700", - "durability": null, - "name": "Blue ele' shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10408", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "134700", - "durability": null, - "name": "Blue ele' shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10409" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant pair of men's blue pantaloons", - "grand_exchange_price": "80700", - "durability": null, - "name": "Blue ele' legs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10410", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "80700", - "durability": null, - "name": "Blue ele' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10411" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant men's green shirt.", - "grand_exchange_price": "162600", - "durability": null, - "name": "Green ele' shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10412", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "162600", - "durability": null, - "name": "Green ele' shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10413" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant green pair of men's pantaloons.", - "grand_exchange_price": "36100", - "durability": null, - "name": "Green ele' legs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10414", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "36100", - "durability": null, - "name": "Green ele' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10415" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant men's purple shirt.", - "grand_exchange_price": "116200", - "durability": null, - "name": "Purple ele' shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10416", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "116200", - "durability": null, - "name": "Purple ele' shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10417" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant purple pair of pantaloons.", - "grand_exchange_price": "73400", - "durability": null, - "name": "Purple ele' legs", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10418", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "73400", - "durability": null, - "name": "Purple ele' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10419" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made ladies' elegant white blouse.", - "grand_exchange_price": "571500", - "durability": null, - "name": "White ele' blouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10420", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "571500", - "durability": null, - "name": "White ele' blouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10421" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant white skirt.", - "grand_exchange_price": "56600", - "durability": null, - "name": "White ele' skirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10422", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "56600", - "durability": null, - "name": "White ele' skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10423" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant ladies' red blouse.", - "grand_exchange_price": "15000", - "durability": null, - "name": "Red ele' blouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10424", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "15000", - "durability": null, - "name": "Red ele' blouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10425" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant Red skirt.", - "grand_exchange_price": "7132", - "durability": null, - "name": "Red ele' skirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10426", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "7132", - "durability": null, - "name": "Red ele' skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10427" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made ladies' elegant blue blouse.", - "grand_exchange_price": "22200", - "durability": null, - "name": "Blue ele' blouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10428", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "22200", - "durability": null, - "name": "Blue ele' blouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10429" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant blue skirt.", - "grand_exchange_price": "12200", - "durability": null, - "name": "Blue ele' skirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10430", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "12200", - "durability": null, - "name": "Blue ele' skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10431" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made ladies' elegant green blouse.", - "grand_exchange_price": "12300", - "durability": null, - "name": "Green ele' blouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10432", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "12300", - "durability": null, - "name": "Green ele' blouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10433" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant green skirt.", - "grand_exchange_price": "10300", - "durability": null, - "name": "Green ele' skirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10434", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "10300", - "durability": null, - "name": "Green ele' skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10435" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "A well made elegant ladies' purple blouse.", - "grand_exchange_price": "22800", - "durability": null, - "name": "Purple ele' blouse", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10436", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "22800", - "durability": null, - "name": "Purple ele' blouse", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10437" - }, - { - "ge_buy_limit": "2", - "examine": "A rather elegant purple skirt.", - "grand_exchange_price": "16700", - "durability": null, - "name": "Purple ele' skirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10438", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "16700", - "durability": null, - "name": "Purple ele' skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "equipment_slot": "3", - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "8611", - "durability": null, - "name": "Saradomin crozier", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "equipment_slot": "3", - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2925", - "durability": null, - "name": "Guthix crozier", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "equipment_slot": "3", - "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" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2794", - "durability": null, - "name": "Zamorak crozier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10445" - }, - { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Saradomin cloak.", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Saradomin cloak", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10446", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Saradomin cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10447" - }, - { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Guthix cloak.", - "grand_exchange_price": "391000", - "durability": null, - "name": "Guthix cloak", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10448", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "391000", - "durability": null, - "name": "Guthix cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10449" - }, - { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Zamorak cloak.", - "grand_exchange_price": "1000000", - "durability": null, - "name": "Zamorak cloak", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10450", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", - "equipment_slot": "1" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1000000", - "durability": null, - "name": "Zamorak cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10451" - }, - { - "remove_head": "true", - "requirements": "{5,40}-{6,40}", - "ge_buy_limit": "2", - "examine": "A Saradomin mitre.", - "grand_exchange_price": "299200", - "durability": null, - "name": "Saradomin mitre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10452", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "299200", - "durability": null, - "name": "Saradomin mitre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10453" - }, - { - "remove_head": "true", - "requirements": "{5,40}-{6,40}", - "ge_buy_limit": "2", - "examine": "A Guthix mitre.", - "grand_exchange_price": "130300", - "durability": null, - "name": "Guthix mitre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10454", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "130300", - "durability": null, - "name": "Guthix mitre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10455" - }, - { - "remove_head": "true", - "requirements": "{5,40}-{6,40}", - "ge_buy_limit": "2", - "examine": "A Zamorak mitre.", - "durability": null, - "weight": "0.3", "equipment_slot": "0", - "grand_exchange_price": "346500", - "name": "Zamorak mitre", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", "tradeable": "true", + "weight": "2" + }, + { + "id": "10393", + "name": "A powdered wig", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10394", + "name": "Flared trousers", + "examine": "These'll help me stay alive.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10395", + "name": "Flared trousers", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10396", + "name": "Pantaloons", + "examine": "Alas, someone has slashed my pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10397", + "name": "Pantaloons", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10398", + "name": "Sleeping cap", + "examine": "A cap for wearing whil...zzzzzzzzz", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10399", + "name": "Sleeping cap", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10400", + "name": "Black ele' shirt", + "examine": "A well made elegant mans' black shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10401", + "name": "Black ele' shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10402", + "name": "Black ele' legs", + "examine": "A rather elegant black pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10403", + "name": "Black ele' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10404", + "name": "Red ele' shirt", + "examine": "A well made elegant men's red shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10405", + "name": "Red ele' shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10406", + "name": "Red ele' legs", + "examine": "A rather elegant red pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10407", + "name": "Red ele' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10408", + "name": "Blue ele' shirt", + "examine": "A well made elegant mans' blue shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10409", + "name": "Blue ele' shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10410", + "name": "Blue ele' legs", + "examine": "A rather elegant pair of men's blue pantaloons", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10411", + "name": "Blue ele' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10412", + "name": "Green ele' shirt", + "examine": "A well made elegant men's green shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10413", + "name": "Green ele' shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10414", + "name": "Green ele' legs", + "examine": "A rather elegant green pair of men's pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10415", + "name": "Green ele' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10416", + "name": "Purple ele' shirt", + "examine": "A well made elegant men's purple shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10417", + "name": "Purple ele' shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10418", + "name": "Purple ele' legs", + "examine": "A rather elegant purple pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10419", + "name": "Purple ele' legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10420", + "name": "White ele' blouse", + "examine": "A well made ladies' elegant white blouse.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10421", + "name": "White ele' blouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10422", + "name": "White ele' skirt", + "examine": "A rather elegant white skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10423", + "name": "White ele' skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10424", + "name": "Red ele' blouse", + "examine": "A well made elegant ladies' red blouse.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10425", + "name": "Red ele' blouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10426", + "name": "Red ele' skirt", + "examine": "A rather elegant Red skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10427", + "name": "Red ele' skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10428", + "name": "Blue ele' blouse", + "examine": "A well made ladies' elegant blue blouse.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10429", + "name": "Blue ele' blouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10430", + "name": "Blue ele' skirt", + "examine": "A rather elegant blue skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10431", + "name": "Blue ele' skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10432", + "name": "Green ele' blouse", + "examine": "A well made ladies' elegant green blouse.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10433", + "name": "Green ele' blouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10434", + "name": "Green ele' skirt", + "examine": "A rather elegant green skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10435", + "name": "Green ele' skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10436", + "name": "Purple ele' blouse", + "examine": "A well made elegant ladies' purple blouse.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10437", + "name": "Purple ele' blouse", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10438", + "name": "Purple ele' skirt", + "examine": "A rather elegant purple skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10439", + "name": "Purple ele' skirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10440", + "name": "Saradomin crozier", + "examine": "A Saradomin crozier.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "render_anim": "28", + "requirements": "{5,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "10441", + "name": "Saradomin crozier", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10442", + "name": "Guthix crozier", + "examine": "A Guthix crozier.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "render_anim": "28", + "requirements": "{5,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "10443", + "name": "Guthix crozier", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10444", + "name": "Zamorak crozier", + "examine": "A Zamorak crozier.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "defence_anim": "420", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "render_anim": "28", + "requirements": "{5,60}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "10445", + "name": "Zamorak crozier", + "archery_ticket_price": "0", + "attack_anims": "", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10446", + "name": "Saradomin cloak", + "examine": "A Saradomin cloak.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "2", + "requirements": "{5,40}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10447", + "name": "Saradomin cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10448", + "name": "Guthix cloak", + "examine": "A Guthix cloak.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "2", + "requirements": "{5,40}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10449", + "name": "Guthix cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10450", + "name": "Zamorak cloak", + "examine": "A Zamorak cloak.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "2", + "requirements": "{5,40}", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10451", + "name": "Zamorak cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10452", + "name": "Saradomin mitre", + "examine": "A Saradomin mitre.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{5,40}-{6,40}", + "tradeable": "true" + }, + { + "id": "10453", + "name": "Saradomin mitre", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10454", + "name": "Guthix mitre", + "examine": "A Guthix mitre.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{5,40}-{6,40}", + "tradeable": "true" + }, + { + "id": "10455", + "name": "Guthix mitre", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10456", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "346500", - "durability": null, "name": "Zamorak mitre", - "tradeable": "true", + "examine": "A Zamorak mitre.", "archery_ticket_price": "0", - "id": "10457" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{5,40}-{6,40}", + "tradeable": "true", + "weight": "0.3" }, { - "requirements": "{5,20}", - "remove_sleeves": "true", - "ge_buy_limit": "2", - "examine": "Blessed vestments of Saradomin.", - "grand_exchange_price": "121400", - "durability": null, - "name": "Saradomin robe top", - "tradeable": "true", + "id": "10457", + "name": "Zamorak mitre", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10458", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "121400", - "durability": null, "name": "Saradomin robe top", - "tradeable": "true", + "examine": "Blessed vestments of Saradomin.", "archery_ticket_price": "0", - "id": "10459" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true" }, { - "requirements": "{5,20}", - "ge_buy_limit": "2", - "examine": "Zamorak Vestments.", - "durability": null, - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "154700", - "name": "Zamorak robe top", - "tradeable": "true", + "id": "10459", + "name": "Saradomin robe top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10460", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "154700", - "durability": null, "name": "Zamorak robe top", - "tradeable": "true", + "examine": "Zamorak Vestments.", "archery_ticket_price": "0", - "id": "10461" - }, - { - "requirements": "{5,20}", - "ge_buy_limit": "2", - "examine": "Blessed vestments of Guthix.", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", "durability": null, - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "75200", - "name": "Guthix robe top", + "requirements": "{5,20}", "tradeable": "true", + "weight": "1" + }, + { + "id": "10461", + "name": "Zamorak robe top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10462", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "75200", - "durability": null, "name": "Guthix robe top", - "tradeable": "true", + "examine": "Blessed vestments of Guthix.", "archery_ticket_price": "0", - "id": "10463" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "1" }, { - "requirements": "{5,20}", - "ge_buy_limit": "2", - "examine": "Leggings from the Saradomin Vestments.", - "grand_exchange_price": "56300", - "durability": null, - "name": "Saradomin robe legs", - "tradeable": "true", + "id": "10463", + "name": "Guthix robe top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10464", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "56300", - "durability": null, "name": "Saradomin robe legs", - "tradeable": "true", + "examine": "Leggings from the Saradomin Vestments.", "archery_ticket_price": "0", - "id": "10465" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{5,20}", + "tradeable": "true" }, { - "requirements": "{5,20}", - "ge_buy_limit": "2", - "examine": "Leggings from the Guthix Vestments.", - "grand_exchange_price": "56900", - "durability": null, - "name": "Guthix robe legs", - "tradeable": "true", - "weight": "2", + "id": "10465", + "name": "Saradomin robe legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10466", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "56900", - "durability": null, "name": "Guthix robe legs", - "tradeable": "true", + "examine": "Leggings from the Guthix Vestments.", "archery_ticket_price": "0", - "id": "10467" - }, - { - "requirements": "{5,20}", - "ge_buy_limit": "2", - "examine": "Legs of the Zamorak Vestments.", - "grand_exchange_price": "79400", - "durability": null, - "name": "Zamorak robe legs", - "tradeable": "true", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "10468", "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "79400", + "id": "10467", + "name": "Guthix robe legs", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10468", "name": "Zamorak robe legs", - "tradeable": "true", + "examine": "Legs of the Zamorak Vestments.", "archery_ticket_price": "0", - "id": "10469" + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "2.7" }, { - "requirements": "{5,60}", - "ge_buy_limit": "2", - "examine": "A Saradomin stole.", - "grand_exchange_price": "14000", - "durability": null, - "name": "Saradomin stole", - "tradeable": "true", + "id": "10469", + "name": "Zamorak robe legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10470", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "14000", - "durability": null, "name": "Saradomin stole", - "tradeable": "true", + "examine": "A Saradomin stole.", "archery_ticket_price": "0", - "id": "10471" + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "2", + "requirements": "{5,60}", + "tradeable": "true" }, { - "requirements": "{5,60}", - "ge_buy_limit": "2", - "examine": "A blessed stole.", - "grand_exchange_price": "3849", - "durability": null, - "name": "Guthix stole", - "tradeable": "true", + "id": "10471", + "name": "Saradomin stole", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10472", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3849", - "durability": null, "name": "Guthix stole", - "tradeable": "true", + "examine": "A blessed stole.", "archery_ticket_price": "0", - "id": "10473" - }, - { - "requirements": "{5,60}", - "ge_buy_limit": "2", - "examine": "A Zamorak stole.", - "grand_exchange_price": "13700", - "durability": null, - "name": "Zamorak stole", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10474", "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", - "equipment_slot": "2" - }, - { + "durability": null, + "equipment_slot": "2", "ge_buy_limit": "2", - "grand_exchange_price": "13700", + "requirements": "{5,60}", + "tradeable": "true" + }, + { + "id": "10473", + "name": "Guthix stole", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10474", "name": "Zamorak stole", - "tradeable": "true", + "examine": "A Zamorak stole.", "archery_ticket_price": "0", - "id": "10475" + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "2", + "requirements": "{5,60}", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "Remember to brush after eating!", - "grand_exchange_price": "22700", + "id": "10475", + "name": "Zamorak stole", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10476", "name": "Purple sweets", - "tradeable": "true", + "examine": "Remember to brush after eating!", "archery_ticket_price": "0", - "id": "10476" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "durability": null, + "id": "10485", "name": "Scroll", "archery_ticket_price": "0", - "id": "10485", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "1", - "examine": "An empty sack.", - "grand_exchange_price": "30", - "durability": null, + "id": "10486", "name": "Empty sack", - "tradeable": "true", - "weight": "0.4", + "examine": "An empty sack.", "archery_ticket_price": "0", - "id": "10486" + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.4" }, { - "turn90cw_anim": "821", - "examine": "Yup, definitely a chicken...an undead chicken.", - "walk_anim": "5364", - "durability": null, - "turn90ccw_anim": "822", - "weapon_interface": "12", - "turn180_anim": "5438", - "equip_audio": "3277", - "render_anim": "1171", - "equipment_slot": "3", - "stand_anim": "5363", - "name": "Undead chicken", - "run_anim": "824", - "archery_ticket_price": "0", "id": "10487", - "stand_turn_anim": "823", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "destroy_message": "The Witch in Draynor supplies these bars.", - "examine": "An iron bar supplied by an insane old crone.", - "durability": null, - "name": "Selected iron", - "tradeable": "false", - "destroy": "true", + "name": "Undead chicken", + "examine": "Yup, definitely a chicken...an undead chicken.", "archery_ticket_price": "0", - "id": "10488", - "equipment_slot": "3" - }, - { - "destroy_message": "I can make another magnet using special bars. The Witch in Draynor supplies these bars.", - "examine": "A magnet designed for undead chicken use.", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", "durability": null, - "name": "Bar magnet", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10489" - }, - { - "destroy_message": "I can chop more wood from undead trees using my blessed axe.", - "examine": "Were they trying to escape just then?", - "durability": null, - "name": "Undead twigs", - "tradeable": "false", - "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10490" - }, - { - "examine": "It radiates purity but can still lop off heads.", - "durability": null, - "destroy": "true", - "weight": "1.1", - "attack_speed": "5", - "weapon_interface": "2", - "render_anim": "2586", + "equip_audio": "3277", "equipment_slot": "3", - "destroy_message": "I can obtain a replacement axe from the Burthorpe Slayer Master.", - "name": "Blessed axe", - "tradeable": "false", + "render_anim": "1171", + "run_anim": "824", + "stand_anim": "5363", + "stand_turn_anim": "823", + "turn180_anim": "5438", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "5364", + "weapon_interface": "12" + }, + { + "id": "10488", + "name": "Selected iron", + "examine": "An iron bar supplied by an insane old crone.", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "The Witch in Draynor supplies these bars.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "10489", + "name": "Bar magnet", + "examine": "A magnet designed for undead chicken use.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can make another magnet using special bars. The Witch in Draynor supplies these bars.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10490", + "name": "Undead twigs", + "examine": "Were they trying to escape just then?", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can chop more wood from undead trees using my blessed axe.", + "durability": null, + "tradeable": "false", + "weight": "0.4" + }, + { "id": "10491", - "bonuses": "-2,12,10,0,0,0,1,0,0,0,0,13,2,0,0" + "name": "Blessed axe", + "examine": "It radiates purity but can still lop off heads.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "5", + "bonuses": "-2,12,10,0,0,0,1,0,0,0,0,13,2,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "I can obtain a replacement axe from the Burthorpe Slayer Master.", + "durability": null, + "equip_audio": "2229", + "equipment_slot": "3", + "render_anim": "2586", + "tradeable": "false", + "weapon_interface": "2", + "weight": "1.1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "These make no sense at all.", - "durability": null, + "id": "10492", "name": "Research notes", - "tradeable": "false", - "destroy": "true", + "examine": "These make no sense at all.", "archery_ticket_price": "0", - "id": "10492" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "They still make no sense, but look more meaningful now.", - "durability": null, + "id": "10493", "name": "Translated notes", - "tradeable": "false", - "destroy": "true", + "examine": "They still make no sense, but look more meaningful now.", "archery_ticket_price": "0", - "id": "10493" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Attach tab A to button B then fold along line C...", - "durability": null, + "id": "10494", "name": "A pattern", + "examine": "Attach tab A to button B then fold along line C...", "archery_ticket_price": "0", - "id": "10494" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A desirable residence for discerning, undead chickens.", - "durability": null, + "id": "10495", "name": "A container", - "tradeable": "false", - "destroy": "true", + "examine": "A desirable residence for discerning, undead chickens.", "archery_ticket_price": "0", - "id": "10495" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "examine": "Shiny!", - "durability": null, + "id": "10496", "name": "Polished buttons", - "tradeable": "true", + "examine": "Shiny!", "archery_ticket_price": "0", - "id": "10496" + "durability": null, + "tradeable": "true" }, { - "shop_price": "999", - "examine": "A bagged chicken ready to serve you, magnet in claw.", - "durability": null, - "destroy": "true", - "weight": "4.5", - "equip_audio": "3284", - "equipment_slot": "1", - "destroy_message": "I can buy a replacement from Ava in Draynor Manor; she will charge me 999 coins for this service.", - "name": "Ava's attractor", - "tradeable": "false", - "archery_ticket_price": "0", "id": "10498", - "bonuses": "0,0,0,0,2,0,0,0,2,0,0,0,0,0,0" - }, - { - "shop_price": "999", - "examine": "A superior bagged chicken ready to serve you, magnet in claw.", - "durability": null, + "name": "Ava's attractor", + "examine": "A bagged chicken ready to serve you, magnet in claw.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,2,0,0,0,2,0,0,0,0,0,0", "destroy": "true", - "weight": "4.5", + "destroy_message": "I can buy a replacement from Ava in Draynor Manor; she will charge me 999 coins for this service.", + "durability": null, "equip_audio": "3284", "equipment_slot": "1", - "destroy_message": "I can obtain a replacement for this from Ava in Draynor Manor. She will need 999 coins to buy the lower", - "name": "Ava's accumulator", + "shop_price": "999", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4.5" + }, + { "id": "10499", - "bonuses": "0,0,0,0,4,0,1,0,4,0,0,0,0,0,0" + "name": "Ava's accumulator", + "examine": "A superior bagged chicken ready to serve you, magnet in claw.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,4,0,1,0,4,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "I can obtain a replacement for this from Ava in Draynor Manor. She will need 999 coins to buy the lower", + "durability": null, + "equip_audio": "3284", + "equipment_slot": "1", + "shop_price": "999", + "tradeable": "false", + "weight": "4.5" }, { - "destroy_message": "I can obtain a replacement for this from the crone who lives west of the Port Phasmatys farm.", - "examine": "Allows the ghost farmer to talk to Alice - a breakthrough!", - "durability": null, - "name": "Crone-made amulet", - "tradeable": "false", - "destroy": "true", - "weight": "5", - "archery_ticket_price": "0", "id": "10500", - "equipment_slot": "2" + "name": "Crone-made amulet", + "examine": "Allows the ghost farmer to talk to Alice - a breakthrough!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can obtain a replacement for this from the crone who lives west of the Port Phasmatys farm.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "5" }, { - "examine": "Made from snow. / It's a ball of snow! Uncanny. / How did this not melt in the box?", - "durability": null, - "name": "Snowball", - "archery_ticket_price": "0", "id": "10501", - "weapon_interface": "18", - "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,1", - "equipment_slot": "3" - }, - { - "examine": "A gublinch frozen and crumbled into shards.", - "durability": null, - "name": "Gublinch shards", - "tradeable": "false", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "10506" - }, - { - "remove_head": "true", - "destroy_message": "You can reclaim this item from the place you found it. this item", - "examine": "A reindeer hat and a matching flashing nose.", - "durability": null, - "name": "Reindeer hat", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10507", - "equipment_slot": "0" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "A wintumber tree for your player-owned house.", - "durability": null, - "name": "Wintumber tree", - "tradeable": "false", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "10508" - }, - { + "name": "Snowball", "examine": "Made from snow. / It's a ball of snow! Uncanny. / How did this not melt in the box?", - "durability": null, - "name": "Snowball", - "tradeable": "false", "archery_ticket_price": "0", - "id": "10509" + "bonuses": "0,0,0,0,10,0,0,0,0,0,0,0,0,0,1", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "18" }, { - "durability": null, - "name": "Snowball", - "tradeable": "false", + "id": "10506", + "name": "Gublinch shards", + "examine": "A gublinch frozen and crumbled into shards.", "archery_ticket_price": "0", - "id": "10510" + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" }, { - "examine": "They're going to sing to you!", + "id": "10507", + "name": "Reindeer hat", + "examine": "A reindeer hat and a matching flashing nose.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it. this item", "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false", + "weight": "2" + }, + { + "id": "10508", + "name": "Wintumber tree", + "examine": "A wintumber tree for your player-owned house.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "10509", + "name": "Snowball", + "examine": "Made from snow. / It's a ball of snow! Uncanny. / How did this not melt in the box?", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "10510", + "name": "Snowball", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "10511", "name": "Zanaris choir", + "examine": "They're going to sing to you!", "archery_ticket_price": "0", - "id": "10511" + "durability": null }, { - "destroy_message": "Unknown You can reclaim this item from the place you found it.", - "examine": "Some dry crackers.", - "durability": null, + "id": "10513", "name": "Crackers", - "tradeable": "false", - "destroy": "true", + "examine": "Some dry crackers.", "archery_ticket_price": "0", - "id": "10513" + "destroy": "true", + "destroy_message": "Unknown You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A block of tofu.", - "durability": null, + "id": "10514", "name": "Tofu", + "examine": "A block of tofu.", "archery_ticket_price": "0", - "id": "10514" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some meaty worms.", - "durability": null, + "id": "10515", "name": "Worms", - "tradeable": "false", + "examine": "Some meaty worms.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "10515" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Attacker horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10516" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Attacker horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10517" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Attacker horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10518" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Attacker horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10519" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Attacker horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10520" - }, - { - "examine": "A very small/small/medium-sized/large/very large collection bag.", - "durability": null, - "name": "Collection bag", - "archery_ticket_price": "0", - "id": "10521" - }, - { - "examine": "A very small/small/medium-sized/large/very large collection bag.", - "durability": null, - "name": "Collection bag", - "archery_ticket_price": "0", - "id": "10522" - }, - { - "examine": "A very small/small/medium-sized/large/very large collection bag.", - "durability": null, - "name": "Collection bag", - "archery_ticket_price": "0", - "id": "10523" - }, - { - "examine": "A very small/small/medium-sized/large/very large collection bag.", - "durability": null, - "name": "Collection bag", - "archery_ticket_price": "0", - "id": "10524" - }, - { - "examine": "A very small/small/medium-sized/large/very large collection bag.", - "durability": null, - "name": "Collection bag", - "archery_ticket_price": "0", - "id": "10525" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Healer horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10526" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Healer horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10527" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Healer horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10528" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Healer horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10529" - }, - { - "examine": "A horn to call others.", - "durability": null, - "name": "Healer horn", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10530" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Used to poison your enemies!", "durability": null, + "tradeable": "false" + }, + { + "id": "10516", + "name": "Attacker horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10517", + "name": "Attacker horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10518", + "name": "Attacker horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10519", + "name": "Attacker horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10520", + "name": "Attacker horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10521", + "name": "Collection bag", + "examine": "A very small/small/medium-sized/large/very large collection bag.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10522", + "name": "Collection bag", + "examine": "A very small/small/medium-sized/large/very large collection bag.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10523", + "name": "Collection bag", + "examine": "A very small/small/medium-sized/large/very large collection bag.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10524", + "name": "Collection bag", + "examine": "A very small/small/medium-sized/large/very large collection bag.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10525", + "name": "Collection bag", + "examine": "A very small/small/medium-sized/large/very large collection bag.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10526", + "name": "Healer horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10527", + "name": "Healer horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10528", + "name": "Healer horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10529", + "name": "Healer horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10530", + "name": "Healer horn", + "examine": "A horn to call others.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10531", "name": "Green egg", + "examine": "Used to poison your enemies!", "archery_ticket_price": "0", - "id": "10531" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Will explode when fired.", - "durability": null, + "id": "10532", "name": "Red egg", + "examine": "Will explode when fired.", "archery_ticket_price": "0", - "id": "10532" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Stun your foes!", - "durability": null, + "id": "10533", "name": "Blue egg", + "examine": "Stun your foes!", "archery_ticket_price": "0", - "id": "10533" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "A yellow egg; it needs passing to the Healer to be poisoned!", - "durability": null, + "id": "10534", "name": "Yellow egg", + "examine": "A yellow egg; it needs passing to the Healer to be poisoned!", "archery_ticket_price": "0", - "id": "10534" + "durability": null }, { - "examine": "A poisoned yellow egg; pass it to the Attacker for spikes to be added!", - "durability": null, + "id": "10535", "name": "Poisoned egg", - "tradeable": "false", - "destroy": "true", + "examine": "A poisoned yellow egg; pass it to the Attacker for spikes to be added!", "archery_ticket_price": "0", - "id": "10535" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A poisoned, spiky egg; pass to the defender for heating!", - "durability": null, + "id": "10536", "name": "Spiked/pois. egg", - "tradeable": "false", - "destroy": "true", + "examine": "A poisoned, spiky egg; pass to the defender for heating!", "archery_ticket_price": "0", - "id": "10536" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A heated, poisoned, spiky egg; pass to the Collector for loading.", - "durability": null, + "id": "10537", "name": "Omega egg", - "tradeable": "false", - "destroy": "true", + "examine": "A heated, poisoned, spiky egg; pass to the Collector for loading.", "archery_ticket_price": "0", - "id": "10537" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A horn to call others.", - "durability": null, + "id": "10538", "name": "Defender horn", - "tradeable": "false", - "destroy": "true", + "examine": "A horn to call others.", "archery_ticket_price": "0", - "id": "10538" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some poisoned tofu.", - "durability": null, + "id": "10539", "name": "Poisoned tofu", - "tradeable": "false", - "destroy": "true", + "examine": "Some poisoned tofu.", "archery_ticket_price": "0", - "id": "10539" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some poisoned worms.", - "durability": null, + "id": "10540", "name": "Poisoned worms", - "tradeable": "false", - "destroy": "true", + "examine": "Some poisoned worms.", "archery_ticket_price": "0", - "id": "10540" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A poisoned leg of meat.", - "durability": null, + "id": "10541", "name": "Poisoned meat", - "tradeable": "false", - "destroy": "true", + "examine": "A poisoned leg of meat.", "archery_ticket_price": "0", - "id": "10541" - }, - { + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "tradeable": "false" + }, + { + "id": "10542", "name": "Healing vial(4)", "archery_ticket_price": "0", - "id": "10542" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "durability": null, + "id": "10543", "name": "Healing vial(3)", "archery_ticket_price": "0", - "id": "10543" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "durability": null, + "id": "10544", "name": "Healing vial(2)", "archery_ticket_price": "0", - "id": "10544" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "durability": null, + "id": "10545", "name": "Healing vial(1)", "archery_ticket_price": "0", - "id": "10545" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A full healing vial.", - "durability": null, + "durability": null + }, + { + "id": "10546", "name": "Healing vial", - "tradeable": "false", - "destroy": "true", + "examine": "A full healing vial.", "archery_ticket_price": "0", - "id": "10546" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "remove_head": "true", - "requirements": "{1,40}", - "shop_price": "275", - "examine": "A Penance Healer hat.", - "durability": null, - "name": "Healer hat", - "weight": "2", - "archery_ticket_price": "0", "id": "10547", + "name": "Healer hat", + "examine": "A Penance Healer hat.", "absorb": "3,1,0", + "archery_ticket_price": "0", "bonuses": "-5,-5,-5,7,-5,6,8,10,8,0,7,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}", "shop_price": "275", - "examine": "A Penance Fighter hat.", - "durability": null, - "name": "Fighter hat", - "weight": "2", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "10548", + "name": "Fighter hat", + "examine": "A Penance Fighter hat.", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "5,5,5,-7,-7,30,32,28,-3,28,7,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", "requirements": "{1,40}", "shop_price": "275", - "examine": "A Penance Runner hat.", - "durability": null, - "name": "Runner hat", - "weight": "1", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "10549", + "name": "Runner hat", + "examine": "A Penance Runner hat.", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,30,32,27,1,30,7,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", "requirements": "{1,40}", "shop_price": "275", - "examine": "A Penance ranger hat.", - "durability": null, - "name": "Ranger hat", - "weight": "2", - "archery_ticket_price": "0", - "id": "10550", - "absorb": "0,3,1", - "bonuses": "-5,-5,-5,-5,7,7,10,11,5,9,7,0,0,0,0", - "equipment_slot": "0" + "weight": "1" }, { - "requirements": "{1,40}", - "shop_price": "375", - "examine": "Penance Fighter torso armour.", + "id": "10550", + "name": "Ranger hat", + "examine": "A Penance ranger hat.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,7,7,10,11,5,9,7,0,0,0,0", "durability": null, - "destroy": "true", - "weight": "4", + "equipment_slot": "0", + "requirements": "{1,40}", + "shop_price": "275", + "weight": "2" + }, + { + "id": "10551", + "name": "Fighter torso", + "examine": "Penance Fighter torso armour.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,0,66,85,62,-6,67,40,4,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", - "name": "Fighter torso", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10551", - "bonuses": "0,0,0,-30,0,66,85,62,-6,67,40,4,0,0,0" - }, - { - "requirements": "{1,40}", - "shop_price": "100", - "examine": "Boots made from a Penance Runner.", - "durability": null, - "name": "Runner boots", - "archery_ticket_price": "0", - "id": "10552", - "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0", - "equipment_slot": "10" - }, - { - "requirements": "{1,40}", - "shop_price": "150", - "examine": "These gloves seem unbelievably light!", - "durability": null, - "name": "Penance gloves", - "weight": "-7", - "archery_ticket_price": "0", - "id": "10553", - "bonuses": "0,0,0,0,0,0,3,4,0,2,2,4,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,40}", - "shop_price": "150", - "examine": "These gloves seem unbelievably light!", - "durability": null, - "name": "Penance gloves", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "10554", - "bonuses": "0,0,0,0,0,0,3,4,0,2,2,4,0,0,0", - "equipment_slot": "9" - }, - { "requirements": "{1,40}", "shop_price": "375", - "examine": "A skirt made from the skin of a Penance Ranger.", - "durability": null, - "name": "Penance skirt", - "weight": "3", + "tradeable": "false", + "weight": "4" + }, + { + "id": "10552", + "name": "Runner boots", + "examine": "Boots made from a Penance Runner.", "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0", + "durability": null, + "equipment_slot": "10", + "requirements": "{1,40}", + "shop_price": "100" + }, + { + "id": "10553", + "name": "Penance gloves", + "examine": "These gloves seem unbelievably light!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,3,4,0,2,2,4,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,40}", + "shop_price": "150", + "weight": "-7" + }, + { + "id": "10554", + "name": "Penance gloves", + "examine": "These gloves seem unbelievably light!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,3,4,0,2,2,4,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,40}", + "shop_price": "150", + "weight": "0.3" + }, + { "id": "10555", + "name": "Penance skirt", + "examine": "A skirt made from the skin of a Penance Ranger.", "absorb": "0,4,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-10,16,26,21,28,19,27,15,0,0,0,0", - "equipment_slot": "7" + "durability": null, + "equipment_slot": "7", + "requirements": "{1,40}", + "shop_price": "375", + "weight": "3" }, { - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", "id": "10556", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "1" }, { - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", "id": "10557", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "1" }, { - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", "id": "10558", - "equipment_slot": "1" - }, - { - "durability": null, "name": "Picture", "archery_ticket_price": "0", - "id": "10559", + "durability": null, "equipment_slot": "1" }, { - "examine": "A horn for communication with your team members.", + "id": "10559", + "name": "Picture", + "archery_ticket_price": "0", "durability": null, + "equipment_slot": "1" + }, + { + "id": "10560", "name": "Collector horn", - "tradeable": "false", - "destroy": "true", + "examine": "A horn for communication with your team members.", "archery_ticket_price": "0", - "id": "10560" - }, - { - "destroy_message": "You will have to ask Captain Cain for another.", - "shop_price": "160", - "examine": "A book about the Barbarian Assault Queen.", + "destroy": "true", "durability": null, - "name": "Queen help book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10562" + "tradeable": "false" }, { + "id": "10562", + "name": "Queen help book", + "examine": "A book about the Barbarian Assault Queen.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will have to ask Captain Cain for another.", + "durability": null, + "shop_price": "160", + "tradeable": "false" + }, + { + "id": "10564", + "name": "Granite body", + "examine": "Provides excellent protection.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-22,-5,87,84,79,-6,97,45,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "lendable": "true", + "remove_sleeves": "true", "requirements": "{1,50}-{2,50}", "shop_price": "95000", - "ge_buy_limit": "10", - "examine": "Provides excellent protection.", - "durability": null, - "weight": "22.6", - "absorb": "4,0,8", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "126800", - "name": "Granite body", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10564", - "bonuses": "0,0,0,-22,-5,87,84,79,-6,97,45,0,0,0,0" + "weight": "22.6" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "126800", - "durability": null, + "id": "10565", "name": "Granite body", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10565" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "examine": "A cape of fire.", - "durability": null, - "name": "Fire cape", - "tradeable": "false", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10566", + "name": "Fire cape", + "examine": "A cape of fire.", + "archery_ticket_price": "0", "bonuses": "1,1,1,1,1,11,11,11,11,11,11,4,2,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1.8" }, { - "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "10567", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10568" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10568", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10569", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10570" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10570", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10571", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10572" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10572", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10573", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10574" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10574", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10575", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10576" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10576", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10577", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10578" - }, - { "examine": "Mabob.", - "grand_exchange_price": "2", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "10578", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "10579", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Picture", - "tradeable": "true", + "examine": "Mabob.", "archery_ticket_price": "0", - "id": "10580" - }, - { - "examine": "(Normal) A mystical-feeling dagger. (Poisoned) The twisted blade is covered with a nasty poison.", - "durability": null, "destroy": "true", - "weight": "0.4", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3", - "attack_anims": "376,376,377,376", - "destroy_message": "You can obtain another from Osman. You will then need to poison it.", - "name": "Keris", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10581", - "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0" + "durability": null, + "equipment_slot": "2", + "tradeable": "false" }, { + "id": "10580", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "10581", + "name": "Keris", + "examine": "(Normal) A mystical-feeling dagger. (Poisoned) The twisted blade is covered with a nasty poison.", + "archery_ticket_price": "0", "attack_anims": "376,376,377,376", + "attack_speed": "4", + "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", + "defence_anim": "378", + "destroy": "true", "destroy_message": "You can obtain another from Osman. You will then need to poison it.", "durability": null, + "equipment_slot": "3", + "render_anim": "2584", + "tradeable": "false", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "id": "10582", "name": "Keris(p)", "archery_ticket_price": "0", - "id": "10582", - "weapon_interface": "5", - "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3" - }, - { "attack_anims": "376,376,377,376", + "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", + "defence_anim": "378", "destroy_message": "You can obtain another from Osman. You will then need to poison it.", "durability": null, + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "10583", "name": "Keris(p+)", "archery_ticket_price": "0", - "id": "10583", - "weapon_interface": "5", - "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", - "render_anim": "2584", - "defence_anim": "378", - "equipment_slot": "3" - }, - { "attack_anims": "376,376,377,376", + "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", + "defence_anim": "378", "destroy_message": "You can obtain another from Osman. You will then need to poison it.", "durability": null, + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" + }, + { + "id": "10584", "name": "Keris(p++)", "archery_ticket_price": "0", - "id": "10584", - "weapon_interface": "5", + "attack_anims": "376,376,377,376", "bonuses": "40,20,-10,2,0,0,0,0,2,0,0,30,2,0,0", - "render_anim": "2584", "defence_anim": "378", - "equipment_slot": "3" + "destroy_message": "You can obtain another from Osman. You will then need to poison it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "2584", + "weapon_interface": "5" }, { - "destroy_message": "You can obtain a replacement lamp from the high priest in Sophanem.", - "examine": "A mystical lamp, engraved with scenes of carnage.", - "durability": null, + "id": "10586", "name": "Combat lamp", - "tradeable": "false", - "destroy": "true", + "examine": "A mystical lamp, engraved with scenes of carnage.", "archery_ticket_price": "0", - "id": "10586" + "destroy": "true", + "destroy_message": "You can obtain a replacement lamp from the high priest in Sophanem.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I got this diary in Tarn's Lair, perhaps I can get another one there.", - "examine": "Tarn Razorlor's diary.", - "durability": null, + "id": "10587", "name": "Tarn's diary", - "tradeable": "false", + "examine": "Tarn Razorlor's diary.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "10587" - }, - { - "examine": "Increases the wearer's Strength and Attack by 20% when fighting the undead.", + "destroy_message": "I got this diary in Tarn's Lair, perhaps I can get another one there.", "durability": null, - "name": "Salve amulet(e)", - "weight": "1", - "archery_ticket_price": "0", - "id": "10588", - "bonuses": "0,0,0,0,0,3,3,3,0,0,3,0,3,0,0", - "equipment_slot": "2" + "tradeable": "false" }, { + "id": "10588", + "name": "Salve amulet(e)", + "examine": "Increases the wearer's Strength and Attack by 20% when fighting the undead.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,3,3,3,0,0,3,0,3,0,0", + "durability": null, + "equipment_slot": "2", + "weight": "1" + }, + { + "id": "10589", + "name": "Granite helm", + "examine": "A stone helmet.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-9,-7,31,33,29,-1,39,9,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "lendable": "true", "remove_head": "true", "requirements": "{1,50}-{2,50}", - "ge_buy_limit": "10", - "examine": "A stone helmet.", - "durability": null, - "weight": "4", - "absorb": "2,0,4", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "28000", - "name": "Granite helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10589", - "bonuses": "0,0,0,-9,-7,31,33,29,-1,39,9,0,0,0,0" + "weight": "4" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "28000", - "durability": null, + "id": "10590", "name": "Granite helm", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10590" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "examine": "A terrifying dog beast.", - "durability": null, + "id": "10591", "name": "Terror dog", + "examine": "A terrifying dog beast.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "10591" + "durability": null }, { - "destroy_message": "You can make another with tanned leather and a mahogany plank.", - "examine": "Small drums.", - "durability": null, + "id": "10592", "name": "Penguin bongos", - "tradeable": "false", - "destroy": "true", + "examine": "Small drums.", "archery_ticket_price": "0", - "id": "10592" + "destroy": "true", + "destroy_message": "You can make another with tanned leather and a mahogany plank.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Every song needs a cowbell.", - "durability": null, + "id": "10593", "name": "Cowbells", - "weight": "2", + "examine": "Every song needs a cowbell.", "archery_ticket_price": "0", - "id": "10593" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A book about clockwork mechanisms.", "durability": null, + "weight": "2" + }, + { + "id": "10594", "name": "Clockwork book", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A book about clockwork mechanisms.", "archery_ticket_price": "0", - "id": "10594" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You'll have to make another one at your house. Speak to Larry to learn how to make one.", - "examine": "A clockwork penguin suit.", - "durability": null, + "id": "10595", "name": "Clockwork suit", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10595" - }, - { - "destroy_message": "You'll have to make another one at your house. Speak to Larry to learn how to make one.", "examine": "A clockwork penguin suit.", - "durability": null, - "name": "Clockwork suit", - "tradeable": "false", - "destroy": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "10596" + "destroy": "true", + "destroy_message": "You'll have to make another one at your house. Speak to Larry to learn how to make one.", + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "destroy_message": "You will need to speak to Noodle to get another.", - "examine": "So you can be a penguin too!", + "id": "10596", + "name": "Clockwork suit", + "examine": "A clockwork penguin suit.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to make another one at your house. Speak to Larry to learn how to make one.", "durability": null, + "tradeable": "false", + "weight": "2" + }, + { + "id": "10600", "name": "Kgp id card", - "tradeable": "false", + "examine": "So you can be a penguin too!", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "10600" + "destroy_message": "You will need to speak to Noodle to get another.", + "durability": null, + "tradeable": "false" }, { - "shop_price": "15000", - "examine": "Blue: A magical hat. ", - "durability": null, - "weight": "0.4", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "1", - "name": "Mystic hat", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10601", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "shop_price": "15000", + "name": "Mystic hat", "examine": "Blue: A magical hat. ", - "durability": null, - "weight": "0.4", "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "1", - "name": "Mystic hat", + "shop_price": "15000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "0.4" + }, + { "id": "10602", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "shop_price": "15000", - "examine": "Blue: A magical hat. ", - "durability": null, - "weight": "0.4", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "1", "name": "Mystic hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10603", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "Make your foes cower by wearing a skull as a helmet!", - "durability": null, - "weight": "1.3", + "examine": "Blue: A magical hat. ", "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "25569", - "name": "Skeletal helm", + "shop_price": "15000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10604", - "bonuses": "0,0,0,2,-2,10,9,11,3,0,0,0,0,0,0" + "weight": "0.4" }, { - "remove_sleeves": "true", - "examine": "Mystical robes.", - "grand_exchange_price": "3932123", - "durability": null, - "name": "Infinity top", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "10605", + "id": "10603", + "name": "Mystic hat", + "examine": "Blue: A magical hat. ", "absorb": "3,1,0", - "bonuses": "0,0,0,22,0,0,0,0,22,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { + "archery_ticket_price": "0", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", "lendable": "true", - "examine": "A wooden helmet.", - "grand_exchange_price": "5623", - "durability": null, - "name": "Splitbark helm", + "shop_price": "15000", "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "10606", - "absorb": "3,1,0", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,0,0,0,0,0", - "equipment_slot": "0" + "weight": "0.4" }, { - "examine": "They seem to be not quite of this world...", - "durability": null, - "name": "Ghostly boots", + "id": "10604", + "name": "Skeletal helm", + "examine": "Make your foes cower by wearing a skull as a helmet!", + "absorb": "3,1,0", "archery_ticket_price": "0", + "bonuses": "0,0,0,2,-2,10,9,11,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "10605", + "name": "Infinity top", + "examine": "Mystical robes.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,22,0,0,0,0,22,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "10606", + "name": "Splitbark helm", + "examine": "A wooden helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,10,9,11,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true", + "weight": "0.9" + }, + { "id": "10607", + "name": "Ghostly boots", + "examine": "They seem to be not quite of this world...", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "durability": null, "equipment_slot": "10" }, { - "shop_price": "1000", - "examine": "A mystical hat.", - "durability": null, - "name": "Moonclan hat", - "weight": "1", - "archery_ticket_price": "0", "id": "10608", + "name": "Moonclan hat", + "examine": "A mystical hat.", + "archery_ticket_price": "0", "bonuses": "0,0,0,3,-5,3,3,3,3,-5,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "shop_price": "1000", + "weight": "1" }, { + "id": "10609", + "name": "Lunar helm", + "examine": "A mystical helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,8,7,10,2,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "shop_price": "15000", - "examine": "A mystical helmet.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "absorb": "3,1,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar helm", "tradeable": "false", - "archery_ticket_price": "0", - "id": "10609", - "bonuses": "0,0,0,3,-2,8,7,10,2,0,0,0,0,0,0" + "weight": "0.9" }, { - "remove_sleeves": "true", - "durability": null, + "id": "10610", "name": "Decorative armour", "archery_ticket_price": "0", - "id": "10610", "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { + "id": "10611", + "name": "Void knight top", + "examine": "Torso armour from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,40,40,40,40,40,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", "shop_price": "250", - "examine": "Torso armour from the order of the Void Knights.", - "durability": null, - "name": "Void knight top", "tradeable": "false", - "weight": "6.5", - "archery_ticket_price": "0", - "id": "10611", - "bonuses": "0,0,0,0,0,40,40,40,40,40,0,0,0,0,0", - "equipment_slot": "4" + "weight": "6.5" }, { - "remove_head": "true", - "examine": "Black banded leather armour, a rogue's dream!", - "durability": null, - "name": "Rogue mask", - "weight": "0.5", - "archery_ticket_price": "0", "id": "10612", + "name": "Rogue mask", + "examine": "Black banded leather armour, a rogue's dream!", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,5,5,5,5,5,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "weight": "0.5" }, { - "remove_head": "true", - "examine": "Protective headwear made from crabs. Better than it sounds.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "24661", - "name": "Rock-shell helm", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10613", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A helm fit for any Fremennik ranger.", + "name": "Rock-shell helm", + "examine": "Protective headwear made from crabs. Better than it sounds.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", "durability": null, - "weight": "1", - "absorb": "0,3,1", - "remove_beard": "true", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "35640", - "name": "Spined helm", + "remove_head": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "10614", - "bonuses": "-6,-6,-6,-6,6,6,6,6,6,0,0,0,0,0,0" + "name": "Spined helm", + "examine": "A helm fit for any Fremennik ranger.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "-6,-6,-6,-6,6,6,6,6,6,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "1" }, { - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1021", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10615", - "equipment_slot": "0" + "name": "Tribal mask", + "examine": "A ceremonial wooden mask.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" }, { - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1021", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10616", - "equipment_slot": "0" - }, - { - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1021", - "durability": null, "name": "Tribal mask", - "tradeable": "true", - "weight": "2", + "examine": "A ceremonial wooden mask.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" + }, + { "id": "10617", - "equipment_slot": "0" + "name": "Tribal mask", + "examine": "A ceremonial wooden mask.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" }, { - "shop_price": "3840", - "examine": "Provides excellent protection.", - "durability": null, - "weight": "9.9", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "4595", - "name": "White platebody", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10618", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,1,0,0" - }, - { - "shop_price": "10000", - "examine": "An initiate Temple Knight's Armour.", + "name": "White platebody", + "examine": "Provides excellent protection.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,1,0,0", "durability": null, - "weight": "8", - "absorb": "1,0,3", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "5821", - "name": "Initiate hauberk", + "shop_price": "3840", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "9.9" + }, + { "id": "10619", - "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,0,0,6,0,0" - }, - { - "shop_price": "12000", - "examine": "A Proselyte Temple Knight's armour.", + "name": "Initiate hauberk", + "examine": "An initiate Temple Knight's Armour.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,0,0,6,0,0", "durability": null, - "weight": "8.6", - "absorb": "2,0,4", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "6960", - "name": "Proselyte hauberk", + "shop_price": "10000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "8" + }, + { "id": "10620", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,8,0,0" + "name": "Proselyte hauberk", + "examine": "A Proselyte Temple Knight's armour.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,8,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "12000", + "tradeable": "true", + "weight": "8.6" }, { - "remove_sleeves": "true", - "examine": "A thick heavy leather top.", - "durability": null, - "name": "Mourner top", - "archery_ticket_price": "0", "id": "10621", - "equipment_slot": "4" + "name": "Mourner top", + "examine": "A thick heavy leather top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "remove_sleeves": "true", - "examine": "This should make me harder to spot in polar areas.", - "grand_exchange_price": "216", - "durability": null, - "name": "Kyatt top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10622", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "examine": "This should make me harder to spot in woodland and jungle areas.", - "grand_exchange_price": "1793", - "durability": null, - "name": "Larupia top", - "tradeable": "true", - "weight": "0.2", + "name": "Kyatt top", + "examine": "This should make me harder to spot in polar areas.", "archery_ticket_price": "0", - "id": "10623", "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" }, { + "id": "10623", + "name": "Larupia top", + "examine": "This should make me harder to spot in woodland and jungle areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10624", + "name": "Graahk top", + "examine": "This should make me harder to spot in desert areas.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "300", - "examine": "This should make me harder to spot in desert areas.", - "grand_exchange_price": "246", - "durability": null, - "name": "Graahk top", "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10624", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" + "weight": "0.2" }, { - "remove_sleeves": "true", - "examine": "This should make me harder to spot in wooded areas.", - "grand_exchange_price": "3883", - "durability": null, - "name": "Wood camo top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10625", + "name": "Wood camo top", + "examine": "This should make me harder to spot in wooded areas.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" }, { - "remove_sleeves": "true", - "examine": "This should make me harder to spot in jungle areas.", - "grand_exchange_price": "325", - "durability": null, - "name": "Jungle camo top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10626", + "name": "Jungle camo top", + "examine": "This should make me harder to spot in jungle areas.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" }, { - "remove_sleeves": "true", - "examine": "This should make me harder to spot in desert areas.", - "grand_exchange_price": "606", - "durability": null, - "name": "Desert camo top", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "10627", + "name": "Desert camo top", + "examine": "This should make me harder to spot in desert areas.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" - }, - { + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", - "examine": "This should make me harder to spot in polar areas.", - "grand_exchange_price": "5614", - "durability": null, - "name": "Polar camo top", "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10628", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", - "equipment_slot": "4" + "weight": "0.2" }, { - "examine": "A mime would wear this.", - "durability": null, - "name": "Mime mask", + "id": "10628", + "name": "Polar camo top", + "examine": "This should make me harder to spot in polar areas.", "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { "id": "10629", + "name": "Mime mask", + "examine": "A mime would wear this.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "0" }, { - "remove_sleeves": "true", - "examine": "Very posh!", - "durability": null, - "name": "Princess blouse", - "weight": "2", - "archery_ticket_price": "0", "id": "10630", - "equipment_slot": "4" + "name": "Princess blouse", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" }, { - "examine": "Aside from the braaaains on the lapel, it's still quite good.", - "durability": null, - "name": "Zombie shirt", - "archery_ticket_price": "0", "id": "10631", + "name": "Zombie shirt", + "examine": "Aside from the braaaains on the lapel, it's still quite good.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "4" }, { - "remove_sleeves": "true", - "examine": "Examine what?", - "durability": null, - "name": "Camo top", - "weight": "2.7", - "archery_ticket_price": "0", "id": "10632", - "equipment_slot": "4" + "name": "Camo top", + "examine": "Examine what?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.7" }, { - "remove_sleeves": "true", - "examine": "A leather strapped top.", - "durability": null, - "name": "Lederhosen top", - "weight": "2.2", - "archery_ticket_price": "0", "id": "10633", - "equipment_slot": "4" - }, - { + "name": "Lederhosen top", + "examine": "A leather strapped top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", - "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", - "durability": null, - "name": "Shade robe", - "weight": "2", - "archery_ticket_price": "0", + "weight": "2.2" + }, + { "id": "10634", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", - "equipment_slot": "4" - }, - { - "shop_price": "450", - "examine": "The cape worn by members of the Legends Guild.", - "durability": null, - "name": "Cape of legends", - "tradeable": "false", - "weight": "1.8", + "name": "Shade robe", + "examine": "I feel closer to the gods when I am wearing this. (Top) If a shade had knees, this would keep them nice and warm. (Bottom)", "archery_ticket_price": "0", - "id": "10635", - "bonuses": "0,0,0,0,0,7,7,7,7,7,0,0,0,0,0", - "equipment_slot": "1" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,5,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" }, { + "id": "10635", + "name": "Cape of legends", + "examine": "The cape worn by members of the Legends Guild.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,7,7,7,7,7,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "shop_price": "450", + "tradeable": "false", + "weight": "1.8" + }, + { + "id": "10636", + "name": "Obsidian cape", + "examine": "A cape woven of obsidian plates.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "lendable": "true", "shop_price": "90000", - "examine": "A cape woven of obsidian plates.", - "grand_exchange_price": "38500", - "durability": null, - "name": "Obsidian cape", "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "10636", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "1.8" }, { - "examine": "A cape of fire.", - "durability": null, - "name": "Fire cape", - "tradeable": "false", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10637", - "bonuses": "1,1,1,1,1,11,11,11,11,11,0,4,2,0,0", - "equipment_slot": "1" - }, - { - "shop_price": "50", - "examine": "Ooohhh look at the pretty colours...", - "durability": null, - "name": "Team-1 cape", - "tradeable": "true", - "weight": "0.4", + "name": "Fire cape", + "examine": "A cape of fire.", "archery_ticket_price": "0", - "id": "10638", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "bonuses": "1,1,1,1,1,11,11,11,11,11,0,4,2,0,0", + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1.8" }, { + "id": "10638", + "name": "Team-1 cape", + "examine": "Ooohhh look at the pretty colours...", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10639", + "name": "Attack cape", + "examine": "The cape worn by masters of attack.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{0,99}", "shop_price": "99000", - "examine": "The cape worn by masters of attack.", - "durability": null, - "name": "Attack cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10639", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10640", + "name": "Strength cape", + "examine": "The cape only worn by the strongest people.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{2,99}", "shop_price": "99000", - "examine": "The cape only worn by the strongest people.", - "durability": null, - "name": "Strength cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10640", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10641", + "name": "Defence cape", + "examine": "The cape worn by masters of the art of Defence.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{1,99}", "shop_price": "99000", - "examine": "The cape worn by masters of the art of Defence.", - "durability": null, - "name": "Defence cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10641", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10642", + "name": "Ranging cape", + "examine": "The cape worn by master archers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{4,99}", "shop_price": "99000", - "examine": "The cape worn by master archers.", - "durability": null, - "name": "Ranging cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10642", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10643", + "name": "Prayer cape", + "examine": "The cape worn by the most pious of heroes.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{5,99}", "shop_price": "99000", - "examine": "The cape worn by the most pious of heroes.", - "durability": null, - "name": "Prayer cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10643", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10644", + "name": "Magic cape", + "examine": "The cape worn by the most powerful mages.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{6,99}", "shop_price": "99000", - "examine": "The cape worn by the most powerful mages.", - "durability": null, - "name": "Magic cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10644", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10645", + "name": "Runecraft cape", + "examine": "The cape worn by master runecrafters.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{20,99}", "shop_price": "99000", - "examine": "The cape worn by master runecrafters.", - "durability": null, - "name": "Runecraft cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10645", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10646", + "name": "Hunter cape", + "examine": "The cape worn by master hunters.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{21,99}", "shop_price": "99000", - "examine": "The cape worn by master hunters.", - "durability": null, - "name": "Hunter cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10646", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10647", + "name": "Hitpoints cape", + "examine": "The cape worn by well-constituted adventurers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{3,99}", "shop_price": "99000", - "examine": "The cape worn by well-constituted adventurers.", - "durability": null, - "name": "Hitpoints cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10647", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10648", + "name": "Agility cape", + "examine": "The cape worn by the most agile of heroes.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{16,99}", "shop_price": "99000", - "examine": "The cape worn by the most agile of heroes.", - "durability": null, - "name": "Agility cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10648", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10649", + "name": "Herblore cape", + "examine": "The cape worn by the most skilled at the art of herblore.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{15,99}", "shop_price": "99000", - "examine": "The cape worn by the most skilled at the art of herblore.", - "durability": null, - "name": "Herblore cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10649", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10650", + "name": "Thieving cape", + "examine": "The cape worn by master thieves.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{17,99}", "shop_price": "99000", - "examine": "The cape worn by master thieves.", - "durability": null, - "name": "Thieving cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10650", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10651", + "name": "Crafting cape", + "examine": "The cape worn by master craftworkers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{12,99}", "shop_price": "99000", - "examine": "The cape worn by master craftworkers.", - "durability": null, - "name": "Crafting cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10651", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10652", + "name": "Fletching cape", + "examine": "The cape worn by the best of fletchers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{9,99}", "shop_price": "99000", - "examine": "The cape worn by the best of fletchers.", - "durability": null, - "name": "Fletching cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10652", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10653", + "name": "Slayer cape", + "examine": "The cape worn by slayer masters.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{18,99}", "shop_price": "99000", - "examine": "The cape worn by slayer masters.", - "durability": null, - "name": "Slayer cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10653", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10654", + "name": "Construct. cape", + "examine": "The cape worn by master builders.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{22,99}", "shop_price": "99000", - "examine": "The cape worn by master builders.", - "durability": null, - "name": "Construct. cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10654", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10655", + "name": "Mining cape", + "examine": "The cape worn by the most skilled miners.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{14,99}", "shop_price": "99000", - "examine": "The cape worn by the most skilled miners.", - "durability": null, - "name": "Mining cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10655", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10656", + "name": "Smithing cape", + "examine": "The cape worn by master smiths.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{13,99}", "shop_price": "99000", - "examine": "The cape worn by master smiths.", - "durability": null, - "name": "Smithing cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10656", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10657", + "name": "Fishing cape", + "examine": "The cape worn by the best fishermen.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{10,99}", "shop_price": "99000", - "examine": "The cape worn by the best fishermen.", - "durability": null, - "name": "Fishing cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10657", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10658", + "name": "Cooking cape", + "examine": "The cape worn by the world's best chefs.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{7,99}", "shop_price": "99000", - "examine": "The cape worn by the world's best chefs.", - "durability": null, - "name": "Cooking cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10658", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10659", + "name": "Firemaking cape", + "examine": "The cape worn by master firelighters.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{11,99}", "shop_price": "99000", - "examine": "The cape worn by master firelighters.", - "durability": null, - "name": "Firemaking cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10659", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10660", + "name": "Woodcutting cape", + "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{8,99}", "shop_price": "99000", - "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", - "durability": null, - "name": "Woodcutting cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10660", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { + "id": "10661", + "name": "Farming cape", + "examine": "The cape worn by master farmers.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{19,99}", "shop_price": "99000", - "examine": "The cape worn by master farmers.", - "durability": null, - "name": "Farming cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10661", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by only the most experienced adventurers.", - "durability": null, - "name": "Quest point cape", - "weight": "0.4", - "archery_ticket_price": "0", "id": "10662", + "name": "Quest point cape", + "examine": "The cape worn by only the most experienced adventurers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "shop_price": "99000", + "weight": "0.4" }, { - "examine": "A surprisingly aerodynamic cape.", - "grand_exchange_price": "951", - "durability": null, - "name": "Spotted cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", "id": "10663", + "name": "Spotted cape", + "examine": "A surprisingly aerodynamic cape.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "A really surprisingly aerodynamic cape.", - "grand_exchange_price": "881", - "durability": null, - "name": "Spottier cape", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", "id": "10664", + "name": "Spottier cape", + "examine": "A really surprisingly aerodynamic cape.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "A black kitesheild with a heraldic design.", - "grand_exchange_price": "15249", - "durability": null, - "name": "Black shield(h1)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", "id": "10665", + "name": "Black shield(h1)", + "examine": "A black kitesheild with a heraldic design.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "grand_exchange_price": "16339", - "durability": null, - "name": "Adamant shield(h1)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", "id": "10666", + "name": "Adamant shield(h1)", + "examine": "A shield with a heraldic design.", "absorb": "3,0,6", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "35300", - "durability": null, - "name": "Rune shield(h1)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "10667", + "name": "Rune shield(h1)", + "examine": "A rune kiteshield with a heraldic design.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "examine": "A black kiteshield with a heraldic design.", - "grand_exchange_price": "9751", - "durability": null, - "name": "Black shield(h2)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", "id": "10668", + "name": "Black shield(h2)", + "examine": "A black kiteshield with a heraldic design.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "grand_exchange_price": "3353", - "durability": null, - "name": "Adamant shield(h2)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", "id": "10669", + "name": "Adamant shield(h2)", + "examine": "A shield with a heraldic design.", "absorb": "3,0,6", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "31903", - "durability": null, - "name": "Rune shield(h2)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", "id": "10670", + "name": "Rune shield(h2)", + "examine": "A rune kiteshield with a heraldic design.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "examine": "A black shield with a heraldic design.", - "grand_exchange_price": "6791", - "durability": null, - "name": "Black shield(h3)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", "id": "10671", + "name": "Black shield(h3)", + "examine": "A black shield with a heraldic design.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "grand_exchange_price": "5566", - "durability": null, - "name": "Adamant shield(h3)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", "id": "10672", + "name": "Adamant shield(h3)", + "examine": "A shield with a heraldic design.", "absorb": "3,0,6", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", - "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "32082", - "durability": null, - "name": "Rune shield(h3)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", "id": "10673", + "name": "Rune shield(h3)", + "examine": "A rune kiteshield with a heraldic design.", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "examine": "A black shield with a heraldic design.", - "grand_exchange_price": "5159", - "durability": null, - "name": "Black shield(h4)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", "id": "10674", - "absorb": "1,0,2", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "examine": "A shield with a heraldic design.", - "grand_exchange_price": "3219", - "durability": null, - "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" - }, - { - "requirements": "{1,40}", - "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "32351", - "durability": null, - "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" - }, - { + "name": "Black shield(h4)", "examine": "A black shield with a heraldic design.", - "grand_exchange_price": "8451", - "durability": null, - "name": "Black shield(h5)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", - "id": "10677", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { + "id": "10675", + "name": "Adamant shield(h4)", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "5905", - "durability": null, - "name": "Adamant shield(h5)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "10678", "absorb": "3,0,6", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,40}", + "id": "10676", + "name": "Rune shield(h4)", "examine": "A rune kiteshield with a heraldic design.", - "grand_exchange_price": "35200", - "durability": null, - "name": "Rune shield(h5)", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "10679", "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", - "equipment_slot": "5" + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "examine": "Those studs should provide a bit more protection. Nice trim too!", - "grand_exchange_price": "67430", - "durability": null, - "name": "Studded body (g)", - "tradeable": "true", - "weight": "5.4", + "id": "10677", + "name": "Black shield(h5)", + "examine": "A black shield with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "10678", + "name": "Adamant shield(h5)", + "examine": "A shield with a heraldic design.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "10679", + "name": "Rune shield(h5)", + "examine": "A rune kiteshield with a heraldic design.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" + }, + { "id": "10680", - "absorb": "0,3,1", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,0,0,0,0,0", - "equipment_slot": "4" - }, - { + "name": "Studded body (g)", "examine": "Those studs should provide a bit more protection. Nice trim too!", - "grand_exchange_price": "22400", - "durability": null, - "name": "Studded body (t)", - "tradeable": "true", - "weight": "5.4", - "archery_ticket_price": "0", - "id": "10681", "absorb": "0,3,1", + "archery_ticket_price": "0", "bonuses": "0,0,0,-4,8,18,25,22,8,25,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "tradeable": "true", + "weight": "5.4" }, { + "id": "10681", + "name": "Studded body (t)", + "examine": "Those studs should provide a bit more protection. Nice trim too!", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-4,8,18,25,22,8,25,0,0,0,0,0", "durability": null, + "equipment_slot": "4", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "10682", "name": "D'hide body(g)", "archery_ticket_price": "0", - "id": "10682", "bonuses": "0,0,0,-15,15,40,32,45,20,40,0,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4" }, { - "durability": null, + "id": "10683", "name": "D'hide body (t)", "archery_ticket_price": "0", - "id": "10683", "bonuses": "0,0,0,-15,15,40,32,45,20,40,0,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4" }, { - "durability": null, + "id": "10684", "name": "D'hide body (g)", "archery_ticket_price": "0", - "id": "10684", "bonuses": "0,0,0,-15,20,45,37,50,30,45,0,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4" }, { - "durability": null, + "id": "10685", "name": "D'hide body (t)", "archery_ticket_price": "0", - "id": "10685", "bonuses": "0,0,0,-15,20,45,37,50,30,45,0,0,0,0,0", + "durability": null, + "equip_audio": "2241", "equipment_slot": "4" }, { - "remove_sleeves": "true", - "examine": "I can practise magic better in this.", - "durability": null, - "name": "Wizard robe (g)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10686", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "examine": "I can do magic better in this.", - "durability": null, - "name": "Wizard robe (t)", - "tradeable": "true", - "weight": "1", + "name": "Wizard robe (g)", + "examine": "I can practise magic better in this.", "archery_ticket_price": "0", - "id": "10687", "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "requirements": "{1,20}-{6,40}", - "examine": "Enchanted Wizards robes.", "durability": null, - "weight": "1", - "absorb": "3,1,0", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "72800", - "name": "Enchanted top", "tradeable": "true", + "weight": "1" + }, + { + "id": "10687", + "name": "Wizard robe (t)", + "examine": "I can do magic better in this.", "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { "id": "10688", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" + "name": "Enchanted top", + "examine": "Enchanted Wizards robes.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "tradeable": "true", + "weight": "1" }, { - "lendable": "true", - "examine": "Slightly magical boots.", - "grand_exchange_price": "763602", - "durability": null, - "name": "Wizard boots", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10689", + "name": "Wizard boots", + "examine": "Slightly magical boots.", + "archery_ticket_price": "0", "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "lendable": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Black platebody with trim.", - "grand_exchange_price": "90744", - "durability": null, - "name": "Black platebody (t)", - "tradeable": "true", - "weight": "9.07", - "archery_ticket_price": "0", "id": "10690", + "name": "Black platebody (t)", + "examine": "Black platebody with trim.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9.07" }, { - "remove_sleeves": "true", - "examine": "Black platebody with gold trim.", - "grand_exchange_price": "230800", - "durability": null, - "name": "Black platebody (g)", - "tradeable": "true", - "weight": "9.07", - "archery_ticket_price": "0", "id": "10691", + "name": "Black platebody (g)", + "examine": "Black platebody with gold trim.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9.07" }, { - "lendable": "true", - "examine": "Your money or your life!", - "grand_exchange_price": "92519", - "durability": null, - "name": "Highwayman mask", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10692", - "equipment_slot": "0" + "name": "Highwayman mask", + "examine": "Your money or your life!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "64066", - "durability": null, - "name": "Blue beret", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, "id": "10693", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "Parlez-vous francais?(Do you speak French?)", - "grand_exchange_price": "158274", - "durability": null, - "name": "Black beret", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "10694", - "equipment_slot": "0" - }, - { - "lendable": "true", + "name": "Blue beret", "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "281900", - "durability": null, - "name": "White beret", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "hat": true, - "id": "10695", - "equipment_slot": "0" - }, - { "lendable": "true", - "examine": "Lightweight boots ideal for rangers.", - "grand_exchange_price": "11781186", - "durability": null, - "name": "Ranger boots", - "tradeable": "true", - "weight": "0.2", + "tradeable": "true" + }, + { + "id": "10694", + "name": "Black beret", + "examine": "Parlez-vous francais?(Do you speak French?)", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "hat": true, + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10695", + "name": "White beret", + "examine": "Parlez-vous francais? (Do you speak French?)", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "hat": true, + "lendable": "true", + "tradeable": "true" + }, + { "id": "10696", + "name": "Ranger boots", + "examine": "Lightweight boots ideal for rangers.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-10,8,2,3,4,2,0,0,0,0,0,0", - "equipment_slot": "10" + "durability": null, + "equipment_slot": "10", + "lendable": "true", + "tradeable": "true", + "weight": "0.2" }, { - "remove_sleeves": "true", - "examine": "Adamant platebody with trim.", - "grand_exchange_price": "79300", - "durability": null, - "name": "Adam platebody (t)", - "tradeable": "true", - "weight": "9.07", - "archery_ticket_price": "0", "id": "10697", + "name": "Adam platebody (t)", + "examine": "Adamant platebody with trim.", "absorb": "2,0,4", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9.07" }, { - "remove_sleeves": "true", - "examine": "Adamant platebody with gold trim.", - "grand_exchange_price": "207912", - "durability": null, - "name": "Adam platebody (g)", - "tradeable": "true", - "weight": "9.07", - "archery_ticket_price": "0", "id": "10698", + "name": "Adam platebody (g)", + "examine": "Adamant platebody with gold trim.", "absorb": "2,0,4", + "archery_ticket_price": "0", "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9.07" }, { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "22022", - "durability": null, - "name": "Black helm (h1)", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10699", + "name": "Black helm (h1)", + "examine": "A black helmet with a heraldic design.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" }, { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "11700", - "durability": null, - "name": "Black helm (h2)", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10700", + "name": "Black helm (h2)", + "examine": "A black helmet with a heraldic design.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" }, { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "10424", - "durability": null, - "name": "Black helm (h3)", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10701", + "name": "Black helm (h3)", + "examine": "A black helmet with a heraldic design.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" }, { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "12717", - "durability": null, - "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" - }, - { - "remove_head": "true", + "name": "Black helm (h4)", "examine": "A black helmet with a heraldic design.", - "grand_exchange_price": "17852", - "durability": null, - "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" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" }, { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "30298", - "durability": null, - "name": "Rune helm (h1)", - "tradeable": "true", + "id": "10703", + "name": "Black helm (h5)", + "examine": "A black helmet with a heraldic design.", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" + }, + { "id": "10704", + "name": "Rune helm (h1)", + "examine": "A rune helmet with a heraldic design.", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true" }, { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "26127", - "durability": null, - "name": "Rune helm (h2)", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", "id": "10705", + "name": "Rune helm (h2)", + "examine": "A rune helmet with a heraldic design.", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "3" }, { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "37333", - "durability": null, - "name": "Rune helm (h3)", - "tradeable": "true", - "weight": "4", - "archery_ticket_price": "0", "id": "10706", + "name": "Rune helm (h3)", + "examine": "A rune helmet with a heraldic design.", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "4" }, { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "29600", - "durability": null, - "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" - }, - { - "remove_head": "true", + "name": "Rune helm (h4)", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "41325", - "durability": null, - "name": "Rune helm (h5)", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10708", "absorb": "1,0,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" }, { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "28407", - "durability": null, - "name": "Adamant helm (h1)", - "tradeable": "true", - "weight": "3", + "id": "10708", + "name": "Rune helm (h5)", + "examine": "A rune helmet with a heraldic design.", + "absorb": "1,0,3", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2" + }, + { "id": "10709", + "name": "Adamant helm (h1)", + "examine": "An adamant helmet with a heraldic design.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" }, { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "9839", - "durability": null, - "name": "Adamant helm (h2)", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", "id": "10710", + "name": "Adamant helm (h2)", + "examine": "An adamant helmet with a heraldic design.", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" }, { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "8485", - "durability": null, - "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" - }, - { - "remove_head": "true", - "examine": "An Adamant helmet with a heraldic design.", - "grand_exchange_price": "10774", - "durability": null, - "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" - }, - { - "remove_head": "true", + "name": "Adamant helm (h3)", "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "17390", - "durability": null, - "name": "Adamant helm (h5)", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "10713", "absorb": "1,0,2", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" }, { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "weight": "2", + "id": "10712", + "name": "Adamant helm (h4)", + "examine": "An Adamant helmet with a heraldic design.", + "absorb": "1,0,2", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10713", + "name": "Adamant helm (h5)", + "examine": "An adamant helmet with a heraldic design.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { "id": "10714", - "equipment_slot": "4" + "name": "Bob shirt", + "examine": "See table", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2" }, { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10715", - "equipment_slot": "4" + "name": "Bob shirt", + "examine": "See table", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2" }, { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10716", - "equipment_slot": "4" + "name": "Bob shirt", + "examine": "See table", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2" }, { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10717", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, "name": "Bob shirt", - "tradeable": "true", - "weight": "2", + "examine": "See table", "archery_ticket_price": "0", - "id": "10718", - "equipment_slot": "4" - }, - { - "examine": "A very powerful dragonstone amulet.", - "grand_exchange_price": "1", "durability": null, - "name": "Amulet of glory(t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10719", - "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", - "equipment_slot": "2" - }, - { - "examine": "A cape from the almighty god Guthix.", - "durability": null, - "name": "Guthix cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10720", - "bonuses": "0,0,0,10,0,1,1,2,10,0,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "remove_head": "true", - "examine": "Now that's just silly.", - "durability": null, - "name": "Frog mask", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10721", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "A reindeer hat and a matching flashing nose.", - "durability": null, - "name": "Reindeer hat", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10722" - }, - { - "remove_head": "true", - "destroy_message": "You may get another from Diango in Draynor Village.", - "examine": "Better not light it!", - "durability": null, - "name": "Jack lantern mask", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10723", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Skeleton feet.", - "durability": null, - "name": "Skeleton boots", - "weight": "2", - "archery_ticket_price": "0", - "id": "10724", - "equipment_slot": "10" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some skeletal gloves.", - "durability": null, - "name": "Skeleton gloves", - "weight": "2", - "archery_ticket_price": "0", - "id": "10725", - "equipment_slot": "9" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Does my pelvis look big in this?", - "durability": null, - "name": "Skeleton leggings", - "weight": "2", - "archery_ticket_price": "0", - "id": "10726", - "equipment_slot": "7" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", + "equipment_slot": "4", "remove_sleeves": "true", - "examine": "The shirt of a fully body skeleton costume.", - "durability": null, - "name": "Skeleton shirt", - "weight": "2", - "archery_ticket_price": "0", - "id": "10727", - "equipment_slot": "4" + "tradeable": "true", + "weight": "2" }, { + "id": "10718", + "name": "Bob shirt", + "examine": "See table", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10719", + "name": "Amulet of glory(t)", + "examine": "A very powerful dragonstone amulet.", + "archery_ticket_price": "0", + "bonuses": "10,10,10,10,10,3,3,3,3,3,3,6,3,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "10720", + "name": "Guthix cape", + "examine": "A cape from the almighty god Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,10,0,1,1,2,10,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "weight": "0.4" + }, + { + "id": "10721", + "name": "Frog mask", + "examine": "Now that's just silly.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A scary skeleton mask.", - "durability": null, - "name": "Skeleton mask", - "weight": "2", - "archery_ticket_price": "0", - "id": "10728", - "equipment_slot": "0" + "weight": "0.4" }, { - "examine": "A ring given to you by the Easter Bunny.", - "durability": null, - "name": "Easter ring", + "id": "10722", + "name": "Reindeer hat", + "examine": "A reindeer hat and a matching flashing nose.", "archery_ticket_price": "0", - "id": "10729" - }, - { - "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", - "examine": "I've got no strings ... oh hang on!", - "durability": null, - "name": "Blue marionette", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "10730" + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "examine": "Alas...I hardly knew him.", + "id": "10723", + "name": "Jack lantern mask", + "examine": "Better not light it!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You may get another from Diango in Draynor Village.", "durability": null, + "equip_audio": "3227", + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "tradeable": "false" + }, + { + "id": "10724", + "name": "Skeleton boots", + "examine": "Skeleton feet.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "10", + "weight": "2" + }, + { + "id": "10725", + "name": "Skeleton gloves", + "examine": "Some skeletal gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "weight": "2" + }, + { + "id": "10726", + "name": "Skeleton leggings", + "examine": "Does my pelvis look big in this?", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "7", + "weight": "2" + }, + { + "id": "10727", + "name": "Skeleton shirt", + "examine": "The shirt of a fully body skeleton costume.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" + }, + { + "id": "10728", + "name": "Skeleton mask", + "examine": "A scary skeleton mask.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "2" + }, + { + "id": "10729", + "name": "Easter ring", + "examine": "A ring given to you by the Easter Bunny.", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "", + "equipment_slot": "" + }, + { + "id": "10730", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10731", "name": "Zombie head", + "examine": "Alas...I hardly knew him.", "archery_ticket_price": "0", - "id": "10731" + "durability": null }, { - "turn90cw_anim": "821", - "examine": "Perhaps not the most powerful weapon in RuneScape.", - "walk_anim": "1830", - "durability": null, - "weight": "0.4", - "turn90ccw_anim": "822", + "id": "10732", + "name": "Rubber chicken", + "examine": "Perhaps not the most powerful weapon in 2009Scape.", + "archery_ticket_price": "0", + "attack_audios": "2257,2257,2257", "attack_speed": "4", - "weapon_interface": "12", - "turn180_anim": "1830", - "render_anim": "182", + "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0", + "durability": null, + "equip_audio": "2238", "equipment_slot": "3", "fun_weapon": "true", + "render_anim": "182", + "run_anim": "824", "stand_anim": "1832", - "name": "Rubber chicken", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "10732", "stand_turn_anim": "823", - "bonuses": "-100,-100,-50,0,0,0,0,0,0,0,0,-10,0,0,0" - }, - { - "examine": "A gift from Santa.", - "durability": null, - "name": "Yo-yo", - "archery_ticket_price": "0", - "id": "10733" - }, - { - "examine": "A rabbit-like adornment.", - "durability": null, - "name": "Bunny ears", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10734", - "equipment_slot": "0" - }, - { - "turn90cw_anim": "821", - "examine": "It's a Scythe.", - "walk_anim": "819", - "durability": null, - "weight": "3", + "turn180_anim": "1830", "turn90ccw_anim": "822", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "820", - "render_anim": "1383", - "equipment_slot": "3", - "stand_anim": "847", - "name": "Scythe", - "run_anim": "824", + "turn90cw_anim": "821", + "walk_anim": "1830", + "weapon_interface": "12", + "weight": "0.4" + }, + { + "id": "10733", + "name": "Yo-yo", + "examine": "A gift from Santa.", "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10734", + "name": "Bunny ears", + "examine": "A rabbit-like adornment.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "weight": "0.2" + }, + { "id": "10735", + "name": "Scythe", + "examine": "It's a Scythe.", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "3,8,3,0,0,0,3,1,0,0,0,10,0,0,0", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "1383", + "run_anim": "824", + "stand_anim": "847", "stand_turn_anim": "823", - "bonuses": "3,8,3,0,0,0,3,1,0,0,0,10,0,0,0" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "819", + "weight": "3" }, { - "examine": "An enchanted ruby amulet with a nice trim.", - "grand_exchange_price": "443800", - "durability": null, - "name": "Strength amulet(t)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10736", + "name": "Strength amulet(t)", + "examine": "An enchanted ruby amulet with a nice trim.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,10,0,0,0", - "equipment_slot": "2" + "durability": null, + "equipment_slot": "2", + "tradeable": "true" }, { - "examine": "An enchanted sapphire amulet of magic.", - "grand_exchange_price": "67158", - "durability": null, - "name": "Amulet of magic(t)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10738", + "name": "Amulet of magic(t)", + "examine": "An enchanted sapphire amulet of magic.", + "archery_ticket_price": "0", "bonuses": "0,0,0,10,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "2" + "durability": null, + "equipment_slot": "2", + "tradeable": "true" }, { - "remove_head": "true", - "lendable": "true", - "examine": "A big 'do about nothing.", - "grand_exchange_price": "36879", - "durability": null, - "name": "A powdered wig", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "10740", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "These'll help me stay alive.", - "grand_exchange_price": "559284", - "durability": null, - "name": "Flared trousers", - "tradeable": "true", - "weight": "1", + "name": "A powdered wig", + "examine": "A big 'do about nothing.", "archery_ticket_price": "0", - "id": "10742", - "equipment_slot": "7" - }, - { - "lendable": "true", - "examine": "Alas, someone has slashed my pantaloons.", - "grand_exchange_price": "12022", "durability": null, - "name": "Pantaloons", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10744", - "equipment_slot": "7" - }, - { + "equipment_slot": "0", + "lendable": "true", "remove_head": "true", - "lendable": "true", - "examine": "A cap for wearing whil...zzzzzzzzz", - "grand_exchange_price": "376084", - "durability": null, - "name": "Sleeping cap", "tradeable": "true", - "weight": "2", + "weight": "2" + }, + { + "id": "10742", + "name": "Flared trousers", + "examine": "These'll help me stay alive.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10744", + "name": "Pantaloons", + "examine": "Alas, someone has slashed my pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { "id": "10746", - "equipment_slot": "0" + "name": "Sleeping cap", + "examine": "A cap for wearing whil...zzzzzzzzz", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "2" }, { - "remove_sleeves": "true", - "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", - "durability": null, - "name": "Elegant shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10748", - "equipment_slot": "4" + "name": "Elegant shirt", + "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", - "durability": null, - "name": "Elegant shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10750", - "equipment_slot": "4" + "name": "Elegant shirt", + "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", - "durability": null, - "name": "Elegant shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10752", - "equipment_slot": "4" + "name": "Elegant shirt", + "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", - "durability": null, - "name": "Elegant shirt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10754", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", - "durability": null, "name": "Elegant shirt", - "tradeable": "true", - "weight": "1", + "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { "id": "10756", - "equipment_slot": "4" + "name": "Elegant shirt", + "examine": "Blouse: A well made elegant ladies' [colour] blouse.Skirt: A rather elegant [colour] skirt.Shirt: A well made elegant mans' [colour] shirt.Legs: A rather elegant [colour] pair of pantaloons.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" }, { - "examine": "Stylish!", - "grand_exchange_price": "56600", - "durability": null, - "name": "Red boater", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10758", - "equipment_slot": "0" + "name": "Red boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "examine": "Stylish!", - "grand_exchange_price": "20300", - "durability": null, - "name": "Orange boater", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10760", - "equipment_slot": "0" + "name": "Orange boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "examine": "Stylish!", - "grand_exchange_price": "59940", - "durability": null, - "name": "Green boater", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10762", - "equipment_slot": "0" + "name": "Green boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "examine": "Stylish!", - "grand_exchange_price": "54236", - "durability": null, - "name": "Blue boater", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10764", - "equipment_slot": "0" - }, - { + "name": "Blue boater", "examine": "Stylish!", - "grand_exchange_price": "56495", - "durability": null, - "name": "Black boater", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { "id": "10766", - "equipment_slot": "0" + "name": "Black boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "lendable": "true", - "examine": "A minimalist's hat.", - "grand_exchange_price": "114319", - "durability": null, - "name": "Red headband", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10768", - "equipment_slot": "0" + "name": "Red headband", + "examine": "A minimalist's hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" }, { - "lendable": "true", - "examine": "A minimalist's hat.", - "grand_exchange_price": "41778", - "durability": null, - "name": "Black headband", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10770", - "equipment_slot": "0" - }, - { - "lendable": "true", + "name": "Black headband", "examine": "A minimalist's hat.", - "grand_exchange_price": "21764", - "durability": null, - "name": "Brown headband", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { "id": "10772", - "equipment_slot": "0" + "name": "Brown headband", + "examine": "A minimalist's hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" }, { - "examine": "Shiver me timbers!", - "grand_exchange_price": "83200", - "durability": null, - "name": "Pirate's hat", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10774", - "equipment_slot": "0" + "name": "Pirate's hat", + "examine": "Shiver me timbers!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" }, { - "examine": "Rune platebody in the colours of Zamorak.", - "durability": null, - "weight": "9", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "647800", - "name": "Zamorak platebody", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10776", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "examine": "Rune platebody in the colours of Saradomin.", - "durability": null, - "weight": "9.07", + "name": "Zamorak platebody", + "examine": "Rune platebody in the colours of Zamorak.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "1097689", - "name": "Saradomin plate", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "9" + }, + { "id": "10778", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "examine": "Rune platebody in the colours of Guthix.", - "durability": null, - "weight": "9.07", + "name": "Saradomin plate", + "examine": "Rune platebody in the colours of Saradomin.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "241600", - "name": "Guthix platebody", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "9.07" + }, + { "id": "10780", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "examine": "Rune platebody with complete gold trim & plating.", - "durability": null, - "weight": "10", + "name": "Guthix platebody", + "examine": "Rune platebody in the colours of Guthix.", "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", - "grand_exchange_price": "1653821", - "name": "Gilded platebody", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "9.07" + }, + { "id": "10782", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" + "name": "Gilded platebody", + "examine": "Rune platebody with complete gold trim & plating.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "10" }, { - "remove_sleeves": "true", - "examine": "Blessed vestments of Saradomin.", - "grand_exchange_price": "106389", - "durability": null, - "name": "Saradomin robe top", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10784", + "name": "Saradomin robe top", + "examine": "Blessed vestments of Saradomin.", + "archery_ticket_price": "0", "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true" }, { - "remove_sleeves": "true", - "examine": "Zamorak Vestments.", - "grand_exchange_price": "151000", - "durability": null, - "name": "Zamorak robe top", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10786", + "name": "Zamorak robe top", + "examine": "Zamorak Vestments.", + "archery_ticket_price": "0", "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Blessed vestments of Guthix.", - "grand_exchange_price": "80181", - "durability": null, - "name": "Guthix robe top", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "10788", + "name": "Guthix robe top", + "examine": "Blessed vestments of Guthix.", + "archery_ticket_price": "0", "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "Zamorak blessed dragonhide body armour.", - "grand_exchange_price": "84966", - "durability": null, - "name": "Zamorak d'hide", - "tradeable": "true", - "weight": "6", - "archery_ticket_price": "0", "id": "10790", + "name": "Zamorak d'hide", + "examine": "Zamorak blessed dragonhide body armour.", "absorb": "0,6,3", + "archery_ticket_price": "0", "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "shop_price": "13000", - "examine": "Saradomin blessed dragonhide body armour.", "durability": null, - "destroy": "true", - "weight": "6", - "absorb": "0,6,3", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "92762", - "name": "Saradomin d'hide", + "requirements": "{1,40}-{4,70}", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "6" + }, + { "id": "10792", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0" - }, - { - "shop_price": "13", - "examine": "Guthix blessed dragonhide body armour.", - "durability": null, - "weight": "6", + "name": "Saradomin d'hide", + "examine": "Saradomin blessed dragonhide body armour.", "absorb": "0,6,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "22384", - "name": "Guthix dragonhide", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10794", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0" - }, - { - "lendable": "true", - "examine": "Endorsed by Robin Hood.", - "grand_exchange_price": "3070557", - "durability": null, - "name": "Robin hood hat", - "tradeable": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10796", - "bonuses": "0,0,0,-10,8,4,6,8,4,4,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "examine": "Rune platebody with gold trim.", - "durability": null, - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "231400", - "name": "Rune platebody (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10798", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "examine": "Rune platebody with trim.", - "durability": null, - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "61100", - "name": "Rune platebody (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10800", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "lendable": "true", - "examine": "All for one and one for all!", - "grand_exchange_price": "56677", - "durability": null, - "name": "Tan cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10802", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "All for one and one for all!", - "grand_exchange_price": "18942", - "durability": null, - "name": "Dark cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10804", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "All for one and one for all!", - "grand_exchange_price": "421305", - "durability": null, - "name": "Black cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10806", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "1000", - "examine": "Arctic Pine logs prepared with sacred oil for a funeral pyre", - "grand_exchange_price": "3453", - "durability": null, - "name": "Arctic pyre logs", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10808" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3453", - "durability": null, - "name": "Arctic pyre logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10809" - }, - { - "ge_buy_limit": "25000", - "examine": "Log cut from an arctic pine.", - "grand_exchange_price": "90", - "durability": null, - "name": "Arctic pine logs", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10810", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "90", - "durability": null, - "name": "Arctic pine logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10811" - }, - { - "ge_buy_limit": "1000", - "examine": "Used to repair bridges.", - "grand_exchange_price": "18", - "durability": null, - "name": "Split log", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "10812" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "18", - "durability": null, - "name": "Split log", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10813" - }, - { - "shop_price": "120", - "ge_buy_limit": "1000", - "examine": "I can spin this into rope.", - "grand_exchange_price": "113", - "durability": null, - "name": "Hair", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10814" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "113", - "durability": null, - "name": "Hair", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10815" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "I need to cook this.", - "grand_exchange_price": "14", - "durability": null, - "name": "Raw yak meat", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "10816" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "14", - "durability": null, - "name": "Raw yak meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10817" - }, - { - "shop_price": "50", - "ge_buy_limit": "5000", - "examine": "Thakkrad, of Neitiznot, can cure this.", - "grand_exchange_price": "301", - "durability": null, - "name": "Yak-hide", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10818" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "301", - "durability": null, - "name": "Yak-hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10819" - }, - { - "ge_buy_limit": "5000", - "examine": "Ready to be cut and sewn into armour.", - "grand_exchange_price": "396", - "durability": null, - "name": "Cured yak-hide", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10820" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "396", - "durability": null, - "name": "Cured yak-hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10821" - }, - { - "requirements": "{1,20}", - "ge_buy_limit": "100", - "examine": "Smelly yak body armour.", - "durability": null, - "weight": "3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "122", - "name": "Yak-hide armour", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10822", - "bonuses": "0,0,0,-5,0,25,20,15,-2,25,5,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "122", - "durability": null, - "name": "Yak-hide armour", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10823" - }, - { - "requirements": "{1,20}", - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "examine": "Leg protection, made from smelly yak-hide.", - "durability": null, - "name": "Yak-hide armour", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "2", - "id": "10824", - "bonuses": "0,0,0,-5,10,25,20,15,-2,10,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Yak-hide armour", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10825" - }, - { - "requirements": "{1,25}", - "ge_buy_limit": "100", - "examine": "A wooden shield with a rope rim.", - "durability": null, + "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0", "destroy": "true", - "weight": "3", - "equipment_slot": "5", - "grand_exchange_price": "402", - "name": "Fremennik round shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10826", - "bonuses": "0,0,0,-10,-10,17,17,31,-4,33,20,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "402", "durability": null, - "name": "Fremennik round shield", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,40}-{4,70}", + "shop_price": "13000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10827" + "weight": "6" }, { + "id": "10794", + "name": "Guthix dragonhide", + "examine": "Guthix blessed dragonhide body armour.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,40}-{4,70}", + "shop_price": "13", + "tradeable": "true", + "weight": "6" + }, + { + "id": "10796", + "name": "Robin hood hat", + "examine": "Endorsed by Robin Hood.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,8,4,6,8,4,4,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "requirements": "{4,40}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10798", + "name": "Rune platebody (g)", + "examine": "Rune platebody with gold trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "10800", + "name": "Rune platebody (t)", + "examine": "Rune platebody with trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "10802", + "name": "Tan cavalier", + "examine": "All for one and one for all!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10804", + "name": "Dark cavalier", + "examine": "All for one and one for all!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10806", + "name": "Black cavalier", + "examine": "All for one and one for all!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10808", + "name": "Arctic pyre logs", + "examine": "Arctic Pine logs prepared with sacred oil for a funeral pyre", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10809", + "name": "Arctic pyre logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "10810", + "name": "Arctic pine logs", + "examine": "Log cut from an arctic pine.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10811", + "name": "Arctic pine logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "10812", + "name": "Split log", + "examine": "Used to repair bridges.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10813", + "name": "Split log", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "10814", + "name": "Hair", + "examine": "I can spin this into rope.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "10815", + "name": "Hair", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "10816", + "name": "Raw yak meat", + "examine": "I need to cook this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10817", + "name": "Raw yak meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "10818", + "name": "Yak-hide", + "examine": "Thakkrad, of Neitiznot, can cure this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10819", + "name": "Yak-hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10820", + "name": "Cured yak-hide", + "examine": "Ready to be cut and sewn into armour.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "10821", + "name": "Cured yak-hide", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10822", + "name": "Yak-hide armour", + "examine": "Smelly yak body armour.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,0,25,20,15,-2,25,5,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{1,20}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10823", + "name": "Yak-hide armour", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10824", + "name": "Yak-hide armour", + "examine": "Leg protection, made from smelly yak-hide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,10,25,20,15,-2,10,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "requirements": "{1,20}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10825", + "name": "Yak-hide armour", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10826", + "name": "Fremennik round shield", + "examine": "A wooden shield with a rope rim.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-10,17,17,31,-4,33,20,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,25}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10827", + "name": "Fremennik round shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10828", + "name": "Helm of neitiznot", + "examine": "A gift from Neitiznot's Burgher.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{1,55}", "shop_price": "50000", - "ge_buy_limit": "100", - "examine": "A gift from Neitiznot's Burgher.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,4", - "equipment_slot": "0", - "grand_exchange_price": "44800", - "name": "Helm of neitiznot", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10828", - "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0" + "weight": "2.2" }, { - "destroy_message": "King Sorvott of Jatizso will give you another one, grudgingly.", - "examine": "The document is stamped with King Gjuki Sorvott IV's royal seal.", - "durability": null, + "id": "10830", "name": "Royal decree", - "tradeable": "false", - "destroy": "true", + "examine": "The document is stamped with King Gjuki Sorvott IV's royal seal.", "archery_ticket_price": "0", - "id": "10830" + "destroy": "true", + "destroy_message": "King Sorvott of Jatizso will give you another one, grudgingly.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Are you sure? You can replace it by talking to King Sorvott.", - "examine": "An empty tax bag.", - "durability": null, + "id": "10831", "name": "Empty tax bag", - "tradeable": "false", - "destroy": "true", + "examine": "An empty tax bag.", "archery_ticket_price": "0", - "id": "10831" + "destroy": "true", + "destroy_message": "Are you sure? You can replace it by talking to King Sorvott.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "examine": "A silly hat with bells.", - "durability": null, - "name": "Silly jester hat", - "tradeable": "false", - "destroy": "true", - "weight": "2.7", - "archery_ticket_price": "0", "id": "10836", + "name": "Silly jester hat", + "examine": "A silly hat with bells.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", - "equipment_slot": "0" + "destroy": "true", + "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "2.7" }, { - "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "remove_sleeves": "true", - "examine": "A jester's jangly top.", - "durability": null, - "name": "Silly jester top", - "tradeable": "false", - "destroy": "true", - "weight": "6.8", - "archery_ticket_price": "0", "id": "10837", + "name": "Silly jester top", + "examine": "A jester's jangly top.", + "archery_ticket_price": "0", "bonuses": "0,0,0,0,0,0,0,0,5,-5,5,0,0,0,0", - "equipment_slot": "4" - }, - { + "destroy": "true", "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "examine": "Silly jester tights.", "durability": null, - "name": "Silly jester tights", - "tradeable": "false", - "destroy": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "10838", - "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "examine": "Silly jester boots.", - "durability": null, - "name": "Silly jester boots", - "tradeable": "false", - "destroy": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "10839", - "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "durability": null, - "name": "A jester stick", - "archery_ticket_price": "0", - "id": "10840", - "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", - "durability": null, - "name": "Decapitated head", - "weight": "3", - "archery_ticket_price": "0", - "id": "10842" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "44800", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Helm of neitiznot", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10843" - }, - { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, - "name": "Spring sq'irk", - "archery_ticket_price": "0", - "id": "10844" - }, - { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, - "name": "Summer sq'irk", - "archery_ticket_price": "0", - "id": "10845" - }, - { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, - "name": "Autumn sq'irk", - "archery_ticket_price": "0", - "id": "10846" - }, - { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, - "name": "Winter sq'irk", - "archery_ticket_price": "0", - "id": "10847" - }, - { - "examine": "Made from 4 spring sq'irks.", - "durability": null, - "name": "Spring sq'irkjuice", - "weight": "1", - "archery_ticket_price": "0", - "id": "10848" - }, - { - "examine": "Made from 2 summer sq'irks.", - "durability": null, - "name": "Summer sq'irkjuice", - "weight": "1", - "archery_ticket_price": "0", - "id": "10849" - }, - { - "examine": "Made from 3 autumn sq'irks.", - "durability": null, - "name": "Autumn sq'irkjuice", - "weight": "1", - "archery_ticket_price": "0", - "id": "10850" - }, - { - "examine": "Made from 5 winter sq'irks.", - "durability": null, - "name": "Winter sq'irkjuice", - "archery_ticket_price": "0", - "id": "10851" - }, - { - "destroy_message": "You will need to speak to Sin Seer in Seers' Village to get another one.", - "shop_price": "40", - "examine": "Her writing is too scrawled to make out what it says.", - "durability": null, - "name": "Sin seer's note", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10856" - }, - { - "destroy_message": "You will need to speak to General Khazard, near the Fremennik Province, to get another.", - "examine": "A severed human leg.", - "durability": null, - "name": "Severed leg", - "tradeable": "false", - "destroy": "true", - "weight": "12", - "archery_ticket_price": "0", - "id": "10857" - }, - { - "turn90cw_anim": "7044", - "examine": "A shadow sword.", - "walk_anim": "7046", - "durability": null, - "destroy": "true", - "weight": "3.6", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "7", - "turn180_anim": "7045", - "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.", - "stand_anim": "7047", - "name": "Shadow sword", - "tradeable": "false", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "10858", - "stand_turn_anim": "7040", - "bonuses": "-4,27,21,4,0,0,0,0,4,-1,0,26,0,0,0" - }, - { - "examine": "You can put nice, hot tea in it.", - "durability": null, - "name": "Tea flask", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10859" - }, - { - "examine": "You can put nice, hot tea in it.", - "durability": null, - "name": "Tea flask", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10860" - }, - { - "examine": "You can put nice, hot tea in it.", - "durability": null, - "name": "Tea flask", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10861" - }, - { - "destroy_message": "You'll have to find another within the Tower of Life!", - "examine": "Can't get any safer than this.", - "durability": null, - "name": "Hard hat", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10862", - "equipment_slot": "0" - }, - { - "destroy_message": "You'll have to find another within the Tower of Life!", - "remove_sleeves": "true", - "examine": "Very fetching.", - "durability": null, - "name": "Builder's shirt", - "tradeable": "false", - "destroy": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "10863", - "equipment_slot": "4" - }, - { - "destroy_message": "You'll have to find another within the Tower of Life!", - "examine": "Slightly tatty in my opinion.", - "durability": null, - "name": "Builder's trousers", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10864", - "equipment_slot": "7" - }, - { - "destroy_message": "You'll have to find another within the Tower of Life!", - "examine": "They'll offer good protection for my toes.", - "durability": null, - "name": "Builder's boots", - "tradeable": "false", - "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10865", - "equipment_slot": "10" - }, - { - "examine": "Used to bolt things together.", - "durability": null, - "name": "Rivets", - "archery_ticket_price": "0", - "id": "10866" - }, - { - "examine": "Some magical fluid to bind surfaces.", - "durability": null, - "name": "Binding fluid", - "archery_ticket_price": "0", - "id": "10870" - }, - { - "examine": "A pipe that belongs in a cannon stand.", - "durability": null, - "name": "Pipe", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "10871" - }, - { - "examine": "A ring used to join two pipes together.", - "durability": null, - "name": "Pipe ring", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10872" - }, - { - "examine": "A sheet of metal.", - "durability": null, - "name": "Metal sheet", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10873" - }, - { - "examine": "Bound to have some use.", - "durability": null, - "name": "Coloured ball", - "archery_ticket_price": "0", - "id": "10874" - }, - { - "examine": "A wheel used to control valves.", - "durability": null, - "name": "Valve wheel", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10875" - }, - { - "examine": "Some unknown metal substance.", - "durability": null, - "name": "Metal bar", - "archery_ticket_price": "0", - "id": "10876" - }, - { - "shop_price": "128", - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Plain satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10877", - "equipment_slot": "5" - }, - { - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Green satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10878", - "equipment_slot": "5" - }, - { - "shop_price": "128", - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Red satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10879", - "equipment_slot": "5" - }, - { - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Black satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10880", - "equipment_slot": "5" - }, - { - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Gold satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10881", - "equipment_slot": "5" - }, - { - "examine": "I can keep my grub in here.", - "durability": null, - "name": "Rune satchel", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "10882", - "equipment_slot": "5" - }, - { - "destroy_message": "You'll have to find another within the Tower of Life!", - "examine": "Can't get any safer than this.", - "durability": null, - "name": "Hard hat", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10883", - "equipment_slot": "0" - }, - { - "examine": "A fuse.", - "durability": null, - "name": "Fuse", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10884" - }, - { - "examine": "A small keg of gunpowder.", - "durability": null, - "name": "Keg", - "weight": "12", - "archery_ticket_price": "0", - "id": "10885" - }, - { - "examine": "Used to cure poison through prayer.", - "durability": null, - "name": "Prayer book", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10886" - }, - { - "turn90cw_anim": "5867", - "examine": "This is likely to put my back out... (when fixed) OR It's broken. I should see if a pirate smith can fix it. (when broken)", - "walk_anim": "5867", - "has_special": "true", - "durability": null, - "weight": "30", - "turn90ccw_anim": "5867", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "8", - "turn180_anim": "5867", - "defence_anim": "5866", - "render_anim": "985", - "equipment_slot": "3", - "attack_anims": "5865,5865,5865,5865", - "destroy_message": "If you drop the Anchor it will be destroyed, and you will need to get a new one from Brother Tranquillity, then pay Smith to re", - "stand_anim": "5869", - "name": "Barrelchest anchor", - "run_anim": "5868", - "archery_ticket_price": "0", - "id": "10887", - "stand_turn_anim": "823", - "bonuses": "-2,10,92,0,0,0,0,0,0,0,0,100,0,0,0" - }, - { - "examine": "This is likely to put my back out... (when fixed) OR It's broken. I should see if a pirate smith can fix it. (when broken)", - "durability": null, - "name": "Barrelchest anchor", - "weight": "30", - "archery_ticket_price": "0", - "attack_speed": "6", - "id": "10888", - "bonuses": "-2,10,92,0,0,0,0,0,0,0,0,100,0,0,0" - }, - { - "destroy_message": "If you drop this lamp it will be destroyed, and you will need to see Brother Tranquility for another.", - "examine": "This will answer my prayers.", - "durability": null, - "name": "Blessed lamp", - "archery_ticket_price": "0", - "id": "10889" - }, - { - "destroy_message": "This book is old and delicate. You can reclaim this item from the place you found it.ping it here will destroy it and you will have to search the Edgeville Monastery bookcase for another.", - "examine": "Used to cure poison through prayer.", - "durability": null, - "name": "Prayer book", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10890" - }, - { - "ge_buy_limit": "100", - "examine": "A wooden cat toy.", - "grand_exchange_price": "922", - "durability": null, - "name": "Wooden cat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10891" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "922", - "durability": null, - "name": "Wooden cat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10892" - }, - { - "examine": "A cranial clamp.", - "durability": null, - "name": "Cranial clamp", - "archery_ticket_price": "0", - "id": "10893" - }, - { - "examine": "Some tongs for use with brains.", - "durability": null, - "name": "Brain tongs", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10894" - }, - { - "examine": "A bell jar for covering things.", - "durability": null, - "name": "Bell jar", - "weight": "1", - "archery_ticket_price": "0", - "id": "10895" - }, - { - "examine": "A silver whistle.", - "durability": null, - "name": "Wolf whistle", - "archery_ticket_price": "0", - "id": "10896" - }, - { - "examine": "A shipping order list.", - "durability": null, - "name": "Shipping order", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10897" - }, - { - "examine": "A small keg of gunpowder.", - "durability": null, - "name": "Keg", - "weight": "12", - "archery_ticket_price": "0", - "id": "10898" - }, - { - "examine": "Part of a crate.", - "durability": null, - "name": "Crate part", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10899" - }, - { - "examine": "One skull staple.", - "durability": null, - "name": "Skull staple", - "archery_ticket_price": "0", - "id": "10904" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "8420", - "durability": null, - "name": "Mixture - step 1(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10909" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8420", - "durability": null, - "name": "Mixture - step 1(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10910" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "6543", - "durability": null, - "name": "Mixture - step 1(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10911" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6543", - "durability": null, - "name": "Mixture - step 1(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10912" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "4335", - "durability": null, - "name": "Mixture - step 1(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10913" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4335", - "durability": null, - "name": "Mixture - step 1(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10914" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "2168", - "durability": null, - "name": "Mixture - step 1(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10915" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2168", - "durability": null, - "name": "Mixture - step 1(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10916" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "8665", - "durability": null, - "name": "Mixture - step 2(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10917" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8665", - "durability": null, - "name": "Mixture - step 2(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10918" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "6695", - "durability": null, - "name": "Mixture - step 2(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10919" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6695", - "durability": null, - "name": "Mixture - step 2(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10920" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "4335", - "durability": null, - "name": "Mixture - step 2(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10921" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4335", - "durability": null, - "name": "Mixture - step 2(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10922" - }, - { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "2168", - "durability": null, - "name": "Mixture - step 2(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10923" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2168", - "durability": null, - "name": "Mixture - step 2(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10924" - }, - { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "15900", - "durability": null, - "name": "Sanfew serum(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10925" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15900", - "durability": null, - "name": "Sanfew serum(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10926" - }, - { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "12000", - "durability": null, - "name": "Sanfew serum(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10927" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12000", - "durability": null, - "name": "Sanfew serum(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10928" - }, - { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "10200", - "durability": null, - "name": "Sanfew serum(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10929" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "10200", - "durability": null, - "name": "Sanfew serum(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10930" - }, - { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "6114", - "durability": null, - "name": "Sanfew serum(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10931" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "6114", - "durability": null, - "name": "Sanfew serum(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10932" - }, - { - "examine": "You certainly will be alright wearing these.", - "durability": null, - "name": "Lumberjack boots", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "10933", - "equipment_slot": "10" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10934" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10935" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10936" - }, - { - "ge_buy_limit": "100", - "examine": "The claws from a nail beast", - "grand_exchange_price": "203", - "durability": null, - "name": "Nail beast nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10937" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "203", - "durability": null, - "name": "Nail beast nails", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10938" - }, - { - "remove_sleeves": "true", - "examine": "You certainly will be alright wearing this.", - "durability": null, - "name": "Lumberjack top", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "10939", - "equipment_slot": "4" - }, - { - "examine": "You certainly will be alright wearing these.", - "durability": null, - "name": "Lumberjack legs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "10940", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "examine": "You certainly will be alright wearing this.", - "durability": null, - "name": "Lumberjack hat", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "10941", - "equipment_slot": "0" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10942" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10943" - }, - { - "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", - "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", - "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10944" - }, - { - "remove_sleeves": "true", - "examine": "You certainly will be alright wearing this.", - "durability": null, - "name": "Lumberjack top", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "10945", - "equipment_slot": "4" - }, - { - "examine": "One skull staple.", - "durability": null, - "name": "Skull staples", - "archery_ticket_price": "0", - "id": "10950" - }, - { - "examine": "One skull staple.", - "durability": null, - "name": "Skull staples", - "archery_ticket_price": "0", - "id": "10951" - }, - { - "requirements": "{18,39}", - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "Don't make anyone jump when you ring this!", - "grand_exchange_price": "57", - "durability": null, - "name": "Slayer bell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10952" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "57", - "durability": null, - "name": "Slayer bell", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10953" - }, - { - "requirements": "{1,25}-{4,25}", - "shop_price": "1000", - "ge_buy_limit": "100", - "examine": "Armour made out of Frog hide.", - "durability": null, - "weight": "3", - "absorb": "0,3,1", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "1797", - "name": "Frog-leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10954", - "bonuses": "0,0,0,-5,10,23,26,30,15,32,20,0,0,0,0" + "tradeable": "false", + "weight": "6.8" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1797", + "id": "10838", + "name": "Silly jester tights", + "examine": "Silly jester tights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", "durability": null, - "name": "Frog-leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10955" + "equipment_slot": "7", + "tradeable": "false", + "weight": "0.9" }, { + "id": "10839", + "name": "Silly jester boots", + "examine": "Silly jester boots.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", + "durability": null, + "equipment_slot": "10", + "tradeable": "false", + "weight": "1.3" + }, + { + "id": "10840", + "name": "A jester stick", + "alchemizable": "true", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "10842", + "name": "Decapitated head", + "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "3" + }, + { + "id": "10843", + "name": "Helm of neitiznot", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10844", + "name": "Spring sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10845", + "name": "Summer sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10846", + "name": "Autumn sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10847", + "name": "Winter sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10848", + "name": "Spring sq'irkjuice", + "examine": "Made from 4 spring sq'irks.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "10849", + "name": "Summer sq'irkjuice", + "examine": "Made from 2 summer sq'irks.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "10850", + "name": "Autumn sq'irkjuice", + "examine": "Made from 3 autumn sq'irks.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "10851", + "name": "Winter sq'irkjuice", + "examine": "Made from 5 winter sq'irks.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10856", + "name": "Sin seer's note", + "examine": "Her writing is too scrawled to make out what it says.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Sin Seer in Seers' Village to get another one.", + "durability": null, + "shop_price": "40", + "tradeable": "false" + }, + { + "id": "10857", + "name": "Severed leg", + "examine": "A severed human leg.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to General Khazard, near the Fremennik Province, to get another.", + "durability": null, + "tradeable": "false", + "weight": "12" + }, + { + "id": "10858", + "name": "Shadow sword", + "examine": "A shadow sword.", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "-4,27,21,4,0,0,0,0,4,-1,0,26,0,0,0", + "destroy": "true", + "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.", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "124", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "false", + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3.6" + }, + { + "id": "10859", + "name": "Tea flask", + "examine": "You can put nice, hot tea in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "10860", + "name": "Tea flask", + "examine": "You can put nice, hot tea in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "10861", + "name": "Tea flask", + "examine": "You can put nice, hot tea in it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "10862", + "name": "Hard hat", + "examine": "Can't get any safer than this.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another within the Tower of Life!", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "2" + }, + { + "id": "10863", + "name": "Builder's shirt", + "examine": "Very fetching.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another within the Tower of Life!", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "0.9" + }, + { + "id": "10864", + "name": "Builder's trousers", + "examine": "Slightly tatty in my opinion.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another within the Tower of Life!", + "durability": null, + "equipment_slot": "7", + "tradeable": "false", + "weight": "1" + }, + { + "id": "10865", + "name": "Builder's boots", + "examine": "They'll offer good protection for my toes.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another within the Tower of Life!", + "durability": null, + "equipment_slot": "10", + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "10866", + "name": "Rivets", + "examine": "Used to bolt things together.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10870", + "name": "Binding fluid", + "examine": "Some magical fluid to bind surfaces.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10871", + "name": "Pipe", + "examine": "A pipe that belongs in a cannon stand.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "10872", + "name": "Pipe ring", + "examine": "A ring used to join two pipes together.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10873", + "name": "Metal sheet", + "examine": "A sheet of metal.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10874", + "name": "Coloured ball", + "examine": "Bound to have some use.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10875", + "name": "Valve wheel", + "examine": "A wheel used to control valves.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10876", + "name": "Metal bar", + "examine": "Some unknown metal substance.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10877", + "name": "Plain satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "shop_price": "128", + "weight": "0.1" + }, + { + "id": "10878", + "name": "Green satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "0.1" + }, + { + "id": "10879", + "name": "Red satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "shop_price": "128", + "weight": "0.1" + }, + { + "id": "10880", + "name": "Black satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "0.1" + }, + { + "id": "10881", + "name": "Gold satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "0.1" + }, + { + "id": "10882", + "name": "Rune satchel", + "examine": "I can keep my grub in here.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "weight": "0.1" + }, + { + "id": "10883", + "name": "Hard hat", + "examine": "Can't get any safer than this.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll have to find another within the Tower of Life!", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "2" + }, + { + "id": "10884", + "name": "Fuse", + "examine": "A fuse.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10885", + "name": "Keg", + "examine": "A small keg of gunpowder.", + "archery_ticket_price": "0", + "durability": null, + "weight": "12" + }, + { + "id": "10886", + "name": "Prayer book", + "examine": "Used to cure poison through prayer.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "10887", + "name": "Barrelchest anchor", + "examine": "This is likely to put my back out... (when fixed) OR It's broken. I should see if a pirate smith can fix it. (when broken)", + "archery_ticket_price": "0", + "attack_anims": "5865,5865,5865,5865", + "attack_speed": "6", + "bonuses": "-2,10,92,0,0,0,0,0,0,0,0,100,0,0,0", + "defence_anim": "5866", + "destroy_message": "If you drop the Anchor it will be destroyed, and you will need to get a new one from Brother Tranquillity, then pay Smith to re", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "985", + "run_anim": "5868", + "stand_anim": "5869", + "stand_turn_anim": "823", + "turn180_anim": "5867", + "turn90ccw_anim": "5867", + "turn90cw_anim": "5867", + "two_handed": "true", + "walk_anim": "5867", + "weapon_interface": "8", + "weight": "30" + }, + { + "id": "10888", + "name": "Barrelchest anchor", + "examine": "This is likely to put my back out... (when fixed) OR It's broken. I should see if a pirate smith can fix it. (when broken)", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "-2,10,92,0,0,0,0,0,0,0,0,100,0,0,0", + "durability": null, + "weight": "30" + }, + { + "id": "10889", + "name": "Blessed lamp", + "examine": "This will answer my prayers.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this lamp it will be destroyed, and you will need to see Brother Tranquility for another.", + "durability": null + }, + { + "id": "10890", + "name": "Prayer book", + "examine": "Used to cure poison through prayer.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "This book is old and delicate. You can reclaim this item from the place you found it.ping it here will destroy it and you will have to search the Edgeville Monastery bookcase for another.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "10891", + "name": "Wooden cat", + "examine": "A wooden cat toy.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10892", + "name": "Wooden cat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10893", + "name": "Cranial clamp", + "examine": "A cranial clamp.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10894", + "name": "Brain tongs", + "examine": "Some tongs for use with brains.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10895", + "name": "Bell jar", + "examine": "A bell jar for covering things.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "10896", + "name": "Wolf whistle", + "examine": "A silver whistle.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10897", + "name": "Shipping order", + "examine": "A shipping order list.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10898", + "name": "Keg", + "examine": "A small keg of gunpowder.", + "archery_ticket_price": "0", + "durability": null, + "weight": "12" + }, + { + "id": "10899", + "name": "Crate part", + "examine": "Part of a crate.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "10904", + "name": "Skull staple", + "examine": "One skull staple.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10909", + "name": "Mixture - step 1(4)", + "examine": "This super restore and unicorn dust potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10910", + "name": "Mixture - step 1(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10911", + "name": "Mixture - step 1(3)", + "examine": "This super restore and unicorn dust potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10912", + "name": "Mixture - step 1(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10913", + "name": "Mixture - step 1(2)", + "examine": "This super restore and unicorn dust potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10914", + "name": "Mixture - step 1(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10915", + "name": "Mixture - step 1(1)", + "examine": "This super restore and unicorn dust potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10916", + "name": "Mixture - step 1(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10917", + "name": "Mixture - step 2(4)", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10918", + "name": "Mixture - step 2(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10919", + "name": "Mixture - step 2(3)", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10920", + "name": "Mixture - step 2(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10921", + "name": "Mixture - step 2(2)", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10922", + "name": "Mixture - step 2(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10923", + "name": "Mixture - step 2(1)", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10924", + "name": "Mixture - step 2(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10925", + "name": "Sanfew serum(4)", + "examine": "A \"X\" dose Sanfew Serum.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10926", + "name": "Sanfew serum(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10927", + "name": "Sanfew serum(3)", + "examine": "A \"X\" dose Sanfew Serum.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10928", + "name": "Sanfew serum(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10929", + "name": "Sanfew serum(2)", + "examine": "A \"X\" dose Sanfew Serum.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10930", + "name": "Sanfew serum(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10931", + "name": "Sanfew serum(1)", + "examine": "A \"X\" dose Sanfew Serum.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10932", + "name": "Sanfew serum(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10933", + "name": "Lumberjack boots", + "examine": "You certainly will be alright wearing these.", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "weight": "0.9" + }, + { + "id": "10934", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10935", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10936", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10937", + "name": "Nail beast nails", + "examine": "The claws from a nail beast", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10938", + "name": "Nail beast nails", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10939", + "name": "Lumberjack top", + "examine": "You certainly will be alright wearing this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "1.3" + }, + { + "id": "10940", + "name": "Lumberjack legs", + "examine": "You certainly will be alright wearing these.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "0.9" + }, + { + "id": "10941", + "name": "Lumberjack hat", + "examine": "You certainly will be alright wearing this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "0.4" + }, + { + "id": "10942", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10943", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10944", + "name": "Reward token", + "examine": "Blue/Red/Yellow reward token exchanged at Burgh de Rott/Paterdomus.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Warning! If you destroy this item, you will lose ALL rewards that it relates to and you will have to make another trek in order to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "10945", + "name": "Lumberjack top", + "examine": "You certainly will be alright wearing this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "1.3" + }, + { + "id": "10950", + "name": "Skull staples", + "examine": "One skull staple.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10951", + "name": "Skull staples", + "examine": "One skull staple.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "10952", + "name": "Slayer bell", + "examine": "Don't make anyone jump when you ring this!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{18,39}", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "10953", + "name": "Slayer bell", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10954", + "name": "Frog-leather body", + "examine": "Armour made out of Frog hide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,10,23,26,30,15,32,20,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "requirements": "{1,25}-{4,25}", + "shop_price": "1000", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10955", + "name": "Frog-leather body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10956", + "name": "Frog-leather chaps", + "examine": "Chaps made out of Frog hide.", + "absorb": "0,2,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,2,7,7,9,4,9,5,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "requirements": "{1,25}-{4,25}", "shop_price": "900", - "ge_buy_limit": "100", - "examine": "Chaps made out of Frog hide.", - "durability": null, - "weight": "3", - "absorb": "0,2,1", - "equipment_slot": "7", - "grand_exchange_price": "361", - "name": "Frog-leather chaps", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10956", - "bonuses": "0,0,0,-5,2,7,7,9,4,9,5,0,0,0,0" + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "361", - "durability": null, + "id": "10957", "name": "Frog-leather chaps", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10957" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "10958", + "name": "Frog-leather boots", + "examine": "Boots made out of Frog hide.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,2,1,1,1,0,1,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", "requirements": "{1,25}-{4,25}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "Boots made out of Frog hide.", - "durability": null, - "weight": "1", - "equipment_slot": "10", - "grand_exchange_price": "1156", - "name": "Frog-leather boots", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10958", - "bonuses": "0,0,0,-8,2,1,1,1,0,1,0,0,0,0,0" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1156", - "durability": null, + "id": "10959", "name": "Frog-leather boots", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10959" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "10", - "examine": "Thick and tasty soup.", - "durability": null, + "id": "10960", "name": "Green gloop soup", + "examine": "Thick and tasty soup.", "archery_ticket_price": "0", - "id": "10960" + "durability": null, + "shop_price": "10" }, { - "shop_price": "10", - "examine": "A big bowl of frogspawn gumbo.", - "durability": null, + "id": "10961", "name": "Frogspawn gumbo", + "examine": "A big bowl of frogspawn gumbo.", "archery_ticket_price": "0", - "id": "10961" + "durability": null, + "shop_price": "10" }, { - "shop_price": "10", - "examine": "A juicy frogburger.", - "durability": null, + "id": "10962", "name": "Frogburger", - "weight": "1", + "examine": "A juicy frogburger.", "archery_ticket_price": "0", - "id": "10962" + "durability": null, + "shop_price": "10", + "weight": "1" }, { - "examine": "Nice and crunchy.", - "durability": null, + "id": "10963", "name": "Coated frogs' legs", + "examine": "Nice and crunchy.", "archery_ticket_price": "0", - "id": "10963" + "durability": null }, { - "shop_price": "10", - "examine": "Bat shish kebab.", - "durability": null, + "id": "10964", "name": "Bat shish", + "examine": "Bat shish kebab.", "archery_ticket_price": "0", - "id": "10964" + "durability": null, + "shop_price": "10" }, { - "shop_price": "10", - "examine": "Wall beast fingers in a white fern sauce.", - "durability": null, + "id": "10965", "name": "Fingers", + "examine": "Wall beast fingers in a white fern sauce.", "archery_ticket_price": "0", - "id": "10965" + "durability": null, + "shop_price": "10" }, { - "examine": "A whole roasted frog.", - "durability": null, + "id": "10967", "name": "Roast frog", - "weight": "1", + "examine": "A whole roasted frog.", "archery_ticket_price": "0", - "id": "10967" + "durability": null, + "weight": "1" }, { - "examine": "Tasty saut??ed mushrooms.", - "durability": null, + "id": "10968", "name": "Mushrooms", + "examine": "Tasty saut??ed mushrooms.", "archery_ticket_price": "0", - "id": "10968" + "durability": null }, { - "examine": "Chunky cave-crawler fillets.", - "durability": null, + "id": "10969", "name": "Fillets", + "examine": "Chunky cave-crawler fillets.", "archery_ticket_price": "0", - "id": "10969" + "durability": null }, { - "examine": "Steamed cave pond loach.", - "durability": null, + "id": "10970", "name": "Loach", - "weight": "1", + "examine": "Steamed cave pond loach.", "archery_ticket_price": "0", - "id": "10970" + "durability": null, + "weight": "1" }, { - "examine": "Cave-eel sushi.", - "durability": null, + "id": "10971", "name": "Eel sushi", + "examine": "Cave-eel sushi.", "archery_ticket_price": "0", - "id": "10971" + "durability": null }, { - "shop_price": "2", - "examine": "Oldak's Marvellous Moving-Over-Distance Sphere.", - "durability": null, + "id": "10972", "name": "Dorgesh-kaan sphere", - "weight": "0.5", + "examine": "Oldak's Marvellous Moving-Over-Distance Sphere.", "archery_ticket_price": "0", - "id": "10972" + "durability": null, + "shop_price": "2", + "weight": "0.5" }, { - "requirements": "{11,52}", - "ge_buy_limit": "1000", + "id": "10973", + "name": "Light orb", "examine": "A component of cave goblin Magic.", - "grand_exchange_price": "90", - "durability": null, - "name": "Light orb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10973" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "90", - "durability": null, - "name": "Light orb", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10974" + "requirements": "{11,52}", + "tradeable": "true" }, { - "examine": "A goblin wrench.", + "id": "10974", + "name": "Light orb", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "10975", "name": "Spanner", - "weight": "1", + "examine": "A goblin wrench.", "archery_ticket_price": "0", - "id": "10975" + "durability": null, + "weight": "1" }, { - "shop_price": "1000", - "examine": "A Construction bone.", - "durability": null, + "id": "10976", "name": "Long bone", - "weight": "1", + "examine": "A Construction bone.", "archery_ticket_price": "0", - "id": "10976" + "durability": null, + "shop_price": "1000", + "weight": "1" }, { - "shop_price": "2000", - "examine": "A curved Construction bone.", - "durability": null, + "id": "10977", "name": "Curved bone", - "weight": "1", + "examine": "A curved Construction bone.", "archery_ticket_price": "0", - "id": "10977" + "durability": null, + "shop_price": "2000", + "weight": "1" }, { - "ge_buy_limit": "500", + "id": "10978", + "name": "Swamp weed", "examine": "Swamp weed found in the caves near Dorgesh-Kaan.", - "grand_exchange_price": "292", - "durability": null, - "name": "Swamp weed", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", - "id": "10978" - }, - { + "durability": null, "ge_buy_limit": "500", - "grand_exchange_price": "292", - "durability": null, + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10979", "name": "Swamp weed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10979" + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" }, { - "examine": "Add a wire to complete it.", - "durability": null, + "id": "10980", "name": "Empty light orb", + "examine": "Add a wire to complete it.", "archery_ticket_price": "0", - "id": "10980" + "durability": null }, { - "ge_buy_limit": "1000", + "id": "10981", + "name": "Cave goblin wire", "examine": "Wire found in Dorgesh-Kaan.", - "grand_exchange_price": "988", - "durability": null, - "name": "Cave goblin wire", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "10981" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "988", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "10982", "name": "Cave goblin wire", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10982" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "destroy_message": "You can get another cog from the old power station.", - "shop_price": "160", - "examine": "A cog. Broken: A broken cog.", - "durability": null, + "id": "10983", "name": "Cog", - "tradeable": "false", - "destroy": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "10983" - }, - { - "destroy_message": "You can get another cog from the old power station.", - "shop_price": "160", "examine": "A cog. Broken: A broken cog.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another cog from the old power station.", "durability": null, + "shop_price": "160", + "tradeable": "false", + "weight": "3" + }, + { + "id": "10984", "name": "Cog", - "tradeable": "false", - "destroy": "true", - "weight": "3", + "examine": "A cog. Broken: A broken cog.", "archery_ticket_price": "0", - "id": "10984" + "destroy": "true", + "destroy_message": "You can get another cog from the old power station.", + "durability": null, + "shop_price": "160", + "tradeable": "false", + "weight": "3" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A fuse.", - "durability": null, + "id": "10985", "name": "Fuse", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10985" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", "examine": "A fuse.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "tradeable": "false" + }, + { + "id": "10986", "name": "Fuse", - "tradeable": "false", - "destroy": "true", + "examine": "A fuse.", "archery_ticket_price": "0", - "id": "10986" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A meter.", - "durability": null, + "id": "10987", "name": "Meter", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10987" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", "examine": "A meter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, + "tradeable": "false" + }, + { + "id": "10988", "name": "Meter", - "tradeable": "false", - "destroy": "true", + "examine": "A meter.", "archery_ticket_price": "0", - "id": "10988" - }, - { + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A capacitor.", "durability": null, - "name": "Capacitor", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "10989" + "tradeable": "false" }, { + "id": "10989", + "name": "Capacitor", + "examine": "A capacitor.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A capacitor.", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "10990", "name": "Capacitor", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A capacitor.", "archery_ticket_price": "0", - "id": "10990" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You can get another power box from the old power station.", - "examine": "A powerbox. Broken: A broken powerbox.", - "durability": null, + "id": "10993", "name": "Powerbox", - "tradeable": "false", - "destroy": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "10993" - }, - { - "destroy_message": "You can get another power box from the old power station.", "examine": "A powerbox. Broken: A broken powerbox.", - "durability": null, - "name": "Powerbox", - "tradeable": "false", - "destroy": "true", - "weight": "3", "archery_ticket_price": "0", - "id": "10994" + "destroy": "true", + "destroy_message": "You can get another power box from the old power station.", + "durability": null, + "tradeable": "false", + "weight": "3" }, { - "examine": "A perfect example of a tortoise shell.", + "id": "10994", + "name": "Powerbox", + "examine": "A powerbox. Broken: A broken powerbox.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another power box from the old power station.", "durability": null, + "tradeable": "false", + "weight": "3" + }, + { + "id": "10995", "name": "Perfect shell", - "weight": "6.8", + "examine": "A perfect example of a tortoise shell.", "archery_ticket_price": "0", - "id": "10995" + "durability": null, + "weight": "6.8" }, { - "examine": "A perfect example of a snail shell.", - "durability": null, + "id": "10996", "name": "Perfect snail shell", - "weight": "7", + "examine": "A perfect example of a snail shell.", "archery_ticket_price": "0", - "id": "10996" + "durability": null, + "weight": "7" }, { - "examine": "On wall: That white dot looks like an eye! Off wall: A strange mole-like being.", - "durability": null, + "id": "10997", "name": "Molanisk", + "examine": "On wall: That white dot looks like an eye! Off wall: A strange mole-like being.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "10997" + "durability": null }, { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "durability": null, + "id": "10998", "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", "archery_ticket_price": "0", - "id": "10998" + "durability": null }, { - "examine": "A tattered goblin holy book.", - "durability": null, + "id": "10999", "name": "Goblin book", - "weight": "0.1", + "examine": "A tattered goblin holy book.", "archery_ticket_price": "0", - "id": "10999" + "durability": null, + "weight": "0.1" }, { - "destroy_message": "You will need to get another copy from the Varrock Palace Library if you destroy this one.", - "examine": "The History of Dagon'hai.", - "durability": null, + "id": "11001", "name": "Dagon'hai history", - "tradeable": "false", - "destroy": "true", + "examine": "The History of Dagon'hai.", "archery_ticket_price": "0", - "id": "11001" - }, - { + "destroy": "true", "destroy_message": "You will need to get another copy from the Varrock Palace Library if you destroy this one.", - "examine": "The diary of Sin'keth Magis.", "durability": null, + "tradeable": "false" + }, + { + "id": "11002", "name": "Sin'keth's diary", - "tradeable": "false", - "destroy": "true", + "examine": "The diary of Sin'keth Magis.", "archery_ticket_price": "0", - "id": "11002" + "destroy": "true", + "destroy_message": "You will need to get another copy from the Varrock Palace Library if you destroy this one.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You will need to ask Rat for another empty folder if you destroy this one.", - "examine": "A folder for Rat's papers.", - "durability": null, + "id": "11003", "name": "An empty folder", - "tradeable": "false", - "destroy": "true", + "examine": "A folder for Rat's papers.", "archery_ticket_price": "0", - "id": "11003" + "destroy": "true", + "destroy_message": "You will need to ask Rat for another empty folder if you destroy this one.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A folder for rat's papers. It needs more pages.", - "durability": null, + "id": "11006", "name": "Used folder", - "tradeable": "false", - "destroy": "true", + "examine": "A folder for rat's papers. It needs more pages.", "archery_ticket_price": "0", - "id": "11006" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A full folder. You should take this back to Rat.", - "durability": null, + "id": "11007", "name": "Full folder", - "tradeable": "false", - "destroy": "true", + "examine": "A full folder. You should take this back to Rat.", "archery_ticket_price": "0", - "id": "11007" + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You will need to find more papers about the outlaws if you destroy this.", - "examine": "A page of Rat's document.", - "durability": null, + "id": "11008", "name": "Rat's paper", - "tradeable": "false", - "destroy": "true", + "examine": "A page of Rat's document.", "archery_ticket_price": "0", - "id": "11008" + "destroy": "true", + "destroy_message": "You will need to find more papers about the outlaws if you destroy this.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You will need to get another copy of the letter from Rat if you destroy this.", - "shop_price": "160", - "examine": "A letter in a strange language.", - "durability": null, + "id": "11009", "name": "Rat's letter", - "tradeable": "false", - "destroy": "true", + "examine": "A letter in a strange language.", "archery_ticket_price": "0", - "id": "11009" + "destroy": "true", + "destroy_message": "You will need to get another copy of the letter from Rat if you destroy this.", + "durability": null, + "shop_price": "160", + "tradeable": "false" }, { - "destroy_message": "You will need to ask Surok for another letter to Rat if you destroy this.", - "shop_price": "27", - "examine": "A letter.", - "durability": null, + "id": "11010", "name": "Surok's letter", - "tradeable": "false", - "destroy": "true", + "examine": "A letter.", "archery_ticket_price": "0", - "id": "11010" + "destroy": "true", + "destroy_message": "You will need to ask Surok for another letter to Rat if you destroy this.", + "durability": null, + "shop_price": "27", + "tradeable": "false" }, { - "examine": "Instructions for the beacon ring.", - "durability": null, + "id": "11011", "name": "Zaff's instructions", + "examine": "Instructions for the beacon ring.", "archery_ticket_price": "0", - "id": "11011" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A wand.", - "durability": null, - "name": "Wand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "11012", - "equipment_slot": "3" - }, - { + "name": "Wand", + "examine": "A wand.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This wand is glowing with chaos magic.", "durability": null, - "name": "Infused wand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11013", - "equipment_slot": "3" - }, - { - "destroy_message": "You will need to get another ring from Zaff if you destroy this one.", - "examine": "A magical ring.", - "durability": null, - "name": "Beacon ring", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11014", - "bonuses": "0,0,0,2,0,0,0,0,1,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "remove_head": "true", - "examine": "Cock-a-doodle-do!", - "durability": null, - "name": "Chicken head", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11015", - "equipment_slot": "0" - }, - { - "examine": "Perfect for crossing the road.", - "durability": null, - "name": "Chicken feet", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11016", - "equipment_slot": "10" - }, - { - "remove_sleeves": "true", - "examine": "I look fowl in this.", - "durability": null, - "name": "Chicken wings", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11017", - "equipment_slot": "4" - }, - { - "examine": "What's the matter, are you chicken?", - "durability": null, - "name": "Chicken legs", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11018", - "equipment_slot": "7" - }, - { - "destroy_message": "You will need to speak to Diango to get another pair of chicken feet.", - "examine": "Perfect for crossing the road.", - "durability": null, - "name": "Chicken feet", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11019", - "equipment_slot": "10" - }, - { - "destroy_message": "You will need to speak to Diango to get another set of chicken wings.", - "remove_sleeves": "true", - "examine": "I look fowl in this.", - "durability": null, - "name": "Chicken wings", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11020", - "equipment_slot": "4" - }, - { - "remove_head": "true", - "destroy_message": "You will need to speak to Diango to get another chicken head.", - "examine": "Cock-a-doodle-do!", - "durability": null, - "name": "Chicken head", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11021", - "equipment_slot": "0" - }, - { - "destroy_message": "You will need to speak to Diango to get another set of chicken legs.", - "examine": "What's the matter, are you chicken?", - "durability": null, - "name": "Chicken legs", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "11022", - "equipment_slot": "7" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Let's see how far the rabbit hole goes.", - "durability": null, - "name": "Magic egg", - "archery_ticket_price": "0", - "id": "11023" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Used for making chocolate rabbits.", - "durability": null, - "name": "Rabbit mould", - "archery_ticket_price": "0", - "id": "11024" - }, - { - "examine": "Some chocolate chunks.", - "durability": null, - "name": "Chocolate chunks", - "archery_ticket_price": "0", - "id": "11025" - }, - { - "examine": "Chocolatey kebbit goodness.", - "durability": null, - "name": "Chocolate kebbit", - "archery_ticket_price": "0", - "id": "11026" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11027", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11028", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11029", - "equipment_slot": "3" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11030", - "equipment_slot": "3" - }, - { - "destroy_message": "These logs will be destroyed if you drop them and you will need to see Olaf Hradson to get some more.", - "examine": "A number of dripping, waterlogged planks.", - "durability": null, - "name": "Damp planks", - "archery_ticket_price": "0", - "id": "11031" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A log with some squiggles carved into it.", - "durability": null, - "name": "Crude carving", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11032" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Another log with some sort of bird hacked into it.", - "durability": null, - "name": "Cruder carving", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11033" - }, - { - "destroy_message": "This map will be destroyed if you drop it and you will need to see Olaf Hradson to get another.", - "examine": "A faded map marking a cross next to a section of the landscape.", - "durability": null, - "name": "Sven's last map", - "archery_ticket_price": "0", - "id": "11034" - }, - { - "destroy_message": "These logs will be destroyed if you drop them and you will need to chop more from the windswept tree.", - "examine": "Logs taken from the exotic-looking windswept tree.", - "durability": null, - "name": "Windswept logs", - "archery_ticket_price": "0", - "id": "11035" - }, - { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "examine": "A salty sword.", - "has_special": "true", - "durability": null, - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "6", "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "120200", - "name": "Brine sabre", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11037", - "bonuses": "7,47,-2,0,0,0,0,0,0,0,0,46,0,0,0" + "tradeable": "false" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "120200", - "durability": null, - "name": "Brine sabre", - "tradeable": "true", + "id": "11013", + "name": "Infused wand", + "examine": "This wand is glowing with chaos magic.", "archery_ticket_price": "0", - "id": "11038" - }, - { - "examine": "Looks like it might still float.", - "durability": null, - "name": "Rotten barrel", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "11044" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Looks like it might still float.", "durability": null, - "name": "Rotten barrel", - "tradeable": "false", + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "11014", + "name": "Beacon ring", + "examine": "A magical ring.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,1,0,0,0,0,0,0", "destroy": "true", - "archery_ticket_price": "0", - "id": "11045" + "destroy_message": "You will need to get another ring from Zaff if you destroy this one.", + "durability": null, + "equipment_slot": "12", + "tradeable": "false" }, { - "shop_price": "18", - "examine": "A coil of rope.", - "grand_exchange_price": "101", + "id": "11015", + "name": "Chicken head", + "examine": "Cock-a-doodle-do!", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, - "name": "Rope", + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11016", + "name": "Chicken feet", + "examine": "Perfect for crossing the road.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "10", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11017", + "name": "Chicken wings", + "examine": "I look fowl in this.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11018", + "name": "Chicken legs", + "examine": "What's the matter, are you chicken?", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "7", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11019", + "name": "Chicken feet", + "examine": "Perfect for crossing the road.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Diango to get another pair of chicken feet.", + "durability": null, + "equipment_slot": "10", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11020", + "name": "Chicken wings", + "examine": "I look fowl in this.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Diango to get another set of chicken wings.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11021", + "name": "Chicken head", + "examine": "Cock-a-doodle-do!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Diango to get another chicken head.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11022", + "name": "Chicken legs", + "examine": "What's the matter, are you chicken?", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to speak to Diango to get another set of chicken legs.", + "durability": null, + "equipment_slot": "7", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "11023", + "name": "Magic egg", + "examine": "Let's see how far the rabbit hole goes.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "11024", + "name": "Rabbit mould", + "examine": "Used for making chocolate rabbits.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "11025", + "name": "Chocolate chunks", + "examine": "Some chocolate chunks.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11026", + "name": "Chocolate kebbit", + "examine": "Chocolatey kebbit goodness.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11027", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "11028", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "11029", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "11030", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "11031", + "name": "Damp planks", + "examine": "A number of dripping, waterlogged planks.", + "archery_ticket_price": "0", + "destroy_message": "These logs will be destroyed if you drop them and you will need to see Olaf Hradson to get some more.", + "durability": null + }, + { + "id": "11032", + "name": "Crude carving", + "examine": "A log with some squiggles carved into it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11033", + "name": "Cruder carving", + "examine": "Another log with some sort of bird hacked into it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11034", + "name": "Sven's last map", + "examine": "A faded map marking a cross next to a section of the landscape.", + "archery_ticket_price": "0", + "destroy_message": "This map will be destroyed if you drop it and you will need to see Olaf Hradson to get another.", + "durability": null + }, + { + "id": "11035", + "name": "Windswept logs", + "examine": "Logs taken from the exotic-looking windswept tree.", + "archery_ticket_price": "0", + "destroy_message": "These logs will be destroyed if you drop them and you will need to chop more from the windswept tree.", + "durability": null + }, + { + "id": "11037", + "name": "Brine sabre", + "examine": "A salty sword.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "4", + "bonuses": "7,47,-2,0,0,0,0,0,0,0,0,46,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", + "requirements": "{0,40}", "tradeable": "true", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "11046" + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "Eww, a bald rat!", + "id": "11038", + "name": "Brine sabre", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11044", + "name": "Rotten barrel", + "examine": "Looks like it might still float.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "11045", + "name": "Rotten barrel", + "examine": "Looks like it might still float.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11046", + "name": "Rope", + "examine": "A coil of rope.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "18", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "11047", "name": "Brine rat", + "examine": "Eww, a bald rat!", "archery_ticket_price": "0", "attack_speed": "4", - "id": "11047" + "durability": null }, { - "examine": "A piece of ancient goblin armour.", - "durability": null, + "id": "11048", "name": "Armour shard", + "examine": "A piece of ancient goblin armour.", "archery_ticket_price": "0", - "id": "11048" + "durability": null }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11049", "name": "Artefact", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient goblin object encrusted in dirt.", "archery_ticket_price": "0", - "id": "11049" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "shop_price": "1", - "examine": "It's missing a handle.", - "durability": null, + "id": "11050", "name": "Axe head", - "tradeable": "true", + "examine": "It's missing a handle.", "archery_ticket_price": "0", - "id": "11050" + "durability": null, + "shop_price": "1", + "tradeable": "true" }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11051", "name": "Artefact", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient goblin object encrusted in dirt.", "archery_ticket_price": "0", - "id": "11051" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A piece of ancient goblin helmet.", - "durability": null, + "id": "11052", "name": "Helmet fragment", - "tradeable": "false", - "destroy": "true", + "examine": "A piece of ancient goblin helmet.", "archery_ticket_price": "0", - "id": "11052" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11053", "name": "Artefact", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient goblin object encrusted in dirt.", "archery_ticket_price": "0", - "id": "11053" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A piece of an ancient goblin shield.", - "durability": null, + "id": "11054", "name": "Shield fragment", - "tradeable": "false", - "destroy": "true", + "examine": "A piece of an ancient goblin shield.", "archery_ticket_price": "0", - "id": "11054" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11055", "name": "Artefact", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient goblin object encrusted in dirt.", "archery_ticket_price": "0", - "id": "11055" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A piece of an ancient goblin sword.", - "durability": null, + "id": "11056", "name": "Sword fragment", + "examine": "A piece of an ancient goblin sword.", "archery_ticket_price": "0", - "id": "11056" + "durability": null }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11057", "name": "Artefact", - "tradeable": "false", - "destroy": "true", + "examine": "An ancient goblin object encrusted in dirt.", "archery_ticket_price": "0", - "id": "11057" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "durability": null, + "id": "11058", "name": "Mace", "archery_ticket_price": "0", + "attack_audios": "2508,2508,25092508", "attack_speed": "5", - "id": "11058" + "durability": null }, { - "examine": "An ancient goblin object encrusted in dirt.", - "durability": null, + "id": "11059", "name": "Artefact", - "tradeable": "false", + "examine": "An ancient goblin object encrusted in dirt.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "11059" - }, - { - "shop_price": "2", - "examine": "Oldak's marvellous Moving-over-distance Sphere", "durability": null, - "name": "Goblin village sphere", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "11060" + "tradeable": "false" }, { + "id": "11060", + "name": "Goblin village sphere", + "examine": "Oldak's marvellous Moving-over-distance Sphere", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "weight": "0.05" + }, + { + "id": "11061", + "name": "Ancient mace", + "examine": "It has ancient goblin symbols on it.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "10,-2,16,0,0,0,0,0,0,0,0,14,3,0,0", + "defence_anim": "397", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "100", + "has_special": "true", "requirements": "{0,25}-{5,15}", "shop_price": "1000", - "ge_buy_limit": "100", - "examine": "It has ancient goblin symbols on it.", - "has_special": "true", - "durability": null, - "weight": "1.8", - "attack_speed": "5", + "tradeable": "true", "weapon_interface": "8", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "grand_exchange_price": "4093", - "name": "Ancient mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11061", - "bonuses": "10,-2,16,0,0,0,0,0,0,0,0,14,3,0,0" + "weight": "1.8" }, { + "id": "11064", + "name": "Ancient mace", + "archery_ticket_price": "0", "durability": null, - "name": "Ancient mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11064" + "tradeable": "true" }, { - "shop_price": "5", - "ge_buy_limit": "100", + "id": "11065", + "name": "Bracelet mould", "examine": "Used to make gold bracelets.", - "grand_exchange_price": "456", - "durability": null, - "name": "Bracelet mould", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "11065" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "456", - "durability": null, - "name": "Bracelet mould", + "shop_price": "5", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11066" + "weight": "0.4" }, { - "shop_price": "165", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "223", - "durability": null, - "name": "Gold bracelet", - "tradeable": "true", - "weight": "0.2", + "id": "11066", + "name": "Bracelet mould", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11069", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "223", - "durability": null, "name": "Gold bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11070" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "165", + "tradeable": "true", + "weight": "0.2" }, { - "shop_price": "1035", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "578", - "durability": null, - "name": "Sapphire bracelet", - "tradeable": "true", - "weight": "0.25", + "id": "11070", + "name": "Gold bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11072", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "578", - "durability": null, "name": "Sapphire bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11073" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "1035", + "tradeable": "true", + "weight": "0.25" }, { - "shop_price": "506", - "ge_buy_limit": "5000", - "examine": "Now I can become a potter.", - "grand_exchange_price": "644", - "durability": null, - "name": "Bracelet of clay", - "tradeable": "true", + "id": "11073", + "name": "Sapphire bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11074", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "644", - "durability": null, "name": "Bracelet of clay", - "tradeable": "true", + "examine": "Now I can become a potter.", "archery_ticket_price": "0", - "id": "11075" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "506", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "786", - "durability": null, - "name": "Emerald bracelet", - "tradeable": "true", - "weight": "0.2", + "id": "11075", + "name": "Bracelet of clay", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11076", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "786", - "durability": null, "name": "Emerald bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11077" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "34464", - "examine": "Must be worn as you enter the game to receive the bonus for that game.", - "grand_exchange_price": "926", - "durability": null, - "name": "Castlewar brace(3)", - "tradeable": "true", - "weight": "0.25", + "id": "11077", + "name": "Emerald bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11079", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "926", - "durability": null, "name": "Castlewar brace(3)", - "tradeable": "true", + "examine": "Must be worn as you enter the game to receive the bonus for that game.", "archery_ticket_price": "0", - "id": "11080" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "34464", + "tradeable": "true", + "weight": "0.25" }, { - "examine": "Must be worn as you enter the game to receive the bonus for that game.", - "grand_exchange_price": "3", - "durability": null, - "name": "Castlewar brace(2)", - "tradeable": "true", - "weight": "0.25", + "id": "11080", + "name": "Castlewar brace(3)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11081", - "equipment_slot": "9" - }, - { - "durability": null, "name": "Castlewar brace(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11082" - }, - { - "ge_buy_limit": "5000", "examine": "Must be worn as you enter the game to receive the bonus for that game.", - "grand_exchange_price": "3484", - "durability": null, - "name": "Castlewar brace(1)", - "tradeable": "true", - "weight": "0.25", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.25" + }, + { + "id": "11082", + "name": "Castlewar brace(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11083", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3484", - "durability": null, "name": "Castlewar brace(1)", - "tradeable": "true", + "examine": "Must be worn as you enter the game to receive the bonus for that game.", "archery_ticket_price": "0", - "id": "11084" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.25" }, { - "shop_price": "2325", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "1271", - "durability": null, - "name": "Ruby bracelet", - "tradeable": "true", + "id": "11084", + "name": "Castlewar brace(1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11085", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1271", - "durability": null, "name": "Ruby bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11086" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "2325", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "It eases diseases!", - "grand_exchange_price": "1376", - "durability": null, - "name": "Inoculation brace", - "tradeable": "true", - "weight": "0.25", + "id": "11086", + "name": "Ruby bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11088", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1376", - "durability": null, "name": "Inoculation brace", - "tradeable": "true", + "examine": "It eases diseases!", "archery_ticket_price": "0", - "id": "11089" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.25" }, { - "ge_buy_limit": "5000", - "examine": "In case of emergency, wear necklace.", - "grand_exchange_price": "15400", - "durability": null, - "name": "Phoenix necklace", - "tradeable": "true", + "id": "11089", + "name": "Inoculation brace", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11090", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "15400", - "durability": null, "name": "Phoenix necklace", - "tradeable": "true", + "examine": "In case of emergency, wear necklace.", "archery_ticket_price": "0", - "id": "11091" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "3422", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "12100", - "durability": null, - "name": "Diamond bracelet", - "tradeable": "true", + "id": "11091", + "name": "Phoenix necklace", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11092", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "12100", - "durability": null, "name": "Diamond bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11093" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "shop_price": "3422", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "examine": "It repels revenants and helps you enter the Abyss.", - "grand_exchange_price": "15200", - "durability": null, - "name": "Forinthry brace(5)", - "tradeable": "true", - "weight": "0.2", + "id": "11093", + "name": "Diamond bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11095", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "15200", - "durability": null, "name": "Forinthry brace(5)", - "tradeable": "true", + "examine": "It repels revenants and helps you enter the Abyss.", "archery_ticket_price": "0", - "id": "11096" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "It repels revenants and helps you enter the Abyss.", - "grand_exchange_price": "5", - "durability": null, - "name": "Forinthry brace(4)", - "tradeable": "true", - "weight": "0.2", + "id": "11096", + "name": "Forinthry brace(5)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11097", - "equipment_slot": "9" - }, - { - "durability": null, "name": "Forinthry brace(4)", - "tradeable": "true", + "examine": "It repels revenants and helps you enter the Abyss.", "archery_ticket_price": "0", - "id": "11098" + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "It repels revenants and helps you enter the Abyss.", - "grand_exchange_price": "5", - "durability": null, - "name": "Forinthry brace(3)", - "tradeable": "true", - "weight": "0.2", + "id": "11098", + "name": "Forinthry brace(4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11099", - "equipment_slot": "9" - }, - { - "durability": null, "name": "Forinthry brace(3)", - "tradeable": "true", + "examine": "It repels revenants and helps you enter the Abyss.", "archery_ticket_price": "0", - "id": "11100" + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "It repels revenants and helps you enter the Abyss.", - "grand_exchange_price": "5", - "durability": null, - "name": "Forinthry brace(2)", - "tradeable": "true", - "weight": "0.2", + "id": "11100", + "name": "Forinthry brace(3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11101", - "equipment_slot": "9" - }, - { - "durability": null, "name": "Forinthry brace(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11102" - }, - { - "ge_buy_limit": "5000", "examine": "It repels revenants and helps you enter the Abyss.", - "grand_exchange_price": "2537", - "durability": null, - "name": "Forinthry brace(1)", - "tradeable": "true", - "weight": "0.2", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "11102", + "name": "Forinthry brace(2)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11103", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2537", - "durability": null, "name": "Forinthry brace(1)", - "tradeable": "true", + "examine": "It repels revenants and helps you enter the Abyss.", "archery_ticket_price": "0", - "id": "11104" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "100", - "examine": "This will help me travel.", - "grand_exchange_price": "30400", - "durability": null, - "name": "Skills necklace(4)", - "tradeable": "true", + "id": "11104", + "name": "Forinthry brace(1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { "id": "11105", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "30400", - "durability": null, "name": "Skills necklace(4)", - "tradeable": "true", + "examine": "This will help me travel.", "archery_ticket_price": "0", - "id": "11106" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "This will help me travel.", - "grand_exchange_price": "1", - "durability": null, - "name": "Skills necklace(3)", - "tradeable": "true", + "id": "11106", + "name": "Skills necklace(4)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11107", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Skills necklace(3)", - "tradeable": "true", + "examine": "This will help me travel.", "archery_ticket_price": "0", - "id": "11108" + "durability": null, + "equipment_slot": "2", + "tradeable": "true" }, { - "examine": "This will help me travel.", - "grand_exchange_price": "1", - "durability": null, - "name": "Skills necklace(2)", - "tradeable": "true", + "id": "11108", + "name": "Skills necklace(3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11109", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Skills necklace(2)", - "tradeable": "true", + "examine": "This will help me travel.", "archery_ticket_price": "0", - "id": "11110" + "durability": null, + "equipment_slot": "2", + "tradeable": "true" }, { - "examine": "This will help me travel.", - "grand_exchange_price": "1", - "durability": null, - "name": "Skills necklace(1)", - "tradeable": "true", + "id": "11110", + "name": "Skills necklace(2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11111", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Skills necklace(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11112" - }, - { - "ge_buy_limit": "100", "examine": "This will help me travel.", - "grand_exchange_price": "28400", - "durability": null, - "name": "Skills necklace", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "tradeable": "true" + }, + { + "id": "11112", + "name": "Skills necklace(1)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "11113", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "28400", - "durability": null, "name": "Skills necklace", - "tradeable": "true", + "examine": "This will help me travel.", "archery_ticket_price": "0", - "id": "11114" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "26000", - "durability": null, - "name": "Dragon bracelet", - "tradeable": "true", - "weight": "0.3", + "id": "11114", + "name": "Skills necklace", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11115", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "26000", - "durability": null, "name": "Dragon bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11116" - }, - { - "ge_buy_limit": "100", - "examine": "A handy way to get around.", - "grand_exchange_price": "29700", - "durability": null, - "name": "Combat bracelet(4)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "11118", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "29700", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Combat bracelet(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11119" - }, - { - "examine": "A handy way to get around.", - "grand_exchange_price": "1", - "durability": null, - "name": "Combat bracelet(3)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "11120", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Combat bracelet(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11121" - }, - { - "examine": "A handy way to get around.", - "grand_exchange_price": "1", - "durability": null, - "name": "Combat bracelet(2)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "11122", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Combat bracelet(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11123" - }, - { - "examine": "A handy way to get around.", - "grand_exchange_price": "1", - "durability": null, - "name": "Combat bracelet(1)", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "11124", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Combat bracelet(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11125" - }, - { - "ge_buy_limit": "100", - "examine": "You will need to recharge the bracelet at the Legends Guild.", - "grand_exchange_price": "29000", - "durability": null, - "name": "Combat bracelet", - "tradeable": "true", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "11126", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "29000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Combat bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11127" - }, - { - "ge_buy_limit": "100", - "examine": "Makes obsidian weapons even stronger!", - "grand_exchange_price": "2400000", - "durability": null, - "name": "Berserker necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11128", - "bonuses": "-10,-10,-10,0,0,-20,-20,-20,-20,-20,-20,7,3,0,0", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2400000", - "durability": null, - "name": "Berserker necklace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11129" - }, - { - "shop_price": "80400", - "ge_buy_limit": "100", "examine": "I wonder if this is valuable.", - "grand_exchange_price": "941200", - "durability": null, - "name": "Onyx bracelet", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11116", + "name": "Dragon bracelet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11118", + "name": "Combat bracelet(4)", + "examine": "A handy way to get around.", + "archery_ticket_price": "0", + "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11119", + "name": "Combat bracelet(4)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11120", + "name": "Combat bracelet(3)", + "examine": "A handy way to get around.", + "archery_ticket_price": "0", + "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11121", + "name": "Combat bracelet(3)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11122", + "name": "Combat bracelet(2)", + "examine": "A handy way to get around.", + "archery_ticket_price": "0", + "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11123", + "name": "Combat bracelet(2)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11124", + "name": "Combat bracelet(1)", + "examine": "A handy way to get around.", + "archery_ticket_price": "0", + "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", + "durability": null, + "equipment_slot": "9", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11125", + "name": "Combat bracelet(1)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11126", + "name": "Combat bracelet", + "examine": "You will need to recharge the bracelet at the Legends Guild.", + "archery_ticket_price": "0", + "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "11127", + "name": "Combat bracelet", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11128", + "name": "Berserker necklace", + "examine": "Makes obsidian weapons even stronger!", + "archery_ticket_price": "0", + "bonuses": "-10,-10,-10,0,0,-20,-20,-20,-20,-20,-20,7,3,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11129", + "name": "Berserker necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11130", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "941200", - "durability": null, "name": "Onyx bracelet", - "tradeable": "true", + "examine": "I wonder if this is valuable.", "archery_ticket_price": "0", - "id": "11131" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "80400", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "Helps to restore your life points.", - "grand_exchange_price": "124200", - "durability": null, - "name": "Regen bracelet", - "tradeable": "true", - "weight": "0.2", + "id": "11131", + "name": "Onyx bracelet", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11133", - "bonuses": "8,8,8,3,7,6,6,6,3,6,5,7,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "124200", - "durability": null, "name": "Regen bracelet", - "tradeable": "true", + "examine": "Helps to restore your life points.", "archery_ticket_price": "0", - "id": "11134" - }, - { - "remove_sleeves": "true", - "examine": "For all your flying needs.", + "bonuses": "8,8,8,3,7,6,6,6,3,6,5,7,0,0,0", "durability": null, - "name": "Bomber jacket", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11135", - "equipment_slot": "4" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pair of pale green Karamja gloves.", - "durability": null, - "name": "Karamja gloves 1", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11136", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11137" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pair of green Karamja gloves.", - "durability": null, - "name": "Karamja gloves 2", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11138", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11139" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pair of dark green Karamja gloves.", - "durability": null, - "name": "Karamja gloves 3", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11140", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11141" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", - "durability": null, - "name": "Dream vial (empty)", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11151" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", - "durability": null, - "name": "Dream vial (water)", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11152" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", - "durability": null, - "name": "Dream vial (herb)", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11153" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Potion of Shared Dreaming. One dream for two!", - "durability": null, - "name": "Dream potion", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11154" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A ground astral rune.", - "durability": null, - "name": "Ground astral rune", - "archery_ticket_price": "0", - "id": "11155" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "An astral rune that has been broken into shards.", - "durability": null, - "name": "Astral rune shards", - "archery_ticket_price": "0", - "id": "11156" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A lunar-styled lamp. I wonder what's inside?", - "durability": null, - "name": "Dreamy lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11157" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Perfect for storing heavy things.", - "durability": null, - "name": "Cyrisus's chest", - "tradeable": "false", - "destroy": "true", - "weight": "6", - "archery_ticket_price": "0", - "id": "11158" - }, - { - "destroy_message": "You will have to create another using the Lunar Spellbook.", + "equipment_slot": "9", "ge_buy_limit": "100", - "examine": "A box of Hunter goodies.", - "grand_exchange_price": "3", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "11134", + "name": "Regen bracelet", + "archery_ticket_price": "0", "durability": null, - "name": "Hunter kit", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11135", + "name": "Bomber jacket", + "examine": "For all your flying needs.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", "tradeable": "false", + "weight": "1" + }, + { + "id": "11136", + "name": "Karamja gloves 1", + "examine": "A pair of pale green Karamja gloves.", + "archery_ticket_price": "0", + "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "11159" - }, - { - "destroy_message": "You'll need to get it back from Captain Rovin.", - "examine": "Complete Shield of Arrav.", - "durability": null, - "name": "Restored shield", - "destroy": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "11164" - }, - { - "turn90cw_anim": "821", - "examine": "Second-rate crossbow, former property of the Phoenix Gang.", - "walk_anim": "4226", - "durability": null, - "weight": "3", - "turn90ccw_anim": "822", - "attack_speed": "6", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "equipment_slot": "3", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "name": "Phoenix crossbow", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "11165", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Phoenix crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11166" - }, - { - "turn90cw_anim": "821", - "examine": "Second-rate crossbow, former property of the Phoenix Gang.", - "walk_anim": "4226", - "durability": null, - "weight": "3", - "turn90ccw_anim": "822", - "attack_speed": "6", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "equipment_slot": "3", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "name": "Phoenix crossbow", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "11167", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Phoenix crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11168" - }, - { - "shop_price": "50", - "examine": "A collection of written news on paper!", - "durability": null, - "name": "Newspaper", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11169" - }, - { - "durability": null, - "name": "Newspaper", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11170" - }, - { - "shop_price": "50", - "examine": "A collection of written news on paper!", - "durability": null, - "name": "Newspaper", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11171" - }, - { - "durability": null, - "name": "Newspaper", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11172" - }, - { - "destroy_message": "Instead of destroying this I should get a friend to help me retrieve the other half.", - "examine": "I can use this to claim a reward from the King, if I get the other half.", - "durability": null, - "name": "Half certificate", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11173" - }, - { - "destroy_message": "Instead of destroying this I should get a friend to help me retrieve the other half.", - "examine": "I can use this to claim a reward from the King, if I get the other half.", - "durability": null, - "name": "Half certificate", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11174" - }, - { - "destroy_message": "You can get another find from the pile in the cleaning area of Varrock Museum.", - "examine": "A roughly-prepared archaeological find ready for cleaning.", - "durability": null, - "name": "Uncleaned find", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11175" - }, - { - "examine": "An old looking coin.", - "durability": null, - "name": "Old coin", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11179" - }, - { - "examine": "A cracked and rusty looking coin.", - "durability": null, - "name": "Ancient coin", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11180" - }, - { - "examine": "A very old symbol of Saradomin.", - "durability": null, - "name": "Ancient symbol", - "archery_ticket_price": "0", - "id": "11181" - }, - { - "examine": "An old symbol of Saradomin.", - "durability": null, - "name": "Old symbol", - "archery_ticket_price": "0", - "id": "11182" - }, - { - "examine": "An old vase with Saradominist markings - it has been chipped.", - "durability": null, - "name": "Old chipped vase", - "archery_ticket_price": "0", - "id": "11183" - }, - { - "examine": "A map of Varrock Museum.", - "durability": null, - "name": "Museum map", - "archery_ticket_price": "0", - "id": "11184" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11185" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11186" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11187" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11188" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11189" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (1)", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11190", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (2)", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11191", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (3)", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11192", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (4)", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11193", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (5)", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11194", - "equipment_slot": "2" - }, - { - "examine": "A recently-cleaned necklace.", - "durability": null, - "name": "Clean necklace", - "archery_ticket_price": "0", - "id": "11195", - "equipment_slot": "2" - }, - { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "One of Grimgnash's feathers.", "durability": null, + "equipment_slot": "9", + "tradeable": "false" + }, + { + "id": "11137", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11138", + "name": "Karamja gloves 2", + "examine": "A pair of green Karamja gloves.", + "archery_ticket_price": "0", + "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false" + }, + { + "id": "11139", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11140", + "name": "Karamja gloves 3", + "examine": "A pair of dark green Karamja gloves.", + "archery_ticket_price": "0", + "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false" + }, + { + "id": "11141", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11151", + "name": "Dream vial (empty)", + "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11152", + "name": "Dream vial (water)", + "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11153", + "name": "Dream vial (herb)", + "examine": "When empty: A vessel for holding liquid. Filled with water: A vessel containing water. Filled with Goutweed: A vessel containing water and goutweed.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11154", + "name": "Dream potion", + "examine": "Potion of Shared Dreaming. One dream for two!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11155", + "name": "Ground astral rune", + "examine": "A ground astral rune.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "11156", + "name": "Astral rune shards", + "examine": "An astral rune that has been broken into shards.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "11157", + "name": "Dreamy lamp", + "examine": "A lunar-styled lamp. I wonder what's inside?", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11158", + "name": "Cyrisus's chest", + "examine": "Perfect for storing heavy things.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "6" + }, + { + "id": "11159", + "name": "Hunter kit", + "examine": "A box of Hunter goodies.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will have to create another using the Lunar Spellbook.", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "11164", + "name": "Restored shield", + "examine": "Complete Shield of Arrav.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll need to get it back from Captain Rovin.", + "durability": null, + "weight": "5" + }, + { + "id": "11165", + "name": "Phoenix crossbow", + "examine": "Second-rate crossbow, former property of the Phoenix Gang.", + "archery_ticket_price": "0", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "175", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "3" + }, + { + "id": "11166", + "name": "Phoenix crossbow", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11167", + "name": "Phoenix crossbow", + "examine": "Second-rate crossbow, former property of the Phoenix Gang.", + "archery_ticket_price": "0", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,6,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "175", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "3" + }, + { + "id": "11168", + "name": "Phoenix crossbow", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11169", + "name": "Newspaper", + "examine": "A collection of written news on paper!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "50", + "tradeable": "true" + }, + { + "id": "11170", + "name": "Newspaper", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11171", + "name": "Newspaper", + "examine": "A collection of written news on paper!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "50", + "tradeable": "true" + }, + { + "id": "11172", + "name": "Newspaper", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11173", + "name": "Half certificate", + "examine": "I can use this to claim a reward from the King, if I get the other half.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Instead of destroying this I should get a friend to help me retrieve the other half.", + "durability": null, + "tradeable": "true" + }, + { + "id": "11174", + "name": "Half certificate", + "examine": "I can use this to claim a reward from the King, if I get the other half.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Instead of destroying this I should get a friend to help me retrieve the other half.", + "durability": null, + "tradeable": "true" + }, + { + "id": "11175", + "name": "Uncleaned find", + "examine": "A roughly-prepared archaeological find ready for cleaning.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another find from the pile in the cleaning area of Varrock Museum.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11179", + "name": "Old coin", + "examine": "An old looking coin.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "11180", + "name": "Ancient coin", + "examine": "A cracked and rusty looking coin.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "11181", + "name": "Ancient symbol", + "examine": "A very old symbol of Saradomin.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11182", + "name": "Old symbol", + "examine": "An old symbol of Saradomin.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11183", + "name": "Old chipped vase", + "examine": "An old vase with Saradominist markings - it has been chipped.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11184", + "name": "Museum map", + "examine": "A map of Varrock Museum.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11185", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11186", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11187", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11188", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11189", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11190", + "name": "Digsite pendant (1)", + "examine": "An enchanted necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.5" + }, + { + "id": "11191", + "name": "Digsite pendant (2)", + "examine": "An enchanted necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.5" + }, + { + "id": "11192", + "name": "Digsite pendant (3)", + "examine": "An enchanted necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.5" + }, + { + "id": "11193", + "name": "Digsite pendant (4)", + "examine": "An enchanted necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.5" + }, + { + "id": "11194", + "name": "Digsite pendant (5)", + "examine": "An enchanted necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.5" + }, + { + "id": "11195", + "name": "Clean necklace", + "examine": "A recently-cleaned necklace.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2" + }, + { + "id": "11196", "name": "Griffin feather", + "examine": "One of Grimgnash's feathers.", "archery_ticket_price": "0", - "id": "11196" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You will need to try and find another pendant in the mouse hole if you destroy this.", - "examine": "The pendant belonging to Miazrqa.", - "durability": null, + "id": "11197", "name": "Miazrqa's pendant", - "tradeable": "false", - "destroy": "true", + "examine": "The pendant belonging to Miazrqa.", "archery_ticket_price": "0", - "id": "11197" + "destroy": "true", + "destroy_message": "You will need to try and find another pendant in the mouse hole if you destroy this.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You will need to find another sheet of music in the witch's basement if you destroy this.", - "examine": "A sheet of music.", - "durability": null, + "id": "11198", "name": "Music sheet", - "tradeable": "false", - "destroy": "true", + "examine": "A sheet of music.", "archery_ticket_price": "0", - "id": "11198" - }, - { - "destroy_message": "You will need to obtain another helmet from Rupert the Beard if you destroy this", - "shop_price": "60000", - "examine": "A sturdy helmet that belonged to Rupert the beard.", + "destroy": "true", + "destroy_message": "You will need to find another sheet of music in the witch's basement if you destroy this.", "durability": null, - "name": "Rupert's helmet", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11199" + "tradeable": "false" }, { + "id": "11199", + "name": "Rupert's helmet", + "examine": "A sturdy helmet that belonged to Rupert the beard.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to obtain another helmet from Rupert the Beard if you destroy this", + "durability": null, + "shop_price": "60000", + "tradeable": "false", + "weight": "2" + }, + { + "id": "11200", + "name": "Dwarven helmet", + "examine": "A sturdy helmet that belonged to Rupert the beard.", + "archery_ticket_price": "0", + "bonuses": "0,0,6,-2,-2,27,28,31,5,24,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{1,50}", "shop_price": "60000", - "ge_buy_limit": "100", - "examine": "A sturdy helmet that belonged to Rupert the beard.", - "durability": null, - "weight": "1.9", - "equipment_slot": "0", - "grand_exchange_price": "35800", - "name": "Dwarven helmet", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11200", - "bonuses": "0,0,6,-2,-2,27,28,31,5,24,8,0,0,0,0" + "weight": "1.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "35800", - "durability": null, + "id": "11201", "name": "Dwarven helmet", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11201" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "destroy_message": "You will need to find another copy in the witch's basement if you destroy this.", - "examine": "A recipe for a shrinking potion.", - "durability": null, + "id": "11202", "name": "Shrinking recipe", - "tradeable": "false", - "destroy": "true", + "examine": "A recipe for a shrinking potion.", "archery_ticket_price": "0", - "id": "11202" - }, - { + "destroy": "true", "destroy_message": "You will need to find another copy in the witch's basement if you destroy this.", - "examine": "A list of tasks for the day.", "durability": null, + "tradeable": "false" + }, + { + "id": "11203", "name": "To-do list", - "tradeable": "false", - "destroy": "true", + "examine": "A list of tasks for the day.", "archery_ticket_price": "0", - "id": "11203" + "destroy": "true", + "destroy_message": "You will need to find another copy in the witch's basement if you destroy this.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You will need to make another shrinking potion if you destroy this.", - "examine": "Apparently it shrinks you. Very fast.", - "durability": null, - "name": "Shrink-me-quick", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "11204", - "equipment_slot": "3" + "name": "Shrink-me-quick", + "examine": "Apparently it shrinks you. Very fast.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to make another shrinking potion if you destroy this.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" }, { - "shop_price": "4", - "ge_buy_limit": "100", - "examine": "A shrunk ogleroot! How odd...", - "grand_exchange_price": "35", - "durability": null, + "id": "11205", "name": "Shrunk ogleroot", - "tradeable": "false", + "examine": "A shrunk ogleroot! How odd...", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "11205" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A shiny golden goblin. Strange!", "durability": null, - "name": "Golden goblin", - "tradeable": "false", - "destroy": "true", - "weight": "30", - "archery_ticket_price": "0", - "id": "11210" - }, - { - "destroy_message": "You will need to get another bag of beans from Sylas if you destroy this one.", - "examine": "A bag of magic beans.", - "durability": null, - "name": "Magic beans", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11211" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "grand_exchange_price": "2674", - "durability": null, - "name": "Dragon arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11212", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", - "grand_exchange_price": "3988", - "durability": null, - "name": "Dragon fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11217", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", - "grand_exchange_price": "3834", - "durability": null, - "name": "Dragon fire arrows", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11222", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "grand_exchange_price": "3141", - "durability": null, - "name": "Dragon arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11227", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "grand_exchange_price": "2866", - "durability": null, - "name": "Dragon arrow(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11228", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "grand_exchange_price": "3766", - "durability": null, - "name": "Dragon arrow(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11229", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", - "equipment_slot": "13" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "equipment_slot": "3", - "grand_exchange_price": "315", - "attack_audios": "2547,0,0,0", - "name": "Dragon dart", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11230", - "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "equipment_slot": "3", - "grand_exchange_price": "445", - "attack_audios": "2547,0,0,0", - "name": "Dragon dart(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11231", - "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20" - }, - { - "ge_buy_limit": "5000", - "examine": "A deadly looking dragon dart tip - needs feathers for flight.", - "grand_exchange_price": "746", - "attack_audios": "2547,0,0,0", - "durability": null, - "name": "Dragon dart tip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11232" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "equipment_slot": "3", - "grand_exchange_price": "516", - "attack_audios": "2547,0,0,0", - "name": "Dragon dart(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11233", - "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "attack_speed": "3", - "weapon_interface": "18", - "equipment_slot": "3", - "grand_exchange_price": "1248", - "attack_audios": "2547,0,0,0", - "name": "Dragon dart(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11234", - "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A bow from a darker dimension.", - "walk_anim": "819", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "822", - "attack_speed": "9", - "two_handed": "true", - "turn180_anim": "820", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "426,426,426,426", - "grand_exchange_price": "143400", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "11235", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,95,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,60}", - "durability": null, - "weight": "1.9", - "weapon_interface": "16", - "equip_audio": "3738", - "render_anim": "1", - "lendable": "true", - "attack_audios": "3731,0,0,0", - "name": "Dark bow" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "143400", - "durability": null, - "name": "Dark bow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11236" - }, - { - "ge_buy_limit": "10000", - "examine": "Dragon talons, usable as arrowheads.", - "grand_exchange_price": "3391", - "durability": null, - "name": "Dragon arrowtips", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11237" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "A baby impling in a jar. That's a bit cruel.", - "grand_exchange_price": "529", - "durability": null, - "name": "Baby impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11238" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "529", - "durability": null, - "name": "Baby impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11239" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "A young impling in a jar. Don't trap me, man.", - "grand_exchange_price": "514", - "durability": null, - "name": "Young impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11240" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "514", - "durability": null, - "name": "Young impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11241" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "A gourmet impling in a jar.", - "grand_exchange_price": "677", - "durability": null, - "name": "Gourm' impling jar", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11242" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "677", - "durability": null, - "name": "Gourm' impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11243" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Earth impling in a jar.", - "grand_exchange_price": "1343", - "durability": null, - "name": "Earth impling jar", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11244" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1343", - "durability": null, - "name": "Earth impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11245" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Essence impling in a jar.", - "grand_exchange_price": "1266", - "durability": null, - "name": "Ess' impling jar", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11246" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1266", - "durability": null, - "name": "Ess' impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11247" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Eclectic impling in a jar.", - "grand_exchange_price": "2943", - "durability": null, - "name": "Eclectic impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11248" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2943", - "durability": null, - "name": "Eclectic impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11249" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Nature impling in a jar.", - "grand_exchange_price": "8593", - "durability": null, - "name": "Nature impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11250" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "8593", - "durability": null, - "name": "Nature impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11251" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Magpie impling in a jar.", - "grand_exchange_price": "22900", - "durability": null, - "name": "Magpie impling jar", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11252" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "22900", - "durability": null, - "name": "Magpie impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11253" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "1000", - "examine": "Ninja impling in a jar.", - "grand_exchange_price": "21800", - "durability": null, - "name": "Ninja impling jar", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11254" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "21800", - "durability": null, - "name": "Ninja impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11255" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "ge_buy_limit": "500", - "examine": "Dragon impling in a jar.", - "grand_exchange_price": "423600", - "durability": null, - "name": "Dragon impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11256" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "423600", - "durability": null, - "name": "Dragon impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11257" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Creates butterfly and impling jars.", - "durability": null, - "name": "Jar generator", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "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", - "equipment_slot": "3", - "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" - }, - { - "shop_price": "1", - "ge_buy_limit": "1000", - "examine": "It's got little holes in the top.", - "grand_exchange_price": "248", - "durability": null, - "name": "Impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11260" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "248", - "durability": null, - "name": "Impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11261" - }, - { - "shop_price": "3", "ge_buy_limit": "100", - "examine": "Imps seem to hate this stuff. Can't say I blame them.", - "grand_exchange_price": "1016", - "durability": null, - "name": "Imp repellent", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11262" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1016", - "durability": null, - "name": "Imp repellent", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11263" - }, - { - "ge_buy_limit": "100", - "examine": "Omega 3 oil. Good for the brain, not so for the nose.", - "grand_exchange_price": "601", - "durability": null, - "name": "Anchovy oil", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11264" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "601", - "durability": null, - "name": "Anchovy oil", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11265" - }, - { - "ge_buy_limit": "100", - "examine": "Fish paste. Urk.", - "grand_exchange_price": "57", - "durability": null, - "name": "Anchovy paste", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11266" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "11267" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "11268" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "11269" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11270" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Picture", - "archery_ticket_price": "0", - "id": "11271" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11272" - }, - { - "destroy_message": "You will have to ask Elnock for another one of these.", - "examine": "Lets you easily identify your prey.", - "durability": null, - "name": "Impling scroll", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11273" - }, - { - "remove_sleeves": "true", - "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", - "grand_exchange_price": "27", - "durability": null, - "name": "Ham shirt", - "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "11274", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "durability": null, - "name": "Mith grapple", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11275" - }, - { - "durability": null, - "name": "Mith grapple", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11276" - }, - { - "lendable": "true", - "examine": "I hope I don't meet any roundheads...", - "durability": null, - "name": "Cavalier mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11277", - "equipment_slot": "8" - }, - { - "examine": "Essential mime wear.", - "durability": null, - "name": "Beret mask", - "weight": "1", - "archery_ticket_price": "0", - "id": "11278", - "equipment_slot": "0" - }, - { - "destroy_message": "You can complete the quest by speaking to Oziach even if you do not have the head.", - "examine": "The severed head of the great dragon Elvarg!", - "durability": null, - "name": "Elvarg's head", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11279", - "equipment_slot": "3" - }, - { - "remove_head": "true", - "lendable": "true", - "examine": "I hope I don't meet any roundheads...", - "durability": null, - "name": "Cavalier and mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11280", - "equipment_slot": "0" - }, - { - "durability": null, - "name": "Cavalier and mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11281" - }, - { - "remove_head": "true", - "examine": "Essential mime wear.", - "durability": null, - "name": "Beret and mask", - "weight": "1", - "archery_ticket_price": "0", - "id": "11282", - "equipment_slot": "0" - }, - { - "requirements": "{1,75}", - "examine": "A heavy shield with a snarling, draconic visage.", - "grand_exchange_price": "5653008", - "rare_item": "true", - "durability": null, - "name": "Dragonfire shield", - "weight": "7.2", - "archery_ticket_price": "0", - "id": "11283", - "bonuses": "0,0,0,-10,-5,20,25,22,10,22,17,7,0,0,0", - "equipment_slot": "5" - }, - { - "requirements": "{1,75}", - "ge_buy_limit": "10", - "examine": "A heavy shield with a snarling, draconic visage.", - "rare_item": "true", - "durability": null, - "weight": "7.2", - "equipment_slot": "5", - "grand_exchange_price": "7700000", - "name": "Dragonfire shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11284", - "bonuses": "0,0,0,-10,-5,20,25,22,10,22,17,7,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "7700000", - "durability": null, - "name": "Dragonfire shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11285" - }, - { - "ge_buy_limit": "10", - "examine": "It looks like this could be attached to a shield somehow.", - "grand_exchange_price": "8000000", - "rare_item": "true", - "durability": null, - "name": "Draconic visage", - "tradeable": "true", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "11286" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "8000000", - "durability": null, - "name": "Draconic visage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11287" - }, - { - "examine": "A heavy barbarian Fishing rod.", - "durability": null, - "name": "Barbarian rod", - "weight": "1", - "archery_ticket_price": "0", - "id": "11323" - }, - { - "shop_price": "6", - "ge_buy_limit": "5000", - "examine": "Roe, or cheap fishy eggs.", - "grand_exchange_price": "17", - "durability": null, - "name": "Roe", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "11324" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "17", - "durability": null, - "name": "Roe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11325" - }, - { - "ge_buy_limit": "5000", - "examine": "Caviar, or expensive fishy eggs.", - "grand_exchange_price": "370", - "durability": null, - "name": "Caviar", - "tradeable": "true", - "weight": "0.05", - "archery_ticket_price": "0", - "id": "11326" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "370", - "durability": null, - "name": "Caviar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11327" - }, - { - "shop_price": "11", - "ge_buy_limit": "5000", - "examine": "A sad-looking trout.", - "grand_exchange_price": "9", - "durability": null, - "name": "Leaping trout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11328" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "9", - "durability": null, - "name": "Leaping trout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11329" - }, - { - "ge_buy_limit": "5000", - "examine": "Some non-tasty salmon.", - "grand_exchange_price": "19", - "durability": null, - "name": "Leaping salmon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11330" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "19", - "durability": null, - "name": "Leaping salmon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11331" - }, - { - "ge_buy_limit": "5000", - "examine": "A bloated sturgeon.", - "grand_exchange_price": "151", - "durability": null, - "name": "Leaping sturgeon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11332" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "151", - "durability": null, - "name": "Leaping sturgeon", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11333" - }, - { - "ge_buy_limit": "100", - "examine": "Slices of inedible fish.", - "grand_exchange_price": "2", - "durability": null, - "name": "Fish offcuts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11334" - }, - { - "remove_head": "true", - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "Protects your head and looks impressive too.", - "rare_item": "true", - "durability": null, - "weight": "2", - "absorb": "2,0,4", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "51600000", - "name": "Dragon full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11335", - "bonuses": "0,0,0,-6,-2,45,48,41,-1,46,12,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "51600000", - "durability": null, - "name": "Dragon full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11336" - }, - { - "destroy_message": "I can gather more from the caverns below the Baxtorian Lake.", - "examine": "The bones of a barbarian warrior, slain by his fellow adventurers.", - "durability": null, - "name": "Mangled bones", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11337" - }, - { - "destroy_message": "I can gather more from the caverns below the Baxtorian Lake.", - "examine": "The bones of a barbarian warrior, slain by vile dragons.", - "durability": null, - "name": "Chewed bones", - "destroy": "true", - "weight": "9.6", - "archery_ticket_price": "0", - "id": "11338" - }, - { - "destroy_message": "I can obtain another from Otto.", - "examine": "Records from my discoveries beneath the lake.", - "durability": null, - "name": "My notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11339" - }, - { - "destroy_message": "I can obtain another from Otto.", - "examine": "A record of Otto's instructions to me.", - "durability": null, - "name": "Barbarian skills", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11340" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11341" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11342" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11343" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11344" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11345" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11346" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11347" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11348" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11349" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11350" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11351" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11352" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11353" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11354" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11355" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11356" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11357" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11358" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11359" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11360" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11361" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11362" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11363" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11364" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11365" - }, - { - "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", - "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", - "durability": null, - "name": "Ancient page", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11366" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "204", - "stand_anim": "813", - "name": "Bronze hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11367", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "204", - "durability": null, - "name": "Bronze hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11368" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An iron-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "25", - "stand_anim": "813", - "name": "Iron hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11369", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25", - "durability": null, - "name": "Iron hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11370" - }, - { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "93", - "stand_anim": "813", - "name": "Steel hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11371", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "93", - "durability": null, - "name": "Steel hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11372" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "357", - "stand_anim": "813", - "name": "Mithril hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11373", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "357", - "durability": null, - "name": "Mithril hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11374" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1095", - "stand_anim": "813", - "name": "Adamant hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11375", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1095", - "durability": null, - "name": "Adamant hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11376" - }, - { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "12300", - "stand_anim": "813", - "name": "Rune hasta", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11377", - "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12300", - "durability": null, - "name": "Rune hasta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11378" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "290", - "stand_anim": "813", - "name": "Bronze hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11379", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "290", - "durability": null, - "name": "Bronze hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11380" - }, - { - "turn90cw_anim": "1207", - "examine": "A karambwan poison-tipped, one-handed bronze hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "stand_anim": "813", - "name": "Bronze hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11381", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "775", - "stand_anim": "813", - "name": "Bronze hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11382", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "775", - "durability": null, - "name": "Bronze hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11383" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A bronze-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "8974", - "stand_anim": "813", - "name": "Bronze hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11384", - "stand_turn_anim": "1209", - "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8974", - "durability": null, - "name": "Bronze hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11385" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An iron-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "99", - "stand_anim": "813", - "name": "Iron hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11386", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "99", - "durability": null, - "name": "Iron hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11387" - }, - { - "turn90cw_anim": "1207", - "examine": "A karambwan poison-tipped, one-handed iron hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "stand_anim": "813", - "name": "Iron hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11388", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An iron-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "584", - "stand_anim": "813", - "name": "Iron hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11389", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "584", - "durability": null, - "name": "Iron hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11390" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An iron-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "8783", - "stand_anim": "813", - "name": "Iron hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11391", - "stand_turn_anim": "1209", - "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8783", - "durability": null, - "name": "Iron hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11392" - }, - { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "151", - "stand_anim": "813", - "name": "Steel hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11393", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "151", - "durability": null, - "name": "Steel hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11394" - }, - { - "requirements": "{0,5}", - "turn90cw_anim": "1207", - "examine": "A karambwan poison-tipped, one-handed steel hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "stand_anim": "813", - "name": "Steel hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11395", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0" - }, - { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "636", - "stand_anim": "813", - "name": "Steel hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11396", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "636", - "durability": null, - "name": "Steel hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11397" - }, - { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "8835", - "stand_anim": "813", - "name": "Steel hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11398", - "stand_turn_anim": "1209", - "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8835", - "durability": null, - "name": "Steel hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11399" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "440", - "stand_anim": "813", - "name": "Mithril hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11400", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "440", - "durability": null, - "name": "Mithril hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11401" - }, - { - "requirements": "{0,20}", - "turn90cw_anim": "1207", - "examine": "A karambwan poison-tipped, one-handed mithril hasta", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "353", - "stand_anim": "813", - "name": "Mithril hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11402", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "925", - "stand_anim": "813", - "name": "Mithril hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11403", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "925", - "durability": null, - "name": "Mithril hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11404" - }, - { - "requirements": "{0,20}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "1.8", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "8711", - "stand_anim": "813", - "name": "Mithril hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11405", - "stand_turn_anim": "1209", - "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8711", - "durability": null, - "name": "Mithril hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11406" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1154", - "stand_anim": "813", - "name": "Adamant hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11407", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1154", - "durability": null, - "name": "Adamant hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11408" - }, - { - "requirements": "{0,30}", - "turn90cw_anim": "1207", - "examine": "\tA karambwan poison-tipped, one-handed adamantite hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "stand_anim": "813", - "name": "Adamant hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11409", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "1639", - "stand_anim": "813", - "name": "Adamant hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11410", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1639", - "durability": null, - "name": "Adamant hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11411" - }, - { - "requirements": "{0,30}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "9388", - "stand_anim": "813", - "name": "Adamant hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11412", - "stand_turn_anim": "1209", - "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "9388", - "durability": null, - "name": "Adamant hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11413" - }, - { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "12300", - "stand_anim": "813", - "name": "Rune hasta(p)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11414", - "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12300", - "durability": null, - "name": "Rune hasta(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11415" - }, - { - "requirements": "{0,40}", - "turn90cw_anim": "1207", - "examine": "A karambwan poison-tipped, one-handed rune hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "stand_anim": "813", - "name": "Rune hasta(kp)", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11416", - "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "12200", - "stand_anim": "813", - "name": "Rune hasta(p+)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11417", - "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12200", - "durability": null, - "name": "Rune hasta(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11418" - }, - { - "requirements": "{0,40}", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "14", - "turn180_anim": "1206", - "render_anim": "28", - "equipment_slot": "3", - "grand_exchange_price": "15500", - "stand_anim": "813", - "name": "Rune hasta(p++)", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11419", - "stand_turn_anim": "1209", - "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15500", - "durability": null, - "name": "Rune hasta(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11420" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy attack potion", - "grand_exchange_price": "20", - "durability": null, - "name": "Attack mix(2)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "11429" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "20", - "durability": null, - "name": "Attack mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11430" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy attack potion", - "grand_exchange_price": "19", - "durability": null, - "name": "Attack mix(1)", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "11431" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "19", - "durability": null, - "name": "Attack mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11432" - }, - { - "ge_buy_limit": "1000", - "examine": "Two doses of fishy antipoison potion.", - "grand_exchange_price": "455", - "durability": null, - "name": "Antipoison mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11433" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "455", - "durability": null, - "name": "Antipoison mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11434" - }, - { - "ge_buy_limit": "1000", - "examine": "Two doses of fishy antipoison potion.", - "grand_exchange_price": "608", - "durability": null, - "name": "Antipoison mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11435" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "608", - "durability": null, - "name": "Antipoison mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11436" - }, - { - "ge_buy_limit": "1000", - "examine": "2 dose: Two doses of fishy Relicym's balm.", - "grand_exchange_price": "264", - "durability": null, - "name": "Relicym's mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11437" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "264", - "durability": null, - "name": "Relicym's mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11438" - }, - { - "ge_buy_limit": "1000", - "examine": "2 dose: Two doses of fishy Relicym's balm.", - "grand_exchange_price": "131", - "durability": null, - "name": "Relicym's mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11439" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "131", - "durability": null, - "name": "Relicym's mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11440" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy strength potion.", - "grand_exchange_price": "159", - "durability": null, - "name": "Strength mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11441" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "159", - "durability": null, - "name": "Strength mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11442" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy strength potion.", - "grand_exchange_price": "164", - "durability": null, - "name": "Strength mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11443" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "164", - "durability": null, - "name": "Strength mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11444" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of a fishy Combat potion", - "grand_exchange_price": "366", - "durability": null, - "name": "Combat mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11445" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "366", - "durability": null, - "name": "Combat mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11446" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of a fishy Combat potion", - "grand_exchange_price": "328", - "durability": null, - "name": "Combat mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11447" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "328", - "durability": null, - "name": "Combat mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11448" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two dose(s) of fishy restore potion.", - "grand_exchange_price": "17", - "durability": null, - "name": "Restore mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11449" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "17", - "durability": null, - "name": "Restore mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11450" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two dose(s) of fishy restore potion.", - "grand_exchange_price": "24", - "durability": null, - "name": "Restore mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11451" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "24", - "durability": null, - "name": "Restore mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11452" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy energy potion.", - "grand_exchange_price": "248", - "durability": null, - "name": "Energy mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11453" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "248", - "durability": null, - "name": "Energy mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11454" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy energy potion.", - "grand_exchange_price": "212", - "durability": null, - "name": "Energy mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11455" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "212", - "durability": null, - "name": "Energy mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11456" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of a fishy defence potion", - "grand_exchange_price": "887", - "durability": null, - "name": "Defence mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11457" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "887", - "durability": null, - "name": "Defence mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11458" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of a fishy defence potion", - "grand_exchange_price": "780", - "durability": null, - "name": "Defence mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11459" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "780", - "durability": null, - "name": "Defence mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11460" - }, - { - "ge_buy_limit": "1000", - "examine": "2 doses of a fishy agility potion", - "grand_exchange_price": "388", - "durability": null, - "name": "Agility mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11461" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "388", - "durability": null, - "name": "Agility mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11462" - }, - { - "ge_buy_limit": "1000", - "examine": "1 dose of a fishy agility potion", - "grand_exchange_price": "459", - "durability": null, - "name": "Agility mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11463" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "459", - "durability": null, - "name": "Agility mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11464" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two dose(s) of a fishy Prayer potion.", - "grand_exchange_price": "2761", - "durability": null, - "name": "Prayer mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11465" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2761", - "durability": null, - "name": "Prayer mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11466" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two dose(s) of a fishy Prayer potion.", - "grand_exchange_price": "1857", - "durability": null, - "name": "Prayer mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11467" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1857", - "durability": null, - "name": "Prayer mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11468" - }, - { - "ge_buy_limit": "1000", - "examine": "one/two doses of fishy super attack potion", - "grand_exchange_price": "375", - "durability": null, - "name": "Superattack mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11469" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "375", - "durability": null, - "name": "Superattack mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11470" - }, - { - "ge_buy_limit": "1000", - "examine": "one/two doses of fishy super attack potion", - "grand_exchange_price": "516", - "durability": null, - "name": "Superattack mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11471" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "516", - "durability": null, - "name": "Superattack mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11472" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy super antipoison potion.", - "grand_exchange_price": "613", - "durability": null, - "name": "Anti-p supermix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11473" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "613", - "durability": null, - "name": "Anti-p supermix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11474" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy super antipoison potion.", - "grand_exchange_price": "1119", - "durability": null, - "name": "Anti-p supermix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11475" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1119", - "durability": null, - "name": "Anti-p supermix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11476" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two Doses of fishy fishing potion.", - "grand_exchange_price": "123", - "durability": null, - "name": "Fishing mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11477" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "123", - "durability": null, - "name": "Fishing mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11478" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two Doses of fishy fishing potion.", - "grand_exchange_price": "87", - "durability": null, - "name": "Fishing mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11479" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "87", - "durability": null, - "name": "Fishing mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11480" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy super energy potion.", - "grand_exchange_price": "1062", - "durability": null, - "name": "Sup. energy mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11481" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1062", - "durability": null, - "name": "Sup. energy mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11482" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy super energy potion.", - "grand_exchange_price": "562", - "durability": null, - "name": "Sup. energy mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11483" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "562", - "durability": null, - "name": "Sup. energy mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11484" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Super strength potion.", - "grand_exchange_price": "955", - "durability": null, - "name": "Sup. str. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11485" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "955", - "durability": null, - "name": "Sup. str. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11486" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Super strength potion.", - "grand_exchange_price": "1040", - "durability": null, - "name": "Sup. str. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11487" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1040", - "durability": null, - "name": "Sup. str. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11488" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Magic essence potion.", - "grand_exchange_price": "808", - "durability": null, - "name": "Magic ess. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11489" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "808", - "durability": null, - "name": "Magic ess. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11490" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Magic essence potion.", - "grand_exchange_price": "447", - "durability": null, - "name": "Magic ess. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11491" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "447", - "durability": null, - "name": "Magic ess. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11492" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy super restore potion.", - "grand_exchange_price": "3670", - "durability": null, - "name": "Sup. restore mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11493" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3670", - "durability": null, - "name": "Sup. restore mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11494" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy super restore potion.", - "grand_exchange_price": "3104", - "durability": null, - "name": "Sup. restore mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11495" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "3104", - "durability": null, - "name": "Sup. restore mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11496" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy super defence potion", - "grand_exchange_price": "38", - "durability": null, - "name": "Sup. def. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11497" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "38", - "durability": null, - "name": "Sup. def. mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11498" - }, - { - "ge_buy_limit": "1000", - "examine": "One/two doses of fishy super defence potion", - "grand_exchange_price": "66", - "durability": null, - "name": "Sup. def. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11499" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "66", - "durability": null, - "name": "Sup. def. mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11500" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy extra strength antidote potion.", - "grand_exchange_price": "1909", - "durability": null, - "name": "Antidote+ mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11501" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1909", - "durability": null, - "name": "Antidote+ mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11502" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy extra strength antidote potion.", - "grand_exchange_price": "1330", - "durability": null, - "name": "Antidote+ mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11503" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1330", - "durability": null, - "name": "Antidote+ mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11504" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy anti-fire breath potion.", - "grand_exchange_price": "2048", - "durability": null, - "name": "Antifire mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11505" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2048", - "durability": null, - "name": "Antifire mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11506" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two dose(s) of fishy anti-fire breath potion.", - "grand_exchange_price": "1444", - "durability": null, - "name": "Antifire mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11507" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1444", - "durability": null, - "name": "Antifire mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11508" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy ranging potion.", - "grand_exchange_price": "2879", - "durability": null, - "name": "Ranging mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11509" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2879", - "durability": null, - "name": "Ranging mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11510" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy ranging potion.", - "grand_exchange_price": "1880", - "durability": null, - "name": "Ranging mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11511" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1880", - "durability": null, - "name": "Ranging mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11512" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy magic potion.", - "grand_exchange_price": "1022", - "durability": null, - "name": "Magic mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11513" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1022", - "durability": null, - "name": "Magic mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11514" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy magic potion.", - "grand_exchange_price": "1323", - "durability": null, - "name": "Magic mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11515" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1323", - "durability": null, - "name": "Magic mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11516" - }, - { - "ge_buy_limit": "1000", - "examine": "Doses of fishy hunting potion", - "grand_exchange_price": "146", - "durability": null, - "name": "Hunting mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11517" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "146", - "durability": null, - "name": "Hunting mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11518" - }, - { - "ge_buy_limit": "1000", - "examine": "Doses of fishy hunting potion", - "grand_exchange_price": "87", - "durability": null, - "name": "Hunting mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11519" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "87", - "durability": null, - "name": "Hunting mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11520" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Zamorak potion.", - "grand_exchange_price": "665", - "durability": null, - "name": "Zamorak mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11521" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "665", - "durability": null, - "name": "Zamorak mix(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11522" - }, - { - "ge_buy_limit": "1000", - "examine": "One/Two doses of fishy Zamorak potion.", - "grand_exchange_price": "339", - "durability": null, - "name": "Zamorak mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "weight": "0.1", - "id": "11523" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "339", - "durability": null, - "name": "Zamorak mix(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11524" - }, - { - "ge_buy_limit": "5000", - "examine": "More fluff than feather.", - "grand_exchange_price": "325", - "durability": null, - "name": "Wimpy feather", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11525" - }, - { - "destroy_message": "If you drop this book it will be destroyed. You cannot replace it.", - "examine": "Educate yourself.", - "durability": null, - "name": "Book of knowledge", - "weight": "1", - "archery_ticket_price": "0", - "id": "11640" - }, - { - "durability": null, - "name": "Astronomy book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11655" - }, - { - "ge_buy_limit": "100", - "examine": "A book that explains the art of crafting items from glass.", - "grand_exchange_price": "278", - "durability": null, - "name": "Glassblowing book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11656" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "278", - "durability": null, - "name": "Glassblowing book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11657" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A mage helm from the order of the Void Knights.", - "durability": null, - "name": "Void mage helm", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11663", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A ranger helm from the order of the Void Knights", - "durability": null, - "name": "Void ranger helm", - "tradeable": "false", - "weight": "3.6", - "archery_ticket_price": "0", - "id": "11664", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A melee helm from the order of the Void Knights.", - "durability": null, - "name": "Void melee helm", - "tradeable": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "11665", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "durability": null, - "name": "Void seal(8)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11666", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(7)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11667", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(6)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11668", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(5)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11669", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(4)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11670", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(3)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11671", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(2)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11672", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "durability": null, - "name": "Void seal(1)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11673", - "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", - "equipment_slot": "2" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A mage helm from the order of the Void Knights.", - "durability": null, - "name": "Void mage helm", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11674", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A ranger helm from the order of the Void Knights", - "durability": null, - "name": "Void ranger helm", - "tradeable": "false", - "weight": "3.6", - "archery_ticket_price": "0", - "id": "11675", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", - "shop_price": "200", - "examine": "A melee helm from the order of the Void Knights.", - "durability": null, - "name": "Void melee helm", - "tradeable": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "11676", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "I can find another book in the Keldagrim library.", - "examine": "\"Beyond Trollheim\" by Nestor Peregrine.", - "durability": null, - "name": "Explorer's notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11677" - }, - { - "destroy_message": "You'll need to visit the Sinclair Mansion to get another one.", - "examine": "A black helm, too small for your head", - "durability": null, - "name": "Black knight helm", - "tradeable": "false", - "destroy": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "11678" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11679" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A change of address form.", - "durability": null, - "name": "Address form", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11680" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It has something written on it.", - "durability": null, - "name": "Scrap paper", - "archery_ticket_price": "0", - "id": "11681" - }, - { - "examine": "I could use this to pick the lock.", - "durability": null, - "name": "Hair clip", - "archery_ticket_price": "0", - "id": "11682" - }, - { - "ge_buy_limit": "10", - "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", - "grand_exchange_price": "331300", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11686" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "331300", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11687" - }, - { - "ge_buy_limit": "10", - "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", - "grand_exchange_price": "342100", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11688" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "342100", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11689" - }, - { - "ge_buy_limit": "10", - "examine": "The blade for the ultimate weapon.", - "grand_exchange_price": "466800", - "durability": null, - "name": "Godsword blade", - "tradeable": "true", - "weight": "7.5", - "archery_ticket_price": "0", - "id": "11690" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "466800", - "durability": null, - "name": "Godsword blade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11691" - }, - { - "ge_buy_limit": "10", - "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", - "grand_exchange_price": "348500", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11692" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "348500", - "durability": null, - "name": "Godsword shards", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11693" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7043", - "examine": "A beautiful, heavy sword.", - "walk_anim": "7046", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "7044", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "28000000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "11694", - "stand_turn_anim": "7040", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", - "requirements": "{0,75}", - "durability": null, - "weight": "10", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "3846,0,0,0", - "name": "Armadyl godsword" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "28000000", - "durability": null, - "name": "Armadyl godsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11695" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7043", - "examine": "A brutally heavy sword.", - "walk_anim": "7046", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "7044", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "3700000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "11696", - "stand_turn_anim": "7040", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", - "requirements": "{0,75}", - "durability": null, - "weight": "10", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "3846,0,0,0", - "name": "Bandos godsword" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "3700000", - "durability": null, - "name": "Bandos godsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11697" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7043", - "examine": "A gracious, heavy sword.", - "walk_anim": "7046", - "has_special": "true", - "turn90ccw_anim": "7044", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "22000000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "11698", - "stand_turn_anim": "7040", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", - "requirements": "{0,75}", - "durability": null, - "weight": "10", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "3846,0,0,0", - "name": "Saradomin godsword" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "22000000", - "durability": null, - "name": "Saradomin godsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11699" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7043", - "examine": "A terrifying, heavy sword.", - "walk_anim": "7046", - "has_special": "true", - "turn90ccw_anim": "7044", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "6100000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "11700", - "stand_turn_anim": "7040", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", - "requirements": "{0,75}", - "durability": null, - "weight": "10", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "3846,0,0,0", - "name": "Zamorak godsword" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "6100000", - "durability": null, - "name": "Zamorak godsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11701" - }, - { - "ge_buy_limit": "10", - "examine": "Brimming with potential.", - "grand_exchange_price": "30600000", - "rare_item": "true", - "durability": null, - "name": "Armadyl hilt", - "tradeable": "true", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "11702" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "30600000", - "durability": null, - "name": "Armadyl hilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11703" - }, - { - "ge_buy_limit": "10", - "examine": "Brimming with potential.", - "grand_exchange_price": "3300000", - "rare_item": "true", - "durability": null, - "name": "Bandos hilt", - "tradeable": "true", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "11704" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "3300000", - "durability": null, - "name": "Bandos hilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11705" - }, - { - "ge_buy_limit": "10", - "examine": "Brimming with potential.", - "grand_exchange_price": "24900000", - "rare_item": "true", - "durability": null, - "name": "Saradomin hilt", - "tradeable": "true", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "11706" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "24900000", - "durability": null, - "name": "Saradomin hilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11707" - }, - { - "ge_buy_limit": "10", - "examine": "Brimming with potential.", - "grand_exchange_price": "6100000", - "rare_item": "true", - "durability": null, - "name": "Zamorak hilt", - "tradeable": "true", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "11708" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "6100000", - "durability": null, - "name": "Zamorak hilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11709" - }, - { - "ge_buy_limit": "10", - "examine": "Part of the Godsword blade.", - "grand_exchange_price": "150200", - "durability": null, - "rare_item": "true", - "name": "Godsword shard 1", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11710" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "150200", - "durability": null, - "name": "Godsword shard 1", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11711" - }, - { - "ge_buy_limit": "10", - "examine": "Part of the Godsword blade.", - "grand_exchange_price": "149100", - "durability": null, - "rare_item": "true", - "name": "Godsword shard 2", - "tradeable": "true", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11712" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "149100", - "durability": null, - "name": "Godsword shard 2", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11713" - }, - { - "ge_buy_limit": "10", - "examine": "Part of the Godsword blade.", - "grand_exchange_price": "149800", - "durability": null, - "rare_item": "true", - "name": "Godsword shard 3", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11714" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "149800", - "durability": null, - "name": "Godsword shard 3", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11715" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "An evil spear.", - "walk_anim": "1205", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "5400000", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11716", - "stand_turn_anim": "1209", - "bonuses": "85,65,65,0,0,13,13,12,0,13,0,75,2,0,0", - "requirements": "{0,70}", - "durability": null, - "weight": "3", - "weapon_interface": "14", - "render_anim": "28", - "lendable": "true", - "name": "Zamorakian spear" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "5400000", - "durability": null, - "name": "Zamorakian spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11717" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", - "examine": "A helmet of great craftmanship.", - "rare_item": "true", - "durability": null, - "weight": "0.5", - "absorb": "0,5,2", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "4100000", - "name": "Armadyl helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11718", - "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "4100000", - "durability": null, - "name": "Armadyl helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11719" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", - "examine": "A chestplate of great craftsmanship", - "rare_item": "true", - "durability": null, - "weight": "4", - "absorb": "0,10,5", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "14800000", - "name": "Armadyl chestplate", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11720", - "bonuses": "-7,-7,-7,-15,33,56,48,61,70,57,52,0,1,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "14800000", - "durability": null, - "name": "Armadyl chestplate", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11721" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "2", - "examine": "A rune plateskirt in the colours of Armadyl.", - "rare_item": "true", - "durability": null, - "weight": "8", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "14800000", - "name": "Armadyl plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11722", - "bonuses": "-6,-6,-6,-10,20,32,26,34,40,33,25,0,1,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "14800000", - "durability": null, - "name": "Armadyl plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11723" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "1", - "examine": "A sturdy chestplate.", - "rare_item": "true", - "durability": null, - "weight": "12", - "absorb": "4,0,9", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "14700000", - "name": "Bandos chestplate", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11724", - "bonuses": "0,0,0,-15,-10,98,93,105,-6,133,52,4,1,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "14700000", - "durability": null, - "name": "Bandos chestplate", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11725" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "1", - "examine": "A sturdy pair of tassets.", - "rare_item": "true", - "durability": null, - "weight": "8", - "absorb": "3,0,6", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "15200000", - "name": "Bandos tassets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11726", - "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,1,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "15200000", - "durability": null, - "name": "Bandos tassets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11727" - }, - { - "requirements": "{1,65}", - "ge_buy_limit": "1", - "examine": "Some sturdy boots", - "rare_item": "true", - "durability": null, - "weight": "6", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "111700", - "name": "Bandos boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11728", - "bonuses": "0,0,0,-5,-3,17,18,19,0,0,15,0,1,0,0" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "111700", - "durability": null, - "name": "Bandos boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11729" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "7043", - "examine": "The incredible blade of an Icyene.", - "walk_anim": "7046", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "7044", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "7045", - "defence_anim": "7050", - "equipment_slot": "3", - "attack_anims": "7041,7041,7048,7049", - "grand_exchange_price": "5300000", - "stand_anim": "7047", - "tradeable": "true", - "run_anim": "7039", - "archery_ticket_price": "0", - "id": "11730", - "stand_turn_anim": "7040", - "bonuses": "0,82,60,0,0,0,0,0,0,0,0,82,2,0,0", - "requirements": "{0,70}", - "durability": null, - "weight": "3", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "3846,0,0,0", - "name": "Saradomin sword" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "5300000", - "durability": null, - "name": "Saradomin sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11731" - }, - { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "examine": "These will protect my feet.", - "durability": null, - "rare_item": "true", - "weight": "1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "95400", - "name": "Dragon boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11732", - "bonuses": "0,0,0,-3,-1,16,17,18,0,0,15,4,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "95400", - "durability": null, - "name": "Dragon boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11733" - }, - { - "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.", - "durability": null, - "name": "Knight's notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11734" - }, - { - "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.", - "durability": null, - "name": "Knight's notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11735" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "231300", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11736", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "equip_audio": "2230", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Steam battlestaff" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "231300", - "durability": null, - "name": "Steam battlestaff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11737" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "267600", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "11738", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic steam staff" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "267600", - "durability": null, - "name": "Mystic steam staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11739" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11741" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11743" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11745" - }, - { - "destroy_message": "There may be another in the chest where you found this.", - "examine": "This probably opens a chest in Golrie's caves. (Path of Glophrie)", - "durability": null, - "name": "A key to a chest", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11747" - }, - { - "destroy_message": "There may be another in the chest where you found this.", - "examine": "This opens the door into Golrie's strongroom. (Path of Glophrie)", - "durability": null, - "name": "Strongroom key", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11748" - }, - { - "destroy_message": "Maybe Brimstail will have another crystal seed you could grow into a chime.", - "examine": "It makes a sound.", - "durability": null, - "name": "Crystal chime", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11749", - "equipment_slot": "3" - }, - { - "destroy_message": "There may be another in the chest where you found this.", - "examine": "Notes written by Yewnock while copying Oaknock's machine.", - "durability": null, - "name": "Yewnock's notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11750" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11753" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11754" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11755" - }, - { - "destroy_message": "You will need to get a new set of armour from Rat Burgiss if you destroy this one.", - "remove_sleeves": "true", - "examine": "Varrock Smithing armour.", - "durability": null, - "name": "Varrock armour 1", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11756", - "bonuses": "0,0,0,-30,-10,15,14,9,-6,14,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "destroy_message": "You will need to get a new set of armour from Reldo if you destroy this one.", - "remove_sleeves": "true", - "examine": "Varrock smithing armour.", - "durability": null, - "name": "Varrock armour 2", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11757", - "bonuses": "0,0,0,-30,-10,21,20,12,-6,20,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "destroy_message": "You will need to get a new set of armour from Vannaka if you destroy this one.", - "remove_sleeves": "true", - "examine": "Varrock smithing armour.", - "durability": null, - "name": "Varrock armour 3", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11758", - "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", - "equipment_slot": "4" - }, - { "shop_price": "4", - "examine": "A number of wooden logs.", - "grand_exchange_price": "124", + "tradeable": "false" + }, + { + "id": "11210", + "name": "Golden goblin", + "examine": "A shiny golden goblin. Strange!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, - "name": "Logs", + "tradeable": "false", + "weight": "30" + }, + { + "id": "11211", + "name": "Magic beans", + "examine": "A bag of magic beans.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to get another bag of beans from Sylas if you destroy this one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11212", + "name": "Dragon arrow", + "examine": "An arrow made using a dragon's talon.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11217", + "name": "Dragon fire arrows", + "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11222", + "name": "Dragon fire arrows", + "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11227", + "name": "Dragon arrow(p)", + "examine": "An arrow made using a dragon's talon.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11228", + "name": "Dragon arrow(p+)", + "examine": "An arrow made using a dragon's talon.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11229", + "name": "Dragon arrow(p++)", + "examine": "An arrow made using a dragon's talon.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true" + }, + { + "id": "11230", + "name": "Dragon dart", + "examine": "A deadly throwing dart with a dragon tip.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,60}", "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11760" + "weapon_interface": "18" }, { - "destroy_message": "You can get the diary again from Sandy's desk.", - "examine": "A locked/unlocked diary.", + "id": "11231", + "name": "Dragon dart(p)", + "examine": "A deadly throwing dart with a dragon tip.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20", "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "11232", + "name": "Dragon dart tip", + "examine": "A deadly looking dragon dart tip - needs feathers for flight.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11233", + "name": "Dragon dart(p+)", + "examine": "A deadly throwing dart with a dragon tip.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "11234", + "name": "Dragon dart(p++)", + "examine": "A deadly throwing dart with a dragon tip.", + "archery_ticket_price": "0", + "attack_audios": "2547,0,0,0", + "attack_speed": "3", + "bonuses": "0,0,0,0,18,0,0,0,0,0,0,0,0,0,20", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "10000", + "requirements": "{4,60}", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "11235", + "name": "Dark bow", + "examine": "A bow from a darker dimension.", + "archery_ticket_price": "0", + "attack_anims": "426,426,426,426", + "attack_audios": "3731,0,0,0", + "attack_speed": "9", + "bonuses": "0,0,0,0,95,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "424", + "durability": null, + "equip_audio": "3738", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "1", + "requirements": "{4,60}", + "run_anim": "824", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "two_handed": "true", + "walk_anim": "819", + "weapon_interface": "16", + "weight": "1.9" + }, + { + "id": "11236", + "name": "Dark bow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11237", + "name": "Dragon arrowtips", + "examine": "Dragon talons, usable as arrowheads.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "11238", + "name": "Baby impling jar", + "examine": "A baby impling in a jar. That's a bit cruel.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11239", + "name": "Baby impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11240", + "name": "Young impling jar", + "examine": "A young impling in a jar. Don't trap me, man.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11241", + "name": "Young impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11242", + "name": "Gourm' impling jar", + "examine": "A gourmet impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11243", + "name": "Gourm' impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11244", + "name": "Earth impling jar", + "examine": "Earth impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11245", + "name": "Earth impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11246", + "name": "Ess' impling jar", + "examine": "Essence impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11247", + "name": "Ess' impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11248", + "name": "Eclectic impling jar", + "examine": "Eclectic impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11249", + "name": "Eclectic impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11250", + "name": "Nature impling jar", + "examine": "Nature impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11251", + "name": "Nature impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11252", + "name": "Magpie impling jar", + "examine": "Magpie impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "11253", + "name": "Magpie impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11254", + "name": "Ninja impling jar", + "examine": "Ninja impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "11255", + "name": "Ninja impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11256", + "name": "Dragon impling jar", + "examine": "Dragon impling in a jar.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "11257", + "name": "Dragon impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "11258", + "name": "Jar generator", + "examine": "Creates butterfly and impling jars.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11259", + "name": "Magic butterfly net", + "examine": "For catching butterflies.", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "You will have to trade with Elnock to get a new magic butterfly net.", + "durability": null, + "equipment_slot": "3", + "render_anim": "1426", + "requirements": "{21,17}", + "run_anim": "6603", + "shop_price": "3", + "stand_anim": "6604", + "stand_turn_anim": "6611", + "tradeable": "false", + "turn180_anim": "6608", + "turn90ccw_anim": "6609", + "turn90cw_anim": "6610", + "two_handed": "", + "walk_anim": "6607", + "weapon_interface": "14", + "weight": "0.2" + }, + { + "id": "11260", + "name": "Impling jar", + "examine": "It's got little holes in the top.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "11261", + "name": "Impling jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11262", + "name": "Imp repellent", + "examine": "Imps seem to hate this stuff. Can't say I blame them.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11263", + "name": "Imp repellent", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11264", + "name": "Anchovy oil", + "examine": "Omega 3 oil. Good for the brain, not so for the nose.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11265", + "name": "Anchovy oil", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11266", + "name": "Anchovy paste", + "examine": "Fish paste. Urk.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11267", + "name": "Picture", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11268", + "name": "Picture", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11269", + "name": "Picture", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11270", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11271", + "name": "Picture", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11272", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11273", + "name": "Impling scroll", + "examine": "Lets you easily identify your prey.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will have to ask Elnock for another one of these.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11274", + "name": "Ham shirt", + "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "11275", + "name": "Mith grapple", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11276", + "name": "Mith grapple", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11277", + "name": "Cavalier mask", + "examine": "I hope I don't meet any roundheads...", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "8", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "11278", + "name": "Beret mask", + "examine": "Essential mime wear.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "weight": "1" + }, + { + "id": "11279", + "name": "Elvarg's head", + "examine": "The severed head of the great dragon Elvarg!", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can complete the quest by speaking to Oziach even if you do not have the head.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "11280", + "name": "Cavalier and mask", + "examine": "I hope I don't meet any roundheads...", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "11281", + "name": "Cavalier and mask", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11282", + "name": "Beret and mask", + "examine": "Essential mime wear.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "1" + }, + { + "id": "11283", + "name": "Dragonfire shield", + "examine": "A heavy shield with a snarling, draconic visage.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-5,20,25,22,10,22,17,7,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "rare_item": "true", + "requirements": "{1,75}", + "weight": "7.2" + }, + { + "id": "11284", + "name": "Dragonfire shield", + "examine": "A heavy shield with a snarling, draconic visage.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-10,-5,20,25,22,10,22,17,7,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "10", + "rare_item": "true", + "requirements": "{1,75}", + "tradeable": "true", + "weight": "7.2" + }, + { + "id": "11285", + "name": "Dragonfire shield", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11286", + "name": "Draconic visage", + "examine": "It looks like this could be attached to a shield somehow.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "11287", + "name": "Draconic visage", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11323", + "name": "Barbarian rod", + "examine": "A heavy barbarian Fishing rod.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "11324", + "name": "Roe", + "examine": "Roe, or cheap fishy eggs.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "6", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "11325", + "name": "Roe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11326", + "name": "Caviar", + "examine": "Caviar, or expensive fishy eggs.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "11327", + "name": "Caviar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11328", + "name": "Leaping trout", + "examine": "A sad-looking trout.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "11", + "tradeable": "true" + }, + { + "id": "11329", + "name": "Leaping trout", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11330", + "name": "Leaping salmon", + "examine": "Some non-tasty salmon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11331", + "name": "Leaping salmon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11332", + "name": "Leaping sturgeon", + "examine": "A bloated sturgeon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11333", + "name": "Leaping sturgeon", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11334", + "name": "Fish offcuts", + "examine": "Slices of inedible fish.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11335", + "name": "Dragon full helm", + "examine": "Protects your head and looks impressive too.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,45,48,41,-1,46,12,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11336", + "name": "Dragon full helm", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11337", + "name": "Mangled bones", + "examine": "The bones of a barbarian warrior, slain by his fellow adventurers.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can gather more from the caverns below the Baxtorian Lake.", + "durability": null, + "weight": "1" + }, + { + "id": "11338", + "name": "Chewed bones", + "examine": "The bones of a barbarian warrior, slain by vile dragons.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can gather more from the caverns below the Baxtorian Lake.", + "durability": null, + "weight": "9.6" + }, + { + "id": "11339", + "name": "My notes", + "examine": "Records from my discoveries beneath the lake.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can obtain another from Otto.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11340", + "name": "Barbarian skills", + "examine": "A record of Otto's instructions to me.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can obtain another from Otto.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11341", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11342", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11343", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11344", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11345", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11346", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11347", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11348", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11349", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11350", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11351", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11352", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11353", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11354", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11355", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11356", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11357", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11358", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11359", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11360", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11361", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11362", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11363", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11364", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11365", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11366", + "name": "Ancient page", + "examine": "Could a dragon have written this?; Tiny writing.;Smells disgusting.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another from the caverns beneath the Baxtorian Lake.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11367", + "name": "Bronze hasta", + "examine": "A bronze-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11368", + "name": "Bronze hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11369", + "name": "Iron hasta", + "examine": "An iron-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11370", + "name": "Iron hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11371", + "name": "Steel hasta", + "examine": "A steel-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11372", + "name": "Steel hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11373", + "name": "Mithril hasta", + "examine": "A mithril-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "11374", + "name": "Mithril hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11375", + "name": "Adamant hasta", + "examine": "An adamant-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11376", + "name": "Adamant hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11377", + "name": "Rune hasta", + "examine": "A rune-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11378", + "name": "Rune hasta", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11379", + "name": "Bronze hasta(p)", + "examine": "A bronze-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11380", + "name": "Bronze hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11381", + "name": "Bronze hasta(kp)", + "examine": "A karambwan poison-tipped, one-handed bronze hasta.", + "archery_ticket_price": "0", + "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11382", + "name": "Bronze hasta(p+)", + "examine": "A bronze-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11383", + "name": "Bronze hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11384", + "name": "Bronze hasta(p++)", + "examine": "A bronze-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "5,5,5,0,0,-1,-1,-1,0,-1,0,6,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11385", + "name": "Bronze hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11386", + "name": "Iron hasta(p)", + "examine": "An iron-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11387", + "name": "Iron hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11388", + "name": "Iron hasta(kp)", + "examine": "A karambwan poison-tipped, one-handed iron hasta.", + "archery_ticket_price": "0", + "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11389", + "name": "Iron hasta(p+)", + "examine": "An iron-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11390", + "name": "Iron hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11391", + "name": "Iron hasta(p++)", + "examine": "An iron-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,-2,-2,-2,0,-2,0,10,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11392", + "name": "Iron hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11393", + "name": "Steel hasta(p)", + "examine": "A steel-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11394", + "name": "Steel hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11395", + "name": "Steel hasta(kp)", + "examine": "A karambwan poison-tipped, one-handed steel hasta.", + "archery_ticket_price": "0", + "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11396", + "name": "Steel hasta(p+)", + "examine": "A steel-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11397", + "name": "Steel hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11398", + "name": "Steel hasta(p++)", + "examine": "A steel-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "12,12,12,0,0,-3,-3,-3,0,-3,0,12,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,5}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11399", + "name": "Steel hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11400", + "name": "Mithril hasta(p)", + "examine": "A mithril-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "11401", + "name": "Mithril hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11402", + "name": "Mithril hasta(kp)", + "examine": "A karambwan poison-tipped, one-handed mithril hasta", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "11403", + "name": "Mithril hasta(p+)", + "examine": "A mithril-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "11404", + "name": "Mithril hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11405", + "name": "Mithril hasta(p++)", + "examine": "A mithril-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "17,17,17,0,0,-5,-5,-4,0,-5,0,18,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,20}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "1.8" + }, + { + "id": "11406", + "name": "Mithril hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11407", + "name": "Adamant hasta(p)", + "examine": "An adamant-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11408", + "name": "Adamant hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11409", + "name": "Adamant hasta(kp)", + "examine": "\tA karambwan poison-tipped, one-handed adamantite hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11410", + "name": "Adamant hasta(p+)", + "examine": "An adamant-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11411", + "name": "Adamant hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11412", + "name": "Adamant hasta(p++)", + "examine": "An adamant-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "bonuses": "24,24,24,0,0,-6,-7,-5,0,-6,0,28,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11413", + "name": "Adamant hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11414", + "name": "Rune hasta(p)", + "examine": "A rune-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11415", + "name": "Rune hasta(p)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11416", + "name": "Rune hasta(kp)", + "examine": "A karambwan poison-tipped, one-handed rune hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11417", + "name": "Rune hasta(p+)", + "examine": "A rune-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11418", + "name": "Rune hasta(p+)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11419", + "name": "Rune hasta(p++)", + "examine": "A rune-tipped, one-handed hasta.", + "archery_ticket_price": "0", + "attack_audios": "2562,2556,2555,2562", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,-10,-10,-9,0,-10,0,42,0,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", + "requirements": "{0,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "2.2" + }, + { + "id": "11420", + "name": "Rune hasta(p++)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11429", + "name": "Attack mix(2)", + "examine": "One/Two dose(s) of fishy attack potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11430", + "name": "Attack mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11431", + "name": "Attack mix(1)", + "examine": "One/Two dose(s) of fishy attack potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11432", + "name": "Attack mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11433", + "name": "Antipoison mix(2)", + "examine": "Two doses of fishy antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11434", + "name": "Antipoison mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11435", + "name": "Antipoison mix(1)", + "examine": "Two doses of fishy antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11436", + "name": "Antipoison mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11437", + "name": "Relicym's mix(2)", + "examine": "2 dose: Two doses of fishy Relicym's balm.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11438", + "name": "Relicym's mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11439", + "name": "Relicym's mix(1)", + "examine": "2 dose: Two doses of fishy Relicym's balm.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11440", + "name": "Relicym's mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11441", + "name": "Strength mix(1)", + "examine": "One/two doses of fishy strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11442", + "name": "Strength mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11443", + "name": "Strength mix(2)", + "examine": "One/two doses of fishy strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11444", + "name": "Strength mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11445", + "name": "Combat mix(2)", + "examine": "One/two doses of a fishy Combat potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11446", + "name": "Combat mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11447", + "name": "Combat mix(1)", + "examine": "One/two doses of a fishy Combat potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11448", + "name": "Combat mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11449", + "name": "Restore mix(2)", + "examine": "One/two dose(s) of fishy restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11450", + "name": "Restore mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11451", + "name": "Restore mix(1)", + "examine": "One/two dose(s) of fishy restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11452", + "name": "Restore mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11453", + "name": "Energy mix(2)", + "examine": "One/two doses of fishy energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11454", + "name": "Energy mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11455", + "name": "Energy mix(1)", + "examine": "One/two doses of fishy energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11456", + "name": "Energy mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11457", + "name": "Defence mix(2)", + "examine": "One/two doses of a fishy defence potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11458", + "name": "Defence mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11459", + "name": "Defence mix(1)", + "examine": "One/two doses of a fishy defence potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11460", + "name": "Defence mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11461", + "name": "Agility mix(2)", + "examine": "2 doses of a fishy agility potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11462", + "name": "Agility mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11463", + "name": "Agility mix(1)", + "examine": "1 dose of a fishy agility potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11464", + "name": "Agility mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11465", + "name": "Prayer mix(2)", + "examine": "One/two dose(s) of a fishy Prayer potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11466", + "name": "Prayer mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11467", + "name": "Prayer mix(1)", + "examine": "One/two dose(s) of a fishy Prayer potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11468", + "name": "Prayer mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11469", + "name": "Superattack mix(2)", + "examine": "one/two doses of fishy super attack potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11470", + "name": "Superattack mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11471", + "name": "Superattack mix(1)", + "examine": "one/two doses of fishy super attack potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11472", + "name": "Superattack mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11473", + "name": "Anti-p supermix(2)", + "examine": "One/Two dose(s) of fishy super antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11474", + "name": "Anti-p supermix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11475", + "name": "Anti-p supermix(1)", + "examine": "One/Two dose(s) of fishy super antipoison potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11476", + "name": "Anti-p supermix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11477", + "name": "Fishing mix(2)", + "examine": "One/Two Doses of fishy fishing potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11478", + "name": "Fishing mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11479", + "name": "Fishing mix(1)", + "examine": "One/Two Doses of fishy fishing potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11480", + "name": "Fishing mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11481", + "name": "Super energy mix(2)", + "examine": "One/Two doses of fishy super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11482", + "name": "Super energy mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11483", + "name": "Super energy mix(1)", + "examine": "One/Two doses of fishy super energy potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11484", + "name": "Super energy mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11485", + "name": "Super strength mix(2)", + "examine": "One/Two doses of fishy Super strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11486", + "name": "Super strength mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11487", + "name": "Super strength mix(1)", + "examine": "One/Two doses of fishy Super strength potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11488", + "name": "Super strength mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11489", + "name": "Magic ess. mix(2)", + "examine": "One/Two doses of fishy Magic essence potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11490", + "name": "Magic ess. mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11491", + "name": "Magic ess. mix(1)", + "examine": "One/Two doses of fishy Magic essence potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11492", + "name": "Magic ess. mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11493", + "name": "Super restore mix(2)", + "examine": "One/Two doses of fishy super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11494", + "name": "Super restore mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11495", + "name": "Super restore mix(1)", + "examine": "One/Two doses of fishy super restore potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11496", + "name": "Super restore mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11497", + "name": "Super defence mix(2)", + "examine": "One/two doses of fishy super defence potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11498", + "name": "Super defence mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11499", + "name": "Super defence mix(1)", + "examine": "One/two doses of fishy super defence potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11500", + "name": "Super defence mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11501", + "name": "Antidote+ mix(2)", + "examine": "One/Two doses of fishy extra strength antidote potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11502", + "name": "Antidote+ mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11503", + "name": "Antidote+ mix(1)", + "examine": "One/Two doses of fishy extra strength antidote potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11504", + "name": "Antidote+ mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11505", + "name": "Antifire mix(2)", + "examine": "One/Two dose(s) of fishy anti-fire breath potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11506", + "name": "Antifire mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11507", + "name": "Antifire mix(1)", + "examine": "One/Two dose(s) of fishy anti-fire breath potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11508", + "name": "Antifire mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11509", + "name": "Ranging mix(2)", + "examine": "One/Two doses of fishy ranging potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11510", + "name": "Ranging mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11511", + "name": "Ranging mix(1)", + "examine": "One/Two doses of fishy ranging potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11512", + "name": "Ranging mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11513", + "name": "Magic mix(2)", + "examine": "One/Two doses of fishy magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11514", + "name": "Magic mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11515", + "name": "Magic mix(1)", + "examine": "One/Two doses of fishy magic potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11516", + "name": "Magic mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11517", + "name": "Hunting mix(2)", + "examine": "Doses of fishy hunting potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11518", + "name": "Hunting mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11519", + "name": "Hunting mix(1)", + "examine": "Doses of fishy hunting potion", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11520", + "name": "Hunting mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11521", + "name": "Zamorak mix(2)", + "examine": "One/Two doses of fishy Zamorak potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11522", + "name": "Zamorak mix(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11523", + "name": "Zamorak mix(1)", + "examine": "One/Two doses of fishy Zamorak potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "11524", + "name": "Zamorak mix(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "11525", + "name": "Wimpy feather", + "examine": "More fluff than feather.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11640", + "name": "Book of knowledge", + "examine": "Educate yourself.", + "archery_ticket_price": "0", + "destroy_message": "If you drop this book it will be destroyed. You cannot replace it.", + "durability": null, + "weight": "1" + }, + { + "id": "11655", + "name": "Astronomy book", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11656", + "name": "Glassblowing book", + "examine": "A book that explains the art of crafting items from glass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11657", + "name": "Glassblowing book", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11663", + "name": "Void mage helm", + "examine": "A mage helm from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false" + }, + { + "id": "11664", + "name": "Void ranger helm", + "examine": "A ranger helm from the order of the Void Knights", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false", + "weight": "3.6" + }, + { + "id": "11665", + "name": "Void melee helm", + "examine": "A melee helm from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11666", + "name": "Void seal(8)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11667", + "name": "Void seal(7)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11668", + "name": "Void seal(6)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11669", + "name": "Void seal(5)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11670", + "name": "Void seal(4)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11671", + "name": "Void seal(3)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11672", + "name": "Void seal(2)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11673", + "name": "Void seal(1)", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,1,1,1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "11674", + "name": "Void mage helm", + "examine": "A mage helm from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false" + }, + { + "id": "11675", + "name": "Void ranger helm", + "examine": "A ranger helm from the order of the Void Knights", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false", + "weight": "3.6" + }, + { + "id": "11676", + "name": "Void melee helm", + "examine": "A melee helm from the order of the Void Knights.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "200", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11677", + "name": "Explorer's notes", + "examine": "\"Beyond Trollheim\" by Nestor Peregrine.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can find another book in the Keldagrim library.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11678", + "name": "Black knight helm", + "examine": "A black helm, too small for your head", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You'll need to visit the Sinclair Mansion to get another one.", + "durability": null, + "tradeable": "false", + "weight": "3" + }, + { + "id": "11679", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11680", + "name": "Address form", + "examine": "A change of address form.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11681", + "name": "Scrap paper", + "examine": "It has something written on it.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "11682", + "name": "Hair clip", + "examine": "I could use this to pick the lock.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11686", + "name": "Godsword shards", + "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11687", + "name": "Godsword shards", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11688", + "name": "Godsword shards", + "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11689", + "name": "Godsword shards", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11690", + "name": "Godsword blade", + "examine": "The blade for the ultimate weapon.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true", + "weight": "7.5" + }, + { + "id": "11691", + "name": "Godsword blade", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11692", + "name": "Godsword shards", + "examine": "Godsword shards 1 and 2.Godsword shards 2 and 3.Godsword shards 1 and 3.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11693", + "name": "Godsword shards", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11694", + "name": "Armadyl godsword", + "examine": "A beautiful, heavy sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "3846,0,0,0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "124", + "requirements": "{0,75}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7044", + "turn90cw_anim": "7043", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "10" + }, + { + "id": "11695", + "name": "Armadyl godsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11696", + "name": "Bandos godsword", + "examine": "A brutally heavy sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "3846,0,0,0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "124", + "requirements": "{0,75}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7044", + "turn90cw_anim": "7043", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "10" + }, + { + "id": "11697", + "name": "Bandos godsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11698", + "name": "Saradomin godsword", + "examine": "A gracious, heavy sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "3846,0,0,0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "124", + "requirements": "{0,75}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7044", + "turn90cw_anim": "7043", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "10" + }, + { + "id": "11699", + "name": "Saradomin godsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11700", + "name": "Zamorak godsword", + "examine": "A terrifying, heavy sword.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "3846,0,0,0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,8,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "render_anim": "124", + "requirements": "{0,75}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7044", + "turn90cw_anim": "7043", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "10" + }, + { + "id": "11701", + "name": "Zamorak godsword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11702", + "name": "Armadyl hilt", + "examine": "Brimming with potential.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "11703", + "name": "Armadyl hilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11704", + "name": "Bandos hilt", + "examine": "Brimming with potential.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "11705", + "name": "Bandos hilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11706", + "name": "Saradomin hilt", + "examine": "Brimming with potential.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "11707", + "name": "Saradomin hilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11708", + "name": "Zamorak hilt", + "examine": "Brimming with potential.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2.5" + }, + { + "id": "11709", + "name": "Zamorak hilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11710", + "name": "Godsword shard 1", + "examine": "Part of the Godsword blade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11711", + "name": "Godsword shard 1", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11712", + "name": "Godsword shard 2", + "examine": "Part of the Godsword blade.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11713", + "name": "Godsword shard 2", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11714", + "name": "Godsword shard 3", + "examine": "Part of the Godsword blade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "rare_item": "true", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11715", + "name": "Godsword shard 3", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11716", + "name": "Zamorakian spear", + "examine": "An evil spear.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "4", + "bonuses": "85,65,65,0,0,13,13,12,0,13,0,75,2,0,0", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,70}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "3" + }, + { + "id": "11717", + "name": "Zamorakian spear", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11718", + "name": "Armadyl helmet", + "examine": "A helmet of great craftmanship.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "1", + "lendable": "true", + "rare_item": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "11719", + "name": "Armadyl helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "11720", + "name": "Armadyl chestplate", + "examine": "A chestplate of great craftsmanship", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "-7,-7,-7,-15,33,56,48,61,70,57,52,0,1,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "1", + "lendable": "true", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "11721", + "name": "Armadyl chestplate", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "11722", + "name": "Armadyl plateskirt", + "examine": "A plateskirt of great craftsmanship.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "-6,-6,-6,-10,20,32,26,34,40,33,25,0,1,0,0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "8" + }, + { + "id": "11723", + "name": "Armadyl plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11724", + "name": "Bandos chestplate", + "examine": "A sturdy chestplate.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,-10,98,93,105,-6,133,52,4,1,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "1", + "lendable": "true", + "rare_item": "true", + "remove_sleeves": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "12" + }, + { + "id": "11725", + "name": "Bandos chestplate", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "11726", + "name": "Bandos tassets", + "examine": "A sturdy pair of tassets.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,1,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "1", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "8" + }, + { + "id": "11727", + "name": "Bandos tassets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "11728", + "name": "Bandos boots", + "examine": "Some sturdy boots", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,-3,17,18,19,0,0,15,0,1,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "1", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "11729", + "name": "Bandos boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" + }, + { + "id": "11730", + "name": "Saradomin sword", + "examine": "The incredible blade of an Icyene.", + "archery_ticket_price": "0", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "3846,0,0,0", + "attack_speed": "4", + "bonuses": "0,82,60,0,0,0,0,0,0,0,0,82,2,0,0", + "defence_anim": "7050", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "124", + "requirements": "{0,70}", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", + "tradeable": "true", + "turn180_anim": "7045", + "turn90ccw_anim": "7044", + "turn90cw_anim": "7043", + "two_handed": "true", + "walk_anim": "7046", + "weapon_interface": "7", + "weight": "3" + }, + { + "id": "11731", + "name": "Saradomin sword", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11732", + "name": "Dragon boots", + "examine": "These will protect my feet.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,16,17,18,0,0,15,4,0,0,0", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "10", + "lendable": "true", + "rare_item": "true", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "11733", + "name": "Dragon boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11734", + "name": "Knight's notes", + "examine": "A scroll for Sir Tiffy. It is sealed with a wax insignia.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11735", + "name": "Knight's notes", + "examine": "A scroll for Sir Tiffy. The wax seal is broken.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11736", + "name": "Steam battlestaff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "7,-1,28,12,0,2,3,1,12,0,0,35,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2230", + "equipment_slot": "3", + "ge_buy_limit": "10", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,30}-{6,30}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "11737", + "name": "Steam battlestaff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11738", + "name": "Mystic steam staff", + "examine": "It's a slightly magical stick.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "10,-1,40,14,0,2,3,1,14,0,0,50,0,0,0", + "defence_anim": "420", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "28", + "requirements": "{0,40}-{6,40}", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" + }, + { + "id": "11739", + "name": "Mystic steam staff", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11741", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11743", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11745", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11747", + "name": "A key to a chest", + "examine": "This probably opens a chest in Golrie's caves. (Path of Glophrie)", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "There may be another in the chest where you found this.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11748", + "name": "Strongroom key", + "examine": "This opens the door into Golrie's strongroom. (Path of Glophrie)", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "There may be another in the chest where you found this.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11749", + "name": "Crystal chime", + "examine": "It makes a sound.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Maybe Brimstail will have another crystal seed you could grow into a chime.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11750", + "name": "Yewnock's notes", + "examine": "Notes written by Yewnock while copying Oaknock's machine.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "There may be another in the chest where you found this.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11753", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11754", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11755", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11756", + "name": "Varrock armour 1", + "examine": "Varrock Smithing armour.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,15,14,9,-6,14,5,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to get a new set of armour from Rat Burgiss if you destroy this one.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11757", + "name": "Varrock armour 2", + "examine": "Varrock smithing armour.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,21,20,12,-6,20,5,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to get a new set of armour from Reldo if you destroy this one.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11758", + "name": "Varrock armour 3", + "examine": "Varrock smithing armour.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to get a new set of armour from Vannaka if you destroy this one.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "1" + }, + { + "id": "11760", + "name": "Logs", + "examine": "A number of wooden logs.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "4", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11761", "name": "Locked diary", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11761" - }, - { - "destroy_message": "You can get the diary again from Sandy's desk.", "examine": "A locked/unlocked diary.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the diary again from Sandy's desk.", "durability": null, + "tradeable": "false" + }, + { + "id": "11762", "name": "Unlocked diary", - "tradeable": "false", - "destroy": "true", + "examine": "A locked/unlocked diary.", "archery_ticket_price": "0", - "id": "11762" + "destroy": "true", + "destroy_message": "You can get the diary again from Sandy's desk.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get the foot again from the creatures in the Jade Vine.", - "examine": "A wizard's severed foot. You can tell by the robes.", - "durability": null, + "id": "11764", "name": "Foot", - "tradeable": "false", - "destroy": "true", + "examine": "A wizard's severed foot. You can tell by the robes.", "archery_ticket_price": "0", - "id": "11764" + "destroy": "true", + "destroy_message": "You can get the foot again from the creatures in the Jade Vine.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get the arm again from the creatures in the Jade Vine.", - "examine": "\"A wizard's severed arm. You can tell by the robes.", - "durability": null, + "id": "11766", "name": "Left arm", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "\"A wizard's severed arm. You can tell by the robes.", "archery_ticket_price": "0", - "id": "11766" - }, - { + "destroy": "true", "destroy_message": "You can get the arm again from the creatures in the Jade Vine.", - "examine": "A wizard's severed arm. You can tell by the robes.", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11767", "name": "Right arm", - "tradeable": "false", - "destroy": "true", + "examine": "A wizard's severed arm. You can tell by the robes.", "archery_ticket_price": "0", - "id": "11767" + "destroy": "true", + "destroy_message": "You can get the arm again from the creatures in the Jade Vine.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get the leg again from the creatures in the Jade Vine.", - "examine": "A wizard's severed leg. You can tell by the robes.", - "durability": null, + "id": "11768", "name": "Left leg", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11768" - }, - { - "destroy_message": "You can get the leg again from the creatures in the Jade Vine.", "examine": "A wizard's severed leg. You can tell by the robes.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get the leg again from the creatures in the Jade Vine.", "durability": null, + "tradeable": "false" + }, + { + "id": "11769", "name": "Right leg", - "tradeable": "false", - "destroy": "true", + "examine": "A wizard's severed leg. You can tell by the robes.", "archery_ticket_price": "0", - "id": "11769" + "destroy": "true", + "destroy_message": "You can get the leg again from the creatures in the Jade Vine.", + "durability": null, + "tradeable": "false" }, { - "examine": "A root cutting.", - "durability": null, + "id": "11770", "name": "Root cutting", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11770" - }, - { "examine": "A root cutting.", - "durability": null, - "name": "Root cutting", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "11771" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { + "id": "11771", + "name": "Root cutting", "examine": "A root cutting.", - "durability": null, - "name": "Root cutting", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "11772" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { + "id": "11772", + "name": "Root cutting", "examine": "A root cutting.", - "durability": null, - "name": "Root cutting", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "11773" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { + "id": "11773", + "name": "Root cutting", "examine": "A root cutting.", - "durability": null, - "name": "Root cutting", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "11774" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A floppy root cutting. Quite dead.", + "id": "11774", + "name": "Root cutting", + "examine": "A root cutting.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "11775", "name": "Wilted cutting", + "examine": "A floppy root cutting. Quite dead.", "archery_ticket_price": "0", - "id": "11775" + "durability": null }, { - "examine": "A potted root.", - "durability": null, + "id": "11776", "name": "Potted root", - "tradeable": "false", - "destroy": "true", + "examine": "A potted root.", "archery_ticket_price": "0", - "id": "11776" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get the sealed pot back by visiting Garth, the farmer on Karamja.", - "examine": "This sealed pot contains your vine cutting - hopefully still alive.", - "durability": null, + "id": "11777", "name": "Sealed pot", - "tradeable": "false", - "destroy": "true", - "weight": "2.2", + "examine": "This sealed pot contains your vine cutting - hopefully still alive.", "archery_ticket_price": "0", - "id": "11777" + "destroy": "true", + "destroy_message": "You can get the sealed pot back by visiting Garth, the farmer on Karamja.", + "durability": null, + "tradeable": "false", + "weight": "2.2" }, { - "examine": "A large seed.", - "durability": null, + "id": "11778", "name": "Jade vine seed", + "examine": "A large seed.", "archery_ticket_price": "0", - "id": "11778" + "durability": null }, { - "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "Surely this holds many a dark tale...", - "durability": null, + "id": "11780", "name": "The grim reaper's diary", - "tradeable": "false", - "destroy": "true", + "examine": "Surely this holds many a dark tale...", "archery_ticket_price": "0", - "id": "11780" + "destroy": "true", + "destroy_message": "You'll have to find another from the Grim Reaper's house.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "Another can be found in the Grim Reaper's house.", - "examine": "Some of the Grim Reaper's garb.", - "durability": null, + "id": "11781", "name": "Grim's robe", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "Some of the Grim Reaper's garb.", "archery_ticket_price": "0", - "id": "11781" - }, - { "bankable": "false", - "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "Someone's Last Will and Testament.", - "durability": null, - "name": "Last will and testament", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "11782" - }, - { - "bankable": "false", - "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "A tasty treat for Muncher.", - "durability": null, - "name": "Human bones", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11783" - }, - { - "bankable": "false", - "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "Ooooh spooky!", - "durability": null, - "name": "Servant's skull", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "11784" - }, - { - "bankable": "false", - "destroy_message": "You can get a replacement from Diango in Draynor Village.", - "examine": "There's not much sand left in the top half...", - "durability": null, - "name": "Hourglass", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11785" - }, - { - "bankable": "false", "destroy_message": "Another can be found in the Grim Reaper's house.", - "examine": "Used to sharpen the blade of a scythe.", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11782", + "name": "Last will and testament", + "examine": "Someone's Last Will and Testament.", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You'll have to find another from the Grim Reaper's house.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11783", + "name": "Human bones", + "examine": "A tasty treat for Muncher.", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You'll have to find another from the Grim Reaper's house.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11784", + "name": "Servant's skull", + "examine": "Ooooh spooky!", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You'll have to find another from the Grim Reaper's house.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "11785", + "name": "Hourglass", + "examine": "There's not much sand left in the top half...", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get a replacement from Diango in Draynor Village.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11786", "name": "Scythe sharpener", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "Used to sharpen the blade of a scythe.", "archery_ticket_price": "0", - "id": "11786" + "attack_audios": "2524,2524,2522,2524", + "bankable": "false", + "destroy": "true", + "destroy_message": "Another can be found in the Grim Reaper's house.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "bankable": "false", - "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "That's gross. Why am I carrying it around?", - "durability": null, + "id": "11787", "name": "Human eye", - "tradeable": "false", - "destroy": "true", + "examine": "That's gross. Why am I carrying it around?", "archery_ticket_price": "0", - "id": "11787" - }, - { "bankable": "false", + "destroy": "true", "destroy_message": "You'll have to find another from the Grim Reaper's house.", - "examine": "A 'Voice of Doom' potion, used to deepen your voice.", "durability": null, + "tradeable": "false" + }, + { + "id": "11788", "name": "'voice of doom' potion", - "tradeable": "false", - "destroy": "true", + "examine": "A 'Voice of Doom' potion, used to deepen your voice.", "archery_ticket_price": "0", - "id": "11788" + "bankable": "false", + "destroy": "true", + "destroy_message": "You'll have to find another from the Grim Reaper's house.", + "durability": null, + "tradeable": "false" }, { - "remove_head": "true", - "destroy_message": "You should be able to get another from Diango in Draynor Village.", - "examine": "Allows me to imitate the harbinger of doom, the stealer of souls, the eternal end...", - "durability": null, - "name": "Grim reaper hood", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "11789", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "destroy_message": "You should be able to get another from Diango in Draynor Village.", - "examine": "Allows me to imitate the harbinger of doom, the stealer of souls, the eternal end...", - "durability": null, "name": "Grim reaper hood", - "tradeable": "false", - "destroy": "true", + "examine": "Allows me to imitate the harbinger of doom, the stealer of souls, the eternal end...", "archery_ticket_price": "0", - "id": "11790", + "destroy": "true", + "destroy_message": "You should be able to get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "0", "remove_beard": "true", - "equipment_slot": "0" + "remove_head": "true", + "tradeable": "false" }, { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "White goblin mail", - "weight": "3", + "id": "11790", + "name": "Grim reaper hood", + "examine": "Allows me to imitate the harbinger of doom, the stealer of souls, the eternal end...", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You should be able to get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "tradeable": "false" + }, + { "id": "11791", - "equipment_slot": "4" + "name": "White goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" }, { - "examine": "Smaller than the average goblin. After The Chosen Commander: Oldak's new assistant.", - "durability": null, + "id": "11792", "name": "Grubfoot", + "examine": "Smaller than the average goblin. After The Chosen Commander: Oldak's new assistant.", "archery_ticket_price": "0", - "id": "11792" + "durability": null }, { - "examine": "See article", - "durability": null, + "id": "11793", "name": "Zanik", + "examine": "See article", "archery_ticket_price": "0", - "id": "11793" + "durability": null }, { - "shop_price": "32", - "examine": "Oldak's Marvellous Moving-Over-Distance Sphere", - "durability": null, + "id": "11794", "name": "Plain of mud sphere", + "examine": "Oldak's Marvellous Moving-Over-Distance Sphere", "archery_ticket_price": "0", - "id": "11794" + "durability": null, + "shop_price": "32" }, { - "destroy_message": "The Ekeleshuun priest probably has a spare.", - "examine": "The key you stole from the Ekeleshuun priest.", - "durability": null, + "id": "11795", "name": "Ekeleshuun key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Ekeleshuun priest.", "archery_ticket_price": "0", - "id": "11795" + "destroy": "true", + "destroy_message": "The Ekeleshuun priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "The Narogoshuun priest probably has a spare.", - "examine": "The key you stole from the Narogoshuun priest.", - "durability": null, + "id": "11796", "name": "Narogoshuun key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Narogoshuun priest.", "archery_ticket_price": "0", - "id": "11796" + "destroy": "true", + "destroy_message": "The Narogoshuun priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "The Huzamogaarb priest probably has a spare.", - "examine": "The key you stole from the Huzamogaarb priest.", - "durability": null, + "id": "11797", "name": "Huzamogaarb key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Huzamogaarb priest.", "archery_ticket_price": "0", - "id": "11797" + "destroy": "true", + "destroy_message": "The Huzamogaarb priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "The Saragorgak priest probably has a spare.", - "examine": "The key you stole from the Saragorgak priest.", - "durability": null, + "id": "11798", "name": "Saragorgak key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Saragorgak priest.", "archery_ticket_price": "0", - "id": "11798" + "destroy": "true", + "destroy_message": "The Saragorgak priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "The Horogothgar priest probably has a spare.", - "examine": "The key you stole from the Horogothgar priest.", - "durability": null, + "id": "11799", "name": "Horogothgar key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Horogothgar priest.", "archery_ticket_price": "0", - "id": "11799" + "destroy": "true", + "destroy_message": "The Horogothgar priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "The Yurkolgokh priest probably has a spare.", - "examine": "The key you stole from the Yurkolgokh priest.", - "durability": null, + "id": "11800", "name": "Yurkolgokh key", - "tradeable": "false", - "destroy": "true", + "examine": "The key you stole from the Yurkolgokh priest.", "archery_ticket_price": "0", - "id": "11800" + "destroy": "true", + "destroy_message": "The Yurkolgokh priest probably has a spare.", + "durability": null, + "tradeable": "false" }, { - "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", - "durability": null, + "id": "11802", "name": "Snothead's bone", - "tradeable": "false", - "destroy": "true", + "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", "archery_ticket_price": "0", - "id": "11802" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", - "durability": null, + "id": "11803", "name": "Snailfeet's bone", - "tradeable": "false", - "destroy": "true", + "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", "archery_ticket_price": "0", - "id": "11803" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", - "durability": null, + "id": "11804", "name": "Mosschin's bone", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11804" - }, - { "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", - "durability": null, - "name": "Redeyes's bone", "archery_ticket_price": "0", - "id": "11805" - }, - { - "examine": "An ancient goblin bone wrapped in fragments of priestly robes.", - "durability": null, - "name": "Strongbones's bone", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "11806" + "durability": null, + "tradeable": "false" }, { - "examine": "You can combine these with a toadflax to make a goblin transformation potion.", + "id": "11805", + "name": "Redeyes's bone", + "examine": "\"An ancient goblin bone wrapped in fragments of priestly robes.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11806", + "name": "Strongbones's bone", + "examine": "An ancient goblin bone wrapped in fragments of priestly robes.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "11807", "name": "Pharmakos berries", + "examine": "You can combine these with a toadflax to make a goblin transformation potion.", "archery_ticket_price": "0", - "id": "11807" + "durability": null }, { - "examine": "An ugly albino fish.", - "durability": null, + "id": "11808", "name": "Whitefish", - "weight": "0.25", + "examine": "An ugly albino fish.", "archery_ticket_price": "0", - "id": "11808" + "durability": null, + "weight": "0.25" }, { - "examine": "4 doses of goblin transmogrification potion.", - "durability": null, - "name": "Goblin potion (4)", - "archery_ticket_price": "0", "id": "11809", + "name": "Goblin potion (4)", + "examine": "4 doses of goblin transmogrification potion.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "5" }, { - "examine": "3 doses of goblin transmogrification potion.", - "durability": null, + "id": "11810", "name": "Goblin potion (3)", + "examine": "3 doses of goblin transmogrification potion.", "archery_ticket_price": "0", - "id": "11810" + "durability": null }, { - "examine": "2 doses of goblin transmogrification potion.", - "durability": null, + "id": "11811", "name": "Goblin potion (2)", + "examine": "2 doses of goblin transmogrification potion.", "archery_ticket_price": "0", - "id": "11811" + "durability": null }, { - "examine": "1 dose of goblin transmogrification potion.", - "durability": null, + "id": "11812", "name": "Goblin potion (1)", + "examine": "1 dose of goblin transmogrification potion.", "archery_ticket_price": "0", - "id": "11812" + "durability": null }, { - "shop_price": "177", - "ge_buy_limit": "100", + "id": "11814", + "name": "Bronze armour set (l)", "examine": "Grand Exchange set containing bronze full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "614", - "durability": null, - "name": "Bronze armour set (l)", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11814" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "614", - "durability": null, - "name": "Bronze armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11815" - }, - { "shop_price": "177", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing bronze full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "938", - "durability": null, - "name": "Bronze armour set (sk)", "tradeable": "true", - "weight": "2.2", + "weight": "2.2" + }, + { + "id": "11815", + "name": "Bronze armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11816", - "bonuses": "0,0,0,-59,-19,27,26,18,-11,25,5,0,0,1,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "938", - "durability": null, "name": "Bronze armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing bronze full helm, platebody, skirt and kiteshield.", "archery_ticket_price": "0", - "id": "11817" + "bonuses": "0,0,0,-59,-19,27,26,18,-11,25,5,0,0,1,0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "177", + "tradeable": "true", + "weight": "2.2" }, { - "shop_price": "1022", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing iron full helm, platebody, legs, and kiteshield.", - "grand_exchange_price": "1830", - "durability": null, - "name": "Iron armour set (l)", - "tradeable": "true", - "weight": "2.2", + "id": "11817", + "name": "Bronze armour set (sk)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11818", - "bonuses": "0,0,0,-65,-21,46,44,33,-12,45,7,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1830", - "durability": null, "name": "Iron armour set (l)", - "tradeable": "true", + "examine": "Grand Exchange set containing iron full helm, platebody, legs, and kiteshield.", "archery_ticket_price": "0", - "id": "11819" - }, - { + "bonuses": "0,0,0,-65,-21,46,44,33,-12,45,7,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "1022", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing iron full helm, platebody, skirt, and kiteshield.", - "grand_exchange_price": "1711", - "durability": null, - "name": "Iron armour set (sk)", "tradeable": "true", - "weight": "2.2", + "weight": "2.2" + }, + { + "id": "11819", + "name": "Iron armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11820", - "bonuses": "0,0,0,-65,-21,46,44,33,-12,45,7,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1711", - "durability": null, "name": "Iron armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing iron full helm, platebody, skirt, and kiteshield.", "archery_ticket_price": "0", - "id": "11821" + "bonuses": "0,0,0,-65,-21,46,44,33,-12,45,7,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1022", + "tradeable": "true", + "weight": "2.2" }, { - "requirements": "{5,1}", - "shop_price": "2365", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing steel full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "3646", - "durability": null, - "name": "Steel armour set (l)", - "tradeable": "true", - "weight": "2.2", + "id": "11821", + "name": "Iron armour set (sk)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11822", - "bonuses": "0,0,0,-65,-21,71,72,60,-12,70,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3646", - "durability": null, "name": "Steel armour set (l)", - "tradeable": "true", + "examine": "Grand Exchange set containing steel full helm, platebody, legs and kiteshield.", "archery_ticket_price": "0", - "id": "11823" - }, - { + "bonuses": "0,0,0,-65,-21,71,72,60,-12,70,15,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{5,1}", "shop_price": "2365", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing steel full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "3796", - "durability": null, - "name": "Steel armour set (sk)", "tradeable": "true", - "weight": "2.2", + "weight": "2.2" + }, + { + "id": "11823", + "name": "Steel armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11824", - "bonuses": "0,0,0,-65,-21,71,72,60,-12,70,15,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3796", - "durability": null, "name": "Steel armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing steel full helm, platebody, skirt and kiteshield.", "archery_ticket_price": "0", - "id": "11825" + "bonuses": "0,0,0,-65,-21,71,72,60,-12,70,15,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{5,1}", + "shop_price": "2365", + "tradeable": "true", + "weight": "2.2" }, { - "requirements": "{10,1}", - "shop_price": "11286", + "id": "11825", + "name": "Steel armour set (sk)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11826", + "name": "Black armour set (l)", "examine": "Grand Exchange set containing Black full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "18900", - "durability": null, - "name": "Black armour set (l)", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11826" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "18900", - "durability": null, - "name": "Black armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11827" - }, - { "requirements": "{10,1}", "shop_price": "11286", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing a black full helm, platebody, skirt and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,4", - "grand_exchange_price": "14600", - "name": "Black armour set (sk)", "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11827", + "name": "Black armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11828", - "bonuses": "0,0,0,-65,-21,91,92,78,-12,90,27,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "14600", - "durability": null, "name": "Black armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing a black full helm, platebody, skirt and kiteshield.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "11829" + "bonuses": "0,0,0,-65,-21,91,92,78,-12,90,27,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{10,1}", + "shop_price": "11286", + "tradeable": "true", + "weight": "2.2" }, { - "requirements": "{20,1}", - "shop_price": "15232", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing a mithril full helm, platebody, legs and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,6", - "grand_exchange_price": "8764", - "name": "Mithril armour set (l)", - "tradeable": "true", + "id": "11829", + "name": "Black armour set (sk)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11830", - "bonuses": "0,0,0,-65,-21,101,102,89,-12,97,52,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8764", - "durability": null, "name": "Mithril armour set (l)", - "tradeable": "true", + "examine": "Grand Exchange set containing a mithril full helm, platebody, legs and kiteshield.", + "absorb": "2,0,6", "archery_ticket_price": "0", - "id": "11831" - }, - { + "bonuses": "0,0,0,-65,-21,101,102,89,-12,97,52,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{20,1}", "shop_price": "15232", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing a mithril full helm, platebody, skirt and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,5", - "grand_exchange_price": "7829", - "name": "Mithril armour set (sk)", "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11831", + "name": "Mithril armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11832", - "bonuses": "0,0,0,-65,-21,101,102,89,-12,99,50,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "7829", - "durability": null, "name": "Mithril armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing a mithril full helm, platebody, skirt and kiteshield.", + "absorb": "2,0,5", "archery_ticket_price": "0", - "id": "11833" + "bonuses": "0,0,0,-65,-21,101,102,89,-12,99,50,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{20,1}", + "shop_price": "15232", + "tradeable": "true", + "weight": "2.2" }, { - "requirements": "{1,30}", - "shop_price": "28276", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing an adamant full helm, platebody, legs and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "7,0,15", - "grand_exchange_price": "20600", - "name": "Adamant armour set (l)", - "tradeable": "true", + "id": "11833", + "name": "Mithril armour set (sk)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11834", - "bonuses": "0,0,0,-65,-21,144,146,129,-12,142,76,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20600", - "durability": null, "name": "Adamant armour set (l)", - "tradeable": "true", + "examine": "Grand Exchange set containing an adamant full helm, platebody, legs and kiteshield.", + "absorb": "7,0,15", "archery_ticket_price": "0", - "id": "11835" - }, - { + "bonuses": "0,0,0,-65,-21,144,146,129,-12,142,76,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{1,30}", "shop_price": "28276", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing an adamant full helm, platebody, skirt and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "7,0,15", - "grand_exchange_price": "21500", - "name": "Adamant armour set (sk)", "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11835", + "name": "Adamant armour set (l)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11836", - "bonuses": "0,0,0,-65,-21,144,146,129,-12,142,76,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "21500", - "durability": null, "name": "Adamant armour set (sk)", - "tradeable": "true", + "examine": "Grand Exchange set containing an adamant full helm, platebody, skirt and kiteshield.", + "absorb": "7,0,15", "archery_ticket_price": "0", - "id": "11837" + "bonuses": "0,0,0,-65,-21,144,146,129,-12,142,76,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{1,30}", + "shop_price": "28276", + "tradeable": "true", + "weight": "2.2" }, { - "requirements": "{1,40}", - "shop_price": "136112", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing a rune full helm, platebody, legs and kiteshield.", - "durability": null, - "weight": "2.2", - "absorb": "9,0,20", - "grand_exchange_price": "136300", - "name": "Rune armour set (l)", - "tradeable": "true", + "id": "11837", + "name": "Adamant armour set (sk)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11838", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "136300", - "durability": null, "name": "Rune armour set (l)", - "tradeable": "true", + "examine": "Grand Exchange set containing a rune full helm, platebody, legs and kiteshield.", + "absorb": "9,0,20", "archery_ticket_price": "0", - "id": "11839" - }, - { + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{1,40}", "shop_price": "136112", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11839", + "name": "Rune armour set (l)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11840", + "name": "Rune armour set (sk)", "examine": "Grand Exchange set containing a rune full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "140300", - "durability": null, - "name": "Rune armour set (sk)", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11840" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "140300", - "durability": null, + "requirements": "{1,40}", + "shop_price": "136112", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11841", "name": "Rune armour set (sk)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11841" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,60}", - "ge_buy_limit": "10", - "grand_exchange_price": "4800000", + "id": "11842", + "name": "Dragon chain armour set (l)", "examine": "Grand Exchange set containing a dragon helm, chainbody, legs.", - "durability": null, - "name": "Dragon chain armour set (l)", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "2.2", - "id": "11842" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "4800000", - "durability": null, - "name": "Dragon chain armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11843" - }, - { "requirements": "{1,60}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11843", + "name": "Dragon chain armour set (l)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "4600000", + "tradeable": "true" + }, + { + "id": "11844", + "name": "Dragon chain armour set (sk)", "examine": "Grand Exchange set containing a dragon helm, chainbody, skirt.", - "durability": null, - "name": "Dragon chain armour set (sk)", - "tradeable": "true", "archery_ticket_price": "0", - "weight": "2.2", - "id": "11844" + "durability": null, + "ge_buy_limit": "10", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "2.2" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "4600000", - "durability": null, + "id": "11845", "name": "Dragon chain armour set (sk)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11845" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{0,70}-{1,70}-{6,70}", - "ge_buy_limit": "10", + "id": "11846", + "name": "Barrows - ahrim's set", "examine": "Grand Exchange set containing Ahrim's robetop, robeskirt, hood, and staff.", - "grand_exchange_price": "3100000", - "durability": null, - "name": "Barrows - ahrim's set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11846" + "durability": null, + "ge_buy_limit": "10", + "requirements": "{0,70}-{1,70}-{6,70}", + "tradeable": "true", + "weight": "2.2" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "3100000", - "durability": null, + "id": "11847", "name": "Barrows - ahrim's set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11847" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{0,70}-{1,70}-{2,70}", - "ge_buy_limit": "10", + "id": "11848", + "name": "Barrows - dharok's set", "examine": "Grand Exchange set containing Dharok's helm, body, legs and greataxe.", - "grand_exchange_price": "3100000", - "durability": null, - "name": "Barrows - dharok's set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11848" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "3100000", - "durability": null, - "name": "Barrows - dharok's set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11849" - }, - { - "requirements": "{0,70}-{1,70}", - "ge_buy_limit": "10", - "examine": "Grand Exchange set containing Guthan's helm, platebody, chainskirt and warspear.", - "grand_exchange_price": "1600000", - "durability": null, - "name": "Barrows - guthan's set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11850" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1600000", - "durability": null, - "name": "Barrows - guthan's set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11851" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "10", - "examine": "A Grand Exchange set containing Karil's coif, crossbow, leather body, and leather skirt.", - "grand_exchange_price": "2200000", - "durability": null, - "name": "Barrows - karil's set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11852" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "2200000", - "durability": null, - "name": "Barrows - karil's set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11853" - }, - { "requirements": "{0,70}-{1,70}-{2,70}", - "ge_buy_limit": "10", - "examine": "Grand Exchange set containing Torag's helm, body, legs and hammers.", - "grand_exchange_price": "1100000", - "durability": null, - "name": "Barrows - torag's set", "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11854" + "weight": "2.2" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "1100000", - "durability": null, - "name": "Barrows - torag's set", - "tradeable": "true", + "id": "11849", + "name": "Barrows - dharok's set", "archery_ticket_price": "0", - "id": "11855" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "11850", + "name": "Barrows - guthan's set", + "examine": "Grand Exchange set containing Guthan's helm, platebody, chainskirt and warspear.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", "requirements": "{0,70}-{1,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11851", + "name": "Barrows - guthan's set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11852", + "name": "Barrows - karil's set", + "examine": "A Grand Exchange set containing Karil's coif, crossbow, leather body, and leather skirt.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11853", + "name": "Barrows - karil's set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11854", + "name": "Barrows - torag's set", + "examine": "Grand Exchange set containing Torag's helm, body, legs and hammers.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "requirements": "{0,70}-{1,70}-{2,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11855", + "name": "Barrows - torag's set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "11856", + "name": "Barrows - verac's set", "examine": "Grand Exchange set containing Verac's helm, brassard, plateskirt and flail.", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Barrows - verac's set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11856" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "1500000", - "durability": null, + "requirements": "{0,70}-{1,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11857", "name": "Barrows - verac's set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11857" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{1,65}", - "ge_buy_limit": "2", + "id": "11858", + "name": "Third age melee set", "examine": "Grand Exchange set containing a helm, body, legs and shield", - "grand_exchange_price": "388700000", - "durability": null, - "name": "Third age melee set", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "11858" + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,65}", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "388700000", - "durability": null, + "id": "11859", "name": "Third age melee set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11859" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,45}-{4,65}", - "ge_buy_limit": "2", + "id": "11860", + "name": "Third age ranger set", "examine": "Grand Exchange set containing coif, body, chaps and vambraces.", - "grand_exchange_price": "87500000", - "durability": null, - "name": "Third age ranger set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11860" + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,45}-{4,65}", + "tradeable": "true", + "weight": "2.2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "87500000", - "durability": null, + "id": "11861", "name": "Third age ranger set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11861" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", + "id": "11862", + "name": "Third age mage set", "examine": "Grand Exchange set containing a body, legs, amulet and hat", - "grand_exchange_price": "163100000", - "durability": null, - "name": "Third age mage set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11862" - }, - { + "durability": null, "ge_buy_limit": "2", - "grand_exchange_price": "163100000", - "durability": null, - "name": "Third age mage set", + "requirements": "{1,30}-{6,65}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11863" + "weight": "2.2" }, { + "id": "11863", + "name": "Third age mage set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11864", + "name": "Green dragonhide set", + "examine": "Grand Exchange set containing body, chaps and vambraces.", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{1,40}-{4,40}", "shop_price": "9000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11865", + "name": "Green dragonhide set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11866", + "name": "Blue dragonhide set", "examine": "Grand Exchange set containing body, chaps and vambraces.", - "durability": null, - "weight": "2.2", - "absorb": "0,10,5", - "grand_exchange_price": "8932", - "name": "Green dragonhide set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11864", - "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "8932", - "durability": null, - "name": "Green dragonhide set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11865" - }, - { "requirements": "{1,40}-{4,50}", "shop_price": "12000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11867", + "name": "Blue dragonhide set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11868", + "name": "Red dragonhide set", "examine": "Grand Exchange set containing body, chaps and vambraces.", - "grand_exchange_price": "9062", - "durability": null, - "name": "Blue dragonhide set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11866" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "9062", - "durability": null, - "name": "Blue dragonhide set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11867" - }, - { "requirements": "{1,40}-{4,60}", "shop_price": "15000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11869", + "name": "Red dragonhide set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11870", + "name": "Black dragonhide set", "examine": "Grand Exchange set containing body, chaps and vambraces.", - "grand_exchange_price": "12800", - "durability": null, - "name": "Red dragonhide set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11868" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "12800", - "durability": null, - "name": "Red dragonhide set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11869" - }, - { "requirements": "{1,40}-{4,70}", "shop_price": "40000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11871", + "name": "Black dragonhide set", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "11872", + "name": "Mystic robes set", + "examine": "Grand Exchange set containing mystic hat, top, bottoms, gloves, and boots.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "examine": "Grand Exchange set containing body, chaps and vambraces.", - "grand_exchange_price": "17600", - "durability": null, - "name": "Black dragonhide set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11870" - }, - { - "durability": null, - "name": "Black dragonhide set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11871" - }, - { "requirements": "{20,1}-{6,40}", "shop_price": "169143", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing mystic hat, top, bottoms, gloves, and boots.", - "grand_exchange_price": "163300", - "durability": null, - "name": "Mystic robes set", "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11872" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "163300", - "durability": null, + "id": "11873", "name": "Mystic robes set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11873" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{1,25}-{6,50}", - "ge_buy_limit": "10", + "id": "11874", + "name": "Infinity robes set", "examine": "Grand Exchange set containing hat, top, bottoms, gloves, and boots", - "grand_exchange_price": "11000000", - "durability": null, - "name": "Infinity robes set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11874" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "11000000", - "durability": null, + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11875", "name": "Infinity robes set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11875" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{1,40}-{6,40}", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing a helm, body, boots, gauntlets and legs.", - "grand_exchange_price": "107400", - "durability": null, - "name": "Splitbark armour set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", "id": "11876", - "absorb": "13,6,0", - "bonuses": "0,0,0,24,-21,74,59,86,32,0,74,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "107400", - "durability": null, "name": "Splitbark armour set", - "tradeable": "true", + "examine": "Grand Exchange set containing a helm, body, boots, gauntlets and legs.", + "absorb": "13,6,0", "archery_ticket_price": "0", - "id": "11877" - }, - { - "requirements": "{10,1}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "341200", + "bonuses": "0,0,0,24,-21,74,59,86,32,0,74,0,0,0,0", "durability": null, - "name": "Black trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11878", - "absorb": "2,0,4", - "bonuses": "0,0,0,-65,-21,91,92,77,-12,90,23,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "341200", - "durability": null, - "name": "Black trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11879" - }, - { - "requirements": "{10,1}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "331600", - "durability": null, - "name": "Black trimmed armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11880", - "absorb": "2,0,4", - "bonuses": "0,0,0,-65,-21,91,92,77,-12,90,23,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "331600", - "durability": null, - "name": "Black trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11881" - }, - { - "requirements": "{10,1}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "1700000", - "durability": null, - "name": "Black gold-trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11882" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1700000", - "durability": null, - "name": "Black gold-trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11883" - }, - { - "requirements": "{10,1}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "1300000", - "durability": null, - "name": "Black gold-trimmed armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11884" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1300000", - "durability": null, - "name": "Black gold-trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11885" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "318100", - "durability": null, - "name": "Adamant trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11886" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "318100", - "durability": null, - "name": "Adamant trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11887" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "293700", - "durability": null, - "name": "Adamant trimmed armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11888" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "293700", - "durability": null, - "name": "Adamant trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11889" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "949400", - "durability": null, - "name": "Adamant gold-trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11890" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "949400", - "durability": null, - "name": "Adamant gold-trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11891" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "880500", - "durability": null, - "name": "Adamant gold-trimmed armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11892" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "880500", - "durability": null, - "name": "Adamant gold-trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11893" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "702500", - "durability": null, - "name": "Rune trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11894", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "702500", - "durability": null, - "name": "Rune trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11895" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "675400", - "durability": null, - "name": "Rune trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11896", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "675400", - "durability": null, - "name": "Rune trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11897" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "1700000", - "durability": null, - "name": "Rune gold-trimmed armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11898", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1700000", - "durability": null, - "name": "Rune gold-trimmed armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11899" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "1400000", - "durability": null, - "name": "Rune gold-trimmed armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11900", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1400000", - "durability": null, - "name": "Rune gold-trimmed armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11901" - }, - { - "requirements": "{20,1}-{6,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing hat, robetops and bottoms", - "grand_exchange_price": "207200", - "durability": null, - "name": "Enchanted set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11902" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "207200", - "durability": null, - "name": "Enchanted set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11903" - }, - { - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing hat, robetops and bottoms", - "grand_exchange_price": "877900", - "durability": null, - "name": "Trimmed blue wizard set", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "11904" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "877900", - "durability": null, - "name": "Trimmed blue wizard set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11905" - }, - { - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing hat, robe top and bottom", - "grand_exchange_price": "3100000", - "durability": null, - "name": "Gold-trimmed blue wizard set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11906" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3100000", - "durability": null, - "name": "Gold-trimmed blue wizard set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11907" - }, - { - "requirements": "{20,4}", - "ge_buy_limit": "10000", - "examine": "Grand Exchange set containing body and chaps", - "grand_exchange_price": "85100", - "durability": null, - "name": "Trimmed leather armour set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11908" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "85100", - "durability": null, - "name": "Trimmed leather armour set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11909" - }, - { - "requirements": "{20,4}", - "ge_buy_limit": "10000", - "examine": "Grand Exchange set containing body and chaps", - "grand_exchange_price": "217200", - "durability": null, - "name": "Gold-trimmed leather armour set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11910" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "217200", - "durability": null, - "name": "Gold-trimmed leather armour set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11911" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body and chaps", - "grand_exchange_price": "371600", - "durability": null, - "name": "Green d'hide trimmed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11912", - "absorb": "0,10,5", - "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "371600", - "durability": null, - "name": "Green d'hide trimmed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11913" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body and chaps", - "grand_exchange_price": "1600000", - "durability": null, - "name": "Green d'hide gold-trimmed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11914", - "absorb": "0,10,5", - "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1600000", - "durability": null, - "name": "Green d'hide gold-trimmed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11915" - }, - { - "requirements": "{1,40}-{4,50}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body and chaps", - "grand_exchange_price": "38300", - "durability": null, - "name": "Blue d'hide trimmed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11916" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "38300", - "durability": null, - "name": "Blue d'hide trimmed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11917" - }, - { - "requirements": "{1,40}-{4,50}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body and chaps.", - "grand_exchange_price": "50000", - "durability": null, - "name": "Blue d'hide gold-trimmed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11918" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "50000", - "durability": null, - "name": "Blue d'hide gold-trimmed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11919" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body, chaps, bracers and coif.", - "grand_exchange_price": "179500", - "durability": null, - "name": "Green d'hide blessed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11920" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "179500", - "durability": null, - "name": "Green d'hide blessed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11921" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body, chaps, bracers and coif.", - "grand_exchange_price": "477500", - "durability": null, - "name": "Blue d'hide blessed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11922" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "477500", - "durability": null, - "name": "Blue d'hide blessed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11923" - }, - { - "requirements": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing body, chaps, bracers and coif.", - "grand_exchange_price": "585300", - "durability": null, - "name": "Red d'hide blessed set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11924" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "585300", - "durability": null, - "name": "Red d'hide blessed set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11925" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and shield.", - "grand_exchange_price": "1100000", - "durability": null, - "name": "Guthix armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11926", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1100000", - "durability": null, - "name": "Guthix armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11927" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield", - "grand_exchange_price": "4000000", - "durability": null, - "name": "Saradomin armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11928", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "4000000", - "durability": null, - "name": "Saradomin armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11929" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield", - "grand_exchange_price": "2300000", - "durability": null, - "name": "Zamorak armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11930", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2300000", - "durability": null, - "name": "Zamorak armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11931" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "1000000", - "durability": null, - "name": "Guthix armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11932", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1000000", - "durability": null, - "name": "Guthix armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11933" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield", - "grand_exchange_price": "3600000", - "durability": null, - "name": "Saradomin armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11934", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "3600000", - "durability": null, - "name": "Saradomin armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11935" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield", - "grand_exchange_price": "2000000", - "durability": null, - "name": "Zamorak armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11936", - "absorb": "9,0,20", - "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "2000000", - "durability": null, - "name": "Zamorak armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11937" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", - "grand_exchange_price": "5900000", - "durability": null, - "name": "Gilded armour set (l)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11938" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "5900000", - "durability": null, - "name": "Gilded armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11939" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", - "grand_exchange_price": "4300000", - "durability": null, - "name": "Gilded armour set (sk)", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11940" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "4300000", - "durability": null, - "name": "Gilded armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11941" - }, - { - "requirements": "{1,40}", "ge_buy_limit": "100", - "examine": "Grand Exchange set containing helm, platebody, legs, boots and gloves", - "grand_exchange_price": "145000", - "durability": null, - "name": "Rockshell armour set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11942" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "145000", - "durability": null, - "name": "Rockshell armour set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11943" - }, - { - "requirements": "{1,40}-{4,40}", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing helm, body, chaps, boots and gloves", - "grand_exchange_price": "94500", - "durability": null, - "name": "Spined armour set", - "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11944", - "absorb": "0,13,6", - "bonuses": "-6,-6,-6,-31,29,68,56,78,34,62,47,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "94500", - "durability": null, - "name": "Spined armour set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11945" - }, - { "requirements": "{1,40}-{6,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11877", + "name": "Splitbark armour set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11878", + "name": "Black trimmed armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,91,92,77,-12,90,23,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{10,1}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11879", + "name": "Black trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11880", + "name": "Black trimmed armour set (sk)", + "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,91,92,77,-12,90,23,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{10,1}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11881", + "name": "Black trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11882", + "name": "Black gold-trimmed armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{10,1}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11883", + "name": "Black gold-trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11884", + "name": "Black gold-trimmed armour set (sk)", + "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{10,1}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11885", + "name": "Black gold-trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11886", + "name": "Adamant trimmed armour set (l)", + "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11887", + "name": "Adamant trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11888", + "name": "Adamant trimmed armour set (sk)", + "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11889", + "name": "Adamant trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11890", + "name": "Adamant gold-trimmed armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11891", + "name": "Adamant gold-trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11892", + "name": "Adamant gold-trimmed armour set (sk)", + "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11893", + "name": "Adamant gold-trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11894", + "name": "Rune trimmed armour set (l)", + "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11895", + "name": "Rune trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11896", + "name": "Rune trimmed armour set (sk)", + "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true" + }, + { + "id": "11897", + "name": "Rune trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11898", + "name": "Rune gold-trimmed armour set (l)", + "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11899", + "name": "Rune gold-trimmed armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11900", + "name": "Rune gold-trimmed armour set (sk)", + "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11901", + "name": "Rune gold-trimmed armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11902", + "name": "Enchanted set", + "examine": "Grand Exchange set containing hat, robetops and bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{20,1}-{6,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11903", + "name": "Enchanted set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11904", + "name": "Trimmed blue wizard set", + "examine": "Grand Exchange set containing hat, robetops and bottoms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "2" + }, + { + "id": "11905", + "name": "Trimmed blue wizard set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11906", + "name": "Gold-trimmed blue wizard set", + "examine": "Grand Exchange set containing hat, robe top and bottom", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11907", + "name": "Gold-trimmed blue wizard set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11908", + "name": "Trimmed leather armour set", + "examine": "Grand Exchange set containing body and chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{20,4}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11909", + "name": "Trimmed leather armour set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "11910", + "name": "Gold-trimmed leather armour set", + "examine": "Grand Exchange set containing body and chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "requirements": "{20,4}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11911", + "name": "Gold-trimmed leather armour set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "11912", + "name": "Green d'hide trimmed set", + "examine": "Grand Exchange set containing body and chaps", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11913", + "name": "Green d'hide trimmed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11914", + "name": "Green d'hide gold-trimmed set", + "examine": "Grand Exchange set containing body and chaps", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-40,31,65,50,73,30,62,58,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11915", + "name": "Green d'hide gold-trimmed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11916", + "name": "Blue d'hide trimmed set", + "examine": "Grand Exchange set containing body and chaps", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11917", + "name": "Blue d'hide trimmed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11918", + "name": "Blue d'hide gold-trimmed set", + "examine": "Grand Exchange set containing body and chaps.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11919", + "name": "Blue d'hide gold-trimmed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11920", + "name": "Green d'hide blessed set", + "examine": "Grand Exchange set containing body, chaps, bracers and coif.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11921", + "name": "Green d'hide blessed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11922", + "name": "Blue d'hide blessed set", + "examine": "Grand Exchange set containing body, chaps, bracers and coif.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11923", + "name": "Blue d'hide blessed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11924", + "name": "Red d'hide blessed set", + "examine": "Grand Exchange set containing body, chaps, bracers and coif.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11925", + "name": "Red d'hide blessed set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11926", + "name": "Guthix armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and shield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11927", + "name": "Guthix armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11928", + "name": "Saradomin armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11929", + "name": "Saradomin armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11930", + "name": "Zamorak armour set (l)", + "examine": "Grand Exchange set containing full helm, platebody, legs and kiteshield", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11931", + "name": "Zamorak armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11932", + "name": "Guthix armour set (sk)", + "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield.", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11933", + "name": "Guthix armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11934", + "name": "Saradomin armour set (sk)", + "examine": "Grand Exchange set containing full helm, platebody, skirt and kiteshield", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11935", + "name": "Saradomin armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11936", + "name": "Zamorak armour set (sk)", + "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield", + "absorb": "9,0,20", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-65,-21,207,209,192,-12,205,102,0,0,0,0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11937", + "name": "Zamorak armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11938", + "name": "Gilded armour set (l)", + "examine": "Grand Exchange set containing a full helm, platebody, legs and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11939", + "name": "Gilded armour set (l)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11940", + "name": "Gilded armour set (sk)", + "examine": "Grand Exchange set containing a full helm, platebody, skirt and kiteshield.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11941", + "name": "Gilded armour set (sk)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "11942", + "name": "Rockshell armour set", + "examine": "Grand Exchange set containing helm, platebody, legs, boots and gloves", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11943", + "name": "Rockshell armour set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11944", + "name": "Spined armour set", + "examine": "Grand Exchange set containing helm, body, chaps, boots and gloves", + "absorb": "0,13,6", + "archery_ticket_price": "0", + "bonuses": "-6,-6,-6,-31,29,68,56,78,34,62,47,0,0,0,0", + "durability": null, + "ge_buy_limit": "100", + "requirements": "{1,40}-{4,40}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11945", + "name": "Spined armour set", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11946", + "name": "Skeletal armour set", "examine": "Grand Exchange set containing helm, top, bottoms, boots and gloves", - "grand_exchange_price": "145200", - "durability": null, - "name": "Skeletal armour set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11946" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "145200", - "durability": null, - "name": "Skeletal armour set", + "requirements": "{1,40}-{6,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11947" + "weight": "2.2" }, { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "A tiny model of Lumbridge Castle inside a glass dome.", - "durability": null, - "name": "Snow globe", - "tradeable": "false", - "destroy": "true", + "id": "11947", + "name": "Skeletal armour set", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "11949", - "equipment_slot": "3" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "A tiny model of Lumbridge Castle inside a glass dome.", - "durability": null, "name": "Snow globe", - "tradeable": "false", - "destroy": "true", + "examine": "A tiny model of Lumbridge Castle inside a glass dome.", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { "id": "11950", - "equipment_slot": "3" + "name": "Snow globe", + "examine": "A tiny model of Lumbridge Castle inside a glass dome.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" }, { - "attack_anims": "5063", - "examine": "Made from snow. / It's a ball of snow! Uncanny. / How did this not melt in the box?", - "durability": null, - "name": "Snowball", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "11951", - "weapon_interface": "18", - "equipment_slot": "3" + "name": "Snowball", + "examine": "Made from snow. / It's a ball of snow! Uncanny. / How did this not melt in the box?", + "archery_ticket_price": "0", + "attack_anims": "5063", + "attack_speed": "4", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "18" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "Add this to a snowman to make a snow warrior.", - "durability": null, + "id": "11952", "name": "Ice sword", - "tradeable": "false", - "destroy": "true", + "examine": "Add this to a snowman to make a snow warrior.", "archery_ticket_price": "0", - "id": "11952" + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "Add this to a snowman to make a snow mage.", - "attack_audios": "2555,0,0,0", - "durability": null, + "id": "11953", "name": "Winter staff", - "tradeable": "false", - "destroy": "true", + "examine": "Add this to a snowman to make a snow mage.", "archery_ticket_price": "0", - "id": "11953" + "attack_audios": "2555,0,0,0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "Add this to a snowman to make a snow ranger.", - "durability": null, + "id": "11954", "name": "Holly bow", - "tradeable": "false", - "destroy": "true", + "examine": "Add this to a snowman to make a snow ranger.", "archery_ticket_price": "0", - "id": "11954" + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "Add this to a snowman to make a barbarian snowman.", - "durability": null, + "id": "11955", "name": "Barbarian snowman hat", - "tradeable": "false", - "destroy": "true", + "examine": "Add this to a snowman to make a barbarian snowman.", "archery_ticket_price": "0", - "id": "11955" + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "It's actually made of cardboard, but it looks like a dragon full helm.", - "durability": null, + "id": "11956", "name": "Dragon snowman hat", - "tradeable": "false", - "destroy": "true", + "examine": "It's actually made of cardboard, but it looks like a dragon full helm.", "archery_ticket_price": "0", - "id": "11956" + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" }, { - "bankable": "false", - "destroy_message": "You can get another from a snow imp.", - "examine": "Use this to make your snowman look like a dwarf.", - "durability": null, + "id": "11957", "name": "Dwarf snowman hat", - "tradeable": "false", - "destroy": "true", + "examine": "Use this to make your snowman look like a dwarf.", "archery_ticket_price": "0", - "id": "11957" - }, - { "bankable": "false", - "destroy_message": "Talk to a snow imp to get another.", - "examine": "Arr! Shiver me snowman!", - "durability": null, - "name": "Pirate snowman hat", - "tradeable": "false", "destroy": "true", - "archery_ticket_price": "0", - "id": "11958" - }, - { - "bankable": "false", "destroy_message": "You can get another from a snow imp.", - "examine": "The most traditional snowman hat.", "durability": null, - "name": "Snowman top hat", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11959" + "tradeable": "false" }, { + "id": "11958", + "name": "Pirate snowman hat", + "examine": "Arr! Shiver me snowman!", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "Talk to a snow imp to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11959", + "name": "Snowman top hat", + "examine": "The most traditional snowman hat.", + "archery_ticket_price": "0", + "bankable": "false", + "destroy": "true", + "destroy_message": "You can get another from a snow imp.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11960", + "name": "Light mystic robes set", + "examine": "Grand Exchange set containing hat, top, bottoms, gloves, and boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "requirements": "{20,1}-{6,40}", "shop_price": "215431", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11961", + "name": "Light mystic robes set", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11962", + "name": "Dark mystic robes set", "examine": "Grand Exchange set containing hat, top, bottoms, gloves, and boots", - "grand_exchange_price": "487300", - "durability": null, - "name": "Light mystic robes set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11960" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "487300", - "durability": null, - "name": "Light mystic robes set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11961" - }, - { "requirements": "{20,1}-{6,40}", "shop_price": "228541", - "ge_buy_limit": "100", - "examine": "Grand Exchange set containing hat, top, bottoms, gloves, and boots", - "grand_exchange_price": "294000", - "durability": null, - "name": "Dark mystic robes set", "tradeable": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "11962" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "294000", - "durability": null, + "id": "11963", "name": "Dark mystic robes set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11963" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "I can hatch this in an incubator.", - "durability": null, + "id": "11964", "name": "Raven egg", - "archery_ticket_price": "0", - "id": "11964" - }, - { "examine": "I can hatch this in an incubator.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "11965", "name": "Vulture egg", + "examine": "I can hatch this in an incubator.", "archery_ticket_price": "0", - "id": "11965" + "durability": null }, { - "examine": "Full: ", - "durability": null, + "id": "11966", "name": "Bird's nest", + "examine": "Full: ", "archery_ticket_price": "0", - "id": "11966" + "durability": null }, { - "shop_price": "750000", - "ge_buy_limit": "10", + "id": "11967", + "name": "Dwarf cannon set", "examine": "Grand Exchange set containing cannon base, stand, barrels and furnace.", - "grand_exchange_price": "718900", - "durability": null, - "name": "Dwarf cannon set", - "tradeable": "true", - "weight": "2.2", "archery_ticket_price": "0", - "id": "11967" - }, - { + "durability": null, "ge_buy_limit": "10", - "grand_exchange_price": "718900", - "durability": null, + "shop_price": "750000", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11968", "name": "Dwarf cannon set", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11968" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "destroy_message": "If I destroy this it will disintegrate into its component parts and drop to the floor.", - "examine": "A water tiara infused with water runes. Protects from the hot desert sun.", - "durability": null, - "name": "Enchanted water tiara", - "weight": "1", - "archery_ticket_price": "0", "id": "11969", + "name": "Enchanted water tiara", + "examine": "A water tiara infused with water runes. Protects from the hot desert sun.", + "archery_ticket_price": "0", "bonuses": "0,0,0,2,0,5,5,5,3,3,0,0,2,0,0", - "equipment_slot": "0" + "destroy_message": "If I destroy this it will disintegrate into its component parts and drop to the floor.", + "durability": null, + "equipment_slot": "0", + "weight": "1" }, { - "destroy_message": "I can obtain and fill another one from the archaeologist near Sophanem.", - "examine": "A crate full of distressed gear.", - "durability": null, + "id": "11970", "name": "Smelly crate", - "tradeable": "false", - "destroy": "true", - "weight": "30", + "examine": "A crate full of distressed gear.", "archery_ticket_price": "0", - "id": "11970" + "destroy": "true", + "destroy_message": "I can obtain and fill another one from the archaeologist near Sophanem.", + "durability": null, + "tradeable": "false", + "weight": "30" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "An empty crate, ready for bronze gear.", - "durability": null, + "id": "11972", "name": "Empty crate", + "examine": "An empty crate, ready for bronze gear.", "archery_ticket_price": "0", - "id": "11972" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "I can obtain another one from the archaeologist near Sophanem.", - "examine": "A book of research on Scabarite history.", - "durability": null, + "id": "11973", "name": "Scabaras research", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A book of research on Scabarite history.", "archery_ticket_price": "0", - "id": "11973" + "destroy": "true", + "destroy_message": "I can obtain another one from the archaeologist near Sophanem.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "I can obtain another one from Simon Templeton.", - "examine": "Issued by Simon Templeton.", - "durability": null, + "id": "11974", "name": "Artefact receipt", - "tradeable": "false", - "destroy": "true", + "examine": "Issued by Simon Templeton.", "archery_ticket_price": "0", - "id": "11974" + "destroy": "true", + "destroy_message": "I can obtain another one from Simon Templeton.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", - "durability": null, + "id": "11975", "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11975" - }, - { - "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", "examine": "See article", - "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", - "id": "11976" - }, - { + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11977" + "tradeable": "false" }, { + "id": "11976", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11978" + "tradeable": "false" }, { + "id": "11977", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11979" + "tradeable": "false" }, { + "id": "11978", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11980" + "tradeable": "false" }, { + "id": "11979", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11981" + "tradeable": "false" }, { + "id": "11980", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11982" + "tradeable": "false" }, { + "id": "11981", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11983" + "tradeable": "false" }, { + "id": "11982", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11984" + "tradeable": "false" }, { + "id": "11983", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11985" + "tradeable": "false" }, { + "id": "11984", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11986" + "tradeable": "false" }, { + "id": "11985", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11987" + "tradeable": "false" }, { + "id": "11986", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11988" + "tradeable": "false" }, { + "id": "11987", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11989" + "tradeable": "false" }, { + "id": "11988", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11990" + "tradeable": "false" }, { + "id": "11989", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11991" + "tradeable": "false" }, { + "id": "11990", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11992" + "tradeable": "false" }, { + "id": "11991", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", - "examine": "See article", "durability": null, - "name": "Scabarite notes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "11993" + "tradeable": "false" }, { - "destroy_message": "I can obtain another one from the archaeologists near Sophanem.", - "examine": "Newly made, but to an ancient pattern. (Dealing with Scabaras)", + "id": "11992", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", "durability": null, + "tradeable": "false" + }, + { + "id": "11993", + "name": "Scabarite notes", + "examine": "See article", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can loot another one from the monsters in the areas east of Sophanem.", + "durability": null, + "tradeable": "false" + }, + { + "id": "11994", "name": "Scabarite key", - "tradeable": "false", - "destroy": "true", + "examine": "Newly made, but to an ancient pattern. (Dealing with Scabaras)", "archery_ticket_price": "0", - "id": "11994" + "destroy": "true", + "destroy_message": "I can obtain another one from the archaeologists near Sophanem.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze dagger.", - "examine": "It has been oxidised.", - "attack_audios": "2517,2517,2500,2517", - "durability": null, + "id": "11995", "name": "Oxidised dagger", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "11995" + "attack_audios": "2517,2517,2500,2517", + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze dagger.", + "durability": null, + "tradeable": "false" }, { - "examine": "It has been oxidised.", - "durability": null, + "id": "11996", "name": "Oxidised medium helm", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "11996" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze sword.", - "examine": "It has been oxidised.", - "durability": null, + "id": "11997", "name": "Oxidised sword", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "11997" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze sword.", + "durability": null, + "tradeable": "false" }, { - "turn90cw_anim": "821", - "examine": "It has been oxidised.", - "walk_anim": "819", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "render_anim": "1", - "destroy_message": "I can make another one by using camel dung on a bronze scimitar.", - "stand_anim": "808", - "attack_audios": "2500,0,2517,0", - "name": "Oxidised scimitar", - "tradeable": "false", - "run_anim": "824", - "archery_ticket_price": "0", "id": "11998", - "stand_turn_anim": "823" + "name": "Oxidised scimitar", + "examine": "It has been oxidised.", + "archery_ticket_price": "0", + "attack_audios": "2500,0,2517,0", + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze scimitar.", + "durability": null, + "render_anim": "1", + "run_anim": "824", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "false", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819" }, { - "destroy_message": "I can make another one by using camel dung on a bronze longsword.", - "examine": "It has been oxidised.", - "durability": null, + "id": "11999", "name": "Oxidised longsword", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "11999" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze longsword.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze full helmet.", - "examine": "It has been oxidised.", - "durability": null, + "id": "12000", "name": "Oxidised full helm", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12000" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze full helmet.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze square shield.", - "examine": "It has been oxidised.", - "durability": null, + "id": "12001", "name": "Oxidised square shield", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12001" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze square shield.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze chainbody.", - "examine": "It has been oxidised.", - "durability": null, + "id": "12002", "name": "Oxidised chainbody", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12002" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze chainbody.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze kiteshield.", - "examine": "It has been oxidised.", - "durability": null, + "id": "12003", "name": "Oxidised kiteshield", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12003" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze kiteshield.", + "durability": null, + "equip_audio": "2245", + "tradeable": "false" }, { - "destroy_message": "I can make another one by using camel dung on a bronze two", - "examine": "It has been oxidised.", - "durability": null, + "id": "12004", "name": "Oxidised two-handed sword", - "tradeable": "false", - "destroy": "true", - "weight": "4", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12004" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze two", + "durability": null, + "tradeable": "false", + "weight": "4" }, { - "destroy_message": "I can make another one by using camel dung on some bronze platelegs.", - "examine": "It has been oxidised.", - "durability": null, + "id": "12005", "name": "Oxidised platelegs", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "12005" - }, - { - "destroy_message": "I can make another one by using camel dung on a bronze plateskirt.", "examine": "It has been oxidised.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on some bronze platelegs.", "durability": null, + "tradeable": "false" + }, + { + "id": "12006", "name": "Oxidised plateskirt", - "tradeable": "false", - "destroy": "true", + "examine": "It has been oxidised.", "archery_ticket_price": "0", - "id": "12006" + "destroy": "true", + "destroy_message": "I can make another one by using camel dung on a bronze plateskirt.", + "durability": null, + "tradeable": "false" }, { - "requirements": "{23,52}", - "ge_buy_limit": "5000", - "grand_exchange_price": "330", + "id": "12007", + "name": "Spirit terrorbird pouch", "examine": "I can summon a spirit terrorbird familiar with this.", - "durability": null, - "name": "Spirit terrorbird pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12007" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,52}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "330", + "id": "12008", + "name": "Spirit terrorbird pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit terrorbird pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12008" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{16,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "239", + "id": "12009", + "name": "Granite crab pouch", "examine": "I can summon a granite crab familiar with this.", - "durability": null, - "name": "Granite crab pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12009" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{16,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "239", + "id": "12010", + "name": "Granite crab pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Granite crab pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12010" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,75}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2918", + "id": "12011", + "name": "Praying mantis pouch", "examine": "I can summon a praying mantis familiar with this.", - "durability": null, - "name": "Praying mantis pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12011" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,75}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2918", + "id": "12012", + "name": "Praying mantis pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Praying mantis pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12012" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,78}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2167", + "id": "12013", + "name": "Giant ent pouch", "examine": "I can summon a giant ent familiar with this.", - "durability": null, - "name": "Giant ent pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12013" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,78}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2167", + "id": "12014", + "name": "Giant ent pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Giant ent pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12014" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,63}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2018", + "id": "12015", + "name": "Spirit cobra pouch", "examine": "I can summon a spirit cobra familiar with this.", - "durability": null, - "name": "Spirit cobra pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12015" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,63}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2018", + "id": "12016", + "name": "Spirit cobra pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit cobra pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12016" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,83}", - "ge_buy_limit": "5000", - "grand_exchange_price": "121", + "id": "12017", + "name": "Spirit dagannoth pouch", "examine": "I can summon a spirit dagannoth familiar with this.", - "durability": null, - "name": "Spirit dagannoth pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12017" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,83}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "121", + "id": "12018", + "name": "Spirit dagannoth pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit dagannoth pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12018" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{13,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "248", + "id": "12019", + "name": "Thorny snail pouch", "examine": "I can summon a thorny snail familiar with this.", - "durability": null, - "name": "Thorny snail pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12019" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{13,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "248", + "id": "12020", + "name": "Thorny snail pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Thorny snail pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12020" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,33}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1268", + "id": "12021", + "name": "Beaver pouch", "examine": "I can summon a beaver familiar with this.", - "durability": null, - "name": "Beaver pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12021" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,33}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1268", + "id": "12022", + "name": "Beaver pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Beaver pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12022" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,58}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2520", + "id": "12023", + "name": "Karam. overlord pouch", "examine": "I can summon a karamthulhu overlord familiar with this.", - "durability": null, - "name": "Karam. overlord pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12023" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,58}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2520", + "id": "12024", + "name": "Karam. overlord pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karam. overlord pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12024" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,80}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2235", + "id": "12025", + "name": "Hydra pouch", "examine": "I can summon a hydra familiar with this.", - "durability": null, - "name": "Hydra pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12025" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,80}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2235", + "id": "12026", + "name": "Hydra pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Hydra pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12026" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,55}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2641", + "id": "12027", + "name": "Spirit jelly pouch", "examine": "I can summon a spirit jelly familiar with this.", - "durability": null, - "name": "Spirit jelly pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12027" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,55}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2641", + "id": "12028", + "name": "Spirit jelly pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit jelly pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12028" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,68}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3506", + "id": "12029", + "name": "Bunyip pouch", "examine": "I can summon a bunyip familiar with this.", - "durability": null, - "name": "Bunyip pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12029" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,68}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "3506", + "id": "12030", + "name": "Bunyip pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Bunyip pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12030" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,67}", - "ge_buy_limit": "5000", - "grand_exchange_price": "13200", + "id": "12031", + "name": "War tortoise pouch", "examine": "I can summon a war tortoise familiar with this.", - "durability": null, - "name": "War tortoise pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12031" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,67}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "13200", + "id": "12032", + "name": "War tortoise pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "War tortoise pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12032" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,69}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2269", + "id": "12033", + "name": "Fruit bat pouch", "examine": "I can summon a fruit bat familiar with this.", - "durability": null, - "name": "Fruit bat pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12033" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,69}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2269", + "id": "12034", + "name": "Fruit bat pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Fruit bat pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12034" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,54}", - "ge_buy_limit": "5000", - "grand_exchange_price": "4004", + "id": "12035", + "name": "Abyssal parasite pouch", "examine": "I can summon an abyssal parasite familiar with this.", - "durability": null, - "name": "Abyssal parasite pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12035" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,54}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "4004", + "id": "12036", + "name": "Abyssal parasite pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Abyssal parasite pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12036" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,62}", - "ge_buy_limit": "5000", - "grand_exchange_price": "5660", + "id": "12037", + "name": "Abyssal lurker pouch", "examine": "I can summon an abyssal lurker familiar with this.", - "durability": null, - "name": "Abyssal lurker pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12037" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,62}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "5660", + "id": "12038", + "name": "Abyssal lurker pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Abyssal lurker pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12038" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,88}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2439", + "id": "12039", + "name": "Unicorn stallion pouch", "examine": "I can summon a unicorn stallion familiar with this.", - "durability": null, - "name": "Unicorn stallion pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12039" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,88}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2439", + "id": "12040", + "name": "Unicorn stallion pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Unicorn stallion pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12040" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,47}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1559", + "id": "12041", + "name": "Magpie pouch", "examine": "I can summon a magpie familiar with this.", - "durability": null, - "name": "Magpie pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12041" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,47}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1559", + "id": "12042", + "name": "Magpie pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Magpie pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12042" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{4,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "258", + "id": "12043", + "name": "Dreadfowl pouch", "examine": "I can summon a dreadfowl familiar with this.", - "durability": null, - "name": "Dreadfowl pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12043" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{4,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "258", + "id": "12044", + "name": "Dreadfowl pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Dreadfowl pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12044" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,64}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2216", + "id": "12045", + "name": "Stranger plant pouch", "examine": "I can summon a stranger plant familiar with this.", - "durability": null, - "name": "Stranger plant pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12045" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,64}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2216", + "id": "12046", + "name": "Stranger plant pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Stranger plant pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12046" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "301", + "id": "12047", + "name": "Spirit wolf pouch", "examine": "I can summon a spirit wolf familiar with this.", - "durability": null, - "name": "Spirit wolf pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12047" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "301", + "id": "12048", + "name": "Spirit wolf pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit wolf pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12048" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{18,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "808", + "id": "12049", + "name": "Desert wyrm pouch", "examine": "I can summon a desert wyrm familiar with this.", - "durability": null, - "name": "Desert wyrm pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12049" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{18,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "808", + "id": "12050", + "name": "Desert wyrm pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Desert wyrm pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12050" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,42}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1812", + "id": "12051", + "name": "Evil turnip pouch", "examine": "I can summon an evil turnip familiar with this.", - "durability": null, - "name": "Evil turnip pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12051" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "1812", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Evil turnip pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12052" + "requirements": "{23,42}", + "tradeable": "true" }, { - "requirements": "{23,31}", - "ge_buy_limit": "10000", - "grand_exchange_price": "1416", + "id": "12052", + "name": "Evil turnip pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12053", + "name": "Vampire bat pouch", "examine": "I can summon a vampire bat familiar with this.", - "durability": null, - "name": "Vampire bat pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12053" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "1416", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Vampire bat pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12054" + "requirements": "{23,31}", + "tradeable": "true" }, { - "requirements": "{19,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "992", + "id": "12054", + "name": "Vampire bat pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "12055", + "name": "Spirit scorpion pouch", "examine": "I can summon a spirit scorpion familiar with this.", - "durability": null, - "name": "Spirit scorpion pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12055" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{19,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "992", + "id": "12056", + "name": "Spirit scorpion pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit scorpion pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12056" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,71}", - "ge_buy_limit": "5000", - "grand_exchange_price": "332", + "id": "12057", + "name": "Arctic bear pouch", "examine": "I can summon an arctic bear familiar with this.", - "durability": null, - "name": "Arctic bear pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12057" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,71}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "332", + "id": "12058", + "name": "Arctic bear pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Arctic bear pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12058" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{10,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2225", + "id": "12059", + "name": "Spirit spider pouch", "examine": "I can summon a spirit spider familiar with this.", - "durability": null, - "name": "Spirit spider pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12059" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{10,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2225", + "id": "12060", + "name": "Spirit spider pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit spider pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12060" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,49}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2045", + "id": "12061", + "name": "Bloated leech pouch", "examine": "I can summon a bloated leech familiar with this.", - "durability": null, - "name": "Bloated leech pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12061" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,49}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2045", + "id": "12062", + "name": "Bloated leech pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Bloated leech pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12062" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,25}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3746", + "id": "12063", + "name": "Spirit kalphite pouch", "examine": "I can summon a spirit kalphite familiar with this.", - "durability": null, - "name": "Spirit kalphite pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12063" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,25}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "3746", + "id": "12064", + "name": "Spirit kalphite pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit kalphite pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12064" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,32}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1470", + "id": "12065", + "name": "Honey badger pouch", "examine": "I can summon a honey badger familiar with this.", - "durability": null, - "name": "Honey badger pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12065" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,32}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1470", + "id": "12066", + "name": "Honey badger pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Honey badger pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12066" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1578", + "id": "12067", + "name": "Albino rat pouch", "examine": "I can summon an albino rat familiar with this.", - "durability": null, - "name": "Albino rat pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12067" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,23}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1578", + "id": "12068", + "name": "Albino rat pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Albino rat pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12068" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,74}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2924", + "id": "12069", + "name": "Granite lobster pouch", "examine": "I can summon a granite lobster familiar with this.", - "durability": null, - "name": "Granite lobster pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12069" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,74}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2924", + "id": "12070", + "name": "Granite lobster pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Granite lobster pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12070" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,41}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1375", + "id": "12071", + "name": "Macaw pouch", "examine": "I can summon a macaw familiar with this.", - "durability": null, - "name": "Macaw pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12071" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,41}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1375", + "id": "12072", + "name": "Macaw pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Macaw pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12072" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,36}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1804", + "id": "12073", + "name": "Bronze minotaur pouch", "examine": "I can summon a bronze minotaur familiar with this.", - "durability": null, - "name": "Bronze minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12073" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,36}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1804", + "id": "12074", + "name": "Bronze minotaur pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Bronze minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12074" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,46}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2216", + "id": "12075", + "name": "Iron minotaur pouch", "examine": "I can summon an iron minotaur familiar with this.", - "durability": null, - "name": "Iron minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12075" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,46}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2216", + "id": "12076", + "name": "Iron minotaur pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Iron minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12076" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,56}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2529", + "id": "12077", + "name": "Steel minotaur pouch", "examine": "I can summon a steel minotaur familiar with this", - "durability": null, - "name": "Steel minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12077" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,56}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "2529", + "id": "12078", + "name": "Steel minotaur pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Steel minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12078" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,66}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2666", + "id": "12079", + "name": "Mithril minotaur pouch", "examine": "I can summon a mithril minotaur with this.", - "durability": null, - "name": "Mithril minotaur pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12079" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "2666", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Mithril minotaur pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12080" - }, - { - "requirements": "{23,76}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2520", - "examine": "I can summon an adamant minotaur familiar with this.", - "durability": null, - "name": "Adamant minotaur pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12081" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2520", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Adamant minotaur pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12082" - }, - { - "requirements": "{23,86}", - "ge_buy_limit": "5000", - "grand_exchange_price": "373", - "examine": "I can summon a rune minotaur with this.", - "durability": null, - "name": "Rune minotaur pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12083" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "373", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Rune minotaur pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12084" - }, - { - "requirements": "{23,61}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2468", - "examine": "I can summon a smoke devil familiar with this.", - "durability": null, - "name": "Smoke devil pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12085" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2468", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Smoke devil pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12086" - }, - { - "requirements": "{23,40}", - "ge_buy_limit": "5000", - "grand_exchange_price": "319", - "examine": "I can summon a bull ant familiar with this.", - "durability": null, - "name": "Bull ant pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12087" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "319", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Bull ant pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12088" - }, - { - "requirements": "{23,92}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3625", - "examine": "I can summon a wolpertinger familiar with this.", - "durability": null, - "name": "Wolpertinger pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12089" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3625", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Wolpertinger pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12090" - }, - { - "requirements": "{23,28}", - "ge_buy_limit": "5000", - "grand_exchange_price": "906", - "examine": "I can summon a compost mound familiar with this.", - "durability": null, - "name": "Compost mound pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12091" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "906", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Compost mound pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12092" - }, - { - "requirements": "{23,96}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3696", - "examine": "I can summon a pack yak familiar with this.", - "durability": null, - "name": "Pack yak pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12093" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3696", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Pack yak pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12094" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1514", - "examine": "I can summon a spirit cockatrice familiar with this.", - "durability": null, - "name": "Sp. cockatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12095" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1514", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. cockatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12096" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1523", - "examine": "I can summon a spirit guthatrice familiar with this.", - "durability": null, - "name": "Sp. guthatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12097" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1523", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. guthatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12098" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1492", - "examine": "I can summon a spirit saratrice familiar with this.", - "durability": null, - "name": "Sp. saratrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12099" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1492", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. saratrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12100" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1515", - "examine": "I can summon a spirit zamatrice familiar with this.", - "durability": null, - "name": "Sp. zamatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12101" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1515", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. zamatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12102" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1531", - "examine": "I can summon a spirit pengatrice familiar with this.", - "durability": null, - "name": "Sp. pengatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12103" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1531", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. pengatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12104" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3085", - "examine": "I can summon a spirit coraxatrice familiar with this.", - "durability": null, - "name": "Sp. coraxatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12105" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3085", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. coraxatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12106" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3881", - "examine": "I can summon a spirit vulatrice familiar with this.", - "durability": null, - "name": "Sp. vulatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12107" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3881", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Sp. vulatrice pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12108" - }, - { - "ge_buy_limit": "10000", - "examine": "A stripy brown egg.", - "grand_exchange_price": "1053", - "durability": null, - "name": "Cockatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12109" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1053", - "durability": null, - "name": "Cockatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12110" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy green egg.", - "grand_exchange_price": "1171", - "durability": null, - "name": "Guthatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12111" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1171", - "durability": null, - "name": "Guthatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12112" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy blue egg.", - "grand_exchange_price": "1080", - "durability": null, - "name": "Saratrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12113" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1080", - "durability": null, - "name": "Saratrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12114" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy red egg.", - "grand_exchange_price": "1154", - "durability": null, - "name": "Zamatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12115" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1154", - "durability": null, - "name": "Zamatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12116" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy grey egg.", - "grand_exchange_price": "2676", - "durability": null, - "name": "Pengatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12117" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2676", - "durability": null, - "name": "Pengatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12118" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy black egg.", - "grand_exchange_price": "2865", - "durability": null, - "name": "Coraxatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12119" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2865", - "durability": null, - "name": "Coraxatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12120" - }, - { - "ge_buy_limit": "5000", - "examine": "A stripy pink egg.", - "grand_exchange_price": "18900", - "durability": null, - "name": "Vulatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12121" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "18900", - "durability": null, - "name": "Vulatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12122" - }, - { "requirements": "{23,66}", - "ge_buy_limit": "5000", - "grand_exchange_price": "293", - "examine": "I can summon a barker toad familiar with this.", - "durability": null, - "name": "Barker toad pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12123" + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "293", + "id": "12080", + "name": "Mithril minotaur pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Barker toad pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12124" - }, - { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "A swarm of little flies.", - "grand_exchange_price": "97", "durability": null, - "name": "Flies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12125" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "97", - "durability": null, - "name": "Flies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12126" - }, - { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "Some beetle bits.", - "grand_exchange_price": "176", - "durability": null, - "name": "Beetle bits", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12127" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "176", - "durability": null, - "name": "Beetle bits", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12128" - }, - { - "ge_buy_limit": "100", - "examine": "Good for feeding birds.", - "grand_exchange_price": "1", - "durability": null, - "name": "Ground fishing bait", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12129" - }, - { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "Mmmm...nuts.", - "grand_exchange_price": "673", - "durability": null, - "name": "Nuts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12130" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "673", - "durability": null, - "name": "Nuts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12131" - }, - { - "examine": "Makes me want to do a truffle shuffle.", - "durability": null, - "name": "Truffle", - "tradeable": "false", - "destroy": "true", - "weight": "28.928", - "archery_ticket_price": "0", - "id": "12132" - }, - { - "durability": null, - "name": "Truffle", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12133", - "equipment_slot": "3" - }, - { "ge_buy_limit": "5000", - "examine": "An evil turnip.", - "grand_exchange_price": "6750", - "durability": null, - "name": "Evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12134" + "tradeable": "true" }, { + "id": "12081", + "name": "Adamant minotaur pouch", + "examine": "I can summon an adamant minotaur familiar with this.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "6750", - "durability": null, - "name": "Evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12135" + "requirements": "{23,76}", + "tradeable": "true" }, { + "id": "12082", + "name": "Adamant minotaur pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "examine": "An evil turnip.", - "grand_exchange_price": "133", - "durability": null, - "name": "2/3 evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12136" + "tradeable": "true" }, { + "id": "12083", + "name": "Rune minotaur pouch", + "examine": "I can summon a rune minotaur with this.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "133", - "durability": null, - "name": "2/3 evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12137" + "requirements": "{23,86}", + "tradeable": "true" }, { + "id": "12084", + "name": "Rune minotaur pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "examine": "An evil turnip.", - "grand_exchange_price": "20", - "durability": null, - "name": "1/3 evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12138" + "tradeable": "true" }, { + "id": "12085", + "name": "Smoke devil pouch", + "examine": "I can summon a smoke devil familiar with this.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "20", - "durability": null, - "name": "1/3 evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12139" + "requirements": "{23,61}", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "4 doses of Summoning potion.", - "grand_exchange_price": "4489", - "durability": null, - "name": "Summoning potion(4)", - "tradeable": "true", - "weight": "1", + "id": "12086", + "name": "Smoke devil pouch", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "12140" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4489", "durability": null, - "name": "Summoning potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12141" - }, - { - "ge_buy_limit": "100", - "examine": "3 doses of Summoning potion.", - "grand_exchange_price": "3320", - "durability": null, - "name": "Summoning potion(3)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12142" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3320", - "durability": null, - "name": "Summoning potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12143" - }, - { - "ge_buy_limit": "100", - "examine": "2 doses of Summoning potion.", - "grand_exchange_price": "1671", - "durability": null, - "name": "Summoning potion(2)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12144" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1671", - "durability": null, - "name": "Summoning potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12145" - }, - { - "ge_buy_limit": "100", - "examine": "1 dose of Summoning potion.", - "grand_exchange_price": "1576", - "durability": null, - "name": "Summoning potion(1)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12146" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1576", - "durability": null, - "name": "Summoning potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12147" - }, - { - "ge_buy_limit": "100", - "examine": "Grows into the root-vegetable of all evil.", - "grand_exchange_price": "68", - "durability": null, - "name": "Evil turnip seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12148" - }, - { "ge_buy_limit": "5000", - "examine": "Utterly terrifying!", - "grand_exchange_price": "7101", - "durability": null, - "name": "Carved evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12153" + "tradeable": "true" }, { + "id": "12087", + "name": "Bull ant pouch", + "examine": "I can summon a bull ant familiar with this.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "7101", - "durability": null, - "name": "Carved evil turnip", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12154" + "requirements": "{23,40}", + "tradeable": "true" }, { + "id": "12088", + "name": "Bull ant pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12089", + "name": "Wolpertinger pouch", + "examine": "I can summon a wolpertinger familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,92}", + "tradeable": "true" + }, + { + "id": "12090", + "name": "Wolpertinger pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12091", + "name": "Compost mound pouch", + "examine": "I can summon a compost mound familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,28}", + "tradeable": "true" + }, + { + "id": "12092", + "name": "Compost mound pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12093", + "name": "Pack yak pouch", + "examine": "I can summon a pack yak familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,96}", + "tradeable": "true" + }, + { + "id": "12094", + "name": "Pack yak pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12095", + "name": "Sp. cockatrice pouch", + "examine": "I can summon a spirit cockatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12096", + "name": "Sp. cockatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12097", + "name": "Sp. guthatrice pouch", + "examine": "I can summon a spirit guthatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12098", + "name": "Sp. guthatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12099", + "name": "Sp. saratrice pouch", + "examine": "I can summon a spirit saratrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12100", + "name": "Sp. saratrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12101", + "name": "Sp. zamatrice pouch", + "examine": "I can summon a spirit zamatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12102", + "name": "Sp. zamatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12103", + "name": "Sp. pengatrice pouch", + "examine": "I can summon a spirit pengatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12104", + "name": "Sp. pengatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12105", + "name": "Sp. coraxatrice pouch", + "examine": "I can summon a spirit coraxatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12106", + "name": "Sp. coraxatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12107", + "name": "Sp. vulatrice pouch", + "examine": "I can summon a spirit vulatrice familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12108", + "name": "Sp. vulatrice pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12109", + "name": "Cockatrice egg", + "examine": "A stripy brown egg.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "3", - "examine": "Can be used to create Summoning pouches.", + "tradeable": "true" + }, + { + "id": "12110", + "name": "Cockatrice egg", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "12111", + "name": "Guthatrice egg", + "examine": "A stripy green egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12112", + "name": "Guthatrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12113", + "name": "Saratrice egg", + "examine": "A stripy blue egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12114", + "name": "Saratrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12115", + "name": "Zamatrice egg", + "examine": "A stripy red egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12116", + "name": "Zamatrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12117", + "name": "Pengatrice egg", + "examine": "A stripy grey egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12118", + "name": "Pengatrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12119", + "name": "Coraxatrice egg", + "examine": "A stripy black egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12120", + "name": "Coraxatrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12121", + "name": "Vulatrice egg", + "examine": "A stripy pink egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12122", + "name": "Vulatrice egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12123", + "name": "Barker toad pouch", + "examine": "I can summon a barker toad familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,66}", + "tradeable": "true" + }, + { + "id": "12124", + "name": "Barker toad pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12125", + "name": "Flies", + "examine": "A swarm of little flies.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "12126", + "name": "Flies", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12127", + "name": "Beetle bits", + "examine": "Some beetle bits.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "12128", + "name": "Beetle bits", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12129", + "name": "Ground fishing bait", + "examine": "Good for feeding birds.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12130", + "name": "Nuts", + "examine": "Mmmm...nuts.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "12131", + "name": "Nuts", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12132", + "name": "Truffle", + "examine": "Makes me want to do a truffle shuffle.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "28.928" + }, + { + "id": "12133", + "name": "Truffle", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "12134", + "name": "Evil turnip", + "examine": "An evil turnip.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12135", + "name": "Evil turnip", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12136", + "name": "2/3 evil turnip", + "examine": "An evil turnip.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12137", + "name": "2/3 evil turnip", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12138", + "name": "1/3 evil turnip", + "examine": "An evil turnip.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12139", + "name": "1/3 evil turnip", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12140", + "name": "Summoning potion(4)", + "examine": "4 doses of Summoning potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12141", + "name": "Summoning potion(4)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12142", + "name": "Summoning potion(3)", + "examine": "3 doses of Summoning potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12143", + "name": "Summoning potion(3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12144", + "name": "Summoning potion(2)", + "examine": "2 doses of Summoning potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12145", + "name": "Summoning potion(2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12146", + "name": "Summoning potion(1)", + "examine": "1 dose of Summoning potion.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12147", + "name": "Summoning potion(1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12148", + "name": "Evil turnip seed", + "examine": "Grows into the root-vegetable of all evil.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12153", + "name": "Carved evil turnip", + "examine": "Utterly terrifying!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12154", + "name": "Carved evil turnip", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12155", "name": "Pouch", - "tradeable": "true", + "examine": "Can be used to create Summoning pouches.", "archery_ticket_price": "0", - "id": "12155" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "5000", + "id": "12156", + "name": "Honeycomb", "examine": "This is what bees craft.", - "grand_exchange_price": "393", - "durability": null, - "name": "Honeycomb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12156" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "393", - "durability": null, + "tradeable": "true" + }, + { + "id": "12157", "name": "Honeycomb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12157" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "shop_price": "1", - "examine": "A charm used to summon familiars.", - "durability": null, + "id": "12158", "name": "Gold charm", - "tradeable": "false", + "examine": "A charm used to summon familiars.", "archery_ticket_price": "0", - "id": "12158" + "durability": null, + "shop_price": "1", + "tradeable": "false" }, { - "examine": "A charm used to summon familiars.", - "durability": null, + "id": "12159", "name": "Green charm", - "tradeable": "false", + "examine": "A charm used to summon familiars.", "archery_ticket_price": "0", - "id": "12159" + "durability": null, + "tradeable": "false" }, { - "examine": "A charm used to summon familiars.", - "durability": null, + "id": "12160", "name": "Crimson charm", - "tradeable": "false", + "examine": "A charm used to summon familiars.", "archery_ticket_price": "0", - "id": "12160" + "durability": null, + "tradeable": "false" }, { - "examine": "A charm used to summon familiars", - "durability": null, + "id": "12161", "name": "Abyssal charm", - "tradeable": "false", + "examine": "A charm used to summon familiars", "archery_ticket_price": "0", - "id": "12161" + "durability": null, + "tradeable": "false" }, { - "examine": "A charm used to summon talon beasts.", - "durability": null, + "id": "12162", "name": "Talon beast charm", + "examine": "A charm used to summon talon beasts.", "archery_ticket_price": "0", - "id": "12162" + "durability": null }, { - "examine": "A charm used to summon familiars.", - "durability": null, + "id": "12163", "name": "Blue charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12163" - }, - { - "shop_price": "2", - "examine": "A charm needed to summon a Void ravager.", - "durability": null, - "name": "Ravager charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12164" - }, - { - "shop_price": "2", - "examine": "A charm needed to summon a Void shifter.", - "durability": null, - "name": "Shifter charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12165" - }, - { - "shop_price": "2", - "examine": "A charm needed to summon a Void Spinner.", - "durability": null, - "name": "Spinner charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12166" - }, - { - "shop_price": "2", - "examine": "A charm needed to summon a Void torcher.", - "durability": null, - "name": "Torcher charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12167" - }, - { "examine": "A charm used to summon familiars.", - "durability": null, - "name": "Obsidian charm", "archery_ticket_price": "0", - "id": "12168" + "durability": null, + "tradeable": "false" }, { + "id": "12164", + "name": "Ravager charm", + "examine": "A charm needed to summon a Void ravager.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "12165", + "name": "Shifter charm", + "examine": "A charm needed to summon a Void shifter.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "12166", + "name": "Spinner charm", + "examine": "A charm needed to summon a Void Spinner.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "12167", + "name": "Torcher charm", + "examine": "A charm needed to summon a Void torcher.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "false" + }, + { + "id": "12168", + "name": "Obsidian charm", + "examine": "A charm used to summon familiars.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12169", + "name": "Summoning cape", + "examine": "The cape worn by master summoners.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{23,99}", "shop_price": "99000", - "examine": "The cape worn by master summoners.", - "durability": null, - "name": "Summoning cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "12169", - "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { - "requirements": "{23,99}", - "durability": null, + "id": "12170", "name": "Summoning cape(t)", "archery_ticket_price": "0", - "id": "12170", "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,4,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{23,99}" }, { + "id": "12171", + "name": "Summoning hood", + "examine": "Summoning skillcape hood.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{23,99}", "shop_price": "99000", - "examine": "Summoning skillcape hood.", - "durability": null, - "name": "Summoning hood", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "12171", - "equipment_slot": "0" + "weight": "0.4" }, { - "ge_buy_limit": "10000", + "id": "12172", + "name": "Clean spirit weed", "examine": "A fresh herb.", - "grand_exchange_price": "3597", - "durability": null, - "name": "Clean spirit weed", - "tradeable": "true", - "weight": "1", "archery_ticket_price": "0", - "id": "12172" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "3597", - "durability": null, + "id": "12173", "name": "Clean spirit weed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12173" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "requirements": "{15,35}", - "ge_buy_limit": "10000", + "id": "12174", + "name": "Grimy spirit weed", "examine": "I need to clean this herb before I can use it.", - "grand_exchange_price": "3634", - "durability": null, - "name": "Grimy spirit weed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12174" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "3634", - "durability": null, - "name": "Grimy spirit weed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12175" + "requirements": "{15,35}", + "tradeable": "true" }, { - "requirements": "{19,36}", - "ge_buy_limit": "100", - "examine": "A seed for spirit weed.", - "grand_exchange_price": "698", + "id": "12175", + "name": "Grimy spirit weed", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "12176", "name": "Spirit weed seed", - "tradeable": "true", + "examine": "A seed for spirit weed.", "archery_ticket_price": "0", - "id": "12176" + "durability": null, + "ge_buy_limit": "100", + "requirements": "{19,36}", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "12181", + "name": "Spirit weed potion(unf)", "examine": "I need another ingredient to finish this spirit weed potion.", - "grand_exchange_price": "2963", - "durability": null, - "name": "Spirit weed potion(unf)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12181" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "2963", - "durability": null, + "tradeable": "true" + }, + { + "id": "12182", "name": "Spirit weed potion(unf)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12182" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "25", - "ge_buy_limit": "10000", - "examine": "Shards of an obelisk.", - "grand_exchange_price": "24", - "durability": null, + "id": "12183", "name": "Spirit shards", - "tradeable": "true", + "examine": "Shards of an obelisk.", "archery_ticket_price": "0", - "id": "12183" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "25", + "tradeable": "true" }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12184", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12184" + "durability": null }, { - "examine": "A fantastic saradomin owl.", - "durability": null, + "id": "12185", "name": "Saradomin owl", + "examine": "A fantastic saradomin owl.", "archery_ticket_price": "0", - "id": "12185" + "durability": null }, { - "examine": "A stately zamorak hawk.", - "durability": null, + "id": "12186", "name": "Zamorak hawk", + "examine": "A stately zamorak hawk.", "archery_ticket_price": "0", - "id": "12186" + "durability": null }, { - "examine": "An impressive Guthix raptor.", - "durability": null, + "id": "12187", "name": "Guthix raptor", + "examine": "An impressive Guthix raptor.", "archery_ticket_price": "0", - "id": "12187" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12190", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12190" + "durability": null }, { - "examine": "It's lively and energetic.", - "durability": null, + "id": "12191", "name": "Terrier puppy", + "examine": "It's lively and energetic.", "archery_ticket_price": "0", - "id": "12191" + "durability": null }, { - "examine": "This is one fast little pooch.", - "durability": null, + "id": "12192", "name": "Greyhound puppy", + "examine": "This is one fast little pooch.", "archery_ticket_price": "0", - "id": "12192" + "durability": null }, { - "examine": "Soft but strong... and cute.", - "durability": null, + "id": "12193", "name": "Labrador puppy", + "examine": "Soft but strong... and cute.", "archery_ticket_price": "0", - "id": "12193" + "durability": null }, { - "examine": "Now, if only I could find another hundred...", - "durability": null, + "id": "12194", "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred...", "archery_ticket_price": "0", - "id": "12194" + "durability": null }, { - "examine": "This little puppy is very quiet. (Puppy)Come by! Come by! (Adult)", - "durability": null, + "id": "12195", "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet. (Puppy)Come by! Come by! (Adult)", "archery_ticket_price": "0", - "id": "12195" + "durability": null }, { - "examine": "One proud little puppy. (Puppy)Looks like it's chewing a wasp. (Adult)", - "durability": null, + "id": "12196", "name": "Bulldog puppy", + "examine": "One proud little puppy. (Puppy)Looks like it's chewing a wasp. (Adult)", "archery_ticket_price": "0", - "id": "12196" + "durability": null }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, + "id": "12197", "name": "Dragon hatchling", + "examine": "A hatchling green dragon.", "archery_ticket_price": "0", - "id": "12197" + "durability": null }, { - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12198", "name": "Baby giant crab", + "examine": "Little Nipper.", "archery_ticket_price": "0", - "id": "12198" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12199", "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", "archery_ticket_price": "0", - "id": "12199" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12200", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12200" + "durability": null }, { - "examine": "It's a colour-coordinated lizard!", - "durability": null, + "id": "12203", "name": "Chameleon", + "examine": "It's a colour-coordinated lizard!", "archery_ticket_price": "0", - "id": "12203" + "durability": null }, { + "id": "12204", + "name": "Antlers", + "examine": "Doors could be a problem, wearing these...", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "requirements": "{23,10}", "shop_price": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12205", + "name": "Antlers", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12206", + "name": "Antlers (charged)", "examine": "Doors could be a problem, wearing these...", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,8,0,0,0,0", "durability": null, - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "40", - "name": "Antlers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12204", - "bonuses": "0,0,0,0,0,0,0,0,0,0,8,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "40", - "durability": null, - "name": "Antlers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12205" - }, - { "remove_head": "true", "shop_price": "100", - "examine": "Doors could be a problem, wearing these...", - "grand_exchange_price": "50", - "durability": null, - "name": "Antlers (charged)", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12206", - "bonuses": "0,0,0,0,0,0,0,0,0,0,8,0,0,0,0", - "equipment_slot": "0" + "weight": "1" }, { + "id": "12207", + "name": "Lizard skull", + "examine": "It's a little smelly in there.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,1,2,3,4,2,3,12,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_beard": "true", "remove_head": "true", "requirements": "{23,30}", "shop_price": "200", + "tradeable": "true", + "weight": "3" + }, + { + "id": "12208", + "name": "Lizard skull", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12209", + "name": "Lizard skull (charged)", "examine": "It's a little smelly in there.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,1,2,3,4,2,3,12,0,0,0,0", "durability": null, - "weight": "3", - "remove_beard": "true", "equipment_slot": "0", - "grand_exchange_price": "254", - "name": "Lizard skull", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12207", - "bonuses": "0,0,0,0,1,2,3,4,2,3,12,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "254", - "durability": null, - "name": "Lizard skull", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12208" - }, - { + "remove_beard": "true", "remove_head": "true", "shop_price": "200", - "examine": "It's a little smelly in there.", - "durability": null, - "weight": "3", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "211", - "name": "Lizard skull (charged)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12209", - "bonuses": "0,0,0,0,1,2,3,4,2,3,12,0,0,0,0" + "weight": "3" }, { - "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "234", - "durability": null, - "name": "Feather headdress", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "12210", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "234", - "durability": null, "name": "Feather headdress", - "tradeable": "true", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", "archery_ticket_price": "0", - "id": "12211" + "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "requirements": "{4,50}-{23,20}", + "tradeable": "true", + "weight": "2" }, { - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, - "name": "Feather headdress (charged)", - "tradeable": "true", - "weight": "2", + "id": "12211", + "name": "Feather headdress", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12212", + "name": "Feather headdress (charged)", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" }, { - "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "662", - "durability": null, - "name": "Feather headdress", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "12213", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "662", - "durability": null, "name": "Feather headdress", - "tradeable": "true", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", "archery_ticket_price": "0", - "id": "12214" + "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "requirements": "{4,50}-{23,20}", + "tradeable": "true", + "weight": "2" }, { - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, - "name": "Feather headdress (charged)", - "tradeable": "true", - "weight": "2", + "id": "12214", + "name": "Feather headdress", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12215", + "name": "Feather headdress (charged)", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" }, { - "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "90", - "durability": null, - "name": "Feather headdress", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "12216", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "90", - "durability": null, "name": "Feather headdress", - "tradeable": "true", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", "archery_ticket_price": "0", - "id": "12217" + "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "requirements": "{4,50}-{23,20}", + "tradeable": "true", + "weight": "2" }, { - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, - "name": "Feather headdress (charged)", - "tradeable": "true", - "weight": "2", + "id": "12217", + "name": "Feather headdress", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12218", + "name": "Feather headdress (charged)", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", + "archery_ticket_price": "0", "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" }, { - "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, - "name": "Feather headdress", - "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", "id": "12219", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "107", - "durability": null, "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12220" - }, - { "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, - "name": "Feather headdress (charged)", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "12221", "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "211", - "durability": null, - "name": "Feather headdress", "tradeable": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12222", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "211", - "durability": null, + "id": "12220", "name": "Feather headdress", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12223" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "It's fun to go to the O-B-E-L-I-S-K!", - "grand_exchange_price": "107", - "durability": null, + "id": "12221", "name": "Feather headdress (charged)", - "tradeable": "true", - "weight": "2", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", "archery_ticket_price": "0", - "id": "12224", "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", - "equipment_slot": "0" - }, - { "durability": null, - "name": "Pouch", + "equipment_slot": "0", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12294" + "weight": "2" }, { + "id": "12222", + "name": "Feather headdress", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", "durability": null, - "name": "Pouch", + "equipment_slot": "0", + "ge_buy_limit": "100", + "requirements": "{4,50}-{23,20}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12296" + "weight": "2" }, { + "id": "12223", + "name": "Feather headdress", + "archery_ticket_price": "0", "durability": null, - "name": "Pouch", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12224", + "name": "Feather headdress (charged)", + "examine": "It's fun to go to the O-B-E-L-I-S-K!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0", + "durability": null, + "equipment_slot": "0", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12298" + "weight": "2" }, { - "durability": null, + "id": "12294", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12300" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12296", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12302" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12298", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12304" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12300", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12306" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12302", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12308" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12304", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12310" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12306", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12312" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12308", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12314" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12310", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12316" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12312", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12318" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12314", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12320" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12316", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12322" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12318", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12324" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12320", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12326" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12322", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12328" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12324", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12330" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12326", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12332" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12328", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12334" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12330", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12336" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12332", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12338" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12334", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12340" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12336", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12342" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12338", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12344" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12340", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12346" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12342", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12348" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12344", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12350" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12346", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12352" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12348", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12354" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12350", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12356" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12352", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12358" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12354", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12360" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12356", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12362" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12358", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12364" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12360", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12366" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12362", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12368" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12364", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12370" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12366", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12372" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12368", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12374" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12370", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12376" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12372", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12378" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12374", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12380" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12376", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12382" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12378", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12384" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12380", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12386" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12382", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12388" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12384", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12390" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12386", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12392" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12388", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12394" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12390", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12396" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12392", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12398" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12394", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12400" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12396", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12402" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12398", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12404" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12400", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12406" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12402", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12408" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12404", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12410" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12406", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12412" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12408", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12414" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12410", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12416" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12412", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12418" + "durability": null, + "tradeable": "true" }, { - "durability": null, + "id": "12414", "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12420" + "durability": null, + "tradeable": "true" }, { - "requirements": "{23,41}", - "ge_buy_limit": "5000", - "grand_exchange_price": "96", - "examine": "A scroll for a macaw familiar.", + "id": "12416", + "name": "Pouch", + "archery_ticket_price": "0", "durability": null, + "tradeable": "true" + }, + { + "id": "12418", + "name": "Pouch", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "12420", + "name": "Pouch", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "12422", "name": "Herbcall scroll", - "tradeable": "true", + "examine": "A scroll for a macaw familiar.", "archery_ticket_price": "0", - "id": "12422" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,41}", + "tradeable": "true" }, { - "requirements": "{23,69}", - "ge_buy_limit": "5000", - "grand_exchange_price": "258", - "examine": "A scroll for a fruit bat familiar.", - "durability": null, + "id": "12423", "name": "Fruitfall scroll", - "tradeable": "true", + "examine": "A scroll for a fruit bat familiar.", "archery_ticket_price": "0", - "id": "12423" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,69}", + "tradeable": "true" }, { - "requirements": "{23,56}", - "ge_buy_limit": "5000", - "grand_exchange_price": "146", - "examine": "A scroll for an ibis familiar.", - "durability": null, + "id": "12424", "name": "Fish rain scroll", - "tradeable": "true", + "examine": "A scroll for an ibis familiar.", "archery_ticket_price": "0", - "id": "12424" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "7", - "examine": "A scroll for a spirit wolf familiar.", - "durability": null, - "name": "Howl scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12425" - }, - { - "requirements": "{23,47}", - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "examine": "A scroll for a magpie familiar.", - "durability": null, - "name": "Thieving fingers scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12426" - }, - { - "requirements": "{23,62}", - "ge_buy_limit": "5000", - "grand_exchange_price": "533", - "examine": "A scroll for an abyssal lurker familiar.", - "durability": null, - "name": "Abyssal stealth scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12427" - }, - { - "requirements": "{23,10}", - "ge_buy_limit": "5000", - "grand_exchange_price": "237", - "examine": "A scroll for a spirit spider familiar.", - "durability": null, - "name": "Egg spawn scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12428" - }, - { - "requirements": "{23,33}", - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "examine": "A scroll for a beaver familiar.", - "durability": null, - "name": "Multichop scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12429" - }, - { - "requirements": "{23,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "121", - "examine": "A scroll for an albino rat familiar.", - "durability": null, - "name": "Cheese feast scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12430" - }, - { - "requirements": "{23,40}", - "ge_buy_limit": "5000", - "grand_exchange_price": "39", - "examine": "A scroll for a bull ant familiar.", - "durability": null, - "name": "Unburden scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12431" - }, - { - "requirements": "{23,19}", - "ge_buy_limit": "5000", - "grand_exchange_price": "72", - "examine": "A scroll for a spirit scorpion familiar.", - "durability": null, - "name": "Venom shot scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12432" - }, - { - "requirements": "{23,32}", - "ge_buy_limit": "5000", - "grand_exchange_price": "99", - "examine": "A scroll for a honey badger familiar.", - "durability": null, - "name": "Insane ferocity scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12433" - }, - { - "requirements": "{23,88}", - "ge_buy_limit": "5000", - "grand_exchange_price": "299", - "examine": "A scroll for a unicorn stallion familiar.", - "durability": null, - "name": "Healing aura scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12434" - }, - { - "requirements": "{23,96}", - "ge_buy_limit": "5000", - "grand_exchange_price": "428", - "examine": "A scroll for a pack yak familiar.", - "durability": null, - "name": "Winter storage scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12435" - }, - { - "requirements": "{23,63}", - "ge_buy_limit": "5000", - "grand_exchange_price": "223", - "examine": "A scroll for a spirit cobra familiar.", - "durability": null, - "name": "Oph. incubation scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12436" - }, - { - "requirements": "{23,92}", - "ge_buy_limit": "5000", - "grand_exchange_price": "419", - "examine": "A scroll for a wolpertinger familiar.", - "durability": null, - "name": "Magic focus scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12437" - }, - { - "requirements": "{23,68}", - "ge_buy_limit": "5000", - "grand_exchange_price": "220", - "examine": "A scroll for a bunyip familiar.", - "durability": null, - "name": "Swallow whole scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12438" - }, - { - "requirements": "{23,67}", - "ge_buy_limit": "5000", - "grand_exchange_price": "269", - "examine": "A scroll for a war tortoise familiar.", - "durability": null, - "name": "Testudo scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12439" - }, - { - "requirements": "{23,28}", - "ge_buy_limit": "5000", - "grand_exchange_price": "47", - "examine": "A scroll for a compost mound familiar.", - "durability": null, - "name": "Generate compost scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12440" - }, - { - "requirements": "{23,52}", - "ge_buy_limit": "5000", - "grand_exchange_price": "35", - "examine": "A scroll for a spirit terrorbird familiar.", - "durability": null, - "name": "Tireless run scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12441" - }, - { - "requirements": "{23,80}", - "ge_buy_limit": "5000", - "grand_exchange_price": "174", - "examine": "A scroll for a hydra familiar.", - "durability": null, - "name": "Regrowth scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12442" - }, - { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1118", - "examine": "A scroll for a Pest Control familiar.", - "durability": null, - "name": "Call to arms scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12443" - }, - { - "requirements": "{23,49}", - "ge_buy_limit": "5000", - "grand_exchange_price": "148", - "examine": "A scroll for a bloated leech familiar.", - "durability": null, - "name": "Blood drain scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12444" - }, - { - "requirements": "{23,4}", - "ge_buy_limit": "5000", - "grand_exchange_price": "7", - "examine": "A scroll for a dreadfowl familiar.", - "durability": null, - "name": "Dreadfowl strike scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12445" - }, - { - "requirements": "{23,25}", - "ge_buy_limit": "5000", - "grand_exchange_price": "122", - "examine": "A scroll for a spirit kalphite familiar.", - "durability": null, - "name": "Sandstorm scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12446" - }, - { - "requirements": "{23,31}", - "ge_buy_limit": "1000", - "grand_exchange_price": "98", - "examine": "A scroll for a vampire bat familiar.", - "durability": null, - "name": "Vampire touch scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12447" - }, - { - "requirements": "{23,42}", - "ge_buy_limit": "5000", - "grand_exchange_price": "123", - "examine": "A scroll for an evil turnip familiar.", - "durability": null, - "name": "Evil flames scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12448" - }, - { - "requirements": "{23,74}", - "ge_buy_limit": "5000", - "grand_exchange_price": "219", - "examine": "A scroll for a granite lobster familiar.", - "durability": null, - "name": "Crushing claw scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12449" - }, - { - "requirements": "{23,75}", - "ge_buy_limit": "5000", - "grand_exchange_price": "221", - "examine": "A scroll for a praying mantis familiar.", - "durability": null, - "name": "Mantis strike scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12450" - }, - { - "requirements": "{23,71}", - "ge_buy_limit": "5000", - "grand_exchange_price": "12", - "examine": "A scroll for an arctic bear familiar.", - "durability": null, - "name": "Arctic blast scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12451" - }, - { - "requirements": "{23,66}", - "ge_buy_limit": "5000", - "grand_exchange_price": "10", - "examine": "A scroll for a barker toad familiar.", - "durability": null, - "name": "Toad bark scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12452" - }, - { - "requirements": "{23,55}", - "ge_buy_limit": "5000", - "grand_exchange_price": "202", - "examine": "A scroll for a spirit jelly familiar.", - "durability": null, - "name": "Dissolve scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12453" - }, - { - "requirements": "{23,54}", - "ge_buy_limit": "5000", - "grand_exchange_price": "163", - "examine": "A scroll for an abyssal parasite familiar.", - "durability": null, - "name": "Abyssal drain scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12454" - }, - { - "requirements": "{23,58}", - "ge_buy_limit": "5000", - "grand_exchange_price": "198", - "examine": "A scroll for a karamthulhu overlord familiar.", - "durability": null, - "name": "Doomsphere scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12455" - }, - { - "requirements": "{23,83}", - "ge_buy_limit": "5000", - "grand_exchange_price": "9", - "examine": "A scroll for a spirit dagannoth familiar.", - "durability": null, - "name": "Spike shot scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12456" - }, - { - "requirements": "{23,78}", - "ge_buy_limit": "5000", - "grand_exchange_price": "146", - "examine": "A scroll for a giant ent familiar.", - "durability": null, - "name": "Acorn missile scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12457" - }, - { - "requirements": "{23,43}", - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "examine": "A scroll for a cockatrice variant familiar.", - "durability": null, - "name": "Petrifying gaze scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12458" - }, - { - "requirements": "{23,13}", - "ge_buy_limit": "5000", - "grand_exchange_price": "10", - "examine": "A scroll for a thorny snail familiar.", - "durability": null, - "name": "Slime spray scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12459" - }, - { - "requirements": "{23,18}", - "ge_buy_limit": "5000", - "grand_exchange_price": "47", - "examine": "A scroll for a desert wyrm familiar.", - "durability": null, - "name": "Electric lash scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12460" - }, - { - "requirements": "{23,36}", - "ge_buy_limit": "5000", - "grand_exchange_price": "135", - "examine": "A scroll for a bronze minotaur familiar.", - "durability": null, - "name": "Bronze bull rush scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12461" - }, - { - "requirements": "{23,46}", - "ge_buy_limit": "5000", - "grand_exchange_price": "180", - "examine": "A scroll for an iron minotaur familiar.", - "durability": null, - "name": "Iron bull rush scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12462" - }, - { "requirements": "{23,56}", - "ge_buy_limit": "5000", - "grand_exchange_price": "182", - "examine": "A scroll for a steel minotaur familiar.", - "durability": null, - "name": "Steel bull rush scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12463" + "tradeable": "true" }, { + "id": "12425", + "name": "Howl scroll", + "examine": "A scroll for a spirit wolf familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12426", + "name": "Thieving fingers scroll", + "examine": "A scroll for a magpie familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,47}", + "tradeable": "true" + }, + { + "id": "12427", + "name": "Abyssal stealth scroll", + "examine": "A scroll for an abyssal lurker familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,62}", + "tradeable": "true" + }, + { + "id": "12428", + "name": "Egg spawn scroll", + "examine": "A scroll for a spirit spider familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,10}", + "tradeable": "true" + }, + { + "id": "12429", + "name": "Multichop scroll", + "examine": "A scroll for a beaver familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,33}", + "tradeable": "true" + }, + { + "id": "12430", + "name": "Cheese feast scroll", + "examine": "A scroll for an albino rat familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,23}", + "tradeable": "true" + }, + { + "id": "12431", + "name": "Unburden scroll", + "examine": "A scroll for a bull ant familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,40}", + "tradeable": "true" + }, + { + "id": "12432", + "name": "Venom shot scroll", + "examine": "A scroll for a spirit scorpion familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,19}", + "tradeable": "true" + }, + { + "id": "12433", + "name": "Insane ferocity scroll", + "examine": "A scroll for a honey badger familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,32}", + "tradeable": "true" + }, + { + "id": "12434", + "name": "Healing aura scroll", + "examine": "A scroll for a unicorn stallion familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,88}", + "tradeable": "true" + }, + { + "id": "12435", + "name": "Winter storage scroll", + "examine": "A scroll for a pack yak familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,96}", + "tradeable": "true" + }, + { + "id": "12436", + "name": "Oph. incubation scroll", + "examine": "A scroll for a spirit cobra familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,63}", + "tradeable": "true" + }, + { + "id": "12437", + "name": "Magic focus scroll", + "examine": "A scroll for a wolpertinger familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,92}", + "tradeable": "true" + }, + { + "id": "12438", + "name": "Swallow whole scroll", + "examine": "A scroll for a bunyip familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,68}", + "tradeable": "true" + }, + { + "id": "12439", + "name": "Testudo scroll", + "examine": "A scroll for a war tortoise familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,67}", + "tradeable": "true" + }, + { + "id": "12440", + "name": "Generate compost scroll", + "examine": "A scroll for a compost mound familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,28}", + "tradeable": "true" + }, + { + "id": "12441", + "name": "Tireless run scroll", + "examine": "A scroll for a spirit terrorbird familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,52}", + "tradeable": "true" + }, + { + "id": "12442", + "name": "Regrowth scroll", + "examine": "A scroll for a hydra familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,80}", + "tradeable": "true" + }, + { + "id": "12443", + "name": "Call to arms scroll", + "examine": "A scroll for a Pest Control familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" + }, + { + "id": "12444", + "name": "Blood drain scroll", + "examine": "A scroll for a bloated leech familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,49}", + "tradeable": "true" + }, + { + "id": "12445", + "name": "Dreadfowl strike scroll", + "examine": "A scroll for a dreadfowl familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,4}", + "tradeable": "true" + }, + { + "id": "12446", + "name": "Sandstorm scroll", + "examine": "A scroll for a spirit kalphite familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,25}", + "tradeable": "true" + }, + { + "id": "12447", + "name": "Vampire touch scroll", + "examine": "A scroll for a vampire bat familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "requirements": "{23,31}", + "tradeable": "true" + }, + { + "id": "12448", + "name": "Evil flames scroll", + "examine": "A scroll for an evil turnip familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,42}", + "tradeable": "true" + }, + { + "id": "12449", + "name": "Crushing claw scroll", + "examine": "A scroll for a granite lobster familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,74}", + "tradeable": "true" + }, + { + "id": "12450", + "name": "Mantis strike scroll", + "examine": "A scroll for a praying mantis familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,75}", + "tradeable": "true" + }, + { + "id": "12451", + "name": "Arctic blast scroll", + "examine": "A scroll for an arctic bear familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,71}", + "tradeable": "true" + }, + { + "id": "12452", + "name": "Toad bark scroll", + "examine": "A scroll for a barker toad familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", "requirements": "{23,66}", - "ge_buy_limit": "5000", - "grand_exchange_price": "198", - "examine": "A scroll for a mithril minotaur familiar.", + "tradeable": "true" + }, + { + "id": "12453", + "name": "Dissolve scroll", + "examine": "A scroll for a spirit jelly familiar.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,55}", + "tradeable": "true" + }, + { + "id": "12454", + "name": "Abyssal drain scroll", + "examine": "A scroll for an abyssal parasite familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,54}", + "tradeable": "true" + }, + { + "id": "12455", + "name": "Doomsphere scroll", + "examine": "A scroll for a karamthulhu overlord familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,58}", + "tradeable": "true" + }, + { + "id": "12456", + "name": "Spike shot scroll", + "examine": "A scroll for a spirit dagannoth familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,83}", + "tradeable": "true" + }, + { + "id": "12457", + "name": "Acorn missile scroll", + "examine": "A scroll for a giant ent familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,78}", + "tradeable": "true" + }, + { + "id": "12458", + "name": "Petrifying gaze scroll", + "examine": "A scroll for a cockatrice variant familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" + }, + { + "id": "12459", + "name": "Slime spray scroll", + "examine": "A scroll for a thorny snail familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,13}", + "tradeable": "true" + }, + { + "id": "12460", + "name": "Electric lash scroll", + "examine": "A scroll for a desert wyrm familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,18}", + "tradeable": "true" + }, + { + "id": "12461", + "name": "Bronze bull rush scroll", + "examine": "A scroll for a bronze minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,36}", + "tradeable": "true" + }, + { + "id": "12462", + "name": "Iron bull rush scroll", + "examine": "A scroll for an iron minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,46}", + "tradeable": "true" + }, + { + "id": "12463", + "name": "Steel bull rush scroll", + "examine": "A scroll for a steel minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,56}", + "tradeable": "true" + }, + { + "id": "12464", "name": "Mith bull rush scroll", - "tradeable": "true", + "examine": "A scroll for a mithril minotaur familiar.", "archery_ticket_price": "0", - "id": "12464" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,66}", + "tradeable": "true" }, { - "requirements": "{23,76}", - "ge_buy_limit": "5000", - "grand_exchange_price": "203", - "examine": "A scroll for an adamant minotaur familiar.", - "durability": null, + "id": "12465", "name": "Addy bull rush scroll", - "tradeable": "true", + "examine": "A scroll for an adamant minotaur familiar.", "archery_ticket_price": "0", - "id": "12465" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,76}", + "tradeable": "true" }, { - "requirements": "{23,86}", - "ge_buy_limit": "5000", - "grand_exchange_price": "67", - "examine": "A scroll for a rune minotaur familiar.", - "durability": null, + "id": "12466", "name": "Rune bull rush scroll", - "tradeable": "true", + "examine": "A scroll for a rune minotaur familiar.", "archery_ticket_price": "0", - "id": "12466" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,86}", + "tradeable": "true" }, { - "requirements": "{23,64}", - "ge_buy_limit": "5000", - "grand_exchange_price": "171", - "examine": "A scroll for a stranger plant familiar.", - "durability": null, + "id": "12467", "name": "Poisonous blast scroll", - "tradeable": "true", + "examine": "A scroll for a stranger plant familiar.", "archery_ticket_price": "0", - "id": "12467" - }, - { - "requirements": "{23,61}", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "170", - "examine": "A scroll for a smoke devil familiar.", - "durability": null, + "requirements": "{23,64}", + "tradeable": "true" + }, + { + "id": "12468", "name": "Dust cloud scroll", - "tradeable": "true", + "examine": "A scroll for a smoke devil familiar.", "archery_ticket_price": "0", - "id": "12468" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,61}", + "tradeable": "true" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, + "id": "12469", + "name": "Dragon hatchling", + "examine": "A hatchling red dragon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12471", "name": "Hatchling dragon", + "examine": "A hatchling blue dragon.", "archery_ticket_price": "0", - "id": "12469" + "durability": null }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, + "id": "12473", "name": "Hatchling dragon", + "examine": "A hatchling green dragon.", "archery_ticket_price": "0", - "id": "12471" + "durability": null }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, + "id": "12475", "name": "Hatchling dragon", + "examine": "A hatchling black dragon.", "archery_ticket_price": "0", - "id": "12473" + "durability": null }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, - "name": "Hatchling dragon", - "archery_ticket_price": "0", - "id": "12475" - }, - { - "examine": "This will hatch into a mighty dragon.", - "durability": null, + "id": "12477", "name": "Red dragon egg", - "tradeable": "false", - "destroy": "false", - "weight": "1", + "examine": "This will hatch into a mighty dragon.", "archery_ticket_price": "0", - "id": "12477" + "destroy": "false", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "This will hatch into a mighty dragon.", - "durability": null, + "id": "12478", "name": "Blue dragon egg", - "tradeable": "false", - "destroy": "false", - "weight": "1", + "examine": "This will hatch into a mighty dragon.", "archery_ticket_price": "0", - "id": "12478" + "destroy": "false", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "This will hatch into a mighty dragon.", - "durability": null, + "id": "12479", "name": "Green dragon egg", - "tradeable": "false", - "destroy": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "12479" - }, - { "examine": "This will hatch into a mighty dragon.", - "durability": null, - "name": "Black dragon egg", - "tradeable": "false", + "archery_ticket_price": "0", "destroy": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "12480" + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", + "id": "12480", + "name": "Black dragon egg", + "examine": "This will hatch into a mighty dragon.", + "archery_ticket_price": "0", + "destroy": "false", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "12481", "name": "Baby penguin", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "archery_ticket_price": "0", - "id": "12481" + "durability": null }, { - "examine": "I can hatch this in an Incubator.", - "durability": null, + "id": "12482", + "name": "Penguin", + "examine": "Emperor of all he surveys." + }, + { + "id": "12483", "name": "Penguin egg", + "examine": "I can hatch this in an incubator.", "archery_ticket_price": "0", - "id": "12483" + "durability": null, + "tradeable": "false" }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12484", "name": "Raven chick", - "archery_ticket_price": "0", - "id": "12484" - }, - { "examine": "Chick: A raven chick.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12485", "name": "Raven", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12485" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12486", "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", "archery_ticket_price": "0", - "id": "12486" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12487", "name": "Raccoon", + "examine": "He can run with us.", "archery_ticket_price": "0", - "id": "12487" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12488", "name": "Baby gecko", + "examine": "It's so adorably tiny!", "archery_ticket_price": "0", - "id": "12488" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12489", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12489" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12490", "name": "Baby squirrel", + "examine": "A tiny nut-thief.", "archery_ticket_price": "0", - "id": "12490" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12491", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12491" + "durability": null }, { - "examine": "It's a colour-coordinated lizard!", - "durability": null, + "id": "12492", "name": "Baby chameleon", - "archery_ticket_price": "0", - "id": "12492" - }, - { "examine": "It's a colour-coordinated lizard!", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12493", "name": "Chameleon", + "examine": "It's a colour-coordinated lizard!", "archery_ticket_price": "0", - "id": "12493" + "durability": null }, { - "examine": "A lizard's egg.", - "durability": null, + "id": "12494", "name": "Chameleon egg", - "weight": "1", - "archery_ticket_price": "0", - "id": "12494" - }, - { "examine": "A lizard's egg.", - "durability": null, - "name": "Chameleon egg", - "weight": "1", "archery_ticket_price": "0", - "id": "12495" + "durability": null, + "weight": "1" }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", + "id": "12495", + "name": "Chameleon egg", + "examine": "A lizard's egg.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "id": "12496", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12496" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12497", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12498", "name": "Vulture chick", - "archery_ticket_price": "0", - "id": "12498" - }, - { "examine": "If you see them circling: run.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12499", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12499" + "durability": null }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12500", "name": "Baby giant crab", - "archery_ticket_price": "0", - "id": "12500" - }, - { - "requirements": "{23,40}", "examine": "Baby: Little NipperAdult: Bigger Nipper.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{23,40}" + }, + { + "id": "12501", "name": "Giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12501" + "durability": null, + "requirements": "{23,40}" }, { - "examine": "Apparently, this token is worth one baby giant crab to someone called Nung.", - "durability": null, + "id": "12502", "name": "Crunchy claw token", + "examine": "Apparently, this token is worth one baby giant crab to someone called Nung.", "archery_ticket_price": "0", - "id": "12502" + "durability": null }, { - "examine": "A fluffy chick.", - "durability": null, + "id": "12503", "name": "Saradomin chick", + "examine": "A fluffy chick.", "archery_ticket_price": "0", - "id": "12503" + "durability": null }, { - "examine": "Lovely blue plumage.", - "durability": null, + "id": "12504", "name": "Saradomin bird", + "examine": "Lovely blue plumage.", "archery_ticket_price": "0", - "id": "12504" + "durability": null }, { - "examine": "A fantastic saradomin owl.", - "durability": null, + "id": "12505", "name": "Saradomin owl", + "examine": "A fantastic saradomin owl.", "archery_ticket_price": "0", - "id": "12505" + "durability": null }, { - "examine": "A fluffy chick.", - "durability": null, + "id": "12506", "name": "Zamorak chick", - "archery_ticket_price": "0", - "id": "12506" - }, - { - "examine": "Lovely red plumage.", - "durability": null, - "name": "Zamorak bird", - "archery_ticket_price": "0", - "id": "12507" - }, - { - "examine": "A stately zamorak hawk.", - "durability": null, - "name": "Zamorak hawk", - "archery_ticket_price": "0", - "id": "12508" - }, - { "examine": "A fluffy chick.", - "durability": null, + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12507", + "name": "Zamorak bird", + "examine": "Lovely red plumage.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12508", + "name": "Zamorak hawk", + "examine": "A stately zamorak hawk.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12509", "name": "Guthix chick", + "examine": "A fluffy chick.", "archery_ticket_price": "0", - "id": "12509" + "durability": null }, { - "examine": "Lovely green plumage.", - "durability": null, + "id": "12510", "name": "Guthix bird", + "examine": "Lovely green plumage.", "archery_ticket_price": "0", - "id": "12510" + "durability": null }, { - "examine": "An impressive Guthix raptor.", - "durability": null, + "id": "12511", "name": "Guthix raptor", + "examine": "An impressive Guthix raptor.", "archery_ticket_price": "0", - "id": "12511" + "durability": null }, { - "requirements": "{4,23}", - "examine": "It's lively and energetic.", - "durability": null, + "id": "12512", "name": "Terrier puppy", - "archery_ticket_price": "0", - "id": "12512" - }, - { - "requirements": "{4,23}", "examine": "It's lively and energetic.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12513", "name": "Terrier", + "examine": "It's lively and energetic.", "archery_ticket_price": "0", - "id": "12513" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "This is one fast little pooch.", - "durability": null, + "id": "12514", "name": "Greyhound puppy", + "examine": "This is one fast little pooch.", "archery_ticket_price": "0", - "id": "12514" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "An aerodynamic doggy.", - "durability": null, + "id": "12515", "name": "Greyhound", + "examine": "An aerodynamic doggy.", "archery_ticket_price": "0", - "id": "12515" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Soft but strong... and cute.", - "durability": null, + "id": "12516", "name": "Labrador puppy", + "examine": "Soft but strong... and cute.", "archery_ticket_price": "0", - "id": "12516" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "My most faithful friend.", - "durability": null, + "id": "12517", "name": "Labrador", + "examine": "My most faithful friend.", "archery_ticket_price": "0", - "id": "12517" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Now, if only I could find another hundred...", - "durability": null, + "id": "12518", "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred...", "archery_ticket_price": "0", - "id": "12518" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "A purebred doggy.", - "durability": null, + "id": "12519", "name": "Dalmatian", + "examine": "A purebred doggy.", "archery_ticket_price": "0", - "id": "12519" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "This little puppy is very quiet.", - "durability": null, + "id": "12520", "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet.", "archery_ticket_price": "0", - "id": "12520" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Come by! Come by!", - "durability": null, + "id": "12521", "name": "Sheepdog", + "examine": "Come by! Come by!", "archery_ticket_price": "0", - "id": "12521" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "One proud little puppy.", - "durability": null, + "id": "12522", "name": "Bulldog puppy", + "examine": "One proud little puppy.", "archery_ticket_price": "0", - "id": "12522" - }, - { - "requirements": "{4,23}", - "examine": "Looks like it's chewing a wasp.", "durability": null, - "name": "Bulldog", - "archery_ticket_price": "0", - "id": "12523" + "requirements": "{4,23}" }, { + "id": "12523", + "name": "Bulldog", + "examine": "Looks like it's chewing a wasp.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12524", + "name": "Summoning cape", + "examine": "The cape worn by master summoners.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", + "durability": null, + "equipment_slot": "1", "requirements": "{23,99}", "shop_price": "99000", - "examine": "The cape worn by master summoners.", - "durability": null, - "name": "Summoning cape", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "12524", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0", - "equipment_slot": "1" + "weight": "0.4" }, { - "shop_price": "1", - "examine": "A charm used to summon familiars.", - "durability": null, + "id": "12527", "name": "Gold charm", - "tradeable": "false", + "examine": "A charm used to summon familiars.", "archery_ticket_price": "0", - "id": "12527" + "durability": null, + "shop_price": "1", + "tradeable": "false" }, { - "examine": "A key to Pikkupstix's trapdoor.", - "durability": null, + "id": "12528", "name": "Trapdoor key", + "examine": "A key to Pikkupstix's trapdoor.", "archery_ticket_price": "0", - "id": "12528" + "durability": null }, { - "shop_price": "25", - "examine": "Shards of an obelisk.", - "grand_exchange_price": "24", - "durability": null, + "id": "12530", "name": "Spirit shards", - "tradeable": "true", + "examine": "Shards of an obelisk.", "archery_ticket_price": "0", - "id": "12530" + "durability": null, + "shop_price": "25", + "tradeable": "true" }, { - "requirements": "{23,56}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1914", + "id": "12531", + "name": "Ibis pouch", "examine": "I can summon an ibis familiar with this.", - "durability": null, - "name": "Ibis pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12531" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,56}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "1914", + "id": "12532", + "name": "Ibis pouch", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ibis pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12532" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,16}", - "ge_buy_limit": "5000", - "grand_exchange_price": "7", - "examine": "A scroll for a granite crab familiar.", - "durability": null, + "id": "12533", "name": "Stony shell scroll", - "tradeable": "true", + "examine": "A scroll for a granite crab familiar.", "archery_ticket_price": "0", - "id": "12533" - }, - { - "ge_buy_limit": "1000", - "examine": "Meat from a creature that can be hunted in Isafdar.", - "grand_exchange_price": "7968", "durability": null, - "name": "Raw pawya meat", - "tradeable": "true", - "weight": "0.33", - "archery_ticket_price": "0", - "id": "12535" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "7968", - "durability": null, - "name": "Raw pawya meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12536" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12538" - }, - { "ge_buy_limit": "5000", - "examine": "Spikes from a grenwall.", - "grand_exchange_price": "1704", - "durability": null, - "name": "Grenwall spikes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12539" + "requirements": "{23,16}", + "tradeable": "true" }, { + "id": "12535", + "name": "Raw pawya meat", + "examine": "Meat from a creature that can be hunted in Isafdar.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.33" + }, + { + "id": "12536", + "name": "Raw pawya meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "12538", "name": "Picture", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12545" + "durability": null, + "tradeable": "true" }, { - "examine": "A most unlikely creature.", + "id": "12539", + "name": "Grenwall spikes", + "examine": "Spikes from a grenwall.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12545", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "12547", "name": "Platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12547" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12548", "name": "Platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12548" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12549", "name": "Platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12549" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12550", "name": "Platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12550" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12551", "name": "Baby platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12551" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12552", "name": "Baby platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12552" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12553", "name": "Baby platypus", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12553" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12554", "name": "Patrick", + "examine": "A most unlikely creature.", "archery_ticket_price": "0", - "id": "12554" + "durability": null }, { - "examine": "A most unlikely creature.", - "durability": null, + "id": "12555", "name": "Penelope", - "archery_ticket_price": "0", - "id": "12555" - }, - { - "examine": "I bet he can't wait until it's cooked.", - "durability": null, - "name": "Peter", - "archery_ticket_price": "0", - "id": "12556" - }, - { "examine": "A most unlikely creature.", - "durability": null, - "name": "Peanut", "archery_ticket_price": "0", - "id": "12557" + "durability": null }, { - "examine": "Marginally better than egg on your face.", - "durability": null, - "name": "Mud", + "id": "12556", + "name": "Peter", + "examine": "I bet he can't wait until it's cooked.", "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12557", + "name": "Peanut", + "examine": "A most unlikely creature.", + "archery_ticket_price": "0", + "durability": null + }, + { "id": "12558", + "name": "Mud", + "examine": "Marginally better than egg on your face.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "0" }, { + "id": "12559", + "name": "Ogre wig", + "examine": "The height of style?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "remove_head": "true", "shop_price": "50", - "ge_buy_limit": "100", - "examine": "The height of style?", - "grand_exchange_price": "1004", - "durability": null, - "name": "Ogre wig", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12559", - "equipment_slot": "0" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1004", - "durability": null, + "id": "12560", "name": "Ogre wig", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12560" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "60", - "ge_buy_limit": "100", - "examine": "It's a little big on me.", - "grand_exchange_price": "197", - "durability": null, - "name": "Ogre kilt", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "12561", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "197", - "durability": null, "name": "Ogre kilt", - "tradeable": "true", + "examine": "It's a little big on me.", "archery_ticket_price": "0", - "id": "12562" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "60", + "tradeable": "true", + "weight": "1" }, { + "id": "12562", + "name": "Ogre kilt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12563", + "name": "Ogre top", + "examine": "It's a little big on me.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", "shop_price": "60", - "ge_buy_limit": "100", - "examine": "It's a little big on me.", - "grand_exchange_price": "536", - "durability": null, - "name": "Ogre top", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12563", - "equipment_slot": "4" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "536", - "durability": null, + "id": "12564", "name": "Ogre top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12564" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "60", - "ge_buy_limit": "100", - "examine": "Good for stomping.", - "grand_exchange_price": "420", - "durability": null, - "name": "Ogre boots", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", "id": "12565", - "equipment_slot": "10" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "420", - "durability": null, "name": "Ogre boots", - "tradeable": "true", + "examine": "Good for stomping.", "archery_ticket_price": "0", - "id": "12566" + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "60", + "tradeable": "true", + "weight": "3" }, { + "id": "12566", + "name": "Ogre boots", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12567", + "name": "Diseased kebbit fur", "examine": "It's a slightly sickly-looking fur.", - "grand_exchange_price": "1468", - "durability": null, - "name": "Diseased kebbit fur", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "12567" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "A furry hat with an unnerving tail.", - "grand_exchange_price": "3656", - "durability": null, - "name": "Davy kebbit hat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "12568", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3656", - "durability": null, "name": "Davy kebbit hat", - "tradeable": "true", + "examine": "A furry hat with an unnerving tail.", "archery_ticket_price": "0", - "id": "12569" + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "tradeable": "true", + "weight": "1" }, { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "8684", - "examine": "A big piece of tree.", - "walk_anim": "8684", - "durability": null, - "weight": "10", - "turn90ccw_anim": "8684", - "attack_speed": "7", - "weapon_interface": "10", - "turn180_anim": "8684", - "render_anim": "1157", - "defence_anim": "403", - "equipment_slot": "3", - "attack_anims": "401,401,401,401", - "grand_exchange_price": "77", - "stand_anim": "8683", - "name": "Ogre club", - "tradeable": "true", - "run_anim": "8685", + "id": "12569", + "name": "Davy kebbit hat", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12570", - "stand_turn_anim": "823", - "bonuses": "-6,-2,15,0,0,0,0,0,0,0,0,12,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "77", - "durability": null, "name": "Ogre club", - "tradeable": "true", + "examine": "A big piece of tree.", "archery_ticket_price": "0", - "id": "12571" - }, - { - "ge_buy_limit": "100", - "examine": "A delicate, sweet-smelling flower.", - "grand_exchange_price": "20", + "attack_anims": "401,401,401,401", + "attack_speed": "7", + "bonuses": "-6,-2,15,0,0,0,0,0,0,0,0,12,0,0,0", + "defence_anim": "403", "durability": null, - "name": "Lavender", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12572" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Lavender", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12573" - }, - { - "ge_buy_limit": "100", - "examine": "A stiff, pungent grass.", - "grand_exchange_price": "31", - "durability": null, - "name": "Fever grass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12574" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "31", - "durability": null, - "name": "Fever grass", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12575" - }, - { - "ge_buy_limit": "100", - "examine": "A cheerful, aromatic flower.", - "grand_exchange_price": "12", - "durability": null, - "name": "Tansymum", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12576" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "12", - "durability": null, - "name": "Tansymum", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12577" - }, - { - "turn90cw_anim": "8681", - "examine": "A stiff, pungent grass.", - "walk_anim": "8681", - "durability": null, - "turn90ccw_anim": "8681", - "turn180_anim": "8681", - "render_anim": "294", "equipment_slot": "3", - "grand_exchange_price": "41", - "stand_anim": "8682", - "name": "Smouldering fever grass", + "ge_buy_limit": "100", + "render_anim": "1157", + "requirements": "{0,5}", + "run_anim": "8685", + "stand_anim": "8683", + "stand_turn_anim": "823", "tradeable": "true", - "run_anim": "8690", + "turn180_anim": "8684", + "turn90ccw_anim": "8684", + "turn90cw_anim": "8684", + "walk_anim": "8684", + "weapon_interface": "10", + "weight": "10" + }, + { + "id": "12571", + "name": "Ogre club", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12572", + "name": "Lavender", + "examine": "A delicate, sweet-smelling flower.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12573", + "name": "Lavender", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12574", + "name": "Fever grass", + "examine": "A stiff, pungent grass.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12575", + "name": "Fever grass", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12576", + "name": "Tansymum", + "examine": "A cheerful, aromatic flower.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12577", + "name": "Tansymum", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12578", - "stand_turn_anim": "8682" + "name": "Smouldering fever grass", + "examine": "A stiff, pungent grass.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "294", + "run_anim": "8690", + "stand_anim": "8682", + "stand_turn_anim": "8682", + "tradeable": "true", + "turn180_anim": "8681", + "turn90ccw_anim": "8681", + "turn90cw_anim": "8681", + "walk_anim": "8681" }, { - "turn90cw_anim": "8681", - "examine": "A delicate, sweet-smelling flower.", - "walk_anim": "8681", - "durability": null, - "turn90ccw_anim": "8681", - "turn180_anim": "8681", - "render_anim": "294", - "equipment_slot": "3", - "grand_exchange_price": "29", - "stand_anim": "8682", - "name": "Smouldering lavender", - "tradeable": "true", - "run_anim": "8690", - "archery_ticket_price": "0", "id": "12579", - "stand_turn_anim": "8682" - }, - { - "turn90cw_anim": "8681", - "examine": "A cheerful, aromatic flower.", - "walk_anim": "8681", + "name": "Smouldering lavender", + "examine": "A delicate, sweet-smelling flower.", + "archery_ticket_price": "0", "durability": null, - "turn90ccw_anim": "8681", - "turn180_anim": "8681", - "render_anim": "294", "equipment_slot": "3", - "grand_exchange_price": "18", - "stand_anim": "8682", - "name": "Smouldering tansymum", - "tradeable": "true", + "render_anim": "294", "run_anim": "8690", - "archery_ticket_price": "0", + "stand_anim": "8682", + "stand_turn_anim": "8682", + "tradeable": "true", + "turn180_anim": "8681", + "turn90ccw_anim": "8681", + "turn90cw_anim": "8681", + "walk_anim": "8681" + }, + { "id": "12580", - "stand_turn_anim": "8682" + "name": "Smouldering tansymum", + "examine": "A cheerful, aromatic flower.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "render_anim": "294", + "run_anim": "8690", + "stand_anim": "8682", + "stand_turn_anim": "8682", + "tradeable": "true", + "turn180_anim": "8681", + "turn90ccw_anim": "8681", + "turn90cw_anim": "8681", + "walk_anim": "8681" }, { - "ge_buy_limit": "25000", - "examine": "Logs cut from a eucalyptus tree.", - "grand_exchange_price": "507", - "durability": null, - "name": "Eucalyptus logs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12581", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "507", - "durability": null, "name": "Eucalyptus logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12582" - }, - { - "ge_buy_limit": "1000", "examine": "Logs cut from a eucalyptus tree.", - "grand_exchange_price": "3609", - "durability": null, - "name": "Eucalyptus pyre logs", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "12583" + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "25000", + "tradeable": "true" }, { + "id": "12582", + "name": "Eucalyptus logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "12583", + "name": "Eucalyptus pyre logs", + "examine": "Logs cut from a eucalyptus tree.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "3609", - "durability": null, + "tradeable": "true", + "weight": "2" + }, + { + "id": "12584", "name": "Eucalyptus pyre logs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12584" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "12588", + "name": "Primweed", "examine": "A modest and mild plant.", - "grand_exchange_price": "20", - "durability": null, - "name": "Primweed", - "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", - "id": "12588" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, + "id": "12589", "name": "Primweed", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12589" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "12590", + "name": "Stinkbloom", "examine": "A pretty but putrid flower.", - "grand_exchange_price": "16", - "durability": null, - "name": "Stinkbloom", - "tradeable": "true", - "weight": "0.01", "archery_ticket_price": "0", - "id": "12590" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "16", - "durability": null, - "name": "Stinkbloom", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12591" + "weight": "0.01" }, { + "id": "12591", + "name": "Stinkbloom", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12592", "name": "Diseased kebbit fur", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12592" + "durability": null, + "tradeable": "true" }, { - "destroy_message": "If you need to, you can get another from the Tyras Camp catapult engineer.", - "examine": "The plans for repairing the Tyras Camp catapult.", - "durability": null, + "id": "12593", "name": "Catapult schematics", - "tradeable": "false", - "destroy": "true", + "examine": "The plans for repairing the Tyras Camp catapult.", "archery_ticket_price": "0", - "id": "12593" + "destroy": "true", + "destroy_message": "If you need to, you can get another from the Tyras Camp catapult engineer.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "If you need to, you can get another from General Hining in the Tyras Camp.", - "examine": "General Hining wants me to deliver this to the Catapult Engineer.", - "durability": null, + "id": "12594", "name": "Engineer's letter", - "tradeable": "false", - "destroy": "true", + "examine": "General Hining wants me to deliver this to the Catapult Engineer.", "archery_ticket_price": "0", - "id": "12594" + "destroy": "true", + "destroy_message": "If you need to, you can get another from General Hining in the Tyras Camp.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can always get another from the drunken sailor in Port Sarim.", - "examine": "Taken from a drunken sailor in Port Sarim.", - "durability": null, - "name": "Sailor's hat", - "tradeable": "false", - "destroy": "true", - "weight": "0.2", - "archery_ticket_price": "0", "id": "12595", - "equipment_slot": "0" + "name": "Sailor's hat", + "examine": "Taken from a drunken sailor in Port Sarim.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can always get another from the drunken sailor in Port Sarim.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.2" }, { - "destroy_message": "You can get another set from Thaki the delivery dwarf in Port Sarim.", - "examine": "Dwarf-made parts for repairing the Tyras Camp catapult.", - "durability": null, + "id": "12596", "name": "Metal catapult parts", - "tradeable": "false", - "destroy": "true", - "weight": "2", + "examine": "Dwarf-made parts for repairing the Tyras Camp catapult.", "archery_ticket_price": "0", - "id": "12596" + "destroy": "true", + "destroy_message": "You can get another set from Thaki the delivery dwarf in Port Sarim.", + "durability": null, + "tradeable": "false", + "weight": "2" }, { - "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", - "durability": null, + "id": "12597", "name": "Mahogany catapult part", - "tradeable": "false", - "destroy": "true", - "weight": "2", + "examine": "Used to repair the Tyras Camp catapult.", "archery_ticket_price": "0", - "id": "12597" - }, - { + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12598" + "weight": "2" }, { + "id": "12598", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12599" + "weight": "2" }, { + "id": "12599", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12600" + "weight": "2" }, { + "id": "12600", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12601" + "weight": "2" }, { + "id": "12601", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12602" + "weight": "2" }, { + "id": "12602", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12603" + "weight": "2" }, { + "id": "12603", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12604" + "weight": "2" }, { + "id": "12604", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12605" + "weight": "2" }, { + "id": "12605", + "name": "Mahogany catapult part", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", - "examine": "Used to repair the Tyras Camp catapult.", "durability": null, - "name": "Mahogany catapult part", "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "12606" + "weight": "2" }, { + "id": "12606", + "name": "Mahogany catapult part", "examine": "Used to repair the Tyras Camp catapult.", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", - "weight": "2", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "If you need to, you can make another from the Tyras Camp catapult engineer's schematics.", + "durability": null, + "tradeable": "false", + "weight": "2" + }, + { "id": "12607", - "equipment_slot": "5" + "name": "Picture", + "examine": "Used to repair the Tyras Camp catapult.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "5", + "tradeable": "false", + "weight": "2" }, { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "An amulet of Farming with 1 charge.", - "grand_exchange_price": "271", - "durability": null, - "name": "Amulet of farming(1)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12608", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "271", - "durability": null, "name": "Amulet of farming(1)", - "tradeable": "true", + "examine": "An amulet of Farming with 1 charge.", "archery_ticket_price": "0", - "id": "12609" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 2 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(2)", - "tradeable": "true", + "id": "12609", + "name": "Amulet of farming(1)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12610", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(2)", - "tradeable": "true", + "examine": "An amulet of Farming with 2 charges.", "archery_ticket_price": "0", - "id": "12611" + "durability": null, + "equipment_slot": "2", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 3 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(3)", - "tradeable": "true", + "id": "12611", + "name": "Amulet of farming(2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12612", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(3)", - "tradeable": "true", + "examine": "An amulet of Farming with 3 charges.", "archery_ticket_price": "0", - "id": "12613" + "durability": null, + "equipment_slot": "2", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 4 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(4)", - "tradeable": "true", + "id": "12613", + "name": "Amulet of farming(3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12614", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(4)", - "tradeable": "true", + "examine": "An amulet of Farming with 4 charges.", "archery_ticket_price": "0", - "id": "12615" + "durability": null, + "equipment_slot": "2", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 5 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(5)", - "tradeable": "true", + "id": "12615", + "name": "Amulet of farming(4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12616", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(5)", - "tradeable": "true", + "examine": "An amulet of Farming with 5 charges.", "archery_ticket_price": "0", - "id": "12617" + "durability": null, + "equipment_slot": "2", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 6 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(6)", - "tradeable": "true", + "id": "12617", + "name": "Amulet of farming(5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12618", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(6)", - "tradeable": "true", + "examine": "An amulet of Farming with 6 charges.", "archery_ticket_price": "0", - "id": "12619" + "durability": null, + "equipment_slot": "2", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "200", - "examine": "An amulet of Farming with 7 charges.", - "grand_exchange_price": "8", - "durability": null, - "name": "Amulet of farming(7)", - "tradeable": "true", + "id": "12619", + "name": "Amulet of farming(6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12620", - "equipment_slot": "2" - }, - { - "durability": null, "name": "Amulet of farming(7)", - "tradeable": "true", + "examine": "An amulet of Farming with 7 charges.", "archery_ticket_price": "0", - "id": "12621" - }, - { + "durability": null, + "equipment_slot": "2", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "An amulet of Farming with 8 charges.", - "grand_exchange_price": "210", - "durability": null, - "name": "Amulet of farming(8)", - "tradeable": "true", + "tradeable": "true" + }, + { + "id": "12621", + "name": "Amulet of farming(7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "12622", - "equipment_slot": "2" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "210", - "durability": null, "name": "Amulet of farming(8)", - "tradeable": "true", + "examine": "An amulet of Farming with 8 charges.", "archery_ticket_price": "0", - "id": "12623" + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true" }, { - "shop_price": "10", - "examine": "An ogleroot. How odd.", + "id": "12623", + "name": "Amulet of farming(8)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12624", "name": "Ogleroot", - "tradeable": "true", + "examine": "An ogleroot. How odd.", "archery_ticket_price": "0", - "weight": "0", - "id": "12624" - }, - { - "destroy_message": "You will need to obtain more flags from Mrs. Winkin or Farmer Blinkin if you have lost all of the flags you have.", - "shop_price": "500", - "examine": "A flag to put in the ground.", "durability": null, - "name": "Flag", - "tradeable": "false", - "destroy": "true", - "weight": "0", - "archery_ticket_price": "0", - "id": "12625" - }, - { - "destroy_message": "You will need to get another exam paper from Professor Henry", - "examine": "Player Safety test", - "durability": null, - "name": "Test paper", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "12626" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "12627" - }, - { - "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "12628" - }, - { - "destroy_message": "You can get another pair of gloves from the chest in the Stronghold of Player Safety.", - "examine": "Gloves from the Stronghold of Player Safety.", - "durability": null, - "name": "Safety gloves", - "tradeable": "false", - "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "12629", - "bonuses": "0,0,0,0,0,1,2,2,1,2,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Rubium is very unstable", "shop_price": "10", - "examine": "Be careful with this near water.", + "tradeable": "true", + "weight": "0" + }, + { + "id": "12625", + "name": "Flag", + "examine": "A flag to put in the ground.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to obtain more flags from Mrs. Winkin or Farmer Blinkin if you have lost all of the flags you have.", "durability": null, + "shop_price": "500", + "tradeable": "false", + "weight": "0" + }, + { + "id": "12626", + "name": "Test paper", + "examine": "Player Safety test", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You will need to get another exam paper from Professor Henry", + "durability": null, + "tradeable": "false" + }, + { + "id": "12627", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "12628", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy the lamp? You won't be able to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "12629", + "name": "Safety gloves", + "examine": "Gloves from the Stronghold of Player Safety.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,1,2,2,1,2,1,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another pair of gloves from the chest in the Stronghold of Player Safety.", + "durability": null, + "equipment_slot": "9", + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "12630", "name": "Rubium", - "weight": "1", + "examine": "Be careful with this near water.", "archery_ticket_price": "0", - "id": "12630" + "destroy_message": "Rubium is very unstable", + "durability": null, + "shop_price": "10", + "weight": "1" }, { - "destroy_message": "The train is tiny", - "examine": "(While on the ground) Kennith's lost toy. (While in your inventory) A wooden toy train.", - "durability": null, + "id": "12631", "name": "Toy train", + "examine": "(While on the ground) Kennith's lost toy. (While in your inventory) A wooden toy train.", "archery_ticket_price": "0", - "id": "12631" + "destroy_message": "The train is tiny", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "160", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "12632", "name": "Note", - "tradeable": "true", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "12632" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "160", + "tradeable": "true" }, { - "examine": "The jar is shaking violently!", - "durability": null, + "id": "12633", "name": "Super fishing explosive", + "examine": "The jar is shaking violently!", "archery_ticket_price": "0", - "id": "12633" + "durability": null }, { - "destroy_message": "You can get another chocatrice cape from Diango in Draynor Village.", - "examine": "A thick, orange cape.", - "durability": null, - "name": "Chocatrice cape", - "tradeable": "false", - "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", "id": "12634", - "equipment_slot": "1" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12635" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12636" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12637" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12638" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12639" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12640" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12641" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12642" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12643" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", - "durability": null, - "name": "Easter egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12644" - }, - { - "destroy_message": "You can get another chocatrice cape from Diango in Draynor Village.", - "examine": "A thick, orange cape.", - "durability": null, "name": "Chocatrice cape", - "tradeable": "false", + "examine": "A thick, orange cape.", + "archery_ticket_price": "0", "destroy": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "12645", - "equipment_slot": "1" - }, - { - "examine": "A chocolate egg wrapped in coloured foil.", + "destroy_message": "You can get another chocatrice cape from Diango in Draynor Village.", "durability": null, - "name": "Chocolate egg", - "archery_ticket_price": "0", - "id": "12646" - }, - { - "examine": "A chocolate egg wrapped in coloured foil.", - "durability": null, - "name": "Chocolate egg", - "archery_ticket_price": "0", - "id": "12647" - }, - { - "examine": "A chocolate egg wrapped in coloured foil.", - "durability": null, - "name": "Chocolate egg", - "archery_ticket_price": "0", - "id": "12648" - }, - { - "shop_price": "2", - "examine": "It's an empty bucket.", - "grand_exchange_price": "50", - "durability": null, - "name": "Bucket", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "12649" - }, - { - "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?", - "grand_exchange_price": "74", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "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?", - "grand_exchange_price": "74", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "weight": "3", - "archery_ticket_price": "0", - "id": "12651" - }, - { - "examine": "It's a bucket of coal from the Easter Bunny's warren.", - "durability": null, - "name": "Bucket of coal", - "weight": "3", - "archery_ticket_price": "0", - "id": "12652" - }, - { - "examine": "It's a bucket of coal from the Easter Bunny's warren.", - "durability": null, - "name": "Bucket of coal", - "weight": "3", - "archery_ticket_price": "0", - "id": "12653" - }, - { - "destroy_message": "Are you sure you want to destroy this object?
You can get another in the Easter Bunny's warren.", - "examine": "A stripy brown egg.", - "grand_exchange_price": "1255", - "durability": null, - "name": "Cockatrice egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12654" - }, - { - "destroy_message": "Are you sure you want to destroy this object?
You can make another at the Easter Bunny's warren.", - "examine": "A cockatrice egg that has been dipped in molten chocolate.", - "durability": null, - "name": "Chocatrice egg", + "equip_audio": "2238", + "equipment_slot": "1", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "12655", - "equipment_slot": "3" + "weight": "0.4" }, { + "id": "12635", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some chocolate chunks.", "durability": null, + "tradeable": "true" + }, + { + "id": "12636", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12637", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12638", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12639", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12640", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12641", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12642", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12643", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12644", + "name": "Easter egg", + "examine": "Happy Easter.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12645", + "name": "Chocatrice cape", + "examine": "A thick, orange cape.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another chocatrice cape from Diango in Draynor Village.", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "1", + "tradeable": "false", + "weight": "0.4" + }, + { + "id": "12646", + "name": "Chocolate egg", + "examine": "A chocolate egg wrapped in coloured foil.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12647", + "name": "Chocolate egg", + "examine": "A chocolate egg wrapped in coloured foil.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12648", + "name": "Chocolate egg", + "examine": "A chocolate egg wrapped in coloured foil.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12649", + "name": "Bucket", + "examine": "It's an empty bucket.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12650", + "name": "Bucket of water", + "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?", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "3" + }, + { + "id": "12651", + "name": "Bucket of water", + "examine": "It's a bucket of water.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "3" + }, + { + "id": "12652", + "name": "Bucket of coal", + "examine": "It's a bucket of coal from the Easter Bunny's warren.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "12653", + "name": "Bucket of coal", + "examine": "It's a bucket of coal from the Easter Bunny's warren.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "12654", + "name": "Cockatrice egg", + "examine": "A stripy brown egg.", + "archery_ticket_price": "0", + "destroy_message": "Are you sure you want to destroy this object?
You can get another in the Easter Bunny's warren.", + "durability": null, + "tradeable": "true" + }, + { + "id": "12655", + "name": "Chocatrice egg", + "examine": "A cockatrice egg that has been dipped in molten chocolate.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Are you sure you want to destroy this object?
You can make another at the Easter Bunny's warren.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "12657", "name": "Chocolate chunks", + "examine": "Some chocolate chunks.", "archery_ticket_price": "0", - "id": "12657" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "remove_head": "true", - "shop_price": "3520", - "examine": "A full face helmet.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "1913", - "name": "Adamant full helm (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12658", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" - }, - { + "name": "Adamant full helm (e)", + "examine": "A full face helmet.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "shop_price": "3520", - "examine": "A full face helmet.", - "durability": null, - "weight": "2.7", - "absorb": "1,0,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "1913", - "name": "Adamant full helm (charged)", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12659", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0" + "name": "Adamant full helm (charged)", + "examine": "A full face helmet.", + "absorb": "1,0,2", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "3520", + "tradeable": "true", + "weight": "2.7" }, { - "remove_head": "true", - "examine": "Lightweight head protection.", - "grand_exchange_price": "1040", - "durability": null, - "name": "Snakeskin bandana (e)", - "tradeable": "false", - "weight": "0.9", - "archery_ticket_price": "0", "id": "12660", - "absorb": "0,2,1", - "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", + "name": "Snakeskin bandana (e)", "examine": "Lightweight head protection.", - "grand_exchange_price": "1040", - "durability": null, - "name": "Snakeskin bandana (charged)", - "tradeable": "false", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "12661", "absorb": "0,2,1", + "archery_ticket_price": "0", "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false", + "weight": "0.9" }, { - "remove_head": "true", - "examine": "A wooden helmet.", - "durability": null, - "weight": "0.9", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5623", - "name": "Splitbark helm (e)", - "tradeable": "true", + "id": "12661", + "name": "Snakeskin bandana (charged)", + "examine": "Lightweight head protection.", + "absorb": "0,2,1", "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false", + "weight": "0.9" + }, + { "id": "12662", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0" - }, - { - "remove_head": "true", + "name": "Splitbark helm (e)", "examine": "A wooden helmet.", - "durability": null, - "weight": "0.9", "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "5623", - "name": "Splitbark helm (charged)", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12663", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A full face helmet.", + "name": "Splitbark helm (charged)", + "examine": "A wooden helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0", "durability": null, - "weight": "2.7", - "absorb": "1,0,3", - "remove_beard": "true", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "21200", - "name": "Rune full helm (e)", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12664", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "remove_head": "true", + "name": "Rune full helm (e)", "examine": "A full face helmet.", - "durability": null, - "weight": "2.7", "absorb": "1,0,3", - "remove_beard": "true", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "21200", - "name": "Rune full helm (charged)", + "remove_beard": "true", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12665", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "Makes the wearer pretty intimidating.", + "name": "Rune full helm (charged)", + "examine": "A full face helmet.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", "durability": null, - "weight": "1.3", - "absorb": "2,0,4", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "61200", - "name": "Dragon med helm (e)", + "remove_beard": "true", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12666", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { - "remove_head": "true", + "name": "Dragon med helm (e)", "examine": "Makes the wearer pretty intimidating.", - "durability": null, - "weight": "1.3", "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "61200", - "name": "Dragon med helm (charged)", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "1.3" + }, + { "id": "12667", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" + "name": "Dragon med helm (charged)", + "examine": "Makes the wearer pretty intimidating.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1.3" }, { - "remove_head": "true", - "shop_price": "15000", - "examine": "A mystical helmet.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "absorb": "3,1,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar helm (e)", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12668", - "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0" - }, - { + "name": "Lunar helm (e)", + "examine": "A mystical helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "shop_price": "15000", - "examine": "A mystical helmet.", - "durability": null, - "destroy": "true", - "weight": "0.9", - "absorb": "3,1,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar helm (charged)", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12669", - "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0" + "name": "Lunar helm (charged)", + "examine": "A mystical helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,-2,8,7,10,2,0,7,0,0,0,0", + "destroy": "true", + "destroy_message": "The Oneiromancer might be able to help you get another.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "15000", + "tradeable": "false", + "weight": "0.9" }, { - "remove_head": "true", - "examine": "A helmet of great craftmanship.", - "durability": null, - "weight": "0.5", - "absorb": "0,5,2", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "3932989", - "name": "Armadyl helmet (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12670", - "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0" - }, - { - "remove_head": "true", + "name": "Armadyl helmet (e)", "examine": "A helmet of great craftmanship.", - "durability": null, - "weight": "0.5", "absorb": "0,5,2", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0", + "durability": null, + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "3932989", - "name": "Armadyl helmet (charged)", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "0.5" + }, + { "id": "12671", - "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", - "examine": "This helmet is worn by archers.", + "name": "Armadyl helmet (charged)", + "examine": "A helmet of great craftmanship.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,10,6,8,10,10,8,12,0,1,0,0", "durability": null, - "weight": "2", - "absorb": "0,3,1", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "41000", - "name": "Archer helm (e)", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "0.5" + }, + { "id": "12672", - "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", + "name": "Archer helm (e)", "examine": "This helmet is worn by archers.", - "durability": null, - "weight": "2", "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "41000", - "name": "Archer helm (charged)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "12673", - "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", - "examine": "This helmet is worn by berserkers.", + "name": "Archer helm (charged)", + "examine": "This helmet is worn by archers.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0", "durability": null, - "weight": "2.7", - "absorb": "1,0,3", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "48800", - "name": "Berserker helm (e)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "12674", - "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", + "name": "Berserker helm (e)", "examine": "This helmet is worn by berserkers.", - "durability": null, - "weight": "2.7", "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "48800", - "name": "Berserker helm (charged)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12675", - "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", - "examine": "This helm is worn by warriors.", - "durability": null, - "weight": "2.7", + "name": "Berserker helm (charged)", + "examine": "This helmet is worn by berserkers.", "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "48200", - "name": "Warrior helm (e)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12676", - "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", + "name": "Warrior helm (e)", "examine": "This helm is worn by warriors.", - "durability": null, - "weight": "2.7", "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "48200", - "name": "Warrior helm (charged)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12677", - "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", - "examine": "This helm is worn by farseers.", + "name": "Warrior helm (charged)", + "examine": "This helm is worn by warriors.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0", "durability": null, - "weight": "2.7", - "absorb": "3,1,0", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "46400", - "name": "Farseer helm (e)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12678", - "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "78000", + "name": "Farseer helm (e)", "examine": "This helm is worn by farseers.", - "durability": null, - "weight": "2.7", "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0", + "durability": null, "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "46400", - "name": "Farseer helm (charged)", + "remove_head": "true", + "shop_price": "78000", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.7" + }, + { "id": "12679", - "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0" + "name": "Farseer helm (charged)", + "examine": "This helm is worn by farseers.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "shop_price": "78000", + "tradeable": "true", + "weight": "2.7" }, { - "remove_head": "true", - "shop_price": "50000", - "examine": "A gift from Neitiznot's Burgher.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,4", - "equipment_slot": "0", - "grand_exchange_price": "44831", - "name": "Helm of neitiznot (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12680", - "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0" - }, - { + "name": "Helm of neitiznot (e)", + "examine": "A gift from Neitiznot's Burgher.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "remove_head": "true", "shop_price": "50000", - "examine": "A gift from Neitiznot's Burgher.", - "durability": null, - "weight": "2.2", - "absorb": "2,0,4", - "equipment_slot": "0", - "grand_exchange_price": "44831", - "name": "Helm of neitiznot (charged)", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "2.2" + }, + { "id": "12681", - "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0" - }, - { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, - "name": "Baby monkey", + "name": "Helm of neitiznot (charged)", + "examine": "A gift from Neitiznot's Burgher.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "12682" + "bonuses": "0,0,0,0,0,31,29,34,3,30,8,3,3,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50000", + "tradeable": "true", + "weight": "2.2" }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12682", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12684" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12683", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12684", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12686" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12685", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12686", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12688" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12687", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12688", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12690" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12689", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12690", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12692" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12691", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12692", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12694" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12693", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12694", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12696" + "durability": null }, { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, + "id": "12695", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12696", "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", "archery_ticket_price": "0", - "id": "12698" + "durability": null }, { - "requirements": "{4,23}", - "examine": "A hyperactive little pup.", - "durability": null, + "id": "12697", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12698", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12699", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12700", "name": "Terrier puppy", - "archery_ticket_price": "0", - "id": "12700" - }, - { - "requirements": "{4,23}", "examine": "A hyperactive little pup.", - "durability": null, - "name": "Terrier", "archery_ticket_price": "0", - "id": "12701" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Such a brave doggy.", + "id": "12701", + "name": "Terrier", + "examine": "A hyperactive little pup.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12702", "name": "Terrier puppy", - "archery_ticket_price": "0", - "id": "12702" - }, - { - "requirements": "{4,23}", "examine": "Such a brave doggy.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12703", "name": "Terrier", + "examine": "Such a brave doggy.", "archery_ticket_price": "0", - "id": "12703" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "This is one fast little pooch.", - "durability": null, + "id": "12704", "name": "Greyhound puppy", - "archery_ticket_price": "0", - "id": "12704" - }, - { - "requirements": "{4,23}", - "examine": "An aerodynamic doggy.", - "durability": null, - "name": "Greyhound", - "archery_ticket_price": "0", - "id": "12705" - }, - { - "requirements": "{4,23}", "examine": "This is one fast little pooch.", - "durability": null, - "name": "Greyhound puppy", "archery_ticket_price": "0", - "id": "12706" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "An aerodynamic doggy.", - "durability": null, + "id": "12705", "name": "Greyhound", + "examine": "An aerodynamic doggy.", "archery_ticket_price": "0", - "id": "12707" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Soft but strong... and cute.", + "id": "12706", + "name": "Greyhound puppy", + "examine": "This is one fast little pooch.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12707", + "name": "Greyhound", + "examine": "An aerodynamic doggy.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12708", "name": "Labrador puppy", - "archery_ticket_price": "0", - "id": "12708" - }, - { - "requirements": "{4,23}", - "examine": "My most faithful friend.", - "durability": null, - "name": "Labrador", - "archery_ticket_price": "0", - "id": "12709" - }, - { - "requirements": "{4,23}", "examine": "Soft but strong... and cute.", - "durability": null, - "name": "Labrador puppy", "archery_ticket_price": "0", - "id": "12710" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "My most faithful friend.", - "durability": null, + "id": "12709", "name": "Labrador", + "examine": "My most faithful friend.", "archery_ticket_price": "0", - "id": "12711" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Now, if only I could find another hundred...", + "id": "12710", + "name": "Labrador puppy", + "examine": "Soft but strong... and cute.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12711", + "name": "Labrador", + "examine": "My most faithful friend.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12712", "name": "Dalmatian puppy", - "archery_ticket_price": "0", - "id": "12712" - }, - { - "requirements": "{4,23}", - "examine": "A purebred doggy.", - "durability": null, - "name": "Dalmatian", - "archery_ticket_price": "0", - "id": "12713" - }, - { - "requirements": "{4,23}", "examine": "Now, if only I could find another hundred...", - "durability": null, - "name": "Dalmatian puppy", "archery_ticket_price": "0", - "id": "12714" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "A purebred doggy.", - "durability": null, + "id": "12713", "name": "Dalmatian", + "examine": "A purebred doggy.", "archery_ticket_price": "0", - "id": "12715" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "This little puppy is very quiet.", + "id": "12714", + "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred...", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12715", + "name": "Dalmatian", + "examine": "A purebred doggy.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12716", "name": "Sheepdog puppy", - "archery_ticket_price": "0", - "id": "12716" - }, - { - "requirements": "{4,23}", - "examine": "Come by! Come by!", - "durability": null, - "name": "Sheepdog", - "archery_ticket_price": "0", - "id": "12717" - }, - { - "requirements": "{4,23}", "examine": "This little puppy is very quiet.", - "durability": null, - "name": "Sheepdog puppy", "archery_ticket_price": "0", - "id": "12718" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Come by! Come by!", - "durability": null, + "id": "12717", "name": "Sheepdog", + "examine": "Come by! Come by!", "archery_ticket_price": "0", - "id": "12719" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "One proud little puppy.", + "id": "12718", + "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12719", + "name": "Sheepdog", + "examine": "Come by! Come by!", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12720", "name": "Bulldog puppy", - "archery_ticket_price": "0", - "id": "12720" - }, - { - "requirements": "{4,23}", - "examine": "Looks like it's chewing a wasp.", - "durability": null, - "name": "Bulldog", - "archery_ticket_price": "0", - "id": "12721" - }, - { - "requirements": "{4,23}", "examine": "One proud little puppy.", - "durability": null, - "name": "Bulldog puppy", "archery_ticket_price": "0", - "id": "12722" + "durability": null, + "requirements": "{4,23}" }, { - "requirements": "{4,23}", - "examine": "Looks like it's chewing a wasp.", - "durability": null, + "id": "12721", "name": "Bulldog", + "examine": "Looks like it's chewing a wasp.", "archery_ticket_price": "0", - "id": "12723" + "durability": null, + "requirements": "{4,23}" }, { - "examine": "Chick: A raven chick.", + "id": "12722", + "name": "Bulldog puppy", + "examine": "One proud little puppy.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12723", + "name": "Bulldog", + "examine": "Looks like it's chewing a wasp.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12724", "name": "Raven chick", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12724" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12725", "name": "Raven", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12725" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12726", "name": "Raven chick", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12726" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12727", "name": "Raven", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12727" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12728", "name": "Raven chick", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12728" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12729", "name": "Raven", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12729" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12730", "name": "Raven chick", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12730" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12731", "name": "Raven", + "examine": "Chick: A raven chick.", "archery_ticket_price": "0", - "id": "12731" + "durability": null }, { - "examine": "Chick: A raven chick.", - "durability": null, + "id": "12732", "name": "Raven chick", - "archery_ticket_price": "0", - "id": "12732" - }, - { "examine": "Chick: A raven chick.", - "durability": null, - "name": "Raven", "archery_ticket_price": "0", - "id": "12733" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12733", + "name": "Raven", + "examine": "Chick: A raven chick.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12734", "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", "archery_ticket_price": "0", - "id": "12734" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12735", "name": "Raccoon", + "examine": "He can run with us.", "archery_ticket_price": "0", - "id": "12735" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12736", "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", "archery_ticket_price": "0", - "id": "12736" + "durability": null }, { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, + "id": "12737", "name": "Raccoon", + "examine": "He can run with us.", "archery_ticket_price": "0", - "id": "12737" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12738", "name": "Baby gecko", + "examine": "It's so adorably tiny!", "archery_ticket_price": "0", - "id": "12738" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12739", "name": "Baby gecko", + "examine": "It's so adorably tiny!", "archery_ticket_price": "0", - "id": "12739" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12740", "name": "Baby gecko", + "examine": "It's so adorably tiny!", "archery_ticket_price": "0", - "id": "12740" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12741", "name": "Baby gecko", + "examine": "It's so adorably tiny!", "archery_ticket_price": "0", - "id": "12741" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12742", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12742" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12743", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12743" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12744", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12744" + "durability": null }, { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, + "id": "12745", "name": "Gecko", + "examine": "How cute!", "archery_ticket_price": "0", - "id": "12745" + "durability": null }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12746", "name": "Baby giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12746" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12747", "name": "Giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12747" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12748", "name": "Baby giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12748" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12749", "name": "Giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12749" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12750", "name": "Baby giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12750" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12751", "name": "Giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12751" + "durability": null, + "requirements": "{23,40}" }, { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, + "id": "12752", "name": "Baby giant crab", - "archery_ticket_price": "0", - "id": "12752" - }, - { - "requirements": "{23,40}", "examine": "Baby: Little NipperAdult: Bigger Nipper.", + "archery_ticket_price": "0", "durability": null, + "requirements": "{23,40}" + }, + { + "id": "12753", "name": "Giant crab", + "examine": "Baby: Little NipperAdult: Bigger Nipper.", "archery_ticket_price": "0", - "id": "12753" + "durability": null, + "requirements": "{23,40}" }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12754", "name": "Baby squirrel", + "examine": "A tiny nut-thief.", "archery_ticket_price": "0", - "id": "12754" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12755", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12755" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12756", "name": "Baby squirrel", + "examine": "A tiny nut-thief.", "archery_ticket_price": "0", - "id": "12756" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12757", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12757" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12758", "name": "Baby squirrel", + "examine": "A tiny nut-thief.", "archery_ticket_price": "0", - "id": "12758" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12759", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12759" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12760", "name": "Baby squirrel", + "examine": "A tiny nut-thief.", "archery_ticket_price": "0", - "id": "12760" + "durability": null }, { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, + "id": "12761", "name": "Squirrel", + "examine": "An experienced nut-thief.", "archery_ticket_price": "0", - "id": "12761" + "durability": null }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", - "durability": null, + "id": "12762", + "name": "Penguin", + "examine": "Emperor of all he surveys." + }, + { + "id": "12763", "name": "Baby penguin", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "archery_ticket_price": "0", - "id": "12763" + "durability": null }, { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", - "durability": null, + "id": "12764", + "name": "Penguin", + "examine": "Emperor of all he surveys." + }, + { + "id": "12765", "name": "Baby penguin", + "examine": "Can't fly and can barely walk, but adorable nonetheless.", "archery_ticket_price": "0", - "id": "12765" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12766", "name": "Vulture chick", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12766" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12767", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12767" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12768", "name": "Vulture chick", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12768" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12769", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12769" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12770", "name": "Vulture chick", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12770" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12771", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12771" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12772", "name": "Vulture chick", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12772" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12773", "name": "Vulture", + "examine": "If you see them circling: run.", "archery_ticket_price": "0", - "id": "12773" + "durability": null }, { - "examine": "If you see them circling: run.", - "durability": null, + "id": "12774", "name": "Vulture chick", - "archery_ticket_price": "0", - "id": "12774" - }, - { "examine": "If you see them circling: run.", - "durability": null, - "name": "Vulture", "archery_ticket_price": "0", - "id": "12775" + "durability": null }, { - "requirements": "{23,85}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2613", + "id": "12775", + "name": "Vulture", + "examine": "If you see them circling: run.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12776", + "name": "Swamp titan pouch", "examine": "I can summon a swamp titan familiar with this.", - "durability": null, - "name": "Swamp titan pouch", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12776" - }, - { + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "2613", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Swamp titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12777" - }, - { - "requirements": "{17,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "130", - "examine": "I can summon a spirit mosquito familiar with this.", - "durability": null, - "name": "Spirit mosquito pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12778" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "130", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit mosquito pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12779" - }, - { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "16100", - "examine": "I can summon a void spinner familiar with this.", - "durability": null, - "name": "Void spinner pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12780" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "16100", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Void spinner pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12781" - }, - { - "requirements": "{23,76}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2458", - "examine": "I can summon a forge regent familiar with this.", - "durability": null, - "name": "Forge regent pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12782" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2458", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Forge regent pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12783" - }, - { - "requirements": "{23,57}", - "ge_buy_limit": "5000", - "grand_exchange_price": "4097", - "examine": "I can summon a spirit larupia familiar with this.", - "durability": null, - "name": "Spirit larupia pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12784" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "4097", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit larupia pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12785" - }, - { - "requirements": "{23,89}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3856", - "examine": "I can summon a geyser titan familiar with this.", - "durability": null, - "name": "Geyser titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12786" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3856", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Geyser titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12787" - }, - { - "requirements": "{23,83}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3840", - "examine": "I can summon a lava titan familiar with this.", - "durability": null, - "name": "Lava titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12788" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3840", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Lava titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12789" - }, - { - "requirements": "{23,99}", - "ge_buy_limit": "5000", - "grand_exchange_price": "5800", - "examine": "I can summon a steel titan familiar with this.", - "durability": null, - "name": "Steel titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12790" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "5800", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Steel titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12791" - }, - { - "requirements": "{23,73}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3436", - "examine": "I can summon an obsidian golem familiar with this.", - "durability": null, - "name": "Obsidian golem pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12792" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3436", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Obsidian golem pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12793" - }, - { - "requirements": "{23,77}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3017", - "examine": "I can summon a talon beast familiar with this.", - "durability": null, - "name": "Talon beast pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12794" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3017", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Talon beast pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12795" - }, - { - "requirements": "{23,93}", - "ge_buy_limit": "5000", - "grand_exchange_price": "2991", - "examine": "I can summon an abyssal titan familiar with this.", - "durability": null, - "name": "Abyssal titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12796" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "2991", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Abyssal titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12797" - }, - { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "18300", - "examine": "I can summon a void torcher familiar with this.", - "durability": null, - "name": "Void torcher pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12798" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "18300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Void torcher pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12799" - }, - { - "requirements": "{23,29}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1513", - "examine": "I can summon a giant chinchompa familiar with this.", - "durability": null, - "name": "Giant chinchompa pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12800" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1513", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Giant chinchompa pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12801" - }, - { - "requirements": "{23,79}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3567", - "examine": "I can summon a fire titan familiar with this.", - "durability": null, - "name": "Fire titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12802" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3567", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Fire titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12803" - }, - { - "requirements": "{23,79}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3548", - "examine": "I can summon a moss titan familiar with this.", - "durability": null, - "name": "Moss titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12804" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3548", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Moss titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12805" - }, - { - "requirements": "{23,79}", - "ge_buy_limit": "5000", - "grand_exchange_price": "7184", - "examine": "I can summon an ice titan familiar with this.", - "durability": null, - "name": "Ice titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12806" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "7184", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ice titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12807" - }, - { - "requirements": "{22,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1116", - "examine": "I can summon a spirit Tz-Kih familiar with this.", - "durability": null, - "name": "Spirit tz-kih pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12808" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1116", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit tz-kih pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12809" - }, - { - "requirements": "{23,57}", - "ge_buy_limit": "5000", - "grand_exchange_price": "7919", - "examine": "I can summon a spirit graahk familiar with this.", - "durability": null, - "name": "Spirit graahk pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12810" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "7919", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit graahk pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12811" - }, - { - "requirements": "{23,57}", - "ge_buy_limit": "5000", - "grand_exchange_price": "13300", - "examine": "I can summon a spirit kyatt familiar with this.", - "durability": null, - "name": "Spirit kyatt pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12812" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "13300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Spirit kyatt pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12813" - }, - { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "18600", - "examine": "I can summon a void shifter familiar with this.", - "durability": null, - "name": "Void shifter pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12814" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "18600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Void shifter pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12815" - }, - { - "requirements": "{23,46}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1951", - "examine": "I can summon a pyrelord familiar with this.", - "durability": null, - "name": "Pyrelord pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12816" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1951", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Pyrelord pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12817" - }, - { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "10700", - "examine": "I can summon a void ravager familiar with this.", - "durability": null, - "name": "Void ravager pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12818" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "10700", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Void ravager pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12819" - }, - { - "requirements": "{23,70}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1382", - "examine": "I can summon a ravenous locust familiar with this.", - "durability": null, - "name": "Ravenous locust pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12820" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1382", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ravenous locust pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12821" - }, - { - "requirements": "{23,95}", - "ge_buy_limit": "5000", - "grand_exchange_price": "3470", - "examine": "I can summon an iron titan familiar with this.", - "durability": null, - "name": "Iron titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12822" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "3470", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Iron titan pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12823" - }, - { - "requirements": "{23,79}", - "ge_buy_limit": "5000", - "grand_exchange_price": "380", - "examine": "A scroll for an elemental titan familiar.", - "durability": null, - "name": "Titan's con. scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12824" - }, - { - "requirements": "{23,99}", - "ge_buy_limit": "5000", - "grand_exchange_price": "646", - "examine": "A scroll for a steel titan familiar.", - "durability": null, - "name": "Steel of legends scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12825" - }, - { - "requirements": "{23,73}", - "ge_buy_limit": "5000", - "grand_exchange_price": "271", - "examine": "A scroll for an obsidian golem familiar.", - "durability": null, - "name": "Volcanic str. scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12826" - }, - { - "requirements": "{23,93}", - "ge_buy_limit": "5000", - "grand_exchange_price": "310", - "examine": "A scroll for an abyssal titan familiar.", - "durability": null, - "name": "Essence shipment scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12827" - }, - { - "requirements": "{23,95}", - "ge_buy_limit": "5000", - "grand_exchange_price": "389", - "examine": "A scroll for an iron titan familiar.", - "durability": null, - "name": "Iron within scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12828" - }, - { - "requirements": "{23,46}", - "ge_buy_limit": "5000", - "grand_exchange_price": "146", - "examine": "A scroll for a pyrelord familiar.", - "durability": null, - "name": "Immense heat scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12829" - }, - { - "requirements": "{23,70}", - "ge_buy_limit": "5000", - "grand_exchange_price": "149", - "examine": "A scroll for a ravenous locust familiar.", - "durability": null, - "name": "Famine scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12830" - }, - { - "requirements": "{23,77}", - "ge_buy_limit": "5000", - "grand_exchange_price": "245", - "examine": "A scroll for a talon beast familiar.", - "durability": null, - "name": "Deadly claw scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12831" - }, - { "requirements": "{23,85}", - "ge_buy_limit": "5000", - "grand_exchange_price": "192", - "examine": "A scroll for a swamp titan familiar.", - "durability": null, - "name": "Swamp plague scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12832" + "tradeable": "true" }, { - "requirements": "{23,89}", - "ge_buy_limit": "5000", - "grand_exchange_price": "294", - "examine": "A scroll for a geyser titan familiar.", - "durability": null, - "name": "Boil scroll", - "tradeable": "true", + "id": "12777", + "name": "Swamp titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "12833" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,29}", - "ge_buy_limit": "5000", - "grand_exchange_price": "103", - "examine": "A scroll for a giant chinchompa familiar.", - "durability": null, - "name": "Explode scroll", - "tradeable": "true", + "id": "12778", + "name": "Spirit mosquito pouch", + "examine": "I can summon a spirit mosquito familiar with this.", "archery_ticket_price": "0", - "id": "12834" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{17,23}", + "tradeable": "true" }, { - "requirements": "{23,57}", - "ge_buy_limit": "5000", - "grand_exchange_price": "250", - "examine": "A scroll for a spirit graahk familiar.", - "durability": null, - "name": "Goad scroll", - "tradeable": "true", + "id": "12779", + "name": "Spirit mosquito pouch", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "12835" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,57}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1428", - "examine": "A scroll for a spirit kyatt familiar.", - "durability": null, - "name": "Ambush scroll", - "tradeable": "true", + "id": "12780", + "name": "Void spinner pouch", + "examine": "I can summon a void spinner familiar with this.", "archery_ticket_price": "0", - "id": "12836" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" }, { - "requirements": "{23,83}", - "ge_buy_limit": "5000", - "grand_exchange_price": "300", - "examine": "A scroll for a lava titan familiar.", - "durability": null, - "name": "Ebon thunder scroll", - "tradeable": "true", + "id": "12781", + "name": "Void spinner pouch", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "12837" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,17}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1", - "examine": "A scroll for a spirit mosquito familiar.", - "durability": null, - "name": "Pester scroll", - "tradeable": "true", + "id": "12782", + "name": "Forge regent pouch", + "examine": "I can summon a forge regent familiar with this.", "archery_ticket_price": "0", - "id": "12838" - }, - { - "requirements": "{23,22}", - "ge_buy_limit": "5000", - "grand_exchange_price": "78", - "examine": "A scroll for a spirit Tz-Kih familiar.", "durability": null, - "name": "Fireball assault scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12839" - }, - { - "requirements": "{23,57}", "ge_buy_limit": "5000", - "grand_exchange_price": "196", - "examine": "A scroll for a spirit larupia familiar.", - "durability": null, - "name": "Rending scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12840" - }, - { "requirements": "{23,76}", - "ge_buy_limit": "5000", - "grand_exchange_price": "173", - "examine": "A scroll for a forge regent familiar.", - "durability": null, - "name": "Inferno scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12841" + "tradeable": "true" }, { - "turn90cw_anim": "8963", - "stand_anim": "8964", - "walk_anim": "8961", - "durability": null, - "name": "Gnomecopter", - "run_anim": "8963", + "id": "12783", + "name": "Forge regent pouch", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "turn90ccw_anim": "8963", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12784", + "name": "Spirit larupia pouch", + "examine": "I can summon a spirit larupia familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12785", + "name": "Spirit larupia pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12786", + "name": "Geyser titan pouch", + "examine": "I can summon a geyser titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,89}", + "tradeable": "true" + }, + { + "id": "12787", + "name": "Geyser titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12788", + "name": "Lava titan pouch", + "examine": "I can summon a lava titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,83}", + "tradeable": "true" + }, + { + "id": "12789", + "name": "Lava titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12790", + "name": "Steel titan pouch", + "examine": "I can summon a steel titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,99}", + "tradeable": "true" + }, + { + "id": "12791", + "name": "Steel titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12792", + "name": "Obsidian golem pouch", + "examine": "I can summon an obsidian golem familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,73}", + "tradeable": "true" + }, + { + "id": "12793", + "name": "Obsidian golem pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12794", + "name": "Talon beast pouch", + "examine": "I can summon a talon beast familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,77}", + "tradeable": "true" + }, + { + "id": "12795", + "name": "Talon beast pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12796", + "name": "Abyssal titan pouch", + "examine": "I can summon an abyssal titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,93}", + "tradeable": "true" + }, + { + "id": "12797", + "name": "Abyssal titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12798", + "name": "Void torcher pouch", + "examine": "I can summon a void torcher familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" + }, + { + "id": "12799", + "name": "Void torcher pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12800", + "name": "Giant chinchompa pouch", + "examine": "I can summon a giant chinchompa familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,29}", + "tradeable": "true" + }, + { + "id": "12801", + "name": "Giant chinchompa pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12802", + "name": "Fire titan pouch", + "examine": "I can summon a fire titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,79}", + "tradeable": "true" + }, + { + "id": "12803", + "name": "Fire titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12804", + "name": "Moss titan pouch", + "examine": "I can summon a moss titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,79}", + "tradeable": "true" + }, + { + "id": "12805", + "name": "Moss titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12806", + "name": "Ice titan pouch", + "examine": "I can summon an ice titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,79}", + "tradeable": "true" + }, + { + "id": "12807", + "name": "Ice titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12808", + "name": "Spirit tz-kih pouch", + "examine": "I can summon a spirit Tz-Kih familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{22,23}", + "tradeable": "true" + }, + { + "id": "12809", + "name": "Spirit tz-kih pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12810", + "name": "Spirit graahk pouch", + "examine": "I can summon a spirit graahk familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12811", + "name": "Spirit graahk pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12812", + "name": "Spirit kyatt pouch", + "examine": "I can summon a spirit kyatt familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12813", + "name": "Spirit kyatt pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12814", + "name": "Void shifter pouch", + "examine": "I can summon a void shifter familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" + }, + { + "id": "12815", + "name": "Void shifter pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12816", + "name": "Pyrelord pouch", + "examine": "I can summon a pyrelord familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,46}", + "tradeable": "true" + }, + { + "id": "12817", + "name": "Pyrelord pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12818", + "name": "Void ravager pouch", + "examine": "I can summon a void ravager familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" + }, + { + "id": "12819", + "name": "Void ravager pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12820", + "name": "Ravenous locust pouch", + "examine": "I can summon a ravenous locust familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,70}", + "tradeable": "true" + }, + { + "id": "12821", + "name": "Ravenous locust pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12822", + "name": "Iron titan pouch", + "examine": "I can summon an iron titan familiar with this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,95}", + "tradeable": "true" + }, + { + "id": "12823", + "name": "Iron titan pouch", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "12824", + "name": "Titan's con. scroll", + "examine": "A scroll for an elemental titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,79}", + "tradeable": "true" + }, + { + "id": "12825", + "name": "Steel of legends scroll", + "examine": "A scroll for a steel titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,99}", + "tradeable": "true" + }, + { + "id": "12826", + "name": "Volcanic str. scroll", + "examine": "A scroll for an obsidian golem familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,73}", + "tradeable": "true" + }, + { + "id": "12827", + "name": "Essence shipment scroll", + "examine": "A scroll for an abyssal titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,93}", + "tradeable": "true" + }, + { + "id": "12828", + "name": "Iron within scroll", + "examine": "A scroll for an iron titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,95}", + "tradeable": "true" + }, + { + "id": "12829", + "name": "Immense heat scroll", + "examine": "A scroll for a pyrelord familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,46}", + "tradeable": "true" + }, + { + "id": "12830", + "name": "Famine scroll", + "examine": "A scroll for a ravenous locust familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,70}", + "tradeable": "true" + }, + { + "id": "12831", + "name": "Deadly claw scroll", + "examine": "A scroll for a talon beast familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,77}", + "tradeable": "true" + }, + { + "id": "12832", + "name": "Swamp plague scroll", + "examine": "A scroll for a swamp titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,85}", + "tradeable": "true" + }, + { + "id": "12833", + "name": "Boil scroll", + "examine": "A scroll for a geyser titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,89}", + "tradeable": "true" + }, + { + "id": "12834", + "name": "Explode scroll", + "examine": "A scroll for a giant chinchompa familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,29}", + "tradeable": "true" + }, + { + "id": "12835", + "name": "Goad scroll", + "examine": "A scroll for a spirit graahk familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12836", + "name": "Ambush scroll", + "examine": "A scroll for a spirit kyatt familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12837", + "name": "Ebon thunder scroll", + "examine": "A scroll for a lava titan familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,83}", + "tradeable": "true" + }, + { + "id": "12838", + "name": "Pester scroll", + "examine": "A scroll for a spirit mosquito familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,17}", + "tradeable": "true" + }, + { + "id": "12839", + "name": "Fireball assault scroll", + "examine": "A scroll for a spirit Tz-Kih familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,22}", + "tradeable": "true" + }, + { + "id": "12840", + "name": "Rending scroll", + "examine": "A scroll for a spirit larupia familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,57}", + "tradeable": "true" + }, + { + "id": "12841", + "name": "Inferno scroll", + "examine": "A scroll for a forge regent familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,76}", + "tradeable": "true" + }, + { "id": "12842", + "name": "Gnomecopter", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "1", + "run_anim": "8963", + "stand_anim": "8964", "stand_turn_anim": "8963", "turn180_anim": "8963", - "equipment_slot": "1" + "turn90ccw_anim": "8963", + "turn90cw_anim": "8963", + "walk_anim": "8961" }, { - "destroy_message": "This is for use with the gnomecopters, north of Lumbridge. Sasquine Huburns will give you another one.", - "examine": "It tells gnomecopters where you wish to go.", - "durability": null, + "id": "12843", "name": "Gnomecopter ticket", - "tradeable": "false", - "destroy": "true", + "examine": "It tells gnomecopters where you wish to go.", "archery_ticket_price": "0", - "id": "12843" + "destroy": "true", + "destroy_message": "This is for use with the gnomecopters, north of Lumbridge. Sasquine Huburns will give you another one.", + "durability": null, + "tradeable": "false" }, { - "shop_price": "100", - "turn90cw_anim": "8983", - "examine": "A kite with a dragon on it.", - "walk_anim": "8982", - "durability": null, - "turn90ccw_anim": "8984", - "attack_speed": "4", - "turn180_anim": "8985", - "render_anim": "593", - "equipment_slot": "3", - "stand_anim": "8981", - "name": "Toy kite", - "run_anim": "8986", - "archery_ticket_price": "0", "id": "12844", - "stand_turn_anim": "8987" + "name": "Toy kite", + "examine": "A kite with a dragon on it.", + "archery_ticket_price": "0", + "attack_speed": "4", + "durability": null, + "equipment_slot": "3", + "render_anim": "593", + "run_anim": "8986", + "shop_price": "100", + "stand_anim": "8981", + "stand_turn_anim": "8987", + "turn180_anim": "8985", + "turn90ccw_anim": "8984", + "turn90cw_anim": "8983", + "walk_anim": "8982" }, { - "turn90cw_anim": "9002", - "examine": "A stone used to gather energy.", - "walk_anim": "8997", - "durability": null, - "destroy": "true", - "weight": "30", - "turn90ccw_anim": "9003", - "turn180_anim": "9001", - "render_anim": "370", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "8998", - "name": "Stone of power", - "tradeable": "false", - "run_anim": "9004", - "archery_ticket_price": "0", "id": "12845", - "stand_turn_anim": "9000" + "name": "Stone of power", + "examine": "A stone used to gather energy.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "370", + "run_anim": "9004", + "stand_anim": "8998", + "stand_turn_anim": "9000", + "tradeable": "false", + "turn180_anim": "9001", + "turn90ccw_anim": "9003", + "turn90cw_anim": "9002", + "walk_anim": "8997", + "weight": "30" }, { - "turn90cw_anim": "9002", - "examine": "A stone used to gather energy.", - "walk_anim": "8997", - "durability": null, - "destroy": "true", - "weight": "30", - "turn90ccw_anim": "9003", - "turn180_anim": "9001", - "render_anim": "370", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "8998", - "name": "Stone of power", - "tradeable": "false", - "run_anim": "9004", - "archery_ticket_price": "0", "id": "12846", - "stand_turn_anim": "9000" + "name": "Stone of power", + "examine": "A stone used to gather energy.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "370", + "run_anim": "9004", + "stand_anim": "8998", + "stand_turn_anim": "9000", + "tradeable": "false", + "turn180_anim": "9001", + "turn90ccw_anim": "9003", + "turn90cw_anim": "9002", + "walk_anim": "8997", + "weight": "30" }, { - "turn90cw_anim": "9002", - "examine": "A stone used to gather energy.", - "walk_anim": "8997", - "durability": null, - "destroy": "true", - "weight": "30", - "turn90ccw_anim": "9003", - "turn180_anim": "9001", - "render_anim": "370", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "8998", - "name": "Stone of power", - "tradeable": "false", - "run_anim": "9004", - "archery_ticket_price": "0", "id": "12847", - "stand_turn_anim": "9000" + "name": "Stone of power", + "examine": "A stone used to gather energy.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "370", + "run_anim": "9004", + "stand_anim": "8998", + "stand_turn_anim": "9000", + "tradeable": "false", + "turn180_anim": "9001", + "turn90ccw_anim": "9003", + "turn90cw_anim": "9002", + "walk_anim": "8997", + "weight": "30" }, { - "turn90cw_anim": "9002", - "examine": "A stone used to gather energy.", - "walk_anim": "8997", - "durability": null, - "destroy": "true", - "weight": "30", - "turn90ccw_anim": "9003", - "turn180_anim": "9001", - "render_anim": "370", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "8998", - "name": "Stone of power", - "tradeable": "false", - "run_anim": "9004", - "archery_ticket_price": "0", "id": "12848", - "stand_turn_anim": "9000" - }, - { - "turn90cw_anim": "9002", - "examine": "A stone used to gather energy.", - "walk_anim": "8997", - "durability": null, - "destroy": "true", - "weight": "30", - "turn90ccw_anim": "9003", - "turn180_anim": "9001", - "render_anim": "370", - "equipment_slot": "3", - "destroy_message": "You can reclaim this item from the place you found it.", - "stand_anim": "8998", "name": "Stone of power", - "tradeable": "false", + "examine": "A stone used to gather energy.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "370", "run_anim": "9004", - "archery_ticket_price": "0", + "stand_anim": "8998", + "stand_turn_anim": "9000", + "tradeable": "false", + "turn180_anim": "9001", + "turn90ccw_anim": "9003", + "turn90cw_anim": "9002", + "walk_anim": "8997", + "weight": "30" + }, + { "id": "12849", - "stand_turn_anim": "9000" + "name": "Stone of power", + "examine": "A stone used to gather energy.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "3", + "render_anim": "370", + "run_anim": "9004", + "stand_anim": "8998", + "stand_turn_anim": "9000", + "tradeable": "false", + "turn180_anim": "9001", + "turn90ccw_anim": "9003", + "turn90cw_anim": "9002", + "walk_anim": "8997", + "weight": "30" }, { - "examine": "A generic elemental rune", - "durability": null, + "id": "12850", "name": "Elemental rune", + "examine": "A generic elemental rune", "archery_ticket_price": "0", - "id": "12850" + "durability": null }, { - "examine": "A generic non-elemental rune", - "durability": null, + "id": "12851", "name": "Catalytic rune", + "examine": "A generic non-elemental rune", "archery_ticket_price": "0", - "id": "12851" + "durability": null }, { - "examine": "A token awarded for taking part in the Fist of Guthix minigame.", - "durability": null, + "id": "12852", "name": "Fist of guthix token", + "examine": "A token awarded for taking part in the Fist of Guthix minigame.", "archery_ticket_price": "0", - "id": "12852" + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A box of bandages for healing.", - "durability": null, + "id": "12853", "name": "Bandages", - "tradeable": "true", + "examine": "A box of bandages for healing.", "archery_ticket_price": "0", - "id": "12853" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Teleports you to the centre of the arena.", - "durability": null, + "id": "12855", "name": "Tele-orb", + "examine": "Teleports you to the centre of the arena.", "archery_ticket_price": "0", - "id": "12855" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "75", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Irit gloves", - "archery_ticket_price": "0", "id": "12856", - "equipment_slot": "9" + "name": "Irit gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{15,50}", + "shop_price": "75", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "100", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Avantoe gloves", - "archery_ticket_price": "0", "id": "12857", - "equipment_slot": "9" + "name": "Avantoe gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{15,60}", + "shop_price": "100", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "200", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Kwuarm gloves", - "archery_ticket_price": "0", "id": "12858", - "equipment_slot": "9" + "name": "Kwuarm gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{15,70}", + "shop_price": "200", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "200", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Cadantine gloves", - "archery_ticket_price": "0", "id": "12859", - "equipment_slot": "9" + "name": "Cadantine gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{15,80}", + "shop_price": "200", + "tradeable": "false" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "200", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Swordfish gloves", - "weight": "0.2", - "archery_ticket_price": "0", "id": "12860", - "equipment_slot": "9" + "name": "Swordfish gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{10,65}", + "shop_price": "200", + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "200", - "examine": "A pair of mystical gloves", - "durability": null, - "name": "Shark gloves", - "weight": "1", - "archery_ticket_price": "0", "id": "12861", - "equipment_slot": "9" + "name": "Shark gloves", + "examine": "A pair of mystical gloves", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{10,90}", + "shop_price": "200", + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "200", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Dragon slayer gloves", - "archery_ticket_price": "0", "id": "12862", - "equipment_slot": "9" + "name": "Dragon slayer gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "9", + "requirements": "{18,70}", + "shop_price": "200", + "tradeable": "false" }, { - "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix activity.", - "shop_price": "75", - "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Air runecrafting gloves", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "12863", - "equipment_slot": "9" - }, - { + "name": "Air runecrafting gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy": "true", "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix activity.", - "shop_price": "75", - "examine": "A pair of mystical gloves.", "durability": null, - "name": "Water runecrafting gloves", + "equipment_slot": "9", + "requirements": "{20,10}", + "shop_price": "75", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12864", - "equipment_slot": "9" - }, - { - "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix minigame.", - "shop_price": "75", + "name": "Water runecrafting gloves", "examine": "A pair of mystical gloves.", - "durability": null, - "name": "Earth runecrafting gloves", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix activity.", + "durability": null, + "equipment_slot": "9", + "requirements": "{20,20}", + "shop_price": "75", + "tradeable": "false", + "weight": "1" + }, + { "id": "12865", - "equipment_slot": "9" + "name": "Earth runecrafting gloves", + "examine": "A pair of mystical gloves.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix minigame.", + "durability": null, + "equipment_slot": "9", + "requirements": "{20,30}", + "shop_price": "75", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "examine": "Its arcane power is waning.", - "durability": null, - "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "70529", - "name": "Battle hood 100", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12866", - "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0" + "name": "Battle hood 100", + "examine": "Its arcane power is waning.", + "absorb": "4,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "false" }, { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "examine": "Its arcane power is waning.", - "durability": null, - "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "70529", - "name": "Battle hood 80", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12867", - "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0" + "name": "Battle hood 80", + "examine": "Its arcane power is waning.", + "absorb": "4,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "false" }, { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "examine": "Its arcane power is waning.", - "durability": null, - "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "70529", - "name": "Battle hood 60", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12868", - "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0" + "name": "Battle hood 60", + "examine": "Its arcane power is waning.", + "absorb": "4,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "false" }, { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "examine": "Its arcane power is waning.", - "durability": null, - "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "70529", - "name": "Battle hood 40", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12869", - "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0" + "name": "Battle hood 40", + "examine": "Its arcane power is waning.", + "absorb": "4,3,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "false" }, { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "examine": "Its arcane power is waning.", - "durability": null, - "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "70529", - "name": "Battle hood 20", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12870", - "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,60}-{6,50}", - "shop_price": "250", - "ge_buy_limit": "10", + "name": "Battle hood 20", "examine": "Its arcane power is waning.", - "durability": null, "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "78900", - "name": "Battle hood 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,5,0,7,6,8,5,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "false" + }, + { "id": "12871", - "bonuses": "0,0,0,2,0,3,2,3,2,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "78900", - "durability": null, "name": "Battle hood 0", - "tradeable": "true", + "examine": "Its arcane power is waning.", + "absorb": "4,3,0", "archery_ticket_price": "0", - "id": "12872" + "bonuses": "0,0,0,2,0,3,2,3,2,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "remove_head": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "250", + "tradeable": "true" }, { - "requirements": "{1,50}-{6,60}", - "shop_price": "1500", - "examine": "A robe top infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "4", - "absorb": "8,4,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "537714", - "name": "Battle robe top 100", - "tradeable": "false", + "id": "12872", + "name": "Battle hood 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12873", - "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1500", + "name": "Battle robe top 100", "examine": "A robe top infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "4", "absorb": "8,4,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "537714", - "name": "Battle robe top 80", + "requirements": "{1,50}-{6,60}", + "shop_price": "1500", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12874", - "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1500", + "name": "Battle robe top 80", "examine": "A robe top infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "4", "absorb": "8,4,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "537714", - "name": "Battle robe top 60", + "requirements": "{1,50}-{6,60}", + "shop_price": "1500", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12875", - "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1500", + "name": "Battle robe top 60", "examine": "A robe top infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "4", "absorb": "8,4,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "537714", - "name": "Battle robe top 40", + "requirements": "{1,50}-{6,60}", + "shop_price": "1500", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12876", - "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1500", + "name": "Battle robe top 40", "examine": "A robe top infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "4", "absorb": "8,4,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "537714", - "name": "Battle robe top 20", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12877", - "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0" - }, - { "requirements": "{1,50}-{6,60}", "shop_price": "1500", - "ge_buy_limit": "10", - "examine": "Its arcane power is waning.", - "durability": null, - "weight": "4", + "tradeable": "false", + "weight": "4" + }, + { + "id": "12877", + "name": "Battle robe top 20", + "examine": "A robe top infused with arcane power, especially created for combat spells.", "absorb": "8,4,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,26,0,17,15,20,26,0,30,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "431400", - "name": "Battle robe top 0", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{1,50}-{6,60}", + "shop_price": "1500", + "tradeable": "false", + "weight": "4" + }, + { "id": "12878", - "bonuses": "0,0,0,7,0,4,4,5,7,0,8,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "431400", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, "name": "Battle robe top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12879" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "examine": "A robe bottom infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "364878", - "name": "Battle robe bottom 100", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12880", - "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "examine": "A robe bottom infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "364878", - "name": "Battle robe bottom 80", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12881", - "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "examine": "A robe bottom infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "364878", - "name": "Battle robe bottom 60", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12882", - "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "examine": "A robe bottom infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "364878", - "name": "Battle robe bottom 40", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12883", - "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "examine": "A robe bottom infused with arcane power, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "364878", - "name": "Battle robe bottom 20", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12884", - "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0" - }, - { - "requirements": "{1,50}-{6,60}", - "shop_price": "1000", - "ge_buy_limit": "10", "examine": "Its arcane power is waning.", - "durability": null, - "weight": "3", - "absorb": "5,2,0", - "equipment_slot": "7", - "grand_exchange_price": "538000", - "name": "Battle robe bottom 0", - "tradeable": "true", + "absorb": "8,4,0", "archery_ticket_price": "0", - "id": "12885", - "bonuses": "0,0,0,6,0,4,3,4,6,0,7,0,0,0,0" + "bonuses": "0,0,0,7,0,4,4,5,7,0,8,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,50}-{6,60}", + "shop_price": "1500", + "tradeable": "true", + "weight": "4" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "538000", + "id": "12879", + "name": "Battle robe top 0", "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "12880", + "name": "Battle robe bottom 100", + "examine": "A robe bottom infused with arcane power, especially created for combat spells.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12881", + "name": "Battle robe bottom 80", + "examine": "A robe bottom infused with arcane power, especially created for combat spells.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12882", + "name": "Battle robe bottom 60", + "examine": "A robe bottom infused with arcane power, especially created for combat spells.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12883", + "name": "Battle robe bottom 40", + "examine": "A robe bottom infused with arcane power, especially created for combat spells.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12884", + "name": "Battle robe bottom 20", + "examine": "A robe bottom infused with arcane power, especially created for combat spells.", + "absorb": "5,2,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,20,0,14,11,16,20,0,23,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12885", "name": "Battle robe bottom 0", - "tradeable": "true", + "examine": "Its arcane power is waning.", + "absorb": "5,2,0", "archery_ticket_price": "0", - "id": "12886" + "bonuses": "0,0,0,6,0,4,3,4,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,50}-{6,60}", + "shop_price": "1000", + "tradeable": "true", + "weight": "3" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 100", - "tradeable": "false", + "id": "12886", + "name": "Battle robe bottom 0", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12887", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "name": "Druidic mage hood 100", + "examine": "A worn-out hood, formerly worn by the followers of Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 80", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12888", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "name": "Druidic mage hood 80", + "examine": "A worn-out hood, formerly worn by the followers of Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 60", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12889", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "name": "Druidic mage hood 60", + "examine": "A worn-out hood, formerly worn by the followers of Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 40", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12890", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" + "name": "Druidic mage hood 40", + "examine": "A worn-out hood, formerly worn by the followers of Guthix.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 20", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12891", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "ge_buy_limit": "10", + "name": "Druidic mage hood 20", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", - "durability": null, - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "8504", - "name": "Druidic mage hood 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" + }, + { "id": "12892", - "bonuses": "0,0,0,1,0,0,0,0,1,0,1,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "8504", - "durability": null, "name": "Druidic mage hood 0", - "tradeable": "true", + "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "archery_ticket_price": "0", - "id": "12893" + "bonuses": "0,0,0,1,0,0,0,0,1,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "true", + "weight": "7" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", - "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 100", - "tradeable": "false", + "id": "12893", + "name": "Druidic mage hood 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12894", - "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", + "name": "Druidic mage top 100", "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 80", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12895", - "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", + "name": "Druidic mage top 80", "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 60", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12896", - "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", + "name": "Druidic mage top 60", "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 40", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12897", - "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", + "name": "Druidic mage top 40", "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 20", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12898", - "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "300", - "ge_buy_limit": "10", + "name": "Druidic mage top 20", "examine": "A magical robe top worn by followers of Guthix.", - "durability": null, - "weight": "4", "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "76300", - "name": "Druidic mage top 0", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "false", + "weight": "4" + }, + { "id": "12899", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "76300", - "durability": null, "name": "Druidic mage top 0", - "tradeable": "true", + "examine": "A magical robe top worn by followers of Guthix.", + "absorb": "2,1,0", "archery_ticket_price": "0", - "id": "12900" + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "54532", - "name": "Druidic mage bottom 100", - "tradeable": "false", + "id": "12900", + "name": "Druidic mage top 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12901", - "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" + "name": "Druidic mage bottom 100", + "examine": "A magical robe bottom worn by a person who follows Guthix.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "false", + "weight": "3" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "54532", - "name": "Druidic mage bottom 80", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12902", - "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" + "name": "Druidic mage bottom 80", + "examine": "A magical robe bottom worn by a person who follows Guthix.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "false", + "weight": "3" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "54532", - "name": "Druidic mage bottom 60", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12903", - "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" + "name": "Druidic mage bottom 60", + "examine": "A magical robe bottom worn by a person who follows Guthix.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "false", + "weight": "3" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "54532", - "name": "Druidic mage bottom 40", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12904", - "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" + "name": "Druidic mage bottom 40", + "examine": "A magical robe bottom worn by a person who follows Guthix.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "false", + "weight": "3" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "54532", - "name": "Druidic mage bottom 20", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12905", - "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "200", - "ge_buy_limit": "10", + "name": "Druidic mage bottom 20", "examine": "A magical robe bottom worn by a person who follows Guthix.", - "durability": null, - "weight": "3", "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "60800", - "name": "Druidic mage bottom 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "false", + "weight": "3" + }, + { "id": "12906", - "bonuses": "0,0,0,2,0,0,0,0,2,0,2,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "60800", - "durability": null, "name": "Druidic mage bottom 0", - "tradeable": "true", + "examine": "A magical robe bottom worn by a person who follows Guthix.", + "absorb": "1,0,0", "archery_ticket_price": "0", - "id": "12907" + "bonuses": "0,0,0,2,0,0,0,0,2,0,2,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,10}-{6,20}", + "shop_price": "200", + "tradeable": "true", + "weight": "3" }, { - "requirements": "{1,35}", - "shop_price": "50", - "examine": "A spiked square shield.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 100", - "tradeable": "false", + "id": "12907", + "name": "Druidic mage bottom 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12908", - "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0" - }, - { + "name": "Adamant spikeshield 100", + "examine": "A spiked square shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "50", - "examine": "A spiked square shield.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "12909", - "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0" - }, - { + "name": "Adamant spikeshield 80", + "examine": "A spiked square shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "50", - "examine": "A spiked square shield.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "12910", - "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0" - }, - { + "name": "Adamant spikeshield 60", + "examine": "A spiked square shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "50", - "examine": "A spiked square shield.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "5" + }, + { "id": "12911", - "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0" - }, - { - "requirements": "{1,35}", - "shop_price": "50", + "name": "Adamant spikeshield 40", "examine": "A spiked square shield.", - "durability": null, - "weight": "5", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 20", - "tradeable": "false", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,35}", + "shop_price": "50", + "tradeable": "false", + "weight": "5" + }, + { "id": "12912", - "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0" - }, - { + "name": "Adamant spikeshield 20", + "examine": "A spiked square shield.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,25,27,23,0,25,26,1,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "50", - "ge_buy_limit": "100", - "examine": "Not so spiky anymore.", - "durability": null, - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2718", - "name": "Adamant spikeshield 0", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "5" + }, + { "id": "12913", - "bonuses": "0,0,0,-6,-2,11,12,10,0,11,4,1,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "Not so spiky anymore!", - "grand_exchange_price": "2718", - "durability": null, "name": "Adamant spikeshield 0", - "tradeable": "true", + "examine": "Not so spiky anymore.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "12914" + "bonuses": "0,0,0,-6,-2,11,12,10,0,11,4,1,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "100", + "requirements": "{1,35}", + "shop_price": "50", + "tradeable": "true", + "weight": "5" }, { - "requirements": "{1,35}", - "shop_price": "100", - "examine": "A large, spiked metal shield", - "durability": null, - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 100", - "tradeable": "false", + "id": "12914", + "name": "Adamant spikeshield 0", + "examine": "Not so spiky anymore!", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12915", - "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0" - }, - { + "name": "Adamant berserker shield 100", + "examine": "A large, spiked metal shield", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "100", - "examine": "A large, spiked metal shield", - "durability": null, - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "7" + }, + { "id": "12916", - "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0" - }, - { + "name": "Adamant berserker shield 80", + "examine": "A large, spiked metal shield", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "100", - "examine": "A large, spiked metal shield", - "durability": null, - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "7" + }, + { "id": "12917", - "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0" - }, - { + "name": "Adamant berserker shield 60", + "examine": "A large, spiked metal shield", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "100", - "examine": "A large, spiked metal shield", - "durability": null, - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "7" + }, + { "id": "12918", - "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0" - }, - { - "requirements": "{1,35}", - "shop_price": "100", + "name": "Adamant berserker shield 40", "examine": "A large, spiked metal shield", - "durability": null, - "weight": "7", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 20", - "tradeable": "false", "archery_ticket_price": "0", - "id": "12919", - "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0" - }, - { + "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,35}", "shop_price": "100", - "ge_buy_limit": "10", - "examine": "Not so spiky anymore!", - "durability": null, - "weight": "7", + "tradeable": "false", + "weight": "7" + }, + { + "id": "12919", + "name": "Adamant berserker shield 20", + "examine": "A large, spiked metal shield", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "23700", - "name": "Adamant berserker shield 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,28,32,30,-1,30,31,2,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,35}", + "shop_price": "100", + "tradeable": "false", + "weight": "7" + }, + { "id": "12920", - "bonuses": "0,0,0,-8,-2,12,14,13,-1,8,4,1,0,0,0" - }, - { - "ge_buy_limit": "10", - "examine": "Not so spiky anymore!", - "grand_exchange_price": "23700", - "durability": null, "name": "Adamant berserker shield 0", - "tradeable": "true", + "examine": "Not so spiky anymore!", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "12921" + "bonuses": "0,0,0,-8,-2,12,14,13,-1,8,4,1,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "requirements": "{1,35}", + "shop_price": "100", + "tradeable": "true", + "weight": "7" }, { - "requirements": "{1,45}", - "shop_price": "200", - "examine": "A spiked, medium square shield.", - "durability": null, - "weight": "4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "57548", - "name": "Rune spikeshield 100", - "tradeable": "false", + "id": "12921", + "name": "Adamant berserker shield 0", + "examine": "Not so spiky anymore!", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12922", + "name": "Rune spikeshield 100", + "examine": "A spiked, medium square shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0", - "point_price": "35" - }, - { + "durability": null, + "equipment_slot": "5", + "point_price": "35", "requirements": "{1,45}", "shop_price": "200", - "examine": "A spiked, medium square shield.", - "durability": null, - "weight": "4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "57548", - "name": "Rune spikeshield 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12923", - "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0" - }, - { + "name": "Rune spikeshield 80", + "examine": "A spiked, medium square shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "200", - "examine": "A spiked, medium square shield.", - "durability": null, - "weight": "4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "57548", - "name": "Rune spikeshield 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12924", - "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0" - }, - { + "name": "Rune spikeshield 60", + "examine": "A spiked, medium square shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "200", - "examine": "A spiked, medium square shield.", - "durability": null, - "weight": "4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "57548", - "name": "Rune spikeshield 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "id": "12925", - "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0" - }, - { - "requirements": "{1,45}", - "shop_price": "200", + "name": "Rune spikeshield 40", "examine": "A spiked, medium square shield.", - "durability": null, - "weight": "4", "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "57548", - "name": "Rune spikeshield 20", - "tradeable": "false", "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,45}", + "shop_price": "200", + "tradeable": "false", + "weight": "4" + }, + { "id": "12926", - "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0" - }, - { + "name": "Rune spikeshield 20", + "examine": "A spiked, medium square shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,39,41,37,0,39,36,3,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "200", - "ge_buy_limit": "10", - "examine": "Not so spiky anymore.", - "durability": null, - "weight": "4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "50500", - "name": "Rune spikeshield 0", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "4" + }, + { "id": "12927", - "bonuses": "0,0,0,-6,-2,14,15,13,0,14,8,2,0,0,0" - }, - { - "ge_buy_limit": "10", - "examine": "Not so spiky anymore.", - "grand_exchange_price": "50500", - "durability": null, "name": "Rune spikeshield 0", - "tradeable": "true", + "examine": "Not so spiky anymore.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "12928" + "bonuses": "0,0,0,-6,-2,14,15,13,0,14,8,2,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "requirements": "{1,45}", + "shop_price": "200", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,45}", - "shop_price": "300", - "examine": "A Large, spiked metal shield.", - "durability": null, - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 100", - "tradeable": "false", + "id": "12928", + "name": "Rune spikeshield 0", + "examine": "Not so spiky anymore.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12929", + "name": "Rune berserker shield 100", + "examine": "A Large, spiked metal shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0", - "point_price": "35" - }, - { + "durability": null, + "equipment_slot": "5", + "point_price": "35", "requirements": "{1,45}", "shop_price": "300", - "examine": "A Large, spiked metal shield.", - "durability": null, - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "6" + }, + { "id": "12930", - "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0" - }, - { + "name": "Rune berserker shield 80", + "examine": "A Large, spiked metal shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "300", - "examine": "A Large, spiked metal shield.", - "durability": null, - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "6" + }, + { "id": "12931", - "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0" - }, - { + "name": "Rune berserker shield 60", + "examine": "A Large, spiked metal shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "300", - "examine": "A Large, spiked metal shield.", - "durability": null, - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "6" + }, + { "id": "12932", - "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0" - }, - { - "requirements": "{1,45}", - "shop_price": "300", + "name": "Rune berserker shield 40", "examine": "A Large, spiked metal shield.", - "durability": null, - "weight": "6", "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 20", - "tradeable": "false", "archery_ticket_price": "0", - "id": "12933", - "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0" - }, - { + "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0", + "durability": null, + "equipment_slot": "5", "requirements": "{1,45}", "shop_price": "300", - "ge_buy_limit": "10", - "examine": "Not so spiky anymore.", - "durability": null, - "weight": "6", + "tradeable": "false", + "weight": "6" + }, + { + "id": "12933", + "name": "Rune berserker shield 20", + "examine": "A Large, spiked metal shield.", "absorb": "3,0,7", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,45,49,47,-1,47,41,4,0,0,0", + "durability": null, "equipment_slot": "5", - "grand_exchange_price": "135300", - "name": "Rune berserker shield 0", - "tradeable": "true", - "archery_ticket_price": "0", + "requirements": "{1,45}", + "shop_price": "300", + "tradeable": "false", + "weight": "6" + }, + { "id": "12934", - "bonuses": "0,0,0,-8,-2,16,18,17,-1,17,9,2,0,0,0" - }, - { - "ge_buy_limit": "10", - "examine": "Not so spiky anymore.", - "grand_exchange_price": "135300", - "durability": null, "name": "Rune berserker shield 0", - "tradeable": "true", + "examine": "Not so spiky anymore.", + "absorb": "3,0,7", "archery_ticket_price": "0", - "id": "12935" + "bonuses": "0,0,0,-8,-2,16,18,17,-1,17,9,2,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "10", + "requirements": "{1,45}", + "shop_price": "300", + "tradeable": "true", + "weight": "6" }, { - "remove_head": "true", - "requirements": "{1,40}-{4,40}", - "shop_price": "150", - "examine": "A coif made of green dragonhide.", - "durability": null, - "weight": "0.9", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "88084", - "name": "Green d'hide coif 100", - "tradeable": "false", + "id": "12935", + "name": "Rune berserker shield 0", + "examine": "Not so spiky anymore.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12936", - "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0" - }, - { + "name": "Green d'hide coif 100", + "examine": "A coif made of green dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,40}", "shop_price": "150", - "examine": "A coif made of green dragonhide.", - "durability": null, - "weight": "0.9", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "88084", - "name": "Green d'hide coif 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12937", - "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0" - }, - { + "name": "Green d'hide coif 80", + "examine": "A coif made of green dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,40}", "shop_price": "150", - "examine": "A coif made of green dragonhide.", - "durability": null, - "weight": "0.9", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "88084", - "name": "Green d'hide coif 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12938", - "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0" - }, - { + "name": "Green d'hide coif 60", + "examine": "A coif made of green dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,40}", "shop_price": "150", - "examine": "A coif made of green dragonhide.", - "durability": null, - "weight": "0.9", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "88084", - "name": "Green d'hide coif 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "12939", - "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{4,40}", - "shop_price": "150", + "name": "Green d'hide coif 40", "examine": "A coif made of green dragonhide.", - "durability": null, - "weight": "0.9", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "88084", - "name": "Green d'hide coif 20", - "tradeable": "false", "archery_ticket_price": "0", - "id": "12940", - "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0" - }, - { + "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,40}", "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A worn out coif made of green dragonhide.", - "durability": null, - "weight": "0.9", + "tradeable": "false", + "weight": "0.9" + }, + { + "id": "12940", + "name": "Green d'hide coif 20", + "examine": "A coif made of green dragonhide.", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "92700", - "name": "Green d'hide coif 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,4,4,6,8,4,5,4,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}-{4,40}", + "shop_price": "150", + "tradeable": "false", + "weight": "0.9" + }, + { "id": "12941", - "bonuses": "0,0,0,-1,1,1,2,2,1,2,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "A worn out coif made of green dragonhide.", - "grand_exchange_price": "92700", - "durability": null, "name": "Green d'hide coif 0", - "tradeable": "true", + "examine": "A worn out coif made of green dragonhide.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "12942" + "bonuses": "0,0,0,-1,1,1,2,2,1,2,1,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "requirements": "{1,40}-{4,40}", + "shop_price": "150", + "tradeable": "true", + "weight": "0.9" }, { - "remove_head": "true", - "requirements": "{1,40}-{4,50}", - "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "186985", - "name": "Blue d'hide coif 100", - "tradeable": "false", + "id": "12942", + "name": "Green d'hide coif 0", + "examine": "A worn out coif made of green dragonhide.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12943", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { + "name": "Blue d'hide coif 100", + "examine": "A coif made of blue dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,50}", "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "186985", - "name": "Blue d'hide coif 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12944", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { + "name": "Blue d'hide coif 80", + "examine": "A coif made of blue dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,50}", "shop_price": "200", - "examine": "Fully charged: A coif made of blue dragonhide. Fully degraded: A worn out coif made of blue dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "186985", - "name": "Blue d'hide coif 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12945", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { + "name": "Blue d'hide coif 60", + "examine": "Fully charged: A coif made of blue dragonhide. Fully degraded: A worn out coif made of blue dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,50}", "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "186985", - "name": "Blue d'hide coif 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12946", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{4,50}", - "shop_price": "200", + "name": "Blue d'hide coif 40", "examine": "A coif made of blue dragonhide.", - "durability": null, - "weight": "1", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "186985", - "name": "Blue d'hide coif 20", - "tradeable": "false", "archery_ticket_price": "0", - "id": "12947", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { + "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,50}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A worn out coif made of blue dragonhide.", - "durability": null, - "weight": "1", + "tradeable": "false", + "weight": "1" + }, + { + "id": "12947", + "name": "Blue d'hide coif 20", + "examine": "A coif made of blue dragonhide.", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "131900", - "name": "Blue d'hide coif 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}-{4,50}", + "shop_price": "200", + "tradeable": "false", + "weight": "1" + }, + { "id": "12948", - "bonuses": "0,0,0,-1,1,1,2,2,1,2,2,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "A worn out coif made of blue dragonhide.", - "grand_exchange_price": "131900", - "durability": null, "name": "Blue d'hide coif 0", - "tradeable": "true", + "examine": "A worn out coif made of blue dragonhide.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "12949" + "bonuses": "0,0,0,-1,1,1,2,2,1,2,2,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "requirements": "{1,40}-{4,50}", + "shop_price": "200", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,40}-{4,60}", - "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "230059", - "name": "Red d'hide coif 100", - "tradeable": "false", + "id": "12949", + "name": "Blue d'hide coif 0", + "examine": "A worn out coif made of blue dragonhide.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12950", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { + "name": "Red d'hide coif 100", + "examine": "A coif made of red dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,60}", "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "230059", - "name": "Red d'hide coif 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12951", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { + "name": "Red d'hide coif 80", + "examine": "A coif made of red dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,60}", "shop_price": "300", - "examine": "Fully charged: A coif made of red dragonhide.Fully degraded: A worn out coif made of red dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "230059", - "name": "Red d'hide coif 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12952", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { + "name": "Red d'hide coif 60", + "examine": "Fully charged: A coif made of red dragonhide.Fully degraded: A worn out coif made of red dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,60}", "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "230059", - "name": "Red d'hide coif 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12953", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,40}-{4,60}", - "shop_price": "300", + "name": "Red d'hide coif 40", "examine": "A coif made of red dragonhide.", - "durability": null, - "weight": "1", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "230059", - "name": "Red d'hide coif 20", - "tradeable": "false", "archery_ticket_price": "0", - "id": "12954", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { + "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,60}", "shop_price": "300", - "ge_buy_limit": "100", - "examine": "A worn out coif made of red dragonhide.", - "durability": null, - "weight": "1", + "tradeable": "false", + "weight": "1" + }, + { + "id": "12954", + "name": "Red d'hide coif 20", + "examine": "A coif made of red dragonhide.", "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "156600", - "name": "Red d'hide coif 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}-{4,60}", + "shop_price": "300", + "tradeable": "false", + "weight": "1" + }, + { "id": "12955", - "bonuses": "0,0,0,-1,2,1,2,3,1,2,2,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "A worn out coif made of red dragonhide.", - "grand_exchange_price": "156600", - "durability": null, "name": "Red d'hide coif 0", - "tradeable": "true", + "examine": "A worn out coif made of red dragonhide.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "12956" + "bonuses": "0,0,0,-1,2,1,2,3,1,2,2,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "requirements": "{1,40}-{4,60}", + "shop_price": "300", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,40}-{4,70}", - "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "379555", - "name": "Black d'hide coif 100", - "tradeable": "false", + "id": "12956", + "name": "Red d'hide coif 0", + "examine": "A worn out coif made of red dragonhide.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12957", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { + "name": "Black d'hide coif 100", + "examine": "A coif made of black dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,70}", "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "379555", - "name": "Black d'hide coif 80", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12958", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { + "name": "Black d'hide coif 80", + "examine": "A coif made of black dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,70}", "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "379555", - "name": "Black d'hide coif 60", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12959", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { + "name": "Black d'hide coif 60", + "examine": "A coif made of black dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,70}", "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "379555", - "name": "Black d'hide coif 40", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12960", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { + "name": "Black d'hide coif 40", + "examine": "A coif made of black dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,70}", "shop_price": "500", - "examine": "Fully charged: A coif made of black dragonhide. Fully degraded: A worn out coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "379555", - "name": "Black d'hide coif 20", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "12961", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { + "name": "Black d'hide coif 20", + "examine": "Fully charged: A coif made of black dragonhide. Fully degraded: A worn out coif made of black dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,40}-{4,70}", "shop_price": "500", - "ge_buy_limit": "100", - "examine": "A coif made of black dragonhide.", - "durability": null, - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "229000", - "name": "Black d'hide coif 0", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "1" + }, + { "id": "12962", - "bonuses": "0,0,0,-1,3,1,3,3,1,2,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "A coif made of black dragonhide.", - "grand_exchange_price": "229000", - "durability": null, "name": "Black d'hide coif 0", - "tradeable": "true", + "examine": "A coif made of black dragonhide.", + "absorb": "0,3,1", "archery_ticket_price": "0", - "id": "12963" + "bonuses": "0,0,0,-1,3,1,3,3,1,2,3,0,0,0,0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "ge_buy_limit": "100", + "remove_head": "true", + "requirements": "{1,40}-{4,70}", + "shop_price": "500", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "5661", - "name": "Combat hood 100", - "tradeable": "false", + "id": "12963", + "name": "Black d'hide coif 0", + "examine": "A coif made of black dragonhide.", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12964", - "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0" + "name": "Combat hood 100", + "examine": "A magical hood, especially created for combat spells.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "false", + "weight": "0.8" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "5661", - "name": "Combat hood 80", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12965", - "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0" + "name": "Combat hood 80", + "examine": "A magical hood, especially created for combat spells.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "false", + "weight": "0.8" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "5661", - "name": "Combat hood 60", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12966", - "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0" + "name": "Combat hood 60", + "examine": "A magical hood, especially created for combat spells.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "false", + "weight": "0.8" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "5661", - "name": "Combat hood 40", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12967", - "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0" + "name": "Combat hood 40", + "examine": "A magical hood, especially created for combat spells.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "false", + "weight": "0.8" }, { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "5661", - "name": "Combat hood 20", - "tradeable": "false", - "archery_ticket_price": "0", "id": "12968", - "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,20}-{6,10}", - "shop_price": "50", - "ge_buy_limit": "10", + "name": "Combat hood 20", "examine": "A magical hood, especially created for combat spells.", - "durability": null, - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "6511", - "name": "Combat hood 0", - "tradeable": "true", "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,1,1,1,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "false", + "weight": "0.8" + }, + { "id": "12969", - "bonuses": "0,0,0,1,0,0,0,0,1,0,2,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "6511", - "durability": null, "name": "Combat hood 0", - "tradeable": "true", + "examine": "A magical hood, especially created for combat spells.", "archery_ticket_price": "0", - "id": "12970" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", + "bonuses": "0,0,0,1,0,0,0,0,1,0,2,0,0,0,0", "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 100", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12971", - "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 80", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12972", - "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 60", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12973", - "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 40", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12974", - "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 20", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12975", - "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "ge_buy_limit": "10", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "61500", - "name": "Combat robe top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12976", - "bonuses": "0,0,0,3,0,0,0,0,3,0,4,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "61500", - "durability": null, - "name": "Combat robe top 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12977" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 100", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12978", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 80", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12979", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 60", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12980", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 40", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12981", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 20", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12982", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "ge_buy_limit": "10", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "47800", - "name": "Combat robe bottom 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12983", - "bonuses": "0,0,0,2,0,0,0,0,2,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "47800", - "durability": null, - "name": "Combat robe bottom 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12984" - }, - { - "shop_price": "15", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "3694", - "durability": null, - "name": "Bronze gauntlets", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12985", - "bonuses": "0,0,0,-1,-1,2,3,3,-1,2,2,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "3275", - "durability": null, - "name": "Worn-out bronze gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12986", - "bonuses": "0,0,0,-1,-1,0,1,1,-1,0,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3275", - "durability": null, - "name": "Worn-out bronze gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12987" - }, - { - "shop_price": "30", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "17339", - "durability": null, - "name": "Iron gauntlets", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12988", - "bonuses": "0,0,0,-1,-1,3,4,4,-1,3,3,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "30", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "16000", - "durability": null, - "name": "Worn-out iron gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12989", - "bonuses": "0,0,0,-1,-1,0,1,1,-1,0,1,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16000", - "durability": null, - "name": "Worn-out iron gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12990" - }, - { - "requirements": "{1,5}", - "shop_price": "50", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "29844", - "durability": null, - "name": "Steel gauntlets", - "tradeable": "false", - "weight": "10", - "archery_ticket_price": "0", - "id": "12991", - "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,0,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,5}", - "shop_price": "50", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "durability": null, - "weight": "10", - "equipment_slot": "9", - "grand_exchange_price": "27000", - "name": "Worn-out steel gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12992", - "bonuses": "0,0,0,-1,-1,1,1,1,-1,1,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "27000", - "durability": null, - "name": "Worn-out steel gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12993" - }, - { - "requirements": "{1,10}", - "shop_price": "75", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "50808", - "durability": null, - "name": "Black gauntlets", - "tradeable": "false", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "12994", - "bonuses": "0,0,0,-1,-1,5,6,6,-1,5,5,0,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,10}", - "shop_price": "75", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "40100", - "name": "Worn-out black gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12995", - "bonuses": "0,0,0,-1,-1,1,2,2,-1,1,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "40100", - "durability": null, - "name": "Worn-out black gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12996" - }, - { - "requirements": "{1,20}", - "shop_price": "100", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "60064", - "durability": null, - "name": "Mithril gauntlets", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12997", - "bonuses": "0,0,0,-1,-1,6,7,7,-1,6,6,0,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,20}", - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "54200", - "durability": null, - "name": "Worn-out mithril gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12998", - "bonuses": "0,0,0,-1,-1,2,2,2,-1,2,2,0,0,0,0", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "54200", - "durability": null, - "name": "Worn-out mithril gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12999" - }, - { - "requirements": "{1,30}", - "shop_price": "150", - "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", - "grand_exchange_price": "74526", - "durability": null, - "name": "Adamant gauntlets", - "tradeable": "false", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "13000", - "bonuses": "0,0,0,-1,-1,8,9,9,-1,8,8,1,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,30}", - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "71500", - "name": "Worn-out adamant gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13001", - "bonuses": "0,0,0,-1,-1,2,3,3,-1,2,2,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "71500", - "durability": null, - "name": "Worn-out adamant gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13002" - }, - { - "requirements": "{1,40}", - "shop_price": "200", - "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", - "grand_exchange_price": "95700", - "durability": null, - "name": "Rune gauntlets", - "tradeable": "false", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "13003", - "bonuses": "0,0,0,-1,-1,10,11,11,-1,10,10,2,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,40}", - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "99900", - "name": "Worn-out rune gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13004", - "bonuses": "0,0,0,-1,-1,3,4,4,-1,3,3,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "99900", - "durability": null, - "name": "Worn-out rune gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13005" - }, - { - "requirements": "{1,60}", - "shop_price": "300", - "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", - "grand_exchange_price": "214700", - "durability": null, - "name": "Dragon gauntlets", - "tradeable": "false", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "13006", - "bonuses": "0,0,0,-1,-1,14,15,15,-1,14,14,3,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,60}", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", - "durability": null, - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "166200", - "name": "Worn-out dragon gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13007", - "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,1,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "166200", - "durability": null, - "name": "Worn-out dragon gauntlets", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13008" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13010" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13011" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13012" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13013" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13014" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13015" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13016" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13017" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13018" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13019" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13020" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13021" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13022" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13023" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13024" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13025" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13026" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13027" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13028" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13029" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13030" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13031" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13032" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13033" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13034" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "5", - "examine": "I need to solve this.", - "durability": null, - "name": "Puzzle box", - "archery_ticket_price": "0", - "id": "13035" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13036" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13037" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13038" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13039" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13040" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13041" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13042" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13043" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13044" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13045" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13046" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13047" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13048" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13049" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13050" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13051" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13052" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13053" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13054" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13055" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13056" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13057" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13058" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13059" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13060" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13061" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13062" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13063" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13064" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13065" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13066" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13067" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13068" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13069" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13070" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13071" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13072" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13074" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13075" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13076" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "weight": "5", - "archery_ticket_price": "0", - "id": "13077" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13078" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13079" - }, - { - "shop_price": "5000", - "examine": "A set of instructions to be followed.", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13080" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A black crossbow.", - "walk_anim": "4226", - "durability": null, - "weight": "5", - "turn90ccw_anim": "822", - "attack_speed": "6", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "equipment_slot": "3", - "grand_exchange_price": "261", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "tradeable": "true", - "name": "Black crossbow", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "13081", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,60,0,0,0,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "261", - "durability": null, - "name": "Black crossbow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13082" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "grand_exchange_price": "55", - "durability": null, - "name": "Black bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13083", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", - "equipment_slot": "13" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "grand_exchange_price": "141", - "durability": null, - "name": "Black bolts(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13084", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", - "equipment_slot": "13" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "grand_exchange_price": "233", - "durability": null, - "name": "Black bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13085", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", - "equipment_slot": "13" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "grand_exchange_price": "1690", - "durability": null, - "name": "Black bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13086", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", - "equipment_slot": "13" - }, - { - "requirements": "{0,8}", - "ge_buy_limit": "2", - "examine": "A sapphire-topped cane.", - "durability": null, - "weight": "0.5", - "attack_speed": "5", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "77000", - "name": "Black cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13095", - "bonuses": "0,-2,16,0,0,0,0,0,0,0,0,13,2,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "77000", - "durability": null, - "name": "Black cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13096" - }, - { - "requirements": "{0,28}", - "ge_buy_limit": "2", - "examine": "A ruby-topped cane.", - "durability": null, - "weight": "0.5", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "19100", - "name": "Adamant cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13097", - "bonuses": "0,-2,25,0,0,0,0,0,0,0,0,23,3,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "19100", - "durability": null, - "name": "Adamant cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13098" - }, - { - "requirements": "{0,38}", - "ge_buy_limit": "2", - "examine": "A diamond-topped cane.", - "durability": null, - "weight": "0.5", - "attack_speed": "5", - "weapon_interface": "10", - "equipment_slot": "3", - "lendable": "true", - "grand_exchange_price": "14500", - "name": "Rune cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13099", - "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,4,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "14500", - "durability": null, - "name": "Rune cane", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13100" - }, - { - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Comes with a free rabbit!", - "grand_exchange_price": "160400", - "durability": null, - "name": "Top hat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13101", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "160400", - "durability": null, - "name": "Top hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13102" - }, - { - "ge_buy_limit": "2", - "examine": "Official explorer headgear!", - "grand_exchange_price": "85400", - "durability": null, - "name": "Pith helmet", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "13103", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "85400", - "durability": null, - "name": "Pith helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13104" - }, - { - "remove_head": "true", - "requirements": "{1,5}", - "ge_buy_limit": "2", - "examine": "Spikier than normal.", - "durability": null, - "weight": "10", "equipment_slot": "0", - "grand_exchange_price": "38300", - "name": "Spiked helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13105", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "38300", - "durability": null, - "name": "Spiked helmet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13106" - }, - { + "ge_buy_limit": "10", "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "Shear elegance.", - "grand_exchange_price": "296700", - "durability": null, - "name": "Sheep mask", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13107", - "equipment_slot": "0" + "weight": "0.8" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "296700", - "durability": null, - "name": "Sheep mask", - "tradeable": "true", + "id": "12970", + "name": "Combat hood 0", "archery_ticket_price": "0", - "id": "13108" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "I can get the look right, but can I do the waddle too?", - "grand_exchange_price": "633600", - "durability": null, - "name": "Penguin mask", - "tradeable": "true", - "weight": "1", + "id": "12971", + "name": "Combat robe top 100", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", "archery_ticket_price": "0", - "id": "13109", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "633600", + "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0", "durability": null, - "name": "Penguin mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13110" - }, - { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "The bats have left the belltower.", - "grand_exchange_price": "399000", - "durability": null, - "name": "Bat mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13111", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "399000", - "durability": null, - "name": "Bat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13112" - }, - { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "It's the year of the cat!", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Cat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13113", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Cat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13114" - }, - { - "remove_head": "true", - "lendable": "true", - "ge_buy_limit": "2", - "examine": "I'm hungry like the wolf.", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Wolf mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13115", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Wolf mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13116" - }, - { - "turn90cw_anim": "9053", - "examine": "A fully charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "defence_anim": "9048", - "equipment_slot": "3", - "attack_anims": "9097,9097,9097,9097", - "stand_anim": "9049", - "name": "Ivandis flail (30)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13117", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (29)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13118", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "turn90ccw_anim": "9054", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail(28)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13119", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (27)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13120", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (26)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13121", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (25)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13122", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (24)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13123", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (23)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13124", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (22)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13125", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (21)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13126", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (20)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13127", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (19)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13128", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (18)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13129", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (17)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13130", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (16)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13131", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (15)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13132", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (14)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13133", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (13)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13134", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (12)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13135", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (11)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13136", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (10)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13137", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (9)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13138", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (8)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13139", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (7)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13140", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (6)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13141", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (5)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13142", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (4)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13143", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (3)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13144", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (2)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13145", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - one last charge left.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "23", - "turn180_anim": "9052", - "render_anim": "712", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (1)", - "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13146", - "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { - "examine": "Flail of Ivandis dust.", - "durability": null, - "name": "Flail dust", - "archery_ticket_price": "0", - "id": "13147" - }, - { - "destroy_message": "You found this page in the top floor of Paterdomus temple on an old table.", - "examine": "A page ripped from a book; it looks like a map of some kind.", - "durability": null, - "name": "Book page", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "13149" + "weight": "4" }, { - "destroy_message": "You got this book from a bunk bed in Sanguinesti Myreque base.", - "examine": "A big book of combat techniques.", + "id": "12972", + "name": "Combat robe top 80", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0", "durability": null, - "name": "Combat book", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "13151" + "weight": "4" }, { - "examine": "Used to make a metal chain.", - "durability": null, - "name": "Chain link mould", - "weight": "1", + "id": "12973", + "name": "Combat robe top 60", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", "archery_ticket_price": "0", - "id": "13153" - }, - { - "examine": "A chain made from mithril and silver.", + "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0", "durability": null, - "name": "Silvthril chain", - "weight": "3", - "archery_ticket_price": "0", - "id": "13154" - }, - { - "examine": "A blessed sickle with an emerald recessed into it.", - "durability": null, - "name": "Silver sickle emerald(b)", - "weight": "1", - "archery_ticket_price": "0", - "id": "13155", - "equipment_slot": "3" - }, - { - "examine": "A blessed and enchanted silver sickle with an emerald recessed into it.", - "durability": null, - "name": "Enchanted sickle emerald(b)", - "weight": "1", - "archery_ticket_price": "0", - "id": "13156", - "equipment_slot": "3" - }, - { - "examine": "A Vyrewatch corpse. Just a husk, really.", - "durability": null, - "name": "Vyre corpse", - "weight": "5", - "archery_ticket_price": "0", - "id": "13157" - }, - { - "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", - "durability": null, - "name": "Columbarium key", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13158" - }, - { - "destroy_message": "You got this key by cremating a Vyrewatch in the Paterdomus Columbarium.", - "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", - "durability": null, - "name": "Ornate tomb key", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "13159" + "weight": "4" }, { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", - "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "id": "12974", + "name": "Combat robe top 40", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0", "durability": null, - "name": "Tome of xp 2nd ed (3)", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13160" + "weight": "4" }, { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", - "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "id": "12975", + "name": "Combat robe top 20", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0", "durability": null, - "name": "Tome of xp 2nd ed (2)", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13161" + "weight": "4" }, { - "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", - "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "id": "12976", + "name": "Combat robe top 0", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,3,0,0,0,0,3,0,4,0,0,0,0", "durability": null, - "name": "Tome of xp 2nd ed (1)", + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", + "tradeable": "true", + "weight": "4" + }, + { + "id": "12977", + "name": "Combat robe top 0", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "12978", + "name": "Combat robe bottom 100", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13162" + "weight": "3" }, { - "examine": "A sapphire-topped cane.", - "grand_exchange_price": "115390", + "id": "12979", + "name": "Combat robe bottom 80", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0", "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12980", + "name": "Combat robe bottom 60", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12981", + "name": "Combat robe bottom 40", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12982", + "name": "Combat robe bottom 20", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "false", + "weight": "3" + }, + { + "id": "12983", + "name": "Combat robe bottom 0", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,3,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", + "tradeable": "true", + "weight": "3" + }, + { + "id": "12984", + "name": "Combat robe bottom 0", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "id": "12985", + "name": "Bronze gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,2,3,3,-1,2,2,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "shop_price": "15", + "tradeable": "false" + }, + { + "id": "12986", + "name": "Worn-out bronze gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,0,1,1,-1,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "12987", + "name": "Worn-out bronze gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12988", + "name": "Iron gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,3,4,4,-1,3,3,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "shop_price": "30", + "tradeable": "false" + }, + { + "id": "12989", + "name": "Worn-out iron gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,0,1,1,-1,0,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "12990", + "name": "Worn-out iron gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12991", + "name": "Steel gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,5}", + "shop_price": "50", + "tradeable": "false", + "weight": "10" + }, + { + "id": "12992", + "name": "Worn-out steel gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,1,1,1,-1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,5}", + "shop_price": "50", + "tradeable": "true", + "weight": "10" + }, + { + "id": "12993", + "name": "Worn-out steel gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12994", + "name": "Black gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,5,6,6,-1,5,5,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,10}", + "shop_price": "75", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "12995", + "name": "Worn-out black gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,1,2,2,-1,1,1,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,10}", + "shop_price": "75", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "12996", + "name": "Worn-out black gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12997", + "name": "Mithril gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,6,7,7,-1,6,6,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,20}", + "shop_price": "100", + "tradeable": "false" + }, + { + "id": "12998", + "name": "Worn-out mithril gauntlets", + "examine": "I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,2,2,2,-1,2,2,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,20}", + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "12999", + "name": "Worn-out mithril gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "13000", + "name": "Adamant gauntlets", + "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,8,9,9,-1,8,8,1,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,30}", + "shop_price": "150", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "13001", + "name": "Worn-out adamant gauntlets", + "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,2,3,3,-1,2,2,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,30}", + "shop_price": "150", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "13002", + "name": "Worn-out adamant gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "13003", + "name": "Rune gauntlets", + "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,10,11,11,-1,10,10,2,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,40}", + "shop_price": "200", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "13004", + "name": "Worn-out rune gauntlets", + "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,3,4,4,-1,3,3,0,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,40}", + "shop_price": "200", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "13005", + "name": "Worn-out rune gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "13006", + "name": "Dragon gauntlets", + "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,14,15,15,-1,14,14,3,0,0,0", + "durability": null, + "equipment_slot": "9", + "requirements": "{1,60}", + "shop_price": "300", + "tradeable": "false", + "weight": "0.2" + }, + { + "id": "13007", + "name": "Worn-out dragon gauntlets", + "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,1,0,0,0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "requirements": "{1,60}", + "shop_price": "300", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "13008", + "name": "Worn-out dragon gauntlets", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "13010", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13011", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13012", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13013", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13014", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13015", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13016", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13017", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13018", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13019", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13020", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13021", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13022", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13023", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13024", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13025", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13026", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13027", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13028", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13029", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13030", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13031", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13032", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13033", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13034", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13035", + "name": "Puzzle box", + "examine": "I need to solve this.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "shop_price": "5" + }, + { + "id": "13036", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13037", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13038", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13039", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13040", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13041", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13042", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13043", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13044", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13045", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13046", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13047", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13048", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13049", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13050", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13051", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13052", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13053", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13054", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13055", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13056", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13057", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13058", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13059", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13060", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13061", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13062", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13063", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13064", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13065", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13066", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13067", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13068", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13069", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13070", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13071", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13072", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13074", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13075", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13076", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13077", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "13078", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13079", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13080", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "13081", + "name": "Black crossbow", + "examine": "A black crossbow.", + "archery_ticket_price": "0", + "attack_audios": "2700,0,0,0", + "attack_speed": "6", + "bonuses": "0,0,0,0,60,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "175", + "requirements": "{4,33}", + "run_anim": "4228", + "stand_anim": "4591", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "4227", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "4226", + "weapon_interface": "17", + "weight": "5" + }, + { + "id": "13082", + "name": "Black crossbow", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "13083", + "name": "Black bolts", + "examine": "Black crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "100", + "requirements": "{4,33}", + "tradeable": "true" + }, + { + "id": "13084", + "name": "Black bolts(p)", + "examine": "Black crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "100", + "requirements": "{4,33}", + "tradeable": "true" + }, + { + "id": "13085", + "name": "Black bolts(p+)", + "examine": "Black crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "100", + "requirements": "{4,33}", + "tradeable": "true" + }, + { + "id": "13086", + "name": "Black bolts(p++)", + "examine": "Black crossbow bolts.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "100", + "requirements": "{4,33}", + "tradeable": "true" + }, + { + "id": "13095", "name": "Black cane", - "tradeable": "true", - "weight": "0.5", + "examine": "A sapphire-topped cane.", "archery_ticket_price": "0", "attack_speed": "5", - "weapon_interface": "10", - "id": "13163", "bonuses": "0,-2,16,0,0,0,0,0,0,0,0,13,2,0,0", - "equipment_slot": "3" - }, - { - "examine": "A ruby-topped cane.", - "grand_exchange_price": "18514", "durability": null, - "name": "Adamant cane", + "equipment_slot": "3", + "ge_buy_limit": "2", + "requirements": "{0,8}", "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", "weapon_interface": "10", - "id": "13164", - "bonuses": "0,-2,25,0,0,0,0,0,0,0,0,23,3,0,0", - "equipment_slot": "3" + "weight": "0.5" }, { - "examine": "A diamond-topped cane.", + "id": "13096", + "name": "Black cane", + "archery_ticket_price": "0", "durability": null, - "weight": "0.5", - "attack_speed": "5", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13097", + "name": "Adamant cane", + "examine": "A ruby-topped cane.", + "archery_ticket_price": "0", + "bonuses": "0,-2,25,0,0,0,0,0,0,0,0,23,3,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "requirements": "{0,28}", + "tradeable": "true", "weapon_interface": "10", + "weight": "0.5" + }, + { + "id": "13098", + "name": "Adamant cane", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13099", + "name": "Rune cane", + "examine": "A diamond-topped cane.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,4,0,0", + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{0,38}", + "tradeable": "true", + "weapon_interface": "10", + "weight": "0.5" + }, + { + "id": "13100", + "name": "Rune cane", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13101", + "name": "Top hat", + "examine": "Comes with a free rabbit!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13102", + "name": "Top hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13103", + "name": "Pith helmet", + "examine": "Official explorer headgear!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "13104", + "name": "Pith helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13105", + "name": "Spiked helmet", + "examine": "Spikier than normal.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,5}", + "tradeable": "true", + "weight": "10" + }, + { + "id": "13106", + "name": "Spiked helmet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13107", + "name": "Sheep mask", + "examine": "Shear elegance.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13108", + "name": "Sheep mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13109", + "name": "Penguin mask", + "examine": "I can get the look right, but can I do the waddle too?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13110", + "name": "Penguin mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13111", + "name": "Bat mask", + "examine": "The bats have left the belltower.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13112", + "name": "Bat mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13113", + "name": "Cat mask", + "examine": "It's the year of the cat!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true" + }, + { + "id": "13114", + "name": "Cat mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13115", + "name": "Wolf mask", + "examine": "I'm hungry like the wolf.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13116", + "name": "Wolf mask", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "13117", + "name": "Ivandis flail (30)", + "examine": "A fully charged flail.", + "archery_ticket_price": "0", + "attack_anims": "9097,9097,9097,9097", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "defence_anim": "9048", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13118", + "name": "Ivandis flail (29)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13119", + "name": "Ivandis flail(28)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23" + }, + { + "id": "13120", + "name": "Ivandis flail (27)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13121", + "name": "Ivandis flail (26)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13122", + "name": "Ivandis flail (25)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13123", + "name": "Ivandis flail (24)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13124", + "name": "Ivandis flail (23)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13125", + "name": "Ivandis flail (22)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13126", + "name": "Ivandis flail (21)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13127", + "name": "Ivandis flail (20)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13128", + "name": "Ivandis flail (19)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13129", + "name": "Ivandis flail (18)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13130", + "name": "Ivandis flail (17)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13131", + "name": "Ivandis flail (16)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13132", + "name": "Ivandis flail (15)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13133", + "name": "Ivandis flail (14)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13134", + "name": "Ivandis flail (13)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13135", + "name": "Ivandis flail (12)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13136", + "name": "Ivandis flail (11)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13137", + "name": "Ivandis flail (10)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13138", + "name": "Ivandis flail (9)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13139", + "name": "Ivandis flail (8)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13140", + "name": "Ivandis flail (7)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13141", + "name": "Ivandis flail (6)", + "examine": "A partially charged flail.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13142", + "name": "Ivandis flail (5)", + "examine": "A partially charged flail - it's nearly out of charges.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13143", + "name": "Ivandis flail (4)", + "examine": "A partially charged flail - it's nearly out of charges.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13144", + "name": "Ivandis flail (3)", + "examine": "A partially charged flail - it's nearly out of charges.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13145", + "name": "Ivandis flail (2)", + "examine": "A partially charged flail - it's nearly out of charges.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13146", + "name": "Ivandis flail (1)", + "examine": "A partially charged flail - one last charge left.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "712", + "run_anim": "9050", + "stand_anim": "9049", + "stand_turn_anim": "823", + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" + }, + { + "id": "13147", + "name": "Flail dust", + "examine": "Flail of Ivandis dust.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "13149", + "name": "Book page", + "examine": "A page ripped from a book; it looks like a map of some kind.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You found this page in the top floor of Paterdomus temple on an old table.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13151", + "name": "Combat book", + "examine": "A big book of combat techniques.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You got this book from a bunk bed in Sanguinesti Myreque base.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13153", + "name": "Chain link mould", + "examine": "Used to make a metal chain.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "13154", + "name": "Silvthril chain", + "examine": "A chain made from mithril and silver.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "13155", + "name": "Silver sickle emerald(b)", + "examine": "A blessed sickle with an emerald recessed into it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1" + }, + { + "id": "13156", + "name": "Enchanted sickle emerald(b)", + "examine": "A blessed and enchanted silver sickle with an emerald recessed into it.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "weight": "1" + }, + { + "id": "13157", + "name": "Vyre corpse", + "examine": "A Vyrewatch corpse. Just a husk, really.", + "archery_ticket_price": "0", + "durability": null, + "weight": "5" + }, + { + "id": "13158", + "name": "Columbarium key", + "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.1" + }, + { + "id": "13159", + "name": "Ornate tomb key", + "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You got this key by cremating a Vyrewatch in the Paterdomus Columbarium.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13160", + "name": "Tome of xp 2nd ed (3)", + "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "13161", + "name": "Tome of xp 2nd ed (2)", + "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "13162", + "name": "Tome of xp 2nd ed (1)", + "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "13163", + "name": "Black cane", + "examine": "A sapphire-topped cane.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-2,16,0,0,0,0,0,0,0,0,13,2,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weapon_interface": "10", + "weight": "0.5" + }, + { + "id": "13164", + "name": "Adamant cane", + "examine": "A ruby-topped cane.", + "archery_ticket_price": "0", + "bonuses": "0,-2,25,0,0,0,0,0,0,0,0,23,3,0,0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true", + "weapon_interface": "10", + "weight": "0.5" + }, + { + "id": "13165", + "name": "Rune cane", + "examine": "A diamond-topped cane.", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,4,0,0", + "durability": null, "equipment_slot": "3", "lendable": "true", - "grand_exchange_price": "15023", - "name": "Rune cane", "tradeable": "true", - "archery_ticket_price": "0", - "id": "13165", - "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,4,0,0" + "weapon_interface": "10", + "weight": "0.5" }, { - "lendable": "true", - "examine": "Comes with a free rabbit!", - "grand_exchange_price": "425919", - "durability": null, - "name": "Top hat", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13166", - "equipment_slot": "0" + "name": "Top hat", + "examine": "Comes with a free rabbit!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "examine": "Official explorer headgear!", - "grand_exchange_price": "99682", - "durability": null, - "name": "Pith helmet", - "tradeable": "true", - "weight": "0.5", - "archery_ticket_price": "0", "id": "13167", - "equipment_slot": "0" + "name": "Pith helmet", + "examine": "Official explorer headgear!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "0.5" }, { - "remove_head": "true", - "examine": "Spikier than normal.", - "grand_exchange_price": "39643", - "durability": null, - "name": "Spiked helmet", - "tradeable": "true", - "weight": "10", - "archery_ticket_price": "0", "id": "13168", + "name": "Spiked helmet", + "examine": "Spikier than normal.", + "archery_ticket_price": "0", "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", - "equipment_slot": "0" + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "true", + "weight": "10" }, { - "remove_head": "true", - "lendable": "true", - "examine": "Shear elegance.", - "grand_exchange_price": "313558", - "durability": null, - "name": "Sheep mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13169", - "equipment_slot": "0" + "name": "Sheep mask", + "examine": "Shear elegance.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "lendable": "true", - "examine": "I can get the look right, but can I do the waddle too?", - "grand_exchange_price": "609515", - "durability": null, - "name": "Penguin mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13170", - "equipment_slot": "0" + "name": "Penguin mask", + "examine": "I can get the look right, but can I do the waddle too?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "lendable": "true", - "examine": "The bats have left the belltower.", - "grand_exchange_price": "371616", - "durability": null, - "name": "Bat mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13171", - "equipment_slot": "0" + "name": "Bat mask", + "examine": "The bats have left the belltower.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_head": "true", - "lendable": "true", - "examine": "It's the year of the cat!", - "grand_exchange_price": "1066648", - "durability": null, - "name": "Cat mask", - "tradeable": "true", - "archery_ticket_price": "0", "id": "13172", - "equipment_slot": "0" - }, - { - "remove_head": "true", + "name": "Cat mask", + "examine": "It's the year of the cat!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", "lendable": "true", - "examine": "I'm hungry like the wolf.", - "grand_exchange_price": "1303443", - "durability": null, - "name": "Wolf mask", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", + "remove_head": "true", + "tradeable": "true" + }, + { "id": "13173", - "equipment_slot": "0" + "name": "Wolf mask", + "examine": "I'm hungry like the wolf.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Blue navy slacks", - "archery_ticket_price": "0", "id": "13174", - "equipment_slot": "7" + "name": "Blue navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Green navy slacks", - "archery_ticket_price": "0", "id": "13175", - "equipment_slot": "7" + "name": "Green navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Red navy slacks", - "archery_ticket_price": "0", "id": "13176", - "equipment_slot": "7" + "name": "Red navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Brown navy slacks", - "archery_ticket_price": "0", "id": "13177", - "equipment_slot": "7" + "name": "Brown navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Black navy slacks", - "archery_ticket_price": "0", "id": "13178", - "equipment_slot": "7" + "name": "Black navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "shop_price": "100", - "examine": "Not for slackers", - "durability": null, - "name": "Purple navy slacks", - "archery_ticket_price": "0", "id": "13179", - "equipment_slot": "7" - }, - { - "shop_price": "100", + "name": "Purple navy slacks", "examine": "Not for slackers", - "durability": null, - "name": "Grey navy slacks", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" + }, + { "id": "13180", - "equipment_slot": "7" + "name": "Grey navy slacks", + "examine": "Not for slackers", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "shop_price": "100" }, { - "remove_sleeves": "true", - "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Blue naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", "id": "13181", - "equipment_slot": "4" - }, - { + "name": "Blue naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Green naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13182", - "equipment_slot": "4" - }, - { + "name": "Green naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Red naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13183", - "equipment_slot": "4" - }, - { + "name": "Red naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Brown naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13184", - "equipment_slot": "4" - }, - { + "name": "Brown naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Black naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13185", - "equipment_slot": "4" - }, - { + "name": "Black naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Purple naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13186", - "equipment_slot": "4" - }, - { + "name": "Purple naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", "remove_sleeves": "true", "shop_price": "100", - "examine": "...You can sail the seven seas...", - "durability": null, - "name": "Grey naval shirt", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "13187", - "equipment_slot": "4" + "name": "Grey naval shirt", + "examine": "...You can sail the seven seas...", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "100", + "tradeable": "false" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Blue tricorn hat", - "archery_ticket_price": "0", "id": "13188", - "equipment_slot": "0" + "name": "Blue tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Green tricorn hat", - "archery_ticket_price": "0", "id": "13189", - "equipment_slot": "0" + "name": "Green tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Red tricorn hat", - "archery_ticket_price": "0", "id": "13190", - "equipment_slot": "0" + "name": "Red tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Brown tricorn hat", - "archery_ticket_price": "0", "id": "13191", - "equipment_slot": "0" + "name": "Brown tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Black tricorn hat", - "archery_ticket_price": "0", "id": "13192", - "equipment_slot": "0" + "name": "Black tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "remove_head": "true", - "shop_price": "50", - "examine": "I could never look square in this.", - "durability": null, - "name": "Purple tricorn hat", - "archery_ticket_price": "0", "id": "13193", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "50", + "name": "Purple tricorn hat", "examine": "I could never look square in this.", - "durability": null, - "name": "Grey tricorn hat", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" + }, + { "id": "13194", - "equipment_slot": "0" + "name": "Grey tricorn hat", + "examine": "I could never look square in this.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "50" }, { - "destroy_message": "If you drop this lamp, it will be destroyed. You can obtain another from Dunstan.", - "examine": "I wonder what happens if I rub it.", - "durability": null, + "id": "13227", "name": "Mysterious lamp", - "tradeable": "false", + "examine": "I wonder what happens if I rub it.", "archery_ticket_price": "0", - "id": "13227" + "destroy_message": "If you drop this lamp, it will be destroyed. You can obtain another from Dunstan.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You can get another net from Professor Arblenap on Ice Mountain.", - "examine": "Professor Arblenap gave you this net to catch the baby icefiends on Ice Mountain.", - "durability": null, - "name": "Icefiend net", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13230", - "equipment_slot": "3" + "name": "Icefiend net", + "examine": "Professor Arblenap gave you this net to catch the baby icefiends on Ice Mountain.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another net from Professor Arblenap on Ice Mountain.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false", + "weight": "1" }, { - "examine": "You should give this to Professor Arblenap.", - "durability": null, - "name": "Baby icefiend", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", "id": "13231", - "equipment_slot": "3" + "name": "Baby icefiend", + "examine": "You should give this to Professor Arblenap.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "3", + "tradeable": "false", + "weight": "1" }, { - "destroy_message": "It will reappear near the bottom of Ice Mountain.", - "examine": "The remains of the Oracle's tent on Ice Mountain.", - "durability": null, + "id": "13232", "name": "Tent", - "tradeable": "false", - "destroy": "true", - "weight": "30", + "examine": "The remains of the Oracle's tent on Ice Mountain.", "archery_ticket_price": "0", - "id": "13232" + "destroy": "true", + "destroy_message": "It will reappear near the bottom of Ice Mountain.", + "durability": null, + "tradeable": "false", + "weight": "30" }, { - "destroy_message": "You can get it back by pickpocketing Drorkar again.", - "examine": "You took this key from Drorkar's pocket in the power station. (Perils of Ice Mountain)", - "durability": null, + "id": "13234", "name": "Dwarven key", - "tradeable": "false", - "destroy": "true", + "examine": "You took this key from Drorkar's pocket in the power station. (Perils of Ice Mountain)", "archery_ticket_price": "0", - "id": "13234" + "destroy": "true", + "destroy_message": "You can get it back by pickpocketing Drorkar again.", + "durability": null, + "tradeable": "false" }, { - "examine": "A plank of oak that has been treated to resist fire.", - "durability": null, + "id": "13238", "name": "Treated oak plank", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "13238" - }, - { - "shop_price": "14", - "examine": "Good for detailed crafting.", - "grand_exchange_price": "33", - "durability": null, - "name": "Chisel", - "tradeable": "true", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "13239" - }, - { - "durability": null, - "name": ".", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13240", - "equipment_slot": "5" - }, - { "examine": "A plank of oak that has been treated to resist fire.", - "durability": null, - "name": "Treated plank", - "weight": "0.8", "archery_ticket_price": "0", - "id": "13241" + "durability": null, + "weight": "0.8" }, { + "id": "13239", + "name": "Chisel", + "examine": "Good for detailed crafting.", + "archery_ticket_price": "0", "durability": null, - "name": ".", + "shop_price": "14", "tradeable": "true", - "archery_ticket_price": "0", - "id": "13242" + "weight": "0.4" }, { - "examine": "An old stone slab with writing on it.", + "id": "13240", + "name": ".", + "archery_ticket_price": "0", "durability": null, + "equipment_slot": "5", + "tradeable": "true" + }, + { + "id": "13241", + "name": "Treated plank", + "examine": "A plank of oak that has been treated to resist fire.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.8" + }, + { + "id": "13242", + "name": ".", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "13243", "name": "Stone tablet", + "examine": "An old stone slab with writing on it.", "archery_ticket_price": "0", - "id": "13243" + "durability": null }, { - "examine": "A book for those who want to visit the TzHaar city.", - "durability": null, + "id": "13244", "name": "Tzhaar tourist guide", + "examine": "A book for those who want to visit the TzHaar city.", "archery_ticket_price": "0", - "id": "13244" + "durability": null }, { - "destroy_message": "You can mine more stone slabs in the TzHaar mine.", - "examine": "A flawed block of obsidian from the Karamja volcano.", - "durability": null, + "id": "13245", "name": "Stone slab", - "tradeable": "false", - "destroy": "true", - "weight": "32", + "examine": "A flawed block of obsidian from the Karamja volcano.", "archery_ticket_price": "0", - "id": "13245" + "destroy": "true", + "destroy_message": "You can mine more stone slabs in the TzHaar mine.", + "durability": null, + "tradeable": "false", + "weight": "32" }, { - "destroy_message": "You can get more pillars by crafting stone slabs from the TzHaar mine.", - "examine": "A support pillar.", - "durability": null, + "id": "13246", "name": "Pillar", - "tradeable": "false", - "destroy": "true", - "weight": "25", + "examine": "A support pillar.", "archery_ticket_price": "0", - "id": "13246" + "destroy": "true", + "destroy_message": "You can get more pillars by crafting stone slabs from the TzHaar mine.", + "durability": null, + "tradeable": "false", + "weight": "25" }, { - "durability": null, - "name": "Splat", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13247", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, "name": "Splat", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "13248", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, "name": "Splat", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "13249", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, "name": "Splat", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "13250", + "name": "Splat", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can obtain another from the mummy guardian in the undead slayer's tomb.", - "examine": "A key obtained in Catolax's tomb.", - "durability": null, - "name": "Ranged path key", - "tradeable": "false", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "13259", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can obtain another from the mummy guardian in the undead slayer's tomb.", + "name": "Ranged path key", "examine": "A key obtained in Catolax's tomb.", - "durability": null, - "name": "Magic path key", - "tradeable": "false", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13260", - "absorb": "0,0,0", - "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", + "destroy": "true", + "destroy_message": "You can obtain another from the mummy guardian in the undead slayer's tomb.", + "durability": null, + "tradeable": "false" }, { + "id": "13260", + "name": "Magic path key", + "examine": "A key obtained in Catolax's tomb.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can obtain another from the mummy guardian in the undead slayer's tomb.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13263", + "name": "Slayer helmet", + "examine": "You don't want to wear it inside-out.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", "remove_head": "true", "requirements": "{1,10}", "shop_price": "650", - "examine": "You don't want to wear it inside-out.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Slayer helmet", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13263", - "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0" + "tradeable": "false" }, { - "destroy_message": "Drop", - "examine": "The remains of a banshee's scream.", - "durability": null, - "name": "Banshee voice", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13276", + "name": "Banshee voice", + "examine": "The remains of a banshee's scream.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "tradeable": "false" }, { - "shop_price": "400", - "examine": "More of that crazy Slayer headgear.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Masked earmuffs", - "tradeable": "false", - "archery_ticket_price": "0", "id": "13277", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50", - "examine": "For use against turoth and kurasks.", - "durability": null, - "destroy": "true", - "attack_speed": "4", + "name": "Masked earmuffs", + "examine": "More of that crazy Slayer headgear.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "Drop", - "grand_exchange_price": "89", - "name": "Broad arrow heads", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13278", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50", - "examine": "For use against turoth and kurasks.", "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "17", - "name": "Unfinished broad bolts", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13279", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50", - "examine": "For use against turoth and kurasks.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "grand_exchange_price": "34", - "name": "Broad-tipped bolts", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13280", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(8)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13281", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(7)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13282", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(6)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13283", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(5)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13284", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(4)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13285", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(3)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13286", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(2)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13287", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "name": "Ring of slaying(1)", - "tradeable": "false", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13288", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "turn90cw_anim": "1207", - "examine": "A razor-sharp sword.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,381,390,381", - "grand_exchange_price": "43082", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "13290", - "stand_turn_anim": "823", - "bonuses": "67,62,0,0,0,0,0,0,0,0,0,50,0,0,0", - "requirements": "{0,50}-{18,55}", - "shop_price": "68000", - "durability": null, - "destroy": "false", - "weapon_interface": "5", - "attack_audios": "2517,2517,2500,2517", - "name": "Leaf-bladed sword" - }, - { - "shop_price": "68000", - "examine": "A razor-sharp sword.", - "grand_exchange_price": "43082", - "durability": null, - "name": "Leaf-bladed sword", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13291" - }, - { - "durability": null, - "name": "Letter", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13292", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Plans", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13293", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop these plans, they will be lost and you will need to see Captain Donnie for a replacement.", - "examine": "A charcoal rubbing taken from 50% Luke.", - "durability": null, - "name": "Charcoal rubbing", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13294", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop Frank's mark, you will need to see him to get another.", - "examine": "Signature from Frank.", - "durability": null, - "name": "Frank's mark", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13295", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop these lockpicks, you will need to look under the mattress to get another set.", - "examine": "A device for opening locked doors.", - "durability": null, - "name": "Crude lockpick", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13296", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "The top of a Custom Officer's uniform.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "If you drop this shirt you will need to kill another Customs officer for a replacement.", - "remove_sleeves": "true", - "name": "Customs shirt", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13297", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop these trousers you will need to kill another Customs officer for a replacement.", - "examine": "The trousers of a Custom Officer's uniform.", - "durability": null, - "name": "Customs trousers", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13298", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "If you drop this hat you will need to kill another Customs officer for a replacement.", - "examine": "The hat of a Custom Officer's uniform.", - "durability": null, - "name": "Customs hat", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13299", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", - "examine": "A folder containing all the marks I have gathered.", - "durability": null, - "name": "File", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13300", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", - "examine": "A folder containing all the marks I have gathered.", - "durability": null, - "name": "File", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13301", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Brooch", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13302", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop Izzy's No-Beard's mark, you will need to see him to get another.", - "examine": "Signature from Izzy No-Beard.", - "durability": null, - "name": "Izzy's mark", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13303", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Key", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13304", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this crowbar, you will need to get another from the chest on the sunken ship.", - "examine": "This could be useful.", - "durability": null, - "name": "Crowbar", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13305", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop the idol, you will need to go diving to get another replacement.", - "examine": "An idol. It has many heads.", - "durability": null, - "name": "Idol of many heads", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13307", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop Bill Teach's mark, you will need to see him to get another.", - "examine": "Signature from Bill Teach.", - "durability": null, - "name": "Bill teach's mark", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13308", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this paper, you will need to call to the guards for a replacement. ", - "examine": "This is generally used for writing on.", - "durability": null, - "name": "Paper", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13309", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this note, you'll need to call the guards for replacement paper.", - "examine": "A written confession.", - "durability": null, - "name": "Confession", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13310", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Note", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13311", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this ink, you will need to call to the guards for a replacement.", - "examine": "An empty ink bottle./This is full of ink.", - "durability": null, - "name": "Ink bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13312", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this paper you will need to call to the guards to replace it and the ink.", - "examine": "It's got ink all over it.", - "durability": null, - "name": "Inky paper", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13313", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this ink, you will need to call to the guards for a replacement.", - "examine": "An empty ink bottle./This is full of ink.", - "durability": null, - "name": "Ink bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13314", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this bottle, you will need to call to the guards for a replacement.", - "examine": "It's smashed. I should mind my fingers.", - "durability": null, - "name": "Smashed bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13315", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "The top part of a prisoner's uniform.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "If you drop this top, then you will need to speak to the guards to get it replaced.", - "remove_sleeves": "true", - "name": "Prison uniform top", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13316", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop these trousers, then you will need to speak to the guards to get it replaced.", - "examine": "The bottom part of a prisoner's uniform.", - "durability": null, - "name": "Prison uniform trousers", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13317", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "examine": "It's covered in fish slop.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "If you drop this top, you will need to speak to the guards to get it replaced, then find a way to get stew on it again.", - "remove_sleeves": "true", - "name": "Fishy prison uniform top", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13318", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this cup it will be destroyed and you will have to pick up another.", - "examine": "A standard prison-issue tin cup.", - "durability": null, - "name": "Tin cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13319", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "If you drop this cup it will be destroyed and you will have to pick up another.", - "examine": "A standard prison-issue tin cup.", - "durability": null, - "name": "Tin cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13320", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "Destroy", - "examine": "A pipe that belongs in a cannon stand.", - "durability": null, - "name": "Pipe", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13321", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", - "durability": null, - "name": "Accordion", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13322", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", - "durability": null, - "name": "Accordion", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13323", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", - "durability": null, - "name": "Accordion", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13324", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this accordion, pipe and inky paper, it will all be destroyed and you will need to get all the items back again.", - "examine": "A very crude vacuum pump.", - "durability": null, - "name": "Vacuum pump", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13325", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this, you will lose all the items you put in it, as well as the seagull.", - "examine": "It sounds like a law firm.", - "durability": null, - "name": "Vacuum pump and gull", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13326", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "If you drop this quill, you will need to talk to the guard for a replacement.", - "examine": "From a bird - can be used for writing.", - "durability": null, - "name": "Quill", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13327", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "From a bird - can be used for writing. It has some ink on the tip.", - "durability": null, - "name": "Inky quill", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13328", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", - "examine": "A folder containing all the marks I have gathered.", - "durability": null, - "name": "File", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13329", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", - "examine": "A folder containing all the marks I have gathered.", - "durability": null, - "name": "File", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13330", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this dead parrot, there is a chance that bad, bad things will happen to you - unless you see 50 Ships Mufassah for a replacement.", - "examine": "A very dead rotten parrot.", - "durability": null, - "name": "Ex-parrot", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13331", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this cage, you will need to see Bill Teach for a replacement.", - "examine": "It feels very cold to the touch.", - "durability": null, - "name": "Magical cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13332", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this dead parrot in a cage (and who wouldn't) then you will need to see ", - "examine": "A very dead rotten parrot in a magical cage.", - "durability": null, - "name": "Ex-parrot in a magic cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13333", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop the cage, the ex-ex-parrot will escape.", - "examine": "A very alive rotten parrot in a magic cage.", - "durability": null, - "name": "Ex-ex-parrot in a magic cage", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13334", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Ex-ex-parrot", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13335", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Ex-ex-parrot", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13336", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50", - "examine": "A pirate impling in a jar.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "grand_exchange_price": "7346", - "name": "Pirate impling jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13337", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", - "shop_price": "50", - "grand_exchange_price": "7346", - "durability": null, - "name": "Pirate impling jar", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13338" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A white bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13339", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A white bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13340", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A red bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13341", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A red bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13342", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A blue bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13343", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A blue bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13344", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A brown bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13345", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A brown bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13346", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A grey bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13347", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A grey bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13348", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A purple bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13349", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A purple bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13350", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A orange bandana and a patch for the left eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13351", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A orange bandana and a patch for both eyes.", - "durability": null, - "name": "Bandana and eyepatches", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13352", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "shop_price": "100", - "examine": "It's dark, and I'm wearing two patches. Hit it!", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Double eyepatches", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13353", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "Two patches and a hat.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Pirate hat and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13354", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A patch for the left eye.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "23485", - "name": "Left eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13355", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A patch for the left eye.", - "grand_exchange_price": "23485", - "durability": null, - "name": "Left eyepatch", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13356" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A hat and a patch.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Pirate hat and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13357", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "A seaworthy grey shirt.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13358", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "300", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13359" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "A seaworthy purple shirt.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13360", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "300", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13361" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "A seaworthy orange shirt.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13362", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "300", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13363" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "examine": "A seaworthy pair of grey trousers.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "7", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13364", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "350", - "grand_exchange_price": "1", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13365" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "examine": "A seaworthy pair of purple trousers.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "7", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13366", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "350", - "grand_exchange_price": "1", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13367" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "examine": "A seaworthy pair of orange trousers.", - "durability": null, - "weight": "1", - "absorb": "0,0,0", - "equipment_slot": "7", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13368", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "350", - "grand_exchange_price": "1", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13369" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A grey bandana.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13370", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "durability": null, - "name": "Bandana", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13371" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A purple bandana.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13372", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "durability": null, - "name": "Bandana", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13373" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "An orange bandana.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13374", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "durability": null, - "name": "Bandana", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13375" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A purple bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13376", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A orange bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13377", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A grey bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "13378", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Destroy", - "examine": "Some rock fragments.", - "durability": null, - "name": "Rock fragments", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13379", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop the letter, it will be destroyed and you'll need to see Bill Teach for another.", - "examine": "Note from Teach to Brass Hand Harry.", - "durability": null, - "name": "Introduction letter", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13380", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", - "examine": "One of many broken hands.", - "durability": null, - "name": "Broken hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13381", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", - "examine": "One of many broken hands.", - "durability": null, - "name": "Broken hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13382", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", - "examine": "One of many broken hands.", - "durability": null, - "name": "Broken hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13383", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", - "examine": "One of many broken hands.", - "durability": null, - "name": "Broken hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13384", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", - "examine": "One of many broken hands.", - "durability": null, - "name": "Broken hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13385", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Wall beast fingers in a white fern sauce.", - "durability": null, - "name": "Fingers", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13386", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", - "examine": "A brass thumb.", - "durability": null, - "name": "Thumb", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13387", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Hand", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13388", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", - "examine": "A brass wrist.", - "durability": null, - "name": "Wrist", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13389", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", - "examine": "A brass brace.", - "durability": null, - "name": "Brace", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13390", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this hand, you will destroy it and you will have to remake it from parts that Brass Hand Harry has.", - "examine": "A completed brass hand.", - "durability": null, - "name": "Brass hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13391", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this ink pad, you will lose it and will need to see Brass Hand Harry for a replacement.", - "examine": "It's soaked with ink.", - "durability": null, - "name": "Ink pad", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13392", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this hand, you will destroy it and you will have to remake it from parts that Brass Hand Harry has.", - "examine": "An inky brass hand.", - "durability": null, - "name": "Inky hand", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13393", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this paper, you will need to call to the guards for a replacement. ", - "examine": "This is generally used for writing on.", - "durability": null, - "name": "Paper", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13394", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop Brass Hand Harry's mark, it will be destroyed and you need to see him to get another.", - "examine": "Handprint from Brass Hand Harry.", - "durability": null, - "name": "Brass hand harry's mark", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13395", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Makes me itch.", - "durability": null, - "name": "Fake beard", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13396", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", - "examine": "It looks like someone has sneezed in it.", - "durability": null, - "name": "Fake moustache and nose", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13397", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", - "examine": "Why is the monocle nailed to it?", - "durability": null, - "name": "Fake monocle, moustache and nose", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13398", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "You can barely see where it connected to the mop handle.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", - "name": "Curly wig", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13399", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "It looks like it is part-dog, part-mould.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", - "name": "Straight wig", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13400", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop this order for 'rum' you will need to speak to the Rusty Anchor barman to get another.", - "examine": "An order simply for 'rum'.", - "durability": null, - "name": "Order", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13401", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop Captain Braindeath's mark, you will need to speak to him to get another.", - "examine": "Signature of Captain Braindeath.", - "durability": null, - "name": "Braindeath's mark", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13402", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "300", - "examine": "A replica tinderbox made of solid gold.", - "durability": null, - "name": "Golden tinderbox", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13403", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A bow from a darker dimension.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "9", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "equip_audio": "3738", - "render_anim": "303", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "86850", - "attack_audios": "3731,0,0,0", - "name": "Dark bow", - "archery_ticket_price": "0", - "id": "13405", - "bonuses": "0,0,0,0,95,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "59171", - "name": "Ancient staff", - "archery_ticket_price": "0", - "id": "13406", - "bonuses": "10,-1,40,15,0,2,3,1,15,0,5,50,0,-1,10" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "This helmet is worn by archers.", - "grand_exchange_price": "46334", - "durability": null, - "name": "Archer helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13407", - "absorb": "0,3,1", - "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "This helmet is worn by berserkers.", - "grand_exchange_price": "45978", - "durability": null, - "name": "Berserker helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13408", - "absorb": "1,0,3", - "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "This helm is worn by warriors.", - "grand_exchange_price": "45664", - "durability": null, - "name": "Warrior helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13409", - "absorb": "1,0,3", - "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "This helm is worn by farseers.", - "grand_exchange_price": "45989", - "durability": null, - "name": "Farseer helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13410", - "absorb": "3,1,0", - "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Protective headwear made from crabs. Better than it sounds.", - "grand_exchange_price": "20378", - "durability": null, - "name": "Rock-shell helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13411", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,6,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A sturdy body armour made from rock crab pieces.", - "grand_exchange_price": "41788", - "durability": null, - "name": "Rock-shell plate", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13412", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Some tough leggings made from rock crab parts.", - "grand_exchange_price": "37139", - "durability": null, - "name": "Rock-shell legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13413", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,10,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A helm fit for any Fremennik ranger.", - "grand_exchange_price": "37724", - "durability": null, - "name": "Spined helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13414", - "absorb": "0,3,1", - "bonuses": "0,-6,-6,-6,6,6,6,6,6,0,6,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A constant reminder that I'm above a Dagannoth in the food chain.", - "grand_exchange_price": "5773", - "durability": null, - "name": "Spined body", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13415", - "absorb": "0,6,3", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,30,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Stylish leg armour for rangers with a lingering smell of raw fish...", - "grand_exchange_price": "2696", - "durability": null, - "name": "Spined chaps", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13416", - "absorb": "0,4,2", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,10,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Make your foes cower by wearing a skull as a helmet!", - "grand_exchange_price": "39060", - "durability": null, - "name": "Skeletal helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13417", - "absorb": "3,1,0", - "bonuses": "0,0,0,2,-2,10,9,11,3,0,6,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The bones in this armour seems to vibrate with a magic quality...", - "grand_exchange_price": "26289", - "durability": null, - "name": "Skeletal top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13418", - "absorb": "6,3,0", - "bonuses": "0,0,0,8,-10,35,25,42,15,0,30,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A superior set of strengthened slacks for any self respecting seer.", - "grand_exchange_price": "24591", - "durability": null, - "name": "Skeletal bottoms", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13419", - "absorb": "4,2,0", - "bonuses": "0,0,0,6,-7,22,20,24,10,0,10,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", - "grand_exchange_price": "158181", - "durability": null, - "name": "Spined boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13420", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,1,1,0,0,0,1,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", - "grand_exchange_price": "1358", - "durability": null, - "name": "Rock-shell boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13421", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", - "grand_exchange_price": "7788", - "durability": null, - "name": "Skeletal boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13422", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Fremennik gloves stitched together from spined dagannoth hide.", - "grand_exchange_price": "3027", - "durability": null, - "name": "Spined gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13423", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,1,2,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Fremennik gloves stitched together from rock crab shell shards.", - "grand_exchange_price": "12839", - "durability": null, - "name": "Rock-shell gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13424", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", - "grand_exchange_price": "25838", - "durability": null, - "name": "Skeletal gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13425", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A mysterious ring that can fill the wearer with magical power...", - "grand_exchange_price": "799703", - "durability": null, - "name": "Seers ring", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13426", - "absorb": "0,0,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A fabled ring that improves the wearer's skill with a bow...", - "grand_exchange_price": "483521", - "durability": null, - "name": "Archers ring", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13427", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,4,0,0,0,0,4,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A legendary ring once worn by Fremennik warriors.", - "grand_exchange_price": "361302", - "durability": null, - "name": "Warrior ring", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13428", - "absorb": "0,0,0", - "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A ring reputed to bring out a berserk fury in its wearer.", - "grand_exchange_price": "963418", - "durability": null, - "name": "Berserker ring", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13429", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0" - }, - { - "examine": "A two-handed dragon sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "124", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1024000", - "name": "Dragon 2h sword", - "archery_ticket_price": "0", - "id": "13430", - "bonuses": "-4,92,80,-4,0,0,0,0,0,-1,0,93,0,0,0" - }, - { - "shop_price": "20", - "examine": "Useful for catching crayfish.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "25", - "name": "Crayfish cage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13431", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Swap this note at any bank for the equivalent item.", - "grand_exchange_price": "25", - "durability": null, - "name": "Crayfish cage", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13432" - }, - { - "shop_price": "5", - "examine": "Some cooked crayfish. Eat it to heal.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "32", - "name": "Crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13433", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "5", - "examine": "Swap this note at any bank for the equivalent item.", - "grand_exchange_price": "32", - "durability": null, - "name": "Crayfish", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13434" - }, - { - "shop_price": "5", - "examine": "I should try cooking this. Used in Cooking (1).", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "41", - "name": "Raw crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13435", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "5", - "examine": "Swap this note at any bank for the equivalent item.", - "grand_exchange_price": "41", - "durability": null, - "name": "Raw crayfish", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13436" - }, - { - "shop_price": "1", - "durability": null, - "name": "Burnt crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "destroy": "false", - "attack_speed": "4", - "id": "13437", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Burnt crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "destroy": "false", - "attack_speed": "4", - "id": "13438" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13439", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "This is a bag of runes for Sir Vant.", - "durability": null, - "name": "Bag of runes", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13440", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A very powerful onyx amulet.", - "grand_exchange_price": "2285210", - "durability": null, - "name": "Amulet of fury", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13442", - "absorb": "0,0,0", - "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,0,5,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A cape woven of obsidian plates.", - "grand_exchange_price": "56575", - "durability": null, - "name": "Obsidian cape", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13443", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0" - }, - { - "examine": "A weapon from the Abyss.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "11", - "absorb": "0,0,0", - "render_anim": "1578", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "114775", - "name": "Abyssal whip", - "archery_ticket_price": "0", - "id": "13444", - "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0" - }, - { - "examine": "Simplicity is the best weapon.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "10", - "absorb": "0,0,0", - "render_anim": "27", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "29584", - "name": "Granite maul", - "archery_ticket_price": "0", - "id": "13445", - "bonuses": "0,0,81,0,0,0,0,0,0,0,0,79,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13446", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13447", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13448", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Protects your head and looks impressive too.", - "grand_exchange_price": "5756658", - "durability": null, - "name": "Dragon full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13449", - "absorb": "2,0,4", - "bonuses": "0,0,0,-6,-2,45,48,41,-1,46,12,0,0,0,0" - }, - { - "examine": "A beautiful, heavy sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "1579", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7767458", - "name": "Armadyl godsword", - "archery_ticket_price": "0", - "id": "13450", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0" - }, - { - "examine": "A brutally heavy sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "1579", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1504919", - "name": "Bandos godsword", - "archery_ticket_price": "0", - "id": "13451", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0" - }, - { - "examine": "A gracious, heavy sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "1579", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "34966317", - "name": "Saradomin godsword", - "archery_ticket_price": "0", - "id": "13452", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0" - }, - { - "examine": "A terrifying, heavy sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "1579", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2132037", - "name": "Zamorak godsword", - "archery_ticket_price": "0", - "id": "13453", - "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0" - }, - { - "examine": "An evil spear.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "1581", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2070507", - "name": "Zamorakian spear", - "archery_ticket_price": "0", - "id": "13454", - "bonuses": "85,65,65,0,0,13,13,12,0,13,0,75,0,2,0" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A sturdy chestplate.", - "grand_exchange_price": "6460055", - "durability": null, - "name": "Bandos chestplate", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13458", - "absorb": "4,0,9", - "bonuses": "0,0,0,-15,-10,98,93,105,-6,133,52,4,0,1,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A sturdy pair of tassets.", - "grand_exchange_price": "4228256", - "durability": null, - "name": "Bandos tassets", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13459", - "absorb": "3,0,6", - "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,0,1,0" - }, - { - "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" - }, - { - "examine": "The incredible blade of an Icyene.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "1579", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "850151", - "name": "Saradomin sword", - "archery_ticket_price": "0", - "id": "13461", - "bonuses": "0,82,60,0,0,0,0,0,0,0,0,82,0,2,0" - }, - { - "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" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13463", - "absorb": "0,0,0", - "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.", - "durability": null, - "name": "Task list", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13464", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A powerful dagger.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "defence_anim": "397", - "attack_anims": "396,396,395,396", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "16744", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger", - "archery_ticket_price": "0", - "id": "13465", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "defence_anim": "397", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p)", - "archery_ticket_price": "0", - "id": "13466", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "defence_anim": "397", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p+)", - "archery_ticket_price": "0", - "id": "13467", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "defence_anim": "397", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dragon dagger(p++)", - "archery_ticket_price": "0", - "id": "13468", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "examine": "A powerful axe.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "2", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7263", - "name": "Rune axe", - "archery_ticket_price": "0", - "id": "13469", - "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0" - }, - { - "examine": "A very powerful axe.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "2", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "3794577", - "name": "Dragon axe", - "archery_ticket_price": "0", - "id": "13470", - "bonuses": "-2,38,32,0,0,0,1,0,0,0,0,42,0,0,0" - }, - { - "examine": "A vicious looking axe.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "2", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "23993", - "name": "Rune battleaxe", - "archery_ticket_price": "0", - "id": "13471", - "bonuses": "-2,48,43,0,0,0,0,0,0,-1,0,64,0,0,0" - }, - { - "requirements": "{0,60}", - "examine": "A vicious looking axe.", - "has_special": "true", - "durability": null, - "destroy": "true", - "weight": "2.7", - "attack_speed": "6", - "weapon_interface": "2", - "absorb": "0,0,0", - "render_anim": "2586", - "defence_anim": "397", - "attack_anims": "395,395,395,395", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "123019", - "name": "Dragon battleaxe", - "archery_ticket_price": "0", - "id": "13472", - "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0" - }, - { - "examine": "I don't think it's intended for joinery.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "render_anim": "1430", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "24357", - "name": "Rune warhammer", - "archery_ticket_price": "0", - "id": "13473", - "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0" - }, - { - "examine": "A razor-sharp longsword", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "1582", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "18420", - "name": "Rune longsword", - "archery_ticket_price": "0", - "id": "13474", - "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0" - }, - { - "examine": "A very powerful sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "1582", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "58355", - "name": "Dragon longsword", - "archery_ticket_price": "0", - "id": "13475", - "bonuses": "58,69,-2,0,0,0,3,2,0,0,0,71,0,0,0" - }, - { - "examine": "A vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1582", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "14690", - "name": "Rune scimitar", - "archery_ticket_price": "0", - "id": "13476", - "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0" - }, - { - "examine": "A vicious, curved sword.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1928", - "attack_anims": "390,390,381,390", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "60405", - "name": "Dragon scimitar", - "archery_ticket_price": "0", - "id": "13477", - "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0" - }, - { - "examine": "A dragon halberd.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "15", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "200189", - "name": "Dragon halberd", - "archery_ticket_price": "0", - "id": "13478", - "bonuses": "70,95,0,-4,0,-1,4,5,0,0,0,89,0,0,0" - }, - { - "requirements": "{0,60}", - "examine": "A spiky mace.", - "has_special": "true", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "8", - "absorb": "0,0,0", - "defence_anim": "397", - "attack_anims": "390,390,381,390", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "28817", - "name": "Dragon mace", - "archery_ticket_price": "0", - "id": "13479", - "bonuses": "40,-2,60,0,0,0,0,0,0,0,0,55,0,5,0" - }, - { - "examine": "Used for mining.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "18546", - "name": "Rune pickaxe", - "archery_ticket_price": "0", - "id": "13480", - "bonuses": "26,-2,24,0,0,0,1,0,0,0,0,29,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A series of connected metal rings.", - "grand_exchange_price": "2280904", - "durability": null, - "name": "Dragon chainbody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13481", - "absorb": "4,0,9", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Provides excellent protection.", - "grand_exchange_price": "38062", - "durability": null, - "name": "Rune platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13482", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These look pretty heavy.", - "grand_exchange_price": "37355", - "durability": null, - "name": "Rune platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13487", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Looks pretty heavy.", - "grand_exchange_price": "160755", - "durability": null, - "name": "Dragon platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13488", - "absorb": "3,0,6", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Designer leg protection.", - "grand_exchange_price": "37248", - "durability": null, - "name": "Rune plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13489", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "This looks pretty heavy.", - "grand_exchange_price": "161006", - "durability": null, - "name": "Dragon plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13490", - "absorb": "3,0,6", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A full face helmet.", - "grand_exchange_price": "20388", - "durability": null, - "name": "Rune full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13496", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A wooden helmet.", - "grand_exchange_price": "5210", - "durability": null, - "name": "Splitbark helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13501", - "absorb": "3,1,0", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Provides good protection.", - "grand_exchange_price": "27106", - "durability": null, - "name": "Splitbark body", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13502", - "absorb": "6,3,0", - "bonuses": "0,0,0,10,-10,36,26,42,15,0,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These should protect my legs.", - "grand_exchange_price": "56050", - "durability": null, - "name": "Splitbark legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13503", - "absorb": "4,2,0", - "bonuses": "0,0,0,7,-7,22,20,25,10,0,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These should keep my hands safe.", - "grand_exchange_price": "2442", - "durability": null, - "name": "Splitbark gauntlets", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13504", - "absorb": "0,0,0", - "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Blue: A magical hat. ", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13508", - "absorb": "3,1,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The upper half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13509", - "absorb": "3,1,0", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The lower half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe bottom", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13510", - "absorb": "4,2,0", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Bright/Dark magical gloves.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13511", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Magical boots.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13512", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Blue: A magical hat. ", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13513", - "absorb": "3,1,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The upper half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13514", - "absorb": "3,1,0", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The lower half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe bottom", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13515", - "absorb": "4,2,0", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Bright/Dark magical gloves.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13516", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Magical boots.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13517", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Blue: A magical hat. ", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13518", - "absorb": "3,1,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The upper half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13519", - "absorb": "3,1,0", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The lower half of a magical robe.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic robe bottom", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13520", - "absorb": "4,2,0", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Bright/Dark magical gloves.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13521", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Magical boots.", - "grand_exchange_price": "1", - "durability": null, - "name": "Mystic boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13522", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "examine": "A nice sturdy bow made out of maple.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "193", - "name": "Maple longbow", - "archery_ticket_price": "0", - "id": "13523", - "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A shortbow made out of maple, still effective.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "111", - "name": "Maple shortbow", - "archery_ticket_price": "0", - "id": "13524", - "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A nice sturdy bow made out of yew.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "486", - "name": "Yew longbow", - "archery_ticket_price": "0", - "id": "13525", - "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A shortbow made out of yew, still effective.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "253", - "name": "Yew shortbow", - "archery_ticket_price": "0", - "id": "13526", - "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A nice sturdy magical bow.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1211", - "name": "Magic longbow", - "archery_ticket_price": "0", - "id": "13527", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Short and magical, but still effective.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "655", - "name": "Magic shortbow", - "archery_ticket_price": "0", - "id": "13528", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "An ancient Fremennik bow that was once used to battle the Moon Clan.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "21698", - "name": "Seercull", - "archery_ticket_price": "0", - "id": "13529", - "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A runite crossbow.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "17", - "absorb": "0,0,0", - "render_anim": "175", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "8890", - "name": "Rune crossbow", - "archery_ticket_price": "0", - "id": "13530", - "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "1696744720", - "durability": null, - "name": "Red partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13531", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "1314730495", - "durability": null, - "name": "Yellow partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13532", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "2147483647", - "durability": null, - "name": "Blue partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13533", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "1481913809", - "durability": null, - "name": "Green partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13534", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "1248900588", - "durability": null, - "name": "Purple partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13535", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "2147473360", - "durability": null, - "name": "White partyhat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13536", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "It's a Santa hat.", - "grand_exchange_price": "130433327", - "durability": null, - "name": "Santa hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13537", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Aaaarrrghhh ... I'm a monster.", - "grand_exchange_price": "104671288", - "durability": null, - "name": "Green h'ween mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13538", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Aaaarrrghhh ... I'm a monster.", - "grand_exchange_price": "127720943", - "durability": null, - "name": "Blue h'ween mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13539", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Aaaarrrghhh ... I'm a monster.", - "grand_exchange_price": "171640098", - "durability": null, - "name": "Red h'ween mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13540", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A powerful bow made from willow.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2606", - "name": "Willow comp bow", - "archery_ticket_price": "0", - "id": "13541", - "bonuses": "0,0,0,0,22,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A powerful bow made from yew wood.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "588", - "name": "Yew comp bow", - "archery_ticket_price": "0", - "id": "13542", - "bonuses": "0,0,0,0,49,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A powerful bow made from magic wood.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1345", - "name": "Magic comp bow", - "archery_ticket_price": "0", - "id": "13543", - "bonuses": "0,0,0,0,71,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient range protection crafted from white dragonhide.", - "grand_exchange_price": "74503559", - "durability": null, - "name": "3rd age range top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13544", - "absorb": "0,6,3", - "bonuses": "0,0,0,-15,30,55,47,60,60,55,52,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Fabulously ancient range protection crafted from white dragonhide.", - "grand_exchange_price": "47837515", - "durability": null, - "name": "3rd age range legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13545", - "absorb": "0,4,2", - "bonuses": "0,0,0,-10,17,31,25,33,30,31,25,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient range protection crafted from white dragonhide.", - "grand_exchange_price": "9746895", - "durability": null, - "name": "3rd age range coif", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13546", - "absorb": "0,3,1", - "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "grand_exchange_price": "138719269", - "durability": null, - "name": "3rd age robe top", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13548", - "absorb": "4,2,0", - "bonuses": "0,0,0,24,0,0,0,0,24,0,24,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "grand_exchange_price": "89961678", - "durability": null, - "name": "3rd age robe", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13549", - "absorb": "4,2,0", - "bonuses": "0,0,0,19,0,0,0,0,19,0,20,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "grand_exchange_price": "14976830", - "durability": null, - "name": "3rd age mage hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13550", - "absorb": "2,1,0", - "bonuses": "0,0,0,8,0,0,0,0,8,0,12,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "grand_exchange_price": "35019596", - "durability": null, - "name": "3rd age amulet", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13551", - "absorb": "0,0,0", - "bonuses": "0,0,0,15,0,0,0,0,10,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient armour beaten from magical silver.", - "grand_exchange_price": "244180930", - "durability": null, - "name": "3rd age platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13552", - "absorb": "3,0,6", - "bonuses": "0,0,0,-25,-2,78,76,83,-5,75,25,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient armour beaten from magical silver.", - "grand_exchange_price": "266509014", - "durability": null, - "name": "3rd age platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13553", - "absorb": "4,0,9", - "bonuses": "0,0,0,-20,0,96,108,113,-4,97,52,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Ancient armour beaten from magical silver.", - "grand_exchange_price": "50697678", - "durability": null, - "name": "3rd age full helmet", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13554", - "absorb": "2,0,4", - "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Lightweight boots ideal for rangers.", - "grand_exchange_price": "6253527", - "durability": null, - "name": "Ranger boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13556", - "absorb": "0,0,0", - "bonuses": "0,0,0,-10,8,2,3,4,2,0,10,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Slightly magical boots.", - "grand_exchange_price": "388955", - "durability": null, - "name": "Wizard boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13557", - "absorb": "0,0,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Endorsed by Robin Hood.", - "grand_exchange_price": "1962571", - "durability": null, - "name": "Robin hood hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13558", - "absorb": "0,0,0", - "bonuses": "0,0,0,-10,8,4,6,8,4,4,7,0,0,0,0" - }, - { - "destroy_message": "You will have talk to Explorer Jack in Lumbridge if you destroy this one.", - "shop_price": "350", - "examine": "A Lumbridge explorer's ring.", - "durability": null, - "name": "Explorer's ring 1", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13560", - "absorb": "0,0,0", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", - "equipment_slot": "12" - }, - { - "destroy_message": "You will have talk to ", - "shop_price": "350", - "examine": "A Lumbridge explorer's ring.", - "durability": null, - "name": "Explorer's ring 2", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13561", - "absorb": "0,0,0", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", - "equipment_slot": "12" - }, - { - "destroy_message": "You will need to obtain a new ring from Ned in Draynor if you destroy this one.", - "shop_price": "350", - "examine": "A Lumbridge explorer's ring.", - "durability": null, - "name": "Explorer's ring 3", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13562", - "absorb": "0,0,0", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", - "equipment_slot": "12" - }, - { - "shop_price": "38", - "examine": "An occasionally edible mushroom.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "100", - "name": "Button mushroom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13563", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "38", - "examine": "An occasionally edible mushroom.", - "grand_exchange_price": "100", - "durability": null, - "name": "Button mushroom", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13564" - }, - { - "durability": null, - "name": "Doll", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13565", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You may be able to find a new ring near where Summer stood outside the wilderness wall.", - "examine": "This magical ring once belonged to Summer's mother.", - "durability": null, - "name": "Jennica's ring", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13566", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "destroy_message": "Drop", - "examine": "Logs cut from a cursed magic tree.", - "durability": null, - "name": "Cursed magic logs", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13567", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Logs cut from a cursed magic tree.", - "durability": null, - "name": "Cursed magic logs", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13568" - }, - { - "destroy_message": "Hopefully, Jorral will be able to return them.", - "examine": "Notes on how to use the enchanted key.", - "durability": null, - "name": "Enchanted key notes", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13569", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Useful for mixing things.", - "grand_exchange_price": "106", - "durability": null, - "name": "Bowl", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13570", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "It's a bucket of milk.", - "grand_exchange_price": "177", - "durability": null, - "name": "Bucket of milk", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13571", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A fresh herb.", - "grand_exchange_price": "595", - "durability": null, - "name": "Clean guam", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13572", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You'll have to start again from stratch.", - "examine": "A bowlful of bees' precious product.", - "durability": null, - "name": "Bowl of honey", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13573", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You'll have to start again from stratch.", - "examine": "A bowlful of milk and honey.", - "durability": null, - "name": "Bowl of milk and honey", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13574", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You'll have to start again from stratch.", - "examine": "A bowl full of milk, honey and guam.", - "durability": null, - "name": "Bowl of milk, honey and guam", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13575", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Hopefully, the family has more papyrus and charcoal.", - "examine": "Your best impression of a map of RuneScape.", - "durability": null, - "name": "Map of runescape", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13576", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "Used for making notes.", - "grand_exchange_price": "290", - "durability": null, - "name": "Papyrus", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13577", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A lump of charcoal.", - "grand_exchange_price": "296", - "durability": null, - "name": "Charcoal", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13578", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A slightly muddy spade. ", - "grand_exchange_price": "130", - "durability": null, - "name": "Spade", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13579", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A plant pot filled with soil.", - "durability": null, - "name": "Plant pot", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13580", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Pear tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13581", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Mango tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13582", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Quince tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13583", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Lemon tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13584", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Avocado tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13585", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A seedling has been sown in this plant pot.", - "durability": null, - "name": "Plum tree seedling", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13586", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "4", - "examine": "I can grind things for potions in this.", - "grand_exchange_price": "169", - "durability": null, - "name": "Pestle and mortar", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13587", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "One of the ingredients for making fish food.", - "grand_exchange_price": "5", - "durability": null, - "name": "Ground guam", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13588", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "2", - "examine": "It's an empty bucket. ", - "grand_exchange_price": "39", - "durability": null, - "name": "Bucket", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13589", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "It seems to change temperature as I walk. (Making History)", - "durability": null, - "name": "Enchanted key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13591", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Hopefully, you can find another.", - "examine": "Some ladies jewellery.", - "durability": null, - "name": "A brooch", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13592", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will not get another chance to read the book!", - "examine": "It's falling apart, but full of knowledge.", - "durability": null, - "name": "Old tome", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13593", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Runecrafting guild teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13598", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Air altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13599", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Mind altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13600", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Water altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13601", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Earth altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13602", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Fire altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13603", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Body altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13604", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Cosmic altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13605", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Chaos altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13606", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Nature altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13607", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Law altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13608", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Death altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13609", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Blood altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13610", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "name": "Astral altar teleport", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13611", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13612", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13613", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Runecrafter robe", - "archery_ticket_price": "0", - "id": "13614", - "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13615", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13616", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "name": "Runecrafter skirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13617", - "absorb": "0,0,0", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "name": "Runecrafter gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13618", - "absorb": "0,0,0", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Runecrafter robe", - "archery_ticket_price": "0", - "id": "13619", - "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13620", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13621", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "name": "Runecrafter skirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13622", - "absorb": "0,0,0", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "name": "Runecrafter gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13623", - "absorb": "0,0,0", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Runecrafter robe", - "archery_ticket_price": "0", - "id": "13624", - "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13625", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13626", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "name": "Runecrafter skirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13627", - "absorb": "0,0,0", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "name": "Runecrafter gloves", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13628", - "absorb": "0,0,0", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Drop", - "shop_price": "15", - "examine": "Staff with a holder for a talisman on top.", - "durability": null, - "name": "Runecrafting staff", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "5", - "id": "13629", - "absorb": "0,0,0", - "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0", - "render_anim": "28" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Air talisman staff", - "archery_ticket_price": "0", - "id": "13630", - "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Mind talisman staff", - "archery_ticket_price": "0", - "id": "13631", - "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Water talisman staff", - "archery_ticket_price": "0", - "id": "13632", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Earth talisman staff", - "archery_ticket_price": "0", - "id": "13633", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Fire talisman staff", - "archery_ticket_price": "0", - "id": "13634", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Body talisman staff", - "archery_ticket_price": "0", - "id": "13635", - "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Cosmic talisman staff", - "archery_ticket_price": "0", - "id": "13636", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Chaos talisman staff", - "archery_ticket_price": "0", - "id": "13637", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "No", - "name": "Nature talisman staff", - "archery_ticket_price": "0", - "id": "13638", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Law talisman staff", - "archery_ticket_price": "0", - "id": "13639", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Death talisman staff", - "archery_ticket_price": "0", - "id": "13640", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Blood talisman staff", - "archery_ticket_price": "0", - "id": "13641", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "1500", - "examine": "A staff with a talisman on top.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "You will have to speak to Wizard Elriss to get another omni-talisman.", - "name": "Omni-talisman staff", - "archery_ticket_price": "0", - "id": "13642", - "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0" - }, - { - "shop_price": "20", - "examine": "Attracts Runecrafting energy orbs in the rune altars.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Yellow attractor", - "archery_ticket_price": "0", - "id": "13643", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Repels Runecrafting energy orbs in the rune altars.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Yellow repeller", - "archery_ticket_price": "0", - "id": "13644", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Attracts Runecrafting energy orbs in the rune altars.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Green attractor", - "archery_ticket_price": "0", - "id": "13645", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Repels Runecrafting energy orbs in the rune altars.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Green repeller", - "archery_ticket_price": "0", - "id": "13646", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "It creates barriers.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Green barrier generator", - "archery_ticket_price": "0", - "id": "13647", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "It creates barriers.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "If you destroy this, you will leave the game.", - "name": "Yellow barrier generator", - "archery_ticket_price": "0", - "id": "13648", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will have to speak to ", - "shop_price": "4", - "examine": "Many mysterious powers emanate from the talisman.", - "durability": null, - "name": "Omni-talisman", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13649", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "Wizard Elriss will exchange these for rewards.", - "durability": null, - "name": "Runecrafting guild token", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13650", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "Wizard Elriss will exchange these for rewards.", - "durability": null, - "name": "Runecrafting guild token", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13651", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "Wizard Elriss will exchange these for rewards.", - "durability": null, - "name": "Runecrafting guild token", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13652", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "Wizard Elriss will exchange these for rewards.", - "durability": null, - "name": "Runecrafting guild token", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13653", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "Wizard Elriss will exchange these for rewards.", - "durability": null, - "name": "Runecrafting guild token", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13654", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will have to speak to Wizard Elriss to get another omni-talisman.", - "shop_price": "100", - "examine": "A tiara infused with the properties of all talismans.", - "durability": null, - "name": "Omni-tiara", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13655", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13656", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13657", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "name": "Runecrafter hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13658", - "absorb": "0,0,0", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "shop_price": "100", - "examine": "It burns, burns, burns...", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "To get another Ring of Fire, you need to keep six beacons alight simultaneously and then talk to King Roald.", - "name": "Ring of fire", - "archery_ticket_price": "0", - "id": "13659", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "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.", - "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" - }, - { - "shop_price": "300", - "examine": "Danger: risk of fire.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "2", - "absorb": "0,0,0", - "render_anim": "1096", - "equipment_slot": "3", - "attack_anims": "395,395,401,395", - "destroy_message": "To get another Inferno Adze, you need to keep all fourteen beacons alight simultaneously and then talk to King Roald.", - "name": "Inferno adze", - "archery_ticket_price": "0", - "id": "13661", - "bonuses": "10,9,7,0,0,0,1,0,0,0,0,11,0,0,0", - "point_price": "75" - }, - { - "durability": null, - "name": "Fire beacon", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13662", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "The ticket says: Tearing this ticket will make you leave the circus. Choose wisely.", - "shop_price": "10", - "examine": "Tear ticket to exit the circus.", - "durability": null, - "name": "Circus ticket", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13663", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "10", - "examine": "Let's give them a big hand!", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "326", - "equipment_slot": "3", - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "name": "Giant's hand", - "archery_ticket_price": "0", - "id": "13666", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "A clown hat!", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Clown hat", - "archery_ticket_price": "0", - "id": "13667", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Buttoning this up is tricky.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "remove_sleeves": "true", - "name": "Clown shirt", - "archery_ticket_price": "0", - "id": "13668", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "These are my funny pants.", - "durability": null, - "name": "Clown leggings", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13669", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "For helping clowns stay upright.", - "durability": null, - "name": "Clown shoes", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13670", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "shop_price": "10", - "examine": "For making a happy noise.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "8", - "absorb": "0,0,0", - "render_anim": "327", - "equipment_slot": "3", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Tambourine", - "archery_ticket_price": "0", - "id": "13671", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "shop_price": "10", - "examine": "Very stylish.", - "durability": null, - "name": "Ringmaster hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13672", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "shop_price": "10", - "examine": "Fits nicely.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "remove_sleeves": "true", - "name": "Ringmaster shirt", - "archery_ticket_price": "0", - "id": "13673", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "shop_price": "10", - "examine": "Very stylish.", - "durability": null, - "name": "Ringmaster pants", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13674", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "shop_price": "10", - "examine": "Shiny.", - "durability": null, - "name": "Ringmaster boots", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13675", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "shop_price": "10", - "examine": "For shouting at people.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "8", - "absorb": "0,0,0", - "render_anim": "328", - "equipment_slot": "3", - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "name": "Mega-phonus", - "archery_ticket_price": "0", - "id": "13676", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Stretchy.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "remove_sleeves": "true", - "name": "Acrobat shirt", - "archery_ticket_price": "0", - "id": "13677", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "Very tight fitting.", - "durability": null, - "name": "Acrobat pants", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13678", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "These keep your feet off the floor.", - "durability": null, - "name": "Acrobat shoes", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13679", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "Oh no, I'm bald! Just kidding.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat hood", - "archery_ticket_price": "0", - "id": "13680", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "shop_price": "400", + "tradeable": "false" }, { - "shop_price": "10", - "examine": "Stretchy.", - "durability": null, + "id": "13278", + "name": "Broad arrow heads", + "examine": "For use against turoth and kurasks.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "remove_sleeves": "true", - "name": "Acrobat shirt", - "archery_ticket_price": "0", - "id": "13681", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "destroy_message": "Drop", + "durability": null, + "shop_price": "50", + "tradeable": "false" }, { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "Very tight fitting.", - "durability": null, - "name": "Acrobat pants", + "id": "13279", + "name": "Unfinished broad bolts", + "examine": "For use against turoth and kurasks.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13682", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" + "destroy_message": "Drop", + "durability": null, + "shop_price": "50", + "tradeable": "false" }, { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "These keep your feet off the floor.", - "durability": null, - "name": "Acrobat shoes", + "id": "13280", + "name": "Broad-tipped bolts", + "examine": "For use against turoth and kurasks.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,100", "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "50", + "tradeable": "true" + }, + { + "id": "13281", + "name": "Ring of slaying(8)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13683", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_head": "true", - "shop_price": "10", - "examine": "Oh no, I'm bald! Just kidding.", - "durability": null, - "destroy": "true", - "attack_speed": "4", + "id": "13282", + "name": "Ring of slaying(7)", + "examine": "An enchanted ring.", "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat hood", - "archery_ticket_price": "0", - "id": "13684", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "name": "A stylish hat", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13685", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "name": "A stylish hat", - "destroy": "true", + "id": "13283", + "name": "Ring of slaying(6)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13686", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "destroy_message": "You will need to speak to the ringmaster to get another.", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "name": "A stylish hat", - "destroy": "true", + "id": "13284", + "name": "Ring of slaying(5)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13687", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "0" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_head": "true", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "destroy": "true", - "attack_speed": "4", + "id": "13285", + "name": "Ring of slaying(4)", + "examine": "An enchanted ring.", "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "A stylish hat", - "archery_ticket_price": "0", - "id": "13688", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "A stylish hat", - "archery_ticket_price": "0", - "id": "13689", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "A stylish hat", - "archery_ticket_price": "0", - "id": "13690", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13691", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", + "id": "13286", + "name": "Ring of slaying(3)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13692", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", + "id": "13287", + "name": "Ring of slaying(2)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13693", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", + "id": "13288", + "name": "Ring of slaying(1)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13694", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" }, { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13695", + "id": "13290", + "name": "Leaf-bladed sword", + "examine": "A razor-sharp sword.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", "archery_ticket_price": "0", + "attack_anims": "381,381,390,381", + "attack_audios": "2517,2517,2500,2517", "attack_speed": "4", - "id": "13696", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "shop_price": "10", + "bonuses": "67,62,0,0,0,0,0,0,0,0,0,50,0,0,0", + "defence_anim": "397", + "destroy": "false", "durability": null, - "name": "Leggings", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13697", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Leggings", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13698", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Leggings", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13699", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Skirt", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13700", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Skirt", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13701", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Skirt", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13702", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "10", - "durability": null, - "name": "Shoes", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13703", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "shop_price": "10", - "durability": null, - "name": "Shoes", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13704", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "shop_price": "10", - "durability": null, - "name": "Shoes", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13705", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "shop_price": "10", - "durability": null, - "name": "Shoes", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13706", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "destroy_message": "You can get another Tightrope 101 by speaking to the Agility assistant.", - "examine": "Shows you the moves you need to succeed!", - "durability": null, - "name": "Tightrope 101", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13707", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another Tightrope 101 by speaking to the Agility assistant.", - "examine": "Shows you the moves you need to succeed!", - "durability": null, - "name": "Tightrope 101", + "equip_audio": "2248", + "equipment_slot": "3", + "rare_item": "true", + "requirements": "{0,50}-{18,55}", + "run_anim": "824", + "shop_price": "68000", + "stand_anim": "813", + "stand_turn_anim": "823", "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13708" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 10 is advised when juggling these.", - "durability": null, - "name": "Balls (level 10 approx.)", - "destroy": "true", + "id": "13291", + "name": "Leaf-bladed sword", + "examine": "A razor-sharp sword.", "archery_ticket_price": "0", "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "68000", + "tradeable": "true" + }, + { + "id": "13292", + "name": "Letter", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13293", + "name": "Plans", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13294", + "name": "Charcoal rubbing", + "examine": "A charcoal rubbing taken from 50% Luke.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop these plans, they will be lost and you will need to see Captain Donnie for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13295", + "name": "Frank's mark", + "examine": "Signature from Frank.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop Frank's mark, you will need to see him to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13296", + "name": "Crude lockpick", + "examine": "A device for opening locked doors.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop these lockpicks, you will need to look under the mattress to get another set.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13297", + "name": "Customs shirt", + "examine": "The top of a Custom Officer's uniform.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this shirt you will need to kill another Customs officer for a replacement.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false" + }, + { + "id": "13298", + "name": "Customs trousers", + "examine": "The trousers of a Custom Officer's uniform.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop these trousers you will need to kill another Customs officer for a replacement.", + "durability": null, + "equipment_slot": "7", + "tradeable": "false" + }, + { + "id": "13299", + "name": "Customs hat", + "examine": "The hat of a Custom Officer's uniform.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this hat you will need to kill another Customs officer for a replacement.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { + "id": "13300", + "name": "File", + "examine": "A folder containing all the marks I have gathered.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13301", + "name": "File", + "examine": "A folder containing all the marks I have gathered.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13302", + "name": "Brooch", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13303", + "name": "Izzy's mark", + "examine": "Signature from Izzy No-Beard.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop Izzy's No-Beard's mark, you will need to see him to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13304", + "name": "Key", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13305", + "name": "Crowbar", + "examine": "This could be useful.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this crowbar, you will need to get another from the chest on the sunken ship.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13307", + "name": "Idol of many heads", + "examine": "An idol. It has many heads.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop the idol, you will need to go diving to get another replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13308", + "name": "Bill teach's mark", + "examine": "Signature from Bill Teach.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop Bill Teach's mark, you will need to see him to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13309", + "name": "Paper", + "examine": "This is generally used for writing on.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this paper, you will need to call to the guards for a replacement. ", + "durability": null, + "tradeable": "false" + }, + { + "id": "13310", + "name": "Confession", + "examine": "A written confession.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this note, you'll need to call the guards for replacement paper.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13311", + "name": "Note", + "examine": "Swap this note at any bank for the equivalent item.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "tradeable": "false" + }, + { + "id": "13312", + "name": "Ink bottle", + "examine": "An empty ink bottle./This is full of ink.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this ink, you will need to call to the guards for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13313", + "name": "Inky paper", + "examine": "It's got ink all over it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this paper you will need to call to the guards to replace it and the ink.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13314", + "name": "Ink bottle", + "examine": "An empty ink bottle./This is full of ink.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this ink, you will need to call to the guards for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13315", + "name": "Smashed bottle", + "examine": "It's smashed. I should mind my fingers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this bottle, you will need to call to the guards for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13316", + "name": "Prison uniform top", + "examine": "The top part of a prisoner's uniform.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this top, then you will need to speak to the guards to get it replaced.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false" + }, + { + "id": "13317", + "name": "Prison uniform trousers", + "examine": "The bottom part of a prisoner's uniform.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop these trousers, then you will need to speak to the guards to get it replaced.", + "durability": null, + "equipment_slot": "7", + "tradeable": "false" + }, + { + "id": "13318", + "name": "Fishy prison uniform top", + "examine": "It's covered in fish slop.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this top, you will need to speak to the guards to get it replaced, then find a way to get stew on it again.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false" + }, + { + "id": "13319", + "name": "Tin cup", + "examine": "A standard prison-issue tin cup.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this cup it will be destroyed and you will have to pick up another.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "13320", + "name": "Tin cup", + "examine": "A standard prison-issue tin cup.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this cup it will be destroyed and you will have to pick up another.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "13321", + "name": "Pipe", + "examine": "A pipe that belongs in a cannon stand.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Destroy", + "durability": null, + "tradeable": "false" + }, + { + "id": "13322", + "name": "Accordion", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13323", + "name": "Accordion", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13324", + "name": "Accordion", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this accordion, it will be destroyed and you will have to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13325", + "name": "Vacuum pump", + "examine": "A very crude vacuum pump.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this accordion, pipe and inky paper, it will all be destroyed and you will need to get all the items back again.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13326", + "name": "Vacuum pump and gull", + "examine": "It sounds like a law firm.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this, you will lose all the items you put in it, as well as the seagull.", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" + }, + { + "id": "13327", + "name": "Quill", + "examine": "From a bird - can be used for writing.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this quill, you will need to talk to the guard for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13328", + "name": "Inky quill", + "examine": "From a bird - can be used for writing. It has some ink on the tip.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null, + "tradeable": "false" + }, + { + "id": "13329", + "name": "File", + "examine": "A folder containing all the marks I have gathered.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13330", + "name": "File", + "examine": "A folder containing all the marks I have gathered.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this file, it and all the marks inside it will be destroyed.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13331", + "name": "Ex-parrot", + "examine": "A very dead rotten parrot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this dead parrot, there is a chance that bad, bad things will happen to you - unless you see 50 Ships Mufassah for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13332", + "name": "Magical cage", + "examine": "It feels very cold to the touch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this cage, you will need to see Bill Teach for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13333", + "name": "Ex-parrot in a magic cage", + "examine": "A very dead rotten parrot in a magical cage.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this dead parrot in a cage (and who wouldn't) then you will need to see ", + "durability": null, + "tradeable": "false" + }, + { + "id": "13334", + "name": "Ex-ex-parrot in a magic cage", + "examine": "A very alive rotten parrot in a magic cage.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop the cage, the ex-ex-parrot will escape.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13335", + "name": "Ex-ex-parrot", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13336", + "name": "Ex-ex-parrot", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13337", + "name": "Pirate impling jar", + "examine": "A pirate impling in a jar.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "shop_price": "50", + "tradeable": "true" + }, + { + "id": "13338", + "name": "Pirate impling jar", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "If you drop this it will disappear. Are you sure you want to do this?", + "durability": null, + "shop_price": "50", + "tradeable": "true" + }, + { + "id": "13339", + "name": "Bandana and eyepatch", + "examine": "A white bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13340", + "name": "Bandana and eyepatches", + "examine": "A white bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13341", + "name": "Bandana and eyepatch", + "examine": "A red bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13342", + "name": "Bandana and eyepatches", + "examine": "A red bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13343", + "name": "Bandana and eyepatch", + "examine": "A blue bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13344", + "name": "Bandana and eyepatches", + "examine": "A blue bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13345", + "name": "Bandana and eyepatch", + "examine": "A brown bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13346", + "name": "Bandana and eyepatches", + "examine": "A brown bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13347", + "name": "Bandana and eyepatch", + "examine": "A grey bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13348", + "name": "Bandana and eyepatches", + "examine": "A grey bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13349", + "name": "Bandana and eyepatch", + "examine": "A purple bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13350", + "name": "Bandana and eyepatches", + "examine": "A purple bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13351", + "name": "Bandana and eyepatch", + "examine": "A orange bandana and a patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13352", + "name": "Bandana and eyepatches", + "examine": "A orange bandana and a patch for both eyes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13353", + "name": "Double eyepatches", + "examine": "It's dark, and I'm wearing two patches. Hit it!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13354", + "name": "Pirate hat and eyepatches", + "examine": "Two patches and a hat.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "false" + }, + { + "id": "13355", + "name": "Left eyepatch", + "examine": "A patch for the left eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "100", + "tradeable": "false" + }, + { + "id": "13356", + "name": "Left eyepatch", + "examine": "A patch for the left eye.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "false" + }, + { + "id": "13357", + "name": "Pirate hat and eyepatch", + "examine": "A hat and a patch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "false" + }, + { + "id": "13358", + "name": "Stripy pirate shirt", + "examine": "A seaworthy grey shirt.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13359", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "13360", + "name": "Stripy pirate shirt", + "examine": "A seaworthy purple shirt.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13361", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "13362", + "name": "Stripy pirate shirt", + "examine": "A seaworthy orange shirt.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13363", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "300", + "tradeable": "true" + }, + { + "id": "13364", + "name": "Pirate leggings", + "examine": "A seaworthy pair of grey trousers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13365", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "13366", + "name": "Pirate leggings", + "examine": "A seaworthy pair of purple trousers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13367", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "13368", + "name": "Pirate leggings", + "examine": "A seaworthy pair of orange trousers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13369", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "13370", + "name": "Bandana", + "examine": "A grey bandana.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13371", + "name": "Bandana", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13372", + "name": "Bandana", + "examine": "A purple bandana.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13373", + "name": "Bandana", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13374", + "name": "Bandana", + "examine": "An orange bandana.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13375", + "name": "Bandana", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "13376", + "name": "Bandana and eyepatch", + "examine": "A purple bandana and a patch for the right eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13377", + "name": "Bandana and eyepatch", + "examine": "A orange bandana and a patch for the right eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13378", + "name": "Bandana and eyepatch", + "examine": "A grey bandana and a patch for the right eye.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "100", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "13379", + "name": "Rock fragments", + "examine": "Some rock fragments.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Destroy", + "durability": null, + "tradeable": "false" + }, + { + "id": "13380", + "name": "Introduction letter", + "examine": "Note from Teach to Brass Hand Harry.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop the letter, it will be destroyed and you'll need to see Bill Teach for another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13381", + "name": "Broken hand", + "examine": "One of many broken hands.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13382", + "name": "Broken hand", + "examine": "One of many broken hands.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13383", + "name": "Broken hand", + "examine": "One of many broken hands.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13384", + "name": "Broken hand", + "examine": "One of many broken hands.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13385", + "name": "Broken hand", + "examine": "One of many broken hands.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The hand is already broken and dropping it will destroy it completely. You will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13386", + "name": "Fingers", + "examine": "Wall beast fingers in a white fern sauce.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "tradeable": "false" + }, + { + "id": "13387", + "name": "Thumb", + "examine": "A brass thumb.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13388", + "name": "Hand", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13389", + "name": "Wrist", + "examine": "A brass wrist.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13390", + "name": "Brace", + "examine": "A brass brace.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "This hand part is small and delicate, and dropping it here will lose it. If you lose it, you will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13391", + "name": "Brass hand", + "examine": "A completed brass hand.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this hand, you will destroy it and you will have to remake it from parts that Brass Hand Harry has.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13392", + "name": "Ink pad", + "examine": "It's soaked with ink.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this ink pad, you will lose it and will need to see Brass Hand Harry for a replacement.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13393", + "name": "Inky hand", + "examine": "An inky brass hand.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this hand, you will destroy it and you will have to remake it from parts that Brass Hand Harry has.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13394", + "name": "Paper", + "examine": "This is generally used for writing on.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this paper, you will need to call to the guards for a replacement. ", + "durability": null, + "tradeable": "false" + }, + { + "id": "13395", + "name": "Brass hand harry's mark", + "examine": "Handprint from Brass Hand Harry.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop Brass Hand Harry's mark, it will be destroyed and you need to see him to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13396", + "name": "Fake beard", + "examine": "Makes me itch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { + "id": "13397", + "name": "Fake moustache and nose", + "examine": "It looks like someone has sneezed in it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { + "id": "13398", + "name": "Fake monocle, moustache and nose", + "examine": "Why is the monocle nailed to it?", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { + "id": "13399", + "name": "Curly wig", + "examine": "You can barely see where it connected to the mop handle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false" + }, + { + "id": "13400", + "name": "Straight wig", + "examine": "It looks like it is part-dog, part-mould.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this disguise you will need to see Captain Braindeath for a replacement.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "tradeable": "false" + }, + { + "id": "13401", + "name": "Order", + "examine": "An order simply for 'rum'.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop this order for 'rum' you will need to speak to the Rusty Anchor barman to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13402", + "name": "Braindeath's mark", + "examine": "Signature of Captain Braindeath.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop Captain Braindeath's mark, you will need to speak to him to get another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13403", + "name": "Golden tinderbox", + "examine": "A replica tinderbox made of solid gold.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "300", + "tradeable": "false" + }, + { + "id": "13405", + "name": "Dark bow", + "examine": "A bow from a darker dimension.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_audios": "3731,0,0,0", + "attack_speed": "9", + "bonuses": "0,0,0,0,95,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "3738", + "has_special": "true", + "lendable": "true", + "render_anim": "303", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13406", + "name": "Ancient staff", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "10,-1,40,15,0,2,3,1,15,0,5,50,0,-1,10", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "28", + "weapon_interface": "1" + }, + { + "id": "13407", + "name": "Archer helm", + "examine": "This helmet is worn by archers.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13408", + "name": "Berserker helm", + "examine": "This helmet is worn by berserkers.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13409", + "name": "Warrior helm", + "examine": "This helm is worn by warriors.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13410", + "name": "Farseer helm", + "examine": "This helm is worn by farseers.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13411", + "name": "Rock-shell helm", + "examine": "Protective headwear made from crabs. Better than it sounds.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,6,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13412", + "name": "Rock-shell plate", + "examine": "A sturdy body armour made from rock crab pieces.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13413", + "name": "Rock-shell legs", + "examine": "Some tough leggings made from rock crab parts.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13414", + "name": "Spined helm", + "examine": "A helm fit for any Fremennik ranger.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,-6,-6,-6,6,6,6,6,6,0,6,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13415", + "name": "Spined body", + "examine": "A constant reminder that I'm above a Dagannoth in the food chain.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13416", + "name": "Spined chaps", + "examine": "Stylish leg armour for rangers with a lingering smell of raw fish...", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13417", + "name": "Skeletal helm", + "examine": "Make your foes cower by wearing a skull as a helmet!", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,-2,10,9,11,3,0,6,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13418", + "name": "Skeletal top", + "examine": "The bones in this armour seems to vibrate with a magic quality...", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,8,-10,35,25,42,15,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13419", + "name": "Skeletal bottoms", + "examine": "A superior set of strengthened slacks for any self respecting seer.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,6,-7,22,20,24,10,0,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13420", + "name": "Spined boots", + "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,1,0,0,0,1,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13421", + "name": "Rock-shell boots", + "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13422", + "name": "Skeletal boots", + "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13423", + "name": "Spined gloves", + "examine": "Fremennik gloves stitched together from spined dagannoth hide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,2,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13424", + "name": "Rock-shell gloves", + "examine": "Fremennik gloves stitched together from rock crab shell shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13425", + "name": "Skeletal gloves", + "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13426", + "name": "Seers ring", + "examine": "A mysterious ring that can fill the wearer with magical power...", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13427", + "name": "Archers ring", + "examine": "A fabled ring that improves the wearer's skill with a bow...", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,4,0,0,0,0,4,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13428", + "name": "Warrior ring", + "examine": "A legendary ring once worn by Fremennik warriors.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13429", + "name": "Berserker ring", + "examine": "A ring reputed to bring out a berserk fury in its wearer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,4,0,0,0,4,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13430", + "name": "Dragon 2h sword", + "examine": "A two-handed dragon sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "-4,92,80,-4,0,0,0,0,0,-1,0,93,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "124", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13431", + "name": "Crayfish cage", + "examine": "Useful for catching crayfish.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "13432", + "name": "Crayfish cage", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "13433", + "name": "Crayfish", + "examine": "Some cooked crayfish. Eat it to heal.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "13434", + "name": "Crayfish", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "13435", + "name": "Raw crayfish", + "examine": "I should try cooking this. Used in Cooking (1).", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "13436", + "name": "Raw crayfish", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "13437", + "name": "Burnt crayfish", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "false", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "13438", + "name": "Burnt crayfish", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "1", + "tradeable": "true" + }, + { + "id": "13439", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13440", + "name": "Bag of runes", + "examine": "This is a bag of runes for Sir Vant.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13442", + "name": "Amulet of fury", + "examine": "A very powerful onyx amulet.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,0,5,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13443", + "name": "Obsidian cape", + "examine": "A cape woven of obsidian plates.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,9,9,9,9,9,9,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13444", + "name": "Abyssal whip", + "examine": "A weapon from the Abyss.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,82,0,0,0,0,0,0,0,0,0,82,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2249", + "has_special": "true", + "lendable": "true", + "render_anim": "1578", + "weapon_interface": "11" + }, + { + "id": "13445", + "name": "Granite maul", + "examine": "Simplicity is the best weapon.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "0,0,81,0,0,0,0,0,0,0,0,79,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "27", + "two_handed": "true", + "weapon_interface": "10" + }, + { + "id": "13446", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13447", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13448", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13449", + "name": "Dragon full helm", + "examine": "Protects your head and looks impressive too.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,45,48,41,-1,46,12,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13450", + "name": "Armadyl godsword", + "examine": "A beautiful, heavy sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1579", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13451", + "name": "Bandos godsword", + "examine": "A brutally heavy sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1579", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13452", + "name": "Saradomin godsword", + "examine": "A gracious, heavy sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1579", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13453", + "name": "Zamorak godsword", + "examine": "A terrifying, heavy sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,132,80,0,0,0,0,0,0,0,0,132,0,8,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1579", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13454", + "name": "Zamorakian spear", + "examine": "An evil spear.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "85,65,65,0,0,13,13,12,0,13,0,75,0,2,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "1581", + "two_handed": "true", + "weapon_interface": "14" + }, + { + "id": "13455", + "name": "Armadyl helmet", + "examine": "A helmet of great craftmanship.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-5,-5,-5,-5,10,6,6,10,10,8,12,0,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "lendable": "true" + }, + { + "id": "13456", + "name": "Armadyl chestplate", + "examine": "A chestplate of great craftsmanship", + "absorb": "0,10,5", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-7,-7,-7,-15,33,56,48,61,70,57,52,0,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "lendable": "true" + }, + { + "id": "13457", + "name": "Armadyl plateskirt", + "examine": "A chainskirt of great craftsmanship.", + "absorb": "0,7,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-6,-6,-6,-10,20,32,26,34,40,33,25,0,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "lendable": "true" + }, + { + "id": "13458", + "name": "Bandos chestplate", + "examine": "A sturdy chestplate.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,-10,98,93,105,-6,133,52,4,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13459", + "name": "Bandos tassets", + "examine": "A sturdy pair of tassets.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13460", + "name": "Bandos boots", + "examine": "Some sturdy boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,-3,17,18,19,0,0,15,0,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2237", + "lendable": "true" + }, + { + "id": "13461", + "name": "Saradomin sword", + "examine": "The incredible blade of an Icyene.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,82,60,0,0,0,0,0,0,0,0,82,0,2,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1579", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13462", + "name": "Dragon boots", + "examine": "These will protect my feet.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,16,17,18,0,0,15,4,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2237", + "lendable": "true" + }, + { + "id": "13463", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "13464", + "name": "Task list", + "examine": "This reminds me of my current job and who set me to the task.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another by talking about jobs with citizens and tutors around Lumbridge.", + "durability": null, + "tradeable": "false" + }, + { + "id": "13465", + "name": "Dragon dagger", + "examine": "A powerful dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "weapon_interface": "5" + }, + { + "id": "13466", + "name": "Dragon dagger(p)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "shop_price": "1" + }, + { + "id": "13467", + "name": "Dragon dagger(p+)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "shop_price": "1" + }, + { + "id": "13468", + "name": "Dragon dagger(p++)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "shop_price": "1" + }, + { + "id": "13469", + "name": "Rune axe", + "examine": "A powerful axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2229", + "lendable": "true", + "weapon_interface": "2" + }, + { + "id": "13470", + "name": "Dragon axe", + "examine": "A very powerful axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "-2,38,32,0,0,0,1,0,0,0,0,42,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2229", + "has_special": "true", + "lendable": "true", + "weapon_interface": "2" + }, + { + "id": "13471", + "name": "Rune battleaxe", + "examine": "A vicious looking axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "-2,48,43,0,0,0,0,0,0,-1,0,64,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "weapon_interface": "2" + }, + { + "id": "13472", + "name": "Dragon battleaxe", + "examine": "A vicious looking axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "2586", + "requirements": "{0,60}", + "weapon_interface": "2", + "weight": "2.7" + }, + { + "id": "13473", + "name": "Rune warhammer", + "examine": "I don't think it's intended for joinery.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "-4,-4,53,-4,0,0,0,0,0,0,0,48,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2233", + "lendable": "true", + "render_anim": "1430", + "weapon_interface": "10" + }, + { + "id": "13474", + "name": "Rune longsword", + "examine": "A razor-sharp longsword", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "38,47,-2,0,0,0,3,2,0,0,0,49,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "1582", + "weapon_interface": "5" + }, + { + "id": "13475", + "name": "Dragon longsword", + "examine": "A very powerful sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "58,69,-2,0,0,0,3,2,0,0,0,71,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "1582", + "weapon_interface": "5" + }, + { + "id": "13476", + "name": "Rune scimitar", + "examine": "A vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "1582", + "weapon_interface": "6" + }, + { + "id": "13477", + "name": "Dragon scimitar", + "examine": "A vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_speed": "4", + "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "has_special": "true", + "lendable": "true", + "render_anim": "1928", + "weapon_interface": "6" + }, + { + "id": "13478", + "name": "Dragon halberd", + "examine": "A dragon halberd.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "70,95,0,-4,0,-1,4,5,0,0,0,89,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "15" + }, + { + "id": "13479", + "name": "Dragon mace", + "examine": "A spiky mace.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "40,-2,60,0,0,0,0,0,0,0,0,55,0,5,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2246", + "has_special": "true", + "lendable": "true", + "requirements": "{0,60}", + "weapon_interface": "8", + "weight": "1.8" + }, + { + "id": "13480", + "name": "Rune pickaxe", + "examine": "Used for mining.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "26,-2,24,0,0,0,1,0,0,0,0,29,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2232", + "lendable": "true", + "weapon_interface": "4" + }, + { + "id": "13481", + "name": "Dragon chainbody", + "examine": "A series of connected metal rings.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13482", + "name": "Rune platebody", + "examine": "Provides excellent protection.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13483", + "name": "Green d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-20,15,40,32,45,20,40,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13484", + "name": "Blue d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13485", + "name": "Red d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,25,50,42,55,40,50,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13486", + "name": "Black d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13487", + "name": "Rune platelegs", + "examine": "These look pretty heavy.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13488", + "name": "Dragon platelegs", + "examine": "Looks pretty heavy.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13489", + "name": "Rune plateskirt", + "examine": "Designer leg protection.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13490", + "name": "Dragon plateskirt", + "examine": "This looks pretty heavy.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13491", + "name": "Green d'hide chaps", + "examine": "100% real dragonhide.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13492", + "name": "Blue d'hide chaps", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,5,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13493", + "name": "Red d'hide chaps", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,14,28,22,30,20,28,25,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13494", + "name": "Black d'hide chaps", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,7,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13495", + "name": "Dragon med helm", + "examine": "Makes the wearer pretty intimidating.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "lendable": "true" + }, + { + "id": "13496", + "name": "Rune full helm", + "examine": "A full face helmet.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13497", + "name": "Green d'hide vamb", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13498", + "name": "Blue d'hide vamb", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13499", + "name": "Red d'hide vamb", + "examine": "Vambraces made from 100% real dragonhide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13500", + "name": "Black d'hide vamb", + "examine": "Vambraces made from 100% real dragonhide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13501", + "name": "Splitbark helm", + "examine": "A wooden helmet.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13502", + "name": "Splitbark body", + "examine": "Provides good protection.", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,10,-10,36,26,42,15,0,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13503", + "name": "Splitbark legs", + "examine": "These should protect my legs.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,7,-7,22,20,25,10,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13504", + "name": "Splitbark gauntlets", + "examine": "These should keep my hands safe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13505", + "name": "Splitbark boots", + "examine": "Wooden foot protection.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,-1,3,2,4,2,0,9,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2237", + "lendable": "true" + }, + { + "id": "13506", + "name": "Dragon sq shield", + "examine": "An ancient and powerful looking Dragon Square shield.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13507", + "name": "Rune kiteshield", + "examine": "A large metal shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13508", + "name": "Mystic hat", + "examine": "Blue: A magical hat. ", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13509", + "name": "Mystic robe top", + "examine": "The upper half of a magical robe.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13510", + "name": "Mystic robe bottom", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13511", + "name": "Mystic gloves", + "examine": "Bright/Dark magical gloves.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13512", + "name": "Mystic boots", + "examine": "Magical boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13513", + "name": "Mystic hat", + "examine": "Blue: A magical hat. ", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13514", + "name": "Mystic robe top", + "examine": "The upper half of a magical robe.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13515", + "name": "Mystic robe bottom", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13516", + "name": "Mystic gloves", + "examine": "Bright/Dark magical gloves.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13517", + "name": "Mystic boots", + "examine": "Magical boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13518", + "name": "Mystic hat", + "examine": "Blue: A magical hat. ", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13519", + "name": "Mystic robe top", + "examine": "The upper half of a magical robe.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13520", + "name": "Mystic robe bottom", + "examine": "The lower half of a magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13521", + "name": "Mystic gloves", + "examine": "Bright/Dark magical gloves.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13522", + "name": "Mystic boots", + "examine": "Magical boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13523", + "name": "Maple longbow", + "examine": "A nice sturdy bow made out of maple.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13524", + "name": "Maple shortbow", + "examine": "A shortbow made out of maple, still effective.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,29,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13525", + "name": "Yew longbow", + "examine": "A nice sturdy bow made out of yew.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13526", + "name": "Yew shortbow", + "examine": "A shortbow made out of yew, still effective.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13527", + "name": "Magic longbow", + "examine": "A nice sturdy magical bow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "has_special": "true", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13528", + "name": "Magic shortbow", + "examine": "Short and magical, but still effective.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "has_special": "true", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13529", + "name": "Seercull", + "examine": "An ancient Fremennik bow that was once used to battle the Moon Clan.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,69,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13530", + "name": "Rune crossbow", + "examine": "A runite crossbow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "lendable": "true", + "render_anim": "175", + "weapon_interface": "17" + }, + { + "id": "13531", + "name": "Red partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13532", + "name": "Yellow partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13533", + "name": "Blue partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13534", + "name": "Green partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13535", + "name": "Purple partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13536", + "name": "White partyhat", + "examine": "A nice hat from a cracker.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13537", + "name": "Santa hat", + "examine": "It's a Santa hat.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13538", + "name": "Green h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13539", + "name": "Blue h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13540", + "name": "Red h'ween mask", + "examine": "Aaaarrrghhh ... I'm a monster.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13541", + "name": "Willow comp bow", + "examine": "A powerful bow made from willow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,22,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13542", + "name": "Yew comp bow", + "examine": "A powerful bow made from yew wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,49,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13543", + "name": "Magic comp bow", + "examine": "A powerful bow made from magic wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,71,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "13544", + "name": "3rd age range top", + "examine": "Ancient range protection crafted from white dragonhide.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,30,55,47,60,60,55,52,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13545", + "name": "3rd age range legs", + "examine": "Fabulously ancient range protection crafted from white dragonhide.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,17,31,25,33,30,31,25,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13546", + "name": "3rd age range coif", + "examine": "Ancient range protection crafted from white dragonhide.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13547", + "name": "3rd age vambraces", + "examine": "Fabulously ancient range protection crafted from white dragonhide.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-11,11,6,5,7,9,0,5,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2241", + "lendable": "true" + }, + { + "id": "13548", + "name": "3rd age robe top", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,24,0,0,0,0,24,0,24,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13549", + "name": "3rd age robe", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,19,0,0,0,0,19,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13550", + "name": "3rd age mage hat", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,8,0,0,0,0,8,0,12,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13551", + "name": "3rd age amulet", + "examine": "Ancient mage protection enchanted in the Third-Age.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,10,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13552", + "name": "3rd age platelegs", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-25,-2,78,76,83,-5,75,25,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13553", + "name": "3rd age platebody", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-20,0,96,108,113,-4,97,52,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13554", + "name": "3rd age full helmet", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13555", + "name": "3rd age kiteshield", + "examine": "Ancient armour beaten from magical silver.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,-4,63,65,61,-3,63,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13556", + "name": "Ranger boots", + "examine": "Lightweight boots ideal for rangers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,2,3,4,2,0,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13557", + "name": "Wizard boots", + "examine": "Slightly magical boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13558", + "name": "Robin hood hat", + "examine": "Endorsed by Robin Hood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,4,6,8,4,4,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13560", + "name": "Explorer's ring 1", + "examine": "A Lumbridge explorer's ring.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", + "destroy": "true", + "destroy_message": "You will have talk to Explorer Jack in Lumbridge if you destroy this one.", + "durability": null, + "equipment_slot": "12", + "shop_price": "350" + }, + { + "id": "13561", + "name": "Explorer's ring 2", + "examine": "A Lumbridge explorer's ring.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", + "destroy": "true", + "destroy_message": "You will have talk to ", + "durability": null, + "equipment_slot": "12", + "shop_price": "350" + }, + { + "id": "13562", + "name": "Explorer's ring 3", + "examine": "A Lumbridge explorer's ring.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,1,0,0", + "destroy": "true", + "destroy_message": "You will need to obtain a new ring from Ned in Draynor if you destroy this one.", + "durability": null, + "equipment_slot": "12", + "shop_price": "350" + }, + { + "id": "13563", + "name": "Button mushroom", + "examine": "An occasionally edible mushroom.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "38", + "tradeable": "true" + }, + { + "id": "13564", + "name": "Button mushroom", + "examine": "An occasionally edible mushroom.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "38", + "tradeable": "true" + }, + { + "id": "13565", + "name": "Doll", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13566", + "name": "Jennica's ring", + "examine": "This magical ring once belonged to Summer's mother.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You may be able to find a new ring near where Summer stood outside the wilderness wall.", + "durability": null, + "equipment_slot": "12" + }, + { + "id": "13567", + "name": "Cursed magic logs", + "examine": "Logs cut from a cursed magic tree.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "tradeable": "true" + }, + { + "id": "13568", + "name": "Cursed magic logs", + "examine": "Logs cut from a cursed magic tree.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "tradeable": "true" + }, + { + "id": "13569", + "name": "Enchanted key notes", + "examine": "Notes on how to use the enchanted key.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Hopefully, Jorral will be able to return them.", + "durability": null + }, + { + "id": "13570", + "name": "Bowl", + "examine": "Useful for mixing things.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13571", + "name": "Bucket of milk", + "examine": "It's a bucket of milk.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13572", + "name": "Clean guam", + "examine": "A fresh herb.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13573", + "name": "Bowl of honey", + "examine": "A bowlful of bees' precious product.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You'll have to start again from stratch.", + "durability": null + }, + { + "id": "13574", + "name": "Bowl of milk and honey", + "examine": "A bowlful of milk and honey.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You'll have to start again from stratch.", + "durability": null + }, + { + "id": "13575", + "name": "Bowl of milk, honey and guam", + "examine": "A bowl full of milk, honey and guam.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You'll have to start again from stratch.", + "durability": null + }, + { + "id": "13576", + "name": "Map of 2009Scape", + "examine": "Your best impression of a map of 2009Scape.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Hopefully, the family has more papyrus and charcoal.", + "durability": null + }, + { + "id": "13577", + "name": "Papyrus", + "examine": "Used for making notes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13578", + "name": "Charcoal", + "examine": "A lump of charcoal.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13579", + "name": "Spade", + "examine": "A slightly muddy spade. ", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13580", + "name": "Plant pot", + "examine": "A plant pot filled with soil.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13581", + "name": "Pear tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13582", + "name": "Mango tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13583", + "name": "Quince tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13584", + "name": "Lemon tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13585", + "name": "Avocado tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13586", + "name": "Plum tree seedling", + "examine": "A seedling has been sown in this plant pot.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13587", + "name": "Pestle and mortar", + "examine": "I can grind things for potions in this.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "4" + }, + { + "id": "13588", + "name": "Ground guam", + "examine": "One of the ingredients for making fish food.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "13589", + "name": "Bucket", + "examine": "It's an empty bucket. ", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "2" + }, + { + "id": "13591", + "name": "Enchanted key", + "examine": "It seems to change temperature as I walk. (Making History)", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null + }, + { + "id": "13592", + "name": "A brooch", + "examine": "Some ladies jewellery.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Hopefully, you can find another.", + "durability": null + }, + { + "id": "13593", + "name": "Old tome", + "examine": "It's falling apart, but full of knowledge.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will not get another chance to read the book!", + "durability": null + }, + { + "id": "13598", + "name": "Runecrafting guild teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13599", + "name": "Air altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13600", + "name": "Mind altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13601", + "name": "Water altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13602", + "name": "Earth altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13603", + "name": "Fire altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13604", + "name": "Body altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13605", + "name": "Cosmic altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13606", + "name": "Chaos altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13607", + "name": "Nature altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13608", + "name": "Law altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13609", + "name": "Death altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13610", + "name": "Blood altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13611", + "name": "Astral altar teleport", + "examine": "A tablet containing a magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100" + }, + { + "id": "13612", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "20" + }, + { + "id": "13613", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "20" + }, + { + "id": "13614", + "name": "Runecrafter robe", + "examine": "Heavy duty wizard robes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "75" + }, + { + "id": "13615", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13616", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13617", + "name": "Runecrafter skirt", + "examine": "A heavy-duty wizard robe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "75" + }, + { + "id": "13618", + "name": "Runecrafter gloves", + "examine": "Essence can be rough on the hands", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "9", + "shop_price": "75" + }, + { + "id": "13619", + "name": "Runecrafter robe", + "examine": "Heavy duty wizard robes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "75" + }, + { + "id": "13620", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13621", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13622", + "name": "Runecrafter skirt", + "examine": "A heavy-duty wizard robe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "75" + }, + { + "id": "13623", + "name": "Runecrafter gloves", + "examine": "Essence can be rough on the hands", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "9", + "shop_price": "75" + }, + { + "id": "13624", + "name": "Runecrafter robe", + "examine": "Heavy duty wizard robes.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,5,0,5,5,5,5,5,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "75" + }, + { + "id": "13625", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13626", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13627", + "name": "Runecrafter skirt", + "examine": "A heavy-duty wizard robe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "75" + }, + { + "id": "13628", + "name": "Runecrafter gloves", + "examine": "Essence can be rough on the hands", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "9", + "shop_price": "75" + }, + { + "id": "13629", + "name": "Runecrafting staff", + "examine": "Staff with a holder for a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "28", + "shop_price": "15" + }, + { + "id": "13630", + "name": "Air talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13631", + "name": "Mind talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13632", + "name": "Water talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13633", + "name": "Earth talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13634", + "name": "Fire talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13635", + "name": "Body talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,15,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13636", + "name": "Cosmic talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13637", + "name": "Chaos talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13638", + "name": "Nature talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "No", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13639", + "name": "Law talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13640", + "name": "Death talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13641", + "name": "Blood talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13642", + "name": "Omni-talisman staff", + "examine": "A staff with a talisman on top.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,-1,7,5,0,2,3,1,16,0,0,33,0,0,0", + "destroy": "true", + "destroy_message": "You will have to speak to Wizard Elriss to get another omni-talisman.", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "1500", + "weapon_interface": "1" + }, + { + "id": "13643", + "name": "Yellow attractor", + "examine": "Attracts Runecrafting energy orbs in the rune altars.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13644", + "name": "Yellow repeller", + "examine": "Repels Runecrafting energy orbs in the rune altars.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13645", + "name": "Green attractor", + "examine": "Attracts Runecrafting energy orbs in the rune altars.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13646", + "name": "Green repeller", + "examine": "Repels Runecrafting energy orbs in the rune altars.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13647", + "name": "Green barrier generator", + "examine": "It creates barriers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13648", + "name": "Yellow barrier generator", + "examine": "It creates barriers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this, you will leave the game.", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "20", + "weapon_interface": "1" + }, + { + "id": "13649", + "name": "Omni-talisman", + "examine": "Many mysterious powers emanate from the talisman.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will have to speak to ", + "durability": null, + "shop_price": "4" + }, + { + "id": "13650", + "name": "Runecrafting guild token", + "examine": "Wizard Elriss will exchange these for rewards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { + "id": "13651", + "name": "Runecrafting guild token", + "examine": "Wizard Elriss will exchange these for rewards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { + "id": "13652", + "name": "Runecrafting guild token", + "examine": "Wizard Elriss will exchange these for rewards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { + "id": "13653", + "name": "Runecrafting guild token", + "examine": "Wizard Elriss will exchange these for rewards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { + "id": "13654", + "name": "Runecrafting guild token", + "examine": "Wizard Elriss will exchange these for rewards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { + "id": "13655", + "name": "Omni-tiara", + "examine": "A tiara infused with the properties of all talismans.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will have to speak to Wizard Elriss to get another omni-talisman.", + "durability": null, + "equipment_slot": "0", + "shop_price": "100" + }, + { + "id": "13656", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13657", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13658", + "name": "Runecrafter hat", + "examine": "Comes with goggles, for extra safety while Runecrafting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "75" + }, + { + "id": "13659", + "name": "Ring of fire", + "examine": "It burns, burns, burns...", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "To get another Ring of Fire, you need to keep six beacons alight simultaneously and then talk to King Roald.", + "durability": null, + "equipment_slot": "12", + "point_price": "50", + "requirements": "{11,62}", + "shop_price": "100" + }, + { + "id": "13660", + "name": "Flame gloves", + "examine": "The hottest gloves in town.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "To get another pair of Flame Gloves, you need to keep ten beacons alight simultaneously and then talk to King Roald.", + "durability": null, + "equipment_slot": "9", + "requirements": "{11,79}", + "shop_price": "200" + }, + { + "id": "13661", + "name": "Inferno adze", + "examine": "Danger: risk of fire.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_speed": "5", + "bonuses": "10,9,7,0,0,0,1,0,0,0,0,11,0,0,0", + "destroy": "true", + "destroy_message": "To get another Inferno Adze, you need to keep all fourteen beacons alight simultaneously and then talk to King Roald.", + "durability": null, + "equipment_slot": "3", + "point_price": "75", + "render_anim": "1096", + "requirements": "{11,92}", + "shop_price": "300", + "weapon_interface": "2" + }, + { + "id": "13662", + "name": "Fire beacon", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "13663", + "name": "Circus ticket", + "examine": "Tear ticket to exit the circus.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "The ticket says: Tearing this ticket will make you leave the circus. Choose wisely.", + "durability": null, + "equipment_slot": "3", + "shop_price": "10" + }, + { + "id": "13666", + "name": "Giant's hand", + "examine": "Let's give them a big hand!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "3", + "render_anim": "326", + "shop_price": "10", + "weapon_interface": "7" + }, + { + "id": "13667", + "name": "Clown hat", + "examine": "A clown hat!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13668", + "name": "Clown shirt", + "examine": "Buttoning this up is tricky.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13669", + "name": "Clown leggings", + "examine": "These are my funny pants.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13670", + "name": "Clown shoes", + "examine": "For helping clowns stay upright.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13671", + "name": "Tambourine", + "examine": "For making a happy noise.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "3", + "render_anim": "327", + "shop_price": "10", + "weapon_interface": "8" + }, + { + "id": "13672", + "name": "Ringmaster hat", + "examine": "Very stylish.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "0", + "shop_price": "10" + }, + { + "id": "13673", + "name": "Ringmaster shirt", + "examine": "Fits nicely.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13674", + "name": "Ringmaster pants", + "examine": "Very stylish.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13675", + "name": "Ringmaster boots", + "examine": "Shiny.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13676", + "name": "Mega-phonus", + "examine": "For shouting at people.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another one.", + "durability": null, + "equipment_slot": "3", + "render_anim": "328", + "shop_price": "10", + "weapon_interface": "8" + }, + { + "id": "13677", + "name": "Acrobat shirt", + "examine": "Stretchy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13678", + "name": "Acrobat pants", + "examine": "Very tight fitting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13679", + "name": "Acrobat shoes", + "examine": "These keep your feet off the floor.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13680", + "name": "Acrobat hood", + "examine": "Oh no, I'm bald! Just kidding.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13681", + "name": "Acrobat shirt", + "examine": "Stretchy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13682", + "name": "Acrobat pants", + "examine": "Very tight fitting.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13683", + "name": "Acrobat shoes", + "examine": "These keep your feet off the floor.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13684", + "name": "Acrobat hood", + "examine": "Oh no, I'm bald! Just kidding.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13685", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "shop_price": "10" + }, + { + "id": "13686", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "shop_price": "10" + }, + { + "id": "13687", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "shop_price": "10" + }, + { + "id": "13688", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13689", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13690", + "name": "A stylish hat", + "examine": "Very dapper!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak to the ringmaster to get another.", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "10" + }, + { + "id": "13691", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13692", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13693", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13694", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13695", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13696", + "name": "Shirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10" + }, + { + "id": "13697", + "name": "Leggings", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13698", + "name": "Leggings", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13699", + "name": "Leggings", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13700", + "name": "Skirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13701", + "name": "Skirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13702", + "name": "Skirt", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", + "shop_price": "10" + }, + { + "id": "13703", + "name": "Shoes", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13704", + "name": "Shoes", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13705", + "name": "Shoes", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13706", + "name": "Shoes", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "10", + "shop_price": "10" + }, + { + "id": "13707", + "name": "Tightrope 101", + "examine": "Shows you the moves you need to succeed!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another Tightrope 101 by speaking to the Agility assistant.", + "durability": null + }, + { + "id": "13708", + "name": "Tightrope 101", + "examine": "Shows you the moves you need to succeed!", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "You can get another Tightrope 101 by speaking to the Agility assistant.", + "durability": null, + "tradeable": "true" + }, + { "id": "13709", + "name": "Balls (level 10 approx.)", + "examine": "An Agility level of 10 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 20 is advised when juggling these.", - "durability": null, - "name": "Plates (level 20 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13710", + "name": "Plates (level 20 approx.)", + "examine": "An Agility level of 20 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 30 is advised when juggling these.", - "durability": null, - "name": "Eggs (level 30 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13711", + "name": "Eggs (level 30 approx.)", + "examine": "An Agility level of 30 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 40 is advised when juggling these.", - "durability": null, - "name": "Knives (level 40 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13712", + "name": "Knives (level 40 approx.)", + "examine": "An Agility level of 40 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 50 is advised when juggling these.", - "durability": null, - "name": "Spades (level 50 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13713", + "name": "Spades (level 50 approx.)", + "examine": "An Agility level of 50 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 60 is advised when juggling these.", - "durability": null, - "name": "Tuna (level 60 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13714", + "name": "Tuna (level 60 approx.)", + "examine": "An Agility level of 60 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 70 is advised when juggling these.", - "durability": null, - "name": "Soap (level 70 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13715", + "name": "Soap (level 70 approx.)", + "examine": "An Agility level of 70 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 80 is advised when juggling these.", - "durability": null, - "name": "Cannon balls (level 80 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13716", + "name": "Cannon balls (level 80 approx.)", + "examine": "An Agility level of 80 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 90 is advised when juggling these.", - "durability": null, - "name": "Torches (level 90 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13717", + "name": "Torches (level 90 approx.)", + "examine": "An Agility level of 90 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "destroy_message": "You can get another one from the equipment box for this performance.", - "examine": "An Agility level of 99 is advised when juggling these.", - "durability": null, - "name": "Chinchompas (level 99 approx.)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "13718", + "name": "Chinchompas (level 99 approx.)", + "examine": "An Agility level of 99 is advised when juggling these.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, "equipment_slot": "3" }, { - "shop_price": "50", - "examine": "Short, but effective.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "16", - "absorb": "0,0,0", - "render_anim": "176", - "equipment_slot": "3", - "destroy_message": "You can get another one from the equipment box for this performance.", - "name": "Performance shortbow", - "archery_ticket_price": "0", "id": "13719", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "14", - "examine": "A finely balanced throwing knife.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "18", + "name": "Performance shortbow", + "examine": "Short, but effective.", "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "You can get another one from the equipment box for this performance.", - "name": "Performance knife", - "archery_ticket_price": "0", - "id": "13720", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "4", - "examine": "A finely balanced throwing axe.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "18", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "You can get another one from the equipment box for this performance.", - "name": "Performance throwing axe", - "archery_ticket_price": "0", - "id": "13721", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another one from the equipment box for this performance.", - "shop_price": "7", - "examine": "Arrows with bronze heads.", - "durability": null, - "name": "Performance arrow", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13722", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "13" - }, - { - "destroy_message": "You can get another one from the equipment box for this performance.", - "shop_price": "100", - "examine": "A tablet containing a Magic spell.", - "durability": null, - "name": "Teleport spell", "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13723", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { "destroy_message": "You can get another one from the equipment box for this performance.", - "shop_price": "100", - "examine": "A tablet containing a Magic spell.", "durability": null, - "name": "Levitation spell", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13724", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another one from the equipment box for this performance.", - "shop_price": "100", - "examine": "A tablet containing a Magic spell.", - "durability": null, - "name": "Alchemy spell", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13725", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another one from the equipment box for this performance.", - "shop_price": "100", - "examine": "A tablet containing a Magic spell.", - "durability": null, - "name": "Elemental spell", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13726", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can mine more if you find another crashed star.", + "equipment_slot": "3", + "render_anim": "176", "shop_price": "50", - "examine": "Small, shiny bits of rock.", - "durability": null, - "name": "Stardust", - "destroy": "true", + "weapon_interface": "16" + }, + { + "id": "13720", + "name": "Performance knife", + "examine": "A finely balanced throwing knife.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "equip_audio": "", + "equipment_slot": "3", + "shop_price": "14", + "weapon_interface": "18" + }, + { + "id": "13721", + "name": "Performance throwing axe", + "examine": "A finely balanced throwing axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "equipment_slot": "3", + "shop_price": "4", + "weapon_interface": "18" + }, + { + "id": "13722", + "name": "Performance arrow", + "examine": "Arrows with bronze heads.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "equipment_slot": "13", + "shop_price": "7" + }, + { + "id": "13723", + "name": "Teleport spell", + "examine": "A tablet containing a Magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "shop_price": "100" + }, + { + "id": "13724", + "name": "Levitation spell", + "examine": "A tablet containing a Magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "shop_price": "100" + }, + { + "id": "13725", + "name": "Alchemy spell", + "examine": "A tablet containing a Magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "shop_price": "100" + }, + { + "id": "13726", + "name": "Elemental spell", + "examine": "A tablet containing a Magic spell.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another one from the equipment box for this performance.", + "durability": null, + "shop_price": "100" + }, + { "id": "13727", + "name": "Stardust", + "examine": "Small, shiny bits of rock.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will need to speak with Larry/Chuck at Ardougne Zoo to get another.", - "examine": "Notes on the locations of penguin spies.", - "durability": null, - "name": "Spy notebook", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13732", - "absorb": "0,0,0", - "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", + "destroy": "true", + "destroy_message": "You can mine more if you find another crashed star.", + "durability": null, + "shop_price": "50" }, { + "id": "13732", + "name": "Spy notebook", + "examine": "Notes on the locations of penguin spies.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will need to speak with Larry/Chuck at Ardougne Zoo to get another.", + "durability": null + }, + { + "id": "13734", + "name": "Spirit shield", + "examine": "An ethereal shield.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,39,41,50,1,45,40,0,1,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "rare_item": "true", "requirements": "{1,40}-{5,55}", "shop_price": "70000", - "examine": "An ethereal shield.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "39982", - "name": "Spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13734", - "bonuses": "0,0,0,0,0,39,41,50,1,45,40,0,1,0,0" + "tradeable": "true" }, { + "id": "13735", + "name": "Spirit shield", + "examine": "An ethereal shield.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", "destroy_message": "Drop", + "durability": null, "shop_price": "70000", - "examine": "An ethereal shield.", - "grand_exchange_price": "39982", - "durability": null, - "name": "Spirit shield", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13735" + "tradeable": "true" }, { + "id": "13736", + "name": "Blessed spirit shield", + "examine": "An ethereal shield that has been blessed with holy powers.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,53,55,73,2,52,65,0,3,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "rare_item": "true", "requirements": "{1,70}-{5,60}", "shop_price": "1400000", - "examine": "An ethereal shield that has been blessed with holy powers.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "936814", - "name": "Blessed spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13736", - "bonuses": "0,0,0,0,0,53,55,73,2,52,65,0,3,0,0" + "tradeable": "true" }, { + "id": "13737", + "name": "Blessed spirit shield", + "examine": "An ethereal shield that has been blessed with holy powers.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", "destroy_message": "Drop", + "durability": null, "shop_price": "1400000", - "examine": "An ethereal shield that has been blessed with holy powers.", - "grand_exchange_price": "936814", - "durability": null, - "name": "Blessed spirit shield", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13737" + "tradeable": "true" }, { + "id": "13738", + "name": "Arcane spirit shield", + "examine": "An ethereal shield with an arcane sigil attached to it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,53,55,73,2,52,65,0,3,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", "requirements": "{1,75}-{6,65}-{5,70}", "shop_price": "2000000", - "examine": "An ethereal shield with an arcane sigil attached to it.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "15076199", - "name": "Arcane spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13738", - "bonuses": "0,0,0,20,0,53,55,73,2,52,65,0,3,0,0" + "tradeable": "true" }, { - "destroy_message": "Drop", - "shop_price": "2000000", - "examine": "An ethereal shield with an arcane sigil attached to it.", - "grand_exchange_price": "15076199", - "durability": null, + "id": "13739", "name": "Arcane spirit shield", - "tradeable": "true", - "destroy": "true", + "examine": "An ethereal shield with an arcane sigil attached to it.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13739" + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "2000000", + "tradeable": "true" }, { - "requirements": "{1,75}-{5,75}", - "shop_price": "2000000", - "examine": "An ethereal shield with a divine sigil attached to it.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "40003587", - "name": "Divine spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", "id": "13740", - "bonuses": "0,0,0,0,0,63,65,75,2,57,65,0,3,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "2000000", - "examine": "An ethereal shield with a divine sigil attached to it.", - "grand_exchange_price": "40003587", - "durability": null, "name": "Divine spirit shield", - "tradeable": "true", - "destroy": "true", + "examine": "An ethereal shield with a divine sigil attached to it.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13741" - }, - { + "bonuses": "0,0,0,0,0,63,65,75,2,57,65,0,3,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", "requirements": "{1,75}-{5,75}", "shop_price": "2000000", - "examine": "An ethereal shield with an elysian sigil attached to it.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "28420591", - "name": "Elysian spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13742", - "bonuses": "0,0,0,0,0,63,65,75,2,57,65,0,3,0,0" + "tradeable": "true" }, { + "id": "13741", + "name": "Divine spirit shield", + "examine": "An ethereal shield with a divine sigil attached to it.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", "destroy_message": "Drop", + "durability": null, "shop_price": "2000000", - "examine": "An ethereal shield with an elysian sigil attached to it.", - "grand_exchange_price": "28420591", - "durability": null, - "name": "Elysian spirit shield", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13743" + "tradeable": "true" }, { + "id": "13742", + "name": "Elysian spirit shield", + "examine": "An ethereal shield with an elysian sigil attached to it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,63,65,75,2,57,65,0,3,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,75}-{5,75}", + "shop_price": "2000000", + "tradeable": "true" + }, + { + "id": "13743", + "name": "Elysian spirit shield", + "examine": "An ethereal shield with an elysian sigil attached to it.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "2000000", + "tradeable": "true" + }, + { + "id": "13744", + "name": "Spectral spirit shield", + "examine": "An ethereal shield with a spectral sigil attached to it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,53,55,73,30,52,65,0,3,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", "requirements": "{1,75}-{5,70}-{6,65}", "shop_price": "2000000", - "examine": "An ethereal shield with a spectral sigil attached to it.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "3616112", - "name": "Spectral spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13744", - "bonuses": "0,0,0,0,0,53,55,73,30,52,65,0,3,0,0" + "tradeable": "true" }, { + "id": "13745", + "name": "Spectral spirit shield", + "examine": "An ethereal shield with a spectral sigil attached to it.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", "destroy_message": "Drop", + "durability": null, "shop_price": "2000000", - "examine": "An ethereal shield with a spectral sigil attached to it.", - "grand_exchange_price": "3616112", - "durability": null, - "name": "Spectral spirit shield", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13745" + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil in the shape of an arcane rune.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "19508553", - "name": "Arcane sigil", - "tradeable": "true", - "archery_ticket_price": "0", "id": "13746", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "750000", - "examine": "A sigil in the shape of an arcane rune.", - "grand_exchange_price": "19508553", - "durability": null, "name": "Arcane sigil", - "tradeable": "true", - "destroy": "true", + "examine": "A sigil in the shape of an arcane rune.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13747" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil in the shape of a divine symbol.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "53026134", - "name": "Divine sigil", - "tradeable": "true", + "id": "13747", + "name": "Arcane sigil", + "examine": "A sigil in the shape of an arcane rune.", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "750000", + "tradeable": "true" + }, + { "id": "13748", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "750000", - "examine": "A sigil in the shape of a divine symbol.", - "grand_exchange_price": "53026134", - "durability": null, "name": "Divine sigil", - "tradeable": "true", - "destroy": "true", + "examine": "A sigil in the shape of a divine symbol.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13749" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil marked with elysian signs.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "36159098", - "name": "Elysian sigil", - "tradeable": "true", + "id": "13749", + "name": "Divine sigil", + "examine": "A sigil in the shape of a divine symbol.", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "750000", + "tradeable": "true" + }, + { "id": "13750", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "750000", - "examine": "A sigil marked with elysian signs.", - "grand_exchange_price": "36159098", - "durability": null, "name": "Elysian sigil", - "tradeable": "true", - "destroy": "true", + "examine": "A sigil marked with elysian signs.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13751" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil tempered with spectral powers.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "3116720", - "name": "Spectral sigil", - "tradeable": "true", + "id": "13751", + "name": "Elysian sigil", + "examine": "A sigil marked with elysian signs.", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "750000", + "tradeable": "true" + }, + { "id": "13752", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "750000", - "examine": "A sigil tempered with spectral powers.", - "grand_exchange_price": "3116720", - "durability": null, "name": "Spectral sigil", - "tradeable": "true", - "destroy": "true", + "examine": "A sigil tempered with spectral powers.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13753" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A bottle of holy elixir.", - "rare_item": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "950904", - "name": "Holy elixir", - "tradeable": "true", + "id": "13753", + "name": "Spectral sigil", + "examine": "A sigil tempered with spectral powers.", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "750000", + "tradeable": "true" + }, + { "id": "13754", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "750000", - "examine": "A bottle of holy elixir.", - "grand_exchange_price": "950904", - "durability": null, "name": "Holy elixir", - "tradeable": "true", - "destroy": "true", + "examine": "A bottle of holy elixir.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13755" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "750000", + "tradeable": "true" }, { - "destroy_message": "Drop", - "shop_price": "40", - "examine": "Logs cut from cursed willow roots.", - "durability": null, - "name": "Cursed willow logs", - "destroy": "true", + "id": "13755", + "name": "Holy elixir", + "examine": "A bottle of holy elixir.", "archery_ticket_price": "0", "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "750000", + "tradeable": "true" + }, + { "id": "13756", + "name": "Cursed willow logs", + "examine": "Logs cut from cursed willow roots.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "A loyal scout of Varrock.", - "durability": null, - "name": "Hartwin", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "40" + }, + { "id": "13757", + "name": "Hartwin", + "examine": "A loyal scout of Varrock.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You'll have to keep searching trees near the zombie trails until you find another.", - "examine": "A key that was found while tracking zombies. (Defender of Varrock)", - "durability": null, - "name": "Grubby key", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { "id": "13758", + "name": "Grubby key", + "examine": "A key that was found while tracking zombies. (Defender of Varrock)", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You'll have to speak to Thurgo to find another.", - "shop_price": "1", - "examine": "The location of ", - "durability": null, - "name": "Scrap of paper", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You'll have to keep searching trees near the zombie trails until you find another.", + "durability": null + }, + { "id": "13759", + "name": "Scrap of paper", + "examine": "The location of ", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Bottle", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You'll have to speak to Thurgo to find another.", + "durability": null, + "shop_price": "1" + }, + { "id": "13760", + "name": "Bottle", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A bottle of red mist.", - "durability": null, - "name": "Bottle of mist", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "13761", + "name": "Bottle of mist", + "examine": "A bottle of red mist.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another from ", - "examine": "This lists each of the founding members of the ", - "durability": null, - "name": "List of elders", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { "id": "13762", + "name": "List of elders", + "examine": "This lists each of the founding members of the ", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "I hope I don't meet any roundheads...", - "durability": null, - "name": "Cavalier and mask", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another from ", + "durability": null + }, + { "id": "13763", + "name": "Cavalier and mask", + "examine": "I hope I don't meet any roundheads...", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" }, { - "examine": "A right hand fighting claw.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "absorb": "0,0,0", - "equip_audio": "1003", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6535", - "name": "Rune claws", - "archery_ticket_price": "0", "id": "13764", - "bonuses": "26,38,-4,0,0,10,19,5,0,0,0,39,0,0,0" + "name": "Rune claws", + "examine": "A right hand fighting claw.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "26,38,-4,0,0,10,19,5,0,0,0,39,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "1003", + "has_special": "true", + "lendable": "true", + "two_handed": "true", + "weapon_interface": "9" }, { - "examine": "A powerful dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "4381", - "name": "Rune dagger", - "archery_ticket_price": "0", "id": "13765", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "durability": null, - "name": "Rune dagger(p)", - "destroy": "true", + "name": "Rune dagger", + "examine": "A powerful dagger.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "weapon_interface": "5" + }, + { "id": "13766", - "absorb": "0,0,0", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", + "name": "Rune dagger(p)", "examine": "A picture of a lady called Elena.", - "durability": null, - "name": "Rune dagger(p+)", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { "id": "13767", - "absorb": "0,0,0", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", + "name": "Rune dagger(p+)", "examine": "A picture of a lady called Elena.", - "durability": null, - "name": "Rune dagger(p++)", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { "id": "13768", + "name": "Rune dagger(p++)", + "examine": "A picture of a lady called Elena.", "absorb": "0,0,0", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" }, { - "examine": "A rune tipped spear.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "11317", - "name": "Rune spear", - "archery_ticket_price": "0", "id": "13769", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" + "name": "Rune spear", + "examine": "A rune tipped spear.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "14" }, { - "examine": "A dragon tipped spear.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "attack_anims": "2080,2081,2082,2080", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "36774", - "name": "Dragon spear", - "archery_ticket_price": "0", "id": "13770", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" + "name": "Dragon spear", + "examine": "A dragon tipped spear.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "14" }, { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "absorb": "0,0,0", - "render_anim": "28", - "destroy_message": "Drop", - "name": "Rune spear(p)", - "archery_ticket_price": "0", "id": "13771", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" + "name": "Rune spear(p)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "28", + "shop_price": "1", + "two_handed": "true" }, { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "name": "Dragon spear(p)", - "archery_ticket_price": "0", "id": "13772", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" + "name": "Dragon spear(p)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "render_anim": "28", + "shop_price": "1", + "two_handed": "true" }, { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "absorb": "0,0,0", - "render_anim": "28", - "destroy_message": "Drop", - "name": "Rune spear(p+)", - "archery_ticket_price": "0", "id": "13773", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" + "name": "Rune spear(p+)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "28", + "shop_price": "1", + "two_handed": "true" }, { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "name": "Dragon spear(p+)", - "archery_ticket_price": "0", "id": "13774", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" + "name": "Dragon spear(p+)", + "examine": "A picture of a lady called Elena.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "has_special": "true", + "render_anim": "28", + "shop_price": "1", + "two_handed": "true" }, { - "shop_price": "1", - "examine": "A picture of a lady called Elena.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "absorb": "0,0,0", - "render_anim": "28", - "destroy_message": "Drop", - "name": "Rune spear(p++)", - "archery_ticket_price": "0", "id": "13775", - "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0" - }, - { - "shop_price": "1", + "name": "Rune spear(p++)", "examine": "A picture of a lady called Elena.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "name": "Dragon spear(p++)", "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "36,36,36,0,0,1,1,0,0,0,0,42,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "28", + "shop_price": "1", + "two_handed": "true" + }, + { "id": "13776", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" - }, - { - "examine": "A razor-sharp sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", + "name": "Dragon spear(p++)", + "examine": "A picture of a lady called Elena.", "absorb": "0,0,0", - "render_anim": "1381", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "11849", - "name": "Rune sword", "archery_ticket_price": "0", - "id": "13777", - "bonuses": "38,26,-2,0,0,0,2,1,0,0,0,39,0,0,0" - }, - { - "examine": "A two handed sword.", - "durability": null, - "destroy": "true", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "7", - "absorb": "0,0,0", - "render_anim": "124", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37511", - "name": "Rune 2h sword", - "archery_ticket_price": "0", - "id": "13778", - "bonuses": "-4,69,50,-4,0,0,0,0,0,-1,0,70,0,0,0" - }, - { - "examine": "A rune halberd.", - "durability": null, - "destroy": "true", - "attack_speed": "7", - "two_handed": "true", - "weapon_interface": "15", - "absorb": "0,0,0", - "render_anim": "28", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "74298", - "name": "Rune halberd", - "archery_ticket_price": "0", - "id": "13779", - "bonuses": "48,67,0,-4,0,-1,4,5,0,0,0,68,0,0,0" - }, - { - "examine": "A spiky mace.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "8", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7917", - "name": "Rune mace", - "archery_ticket_price": "0", - "id": "13780", - "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,0,4,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A series of connected metal rings.", - "grand_exchange_price": "29145", - "durability": null, - "name": "Rune chainbody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13781", - "absorb": "3,0,6", - "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A medium sized helmet.", - "grand_exchange_price": "10678", - "durability": null, - "name": "Rune med helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13783", - "absorb": "1,0,3", - "bonuses": "0,0,0,-3,-1,22,23,21,-1,22,7,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A stone helmet.", - "grand_exchange_price": "26576", - "durability": null, - "name": "Granite helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13784", - "absorb": "2,0,4", - "bonuses": "0,0,0,-9,-7,31,33,29,-1,39,9,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Provides excellent protection.", - "grand_exchange_price": "136414", - "durability": null, - "name": "Granite body", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13785", - "absorb": "4,0,8", - "bonuses": "0,0,0,-22,-5,87,84,79,-6,97,45,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These look pretty heavy.", - "grand_exchange_price": "72213", - "durability": null, - "name": "Granite legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13786", - "absorb": "2,0,5", - "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A solid stone shield.", - "grand_exchange_price": "32512", - "durability": null, - "name": "Granite shield", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13788", - "absorb": "5,0,10", - "bonuses": "0,0,0,-12,-8,40,42,38,0,65,50,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A big 'do about nothing.", - "grand_exchange_price": "42991", - "durability": null, - "name": "A powdered wig", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13789", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These'll help me stay alive.", - "grand_exchange_price": "811293", - "durability": null, - "name": "Flared trousers", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13790", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Alas, someone has slashed my pantaloons.", - "grand_exchange_price": "19286", - "durability": null, - "name": "Pantaloons", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13791", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A cap for wearing whil...zzzzzzzzz", - "grand_exchange_price": "75357", - "durability": null, - "name": "Sleeping cap", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13792", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A diamond-topped cane.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "8", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "76692", - "name": "Rune cane", - "archery_ticket_price": "0", - "id": "13793", - "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,0,4,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Comes with a free rabbit!", - "grand_exchange_price": "333537", - "durability": null, - "name": "Top hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13794", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Shear elegance.", - "grand_exchange_price": "582969", - "durability": null, - "name": "Sheep mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13795", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "I can get the look right, but can I do the waddle too?", - "grand_exchange_price": "1159807", - "durability": null, - "name": "Penguin mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13796", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "The bats have left the belltower.", - "grand_exchange_price": "681595", - "durability": null, - "name": "Bat mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13797", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "It's the year of the cat!", - "grand_exchange_price": "2512014", - "durability": null, - "name": "Cat mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13798", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "I'm hungry like the wolf.", - "grand_exchange_price": "3240770", - "durability": null, - "name": "Wolf mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13799", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platebody with gold trim.", - "grand_exchange_price": "343688", - "durability": null, - "name": "Rune platebody (g)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13800", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "drop", - "examine": "Rune platelegs with gold trim.", - "grand_exchange_price": "238139", - "durability": null, - "name": "Rune platelegs (g)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13801", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt with gold trim.", - "grand_exchange_price": "37616", - "durability": null, - "name": "Rune plateskirt (g)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13802", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune full helmet with gold trim.", - "grand_exchange_price": "247877", - "durability": null, - "name": "Rune full helm(g)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13803", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platebody with trim.", - "grand_exchange_price": "100586", - "durability": null, - "name": "Rune platebody (t)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13805", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platelegs with trim!", - "grand_exchange_price": "81577", - "durability": null, - "name": "Rune platelegs (t)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13806", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt with trim.", - "grand_exchange_price": "37701", - "durability": null, - "name": "Rune plateskirt (t)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13807", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune full helmet with trim.", - "grand_exchange_price": "95809", - "durability": null, - "name": "Rune full helm (t)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13808", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Your money or your life!", - "grand_exchange_price": "252995", - "durability": null, - "name": "Highwayman mask", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13810", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Parlez-vous francais?", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "135411", - "name": "Blue beret", - "archery_ticket_price": "0", - "hat": true, - "id": "13811", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Parlez-vous francais?", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "451371", - "name": "Black beret", - "archery_ticket_price": "0", - "hat": true, - "id": "13812", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Parlez-vous Francais?", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "704147", - "name": "White beret", - "archery_ticket_price": "0", - "hat": true, - "id": "13813", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "All for one and one for all!", - "grand_exchange_price": "42014", - "durability": null, - "name": "Tan cavalier", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13814", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "All for one and one for all!", - "grand_exchange_price": "61518", - "durability": null, - "name": "Dark cavalier", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13815", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "All for one and one for all!", - "grand_exchange_price": "338530", - "durability": null, - "name": "Black cavalier", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13816", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A minimalist's hat.", - "grand_exchange_price": "159694", - "durability": null, - "name": "Red headband", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13817", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A minimalist's hat.", - "grand_exchange_price": "83543", - "durability": null, - "name": "Black headband", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13818", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A minimalist's hat.", - "grand_exchange_price": "26474", - "durability": null, - "name": "Brown headband", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13819", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platebody in the colours of Zamorak.", - "grand_exchange_price": "868398", - "durability": null, - "name": "Zamorak platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13820", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platelegs in the colours of Zamorak.", - "grand_exchange_price": "316989", - "durability": null, - "name": "Zamorak platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13821", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt in the colours of Zamorak.", - "grand_exchange_price": "38597", - "durability": null, - "name": "Zamorak plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13822", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A full helmet in the colours of Zamorak.", - "grand_exchange_price": "618480", - "durability": null, - "name": "Zamorak full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13823", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platebody in the colours of Saradomin.", - "grand_exchange_price": "1555099", - "durability": null, - "name": "Saradomin platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13825", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platelegs in the colours of Saradomin.", - "grand_exchange_price": "376849", - "durability": null, - "name": "Saradomin platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13826", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt in the colours of Saradomin.", - "grand_exchange_price": "53048", - "durability": null, - "name": "Saradomin plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13827", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune full helmet in the colours of Saradomin.", - "grand_exchange_price": "1025974", - "durability": null, - "name": "Saradomin full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13828", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platebody in the colours of Guthix.", - "grand_exchange_price": "406762", - "durability": null, - "name": "Guthix platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13830", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platelegs in the colours of Guthix.", - "grand_exchange_price": "181471", - "durability": null, - "name": "Guthix platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13831", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt in the colours of Guthix.", - "grand_exchange_price": "38388", - "durability": null, - "name": "Guthix plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13832", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "A rune full helmet in the colours of Guthix.", - "grand_exchange_price": "399963", - "durability": null, - "name": "Guthix full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13833", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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.", - "grand_exchange_price": "2034838", - "durability": null, - "name": "Gilded platebody", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13835", - "absorb": "3,0,6", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune platelegs with gold plate.", - "grand_exchange_price": "1299758", - "durability": null, - "name": "Gilded platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13836", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune plateskirt with gold plate.", - "grand_exchange_price": "84191", - "durability": null, - "name": "Gilded plateskirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13837", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Rune full helmet with gold plate.", - "grand_exchange_price": "1057068", - "durability": null, - "name": "Gilded full helm", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13838", - "absorb": "1,0,3", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "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" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Atk, Def, Str and LP xp.", - "durability": null, - "name": "Brawling gloves (melee)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13845", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Ranged xp.", - "durability": null, - "name": "Brawling gloves (ranged)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13846", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Magic xp.", - "durability": null, - "name": "Brawling gloves (magic)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13847", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Prayer xp.", - "durability": null, - "name": "Brawling gloves (prayer)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13848", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Agility xp.", - "durability": null, - "name": "Brawling gloves (agility)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13849", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Woodcutting xp.", - "durability": null, - "name": "Brawling gloves (wc)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13850", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Firemaking xp.", - "durability": null, - "name": "Brawling gloves (fm)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13851", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Mining xp.", - "durability": null, - "name": "Brawling gloves (mining)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13852", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Hunting xp.", - "durability": null, - "name": "Brawling gloves (hunter)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13853", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Thieving xp.", - "durability": null, - "name": "Brawling gloves (thieving)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13854", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Smithing xp.", - "durability": null, - "name": "Brawling gloves (smithing)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13855", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Fishing xp.", - "durability": null, - "name": "Brawling gloves (fishing)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13856", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Cooking xp.", - "durability": null, - "name": "Brawling gloves (cooking)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13857", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "12,6,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1412812", - "name": "Zuriel's robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13858", - "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "1412812", - "durability": null, - "name": "Zuriel's robe top", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13859" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "12,6,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1412812", - "name": "Zuriel's robe top (deg)", - "archery_ticket_price": "0", - "id": "13860", - "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "8,4,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "986668", - "name": "Zuriel's robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13861", - "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "986668", - "durability": null, - "name": "Zuriel's robe bottom", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13862" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "8,4,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "986668", - "name": "Zuriel's robe bottom (deg)", - "archery_ticket_price": "0", - "id": "13863", - "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{6,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "6,3,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "361542", - "name": "Zuriel's hood", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13864", - "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "361542", - "durability": null, - "name": "Zuriel's hood", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13865" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{6,78}", - "shop_price": "150000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "361542", - "name": "Zuriel's hood (deg)", - "archery_ticket_price": "0", - "id": "13866", - "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "destroy_message": "Drop", - "grand_exchange_price": "2116989", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "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}", - "shop_price": "300000", - "durability": null, - "destroy": "false", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Zuriel's staff" - }, - { - "destroy_message": "Drop", - "requirements": "{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "2116989", - "durability": null, - "name": "Zuriel's staff", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13868" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "destroy_message": "Drop", - "grand_exchange_price": "2116989", - "stand_anim": "813", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "13869", - "stand_turn_anim": "1209", - "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", - "shop_price": "300000", - "durability": null, - "destroy": "true", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Zuriel's staff (deg)" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,12,6", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "926341", - "name": "Morrigan's leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13870", - "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "926341", - "durability": null, - "name": "Morrigan's leather body", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13871" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,12,6", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "926341", - "name": "Morrigan's leather body (deg)", - "archery_ticket_price": "0", - "id": "13872", - "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,8,4", - "equipment_slot": "7", - "destroy_message": "Drop, Destroy after degrading.", - "grand_exchange_price": "510395", - "name": "Morrigan's leather chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13873", - "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0" - }, - { - "destroy_message": "Drop, Destroy after degrading.", - "requirements": "{1,78}-{4,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "510395", - "durability": null, - "name": "Morrigan's leather chaps", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13874" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,8,4", - "equipment_slot": "7", - "destroy_message": "Drop, Destroy after degrading.", - "grand_exchange_price": "510395", - "name": "Morrigan's leather chaps (deg)", - "archery_ticket_price": "0", - "id": "13875", - "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{4,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,6,3", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "236411", - "name": "Morrigan's coif", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13876", - "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "236411", - "durability": null, - "name": "Morrigan's coif", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13877" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{4,78}", - "shop_price": "150000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "236411", - "name": "Morrigan's coif (deg)", - "archery_ticket_price": "0", - "id": "13878", - "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "4000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "destroy": "false", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "19944", - "name": "Morrigan's javelin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13879", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "destroy": "false", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "19944", - "name": "Morrigan's javelin(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13880", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "destroy": "false", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "19944", - "name": "Morrigan's javelin(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13881", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "destroy": "false", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "19944", - "name": "Morrigan's javelin(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13882", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "4000", - "examine": "A vicious throwing axe.", - "has_special": "true", - "durability": null, - "destroy": "false", - "attack_speed": "5", - "weapon_interface": "18", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "9994", - "name": "Morrigan's throwing axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13883", - "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,117" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "6,0,12", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1619096", - "name": "Statius's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13884", - "bonuses": "5,5,7,-30,-10,154,145,121,-6,157,60,5,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "1619096", - "durability": null, - "name": "Statius's platebody", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13885" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,0,12", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1619096", - "name": "Statius's platebody (deg)", - "archery_ticket_price": "0", - "id": "13886", - "bonuses": "5,5,7,-30,-10,154,145,121,-6,157,60,5,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "6,0,12", - "equipment_slot": "4", - "destroy_message": "Drop, Destroy once it has been degraded.", - "remove_sleeves": "true", - "grand_exchange_price": "2154168", - "name": "Vesta's chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13887", - "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0" - }, - { - "destroy_message": "Drop, Destroy once it has been degraded.", - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "2154168", - "durability": null, - "name": "Vesta's chainbody", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13888" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,0,12", - "equipment_slot": "4", - "destroy_message": "Drop, Destroy once it has been degraded.", - "remove_sleeves": "true", - "grand_exchange_price": "2154168", - "name": "Vesta's chainbody (deg)", - "archery_ticket_price": "0", - "id": "13889", - "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1735029", - "name": "Statius's platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13890", - "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "1735029", - "durability": null, - "name": "Statius's platelegs", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13891" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1735029", - "name": "Statius's platelegs (deg)", - "archery_ticket_price": "0", - "id": "13892", - "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1860767", - "name": "Vesta's plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13893", - "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "1860767", - "durability": null, - "name": "Vesta's plateskirt", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13894" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1860767", - "name": "Vesta's plateskirt (deg)", - "archery_ticket_price": "0", - "id": "13895", - "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "1301986", - "name": "Statius's full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13896", - "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, - "name": "Statius's full helm", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13897" - }, - { - "remove_head": "true", - "requirements": "{1,78}", - "shop_price": "150000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "1301986", - "name": "Statius' full helm (deg)", - "archery_ticket_price": "0", - "id": "13898", - "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "destroy_message": "Drop", - "grand_exchange_price": "10464249", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "13899", - "stand_turn_anim": "823", - "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", - "requirements": "{0,78}", - "shop_price": "300000", - "durability": null, - "destroy": "false", - "weapon_interface": "5", - "render_anim": "1426", - "attack_audios": "2500,2500,2517,2500", - "name": "Vesta's longsword" - }, - { - "destroy_message": "Drop", - "requirements": "{0,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "10464249", - "durability": null, - "name": "Vesta's longsword", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13900" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "has_special": "true", - "rare_item": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "destroy_message": "Drop", - "grand_exchange_price": "10464249", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "13901", - "stand_turn_anim": "823", - "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", - "requirements": "{0,78}", - "shop_price": "300000", - "durability": null, - "destroy": "true", - "weapon_interface": "5", - "render_anim": "1426", - "attack_audios": "2500,2500,2517,2500", - "name": "Vesta's longsword (deg)" - }, - { - "turn90cw_anim": "821", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "819", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "destroy_message": "Drop, Destroy once it has been degraded.", - "grand_exchange_price": "7424450", - "stand_anim": "808", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "13902", - "stand_turn_anim": "823", - "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0", - "requirements": "{0,78}", - "shop_price": "300000", - "durability": null, - "destroy": "false", - "weapon_interface": "10", - "attack_audios": "2504,0,0,0", - "name": "Statius's warhammer" - }, - { - "destroy_message": "Drop, Destroy once it has been degraded.", - "requirements": "{0,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "grand_exchange_price": "7424450", - "durability": null, - "name": "Statius's warhammer", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13903" - }, - { - "turn90cw_anim": "821", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "819", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "820", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "destroy_message": "Drop, Destroy once it has been degraded.", - "grand_exchange_price": "7424450", - "stand_anim": "808", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "13904", - "stand_turn_anim": "823", - "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0", - "shop_price": "300000", - "durability": null, - "destroy": "true", - "weapon_interface": "10", - "render_anim": "1426", - "attack_audios": "2504,0,0,0", - "name": "Statius' warhammer (deg)" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "has_special": "true", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "1206", - "absorb": "0,0,0", - "defence_anim": "2079", - "equipment_slot": "3", "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", "destroy_message": "Drop", - "grand_exchange_price": "5264612", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "13905", - "stand_turn_anim": "1209", - "bonuses": "133,113,120,0,0,18,21,21,0,0,0,122,0,0,0", - "requirements": "{0,78}", - "shop_price": "300000", "durability": null, - "destroy": "false", - "weight": "4.2", - "weapon_interface": "14", + "has_special": "true", "render_anim": "28", - "name": "Vesta's spear" + "shop_price": "1", + "two_handed": "true" }, { + "id": "13777", + "name": "Rune sword", + "examine": "A razor-sharp sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "38,26,-2,0,0,0,2,1,0,0,0,39,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "render_anim": "1381", + "weapon_interface": "5" + }, + { + "id": "13778", + "name": "Rune 2h sword", + "examine": "A two handed sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "-4,69,50,-4,0,0,0,0,0,-1,0,70,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "lendable": "true", + "render_anim": "124", + "two_handed": "true", + "weapon_interface": "7" + }, + { + "id": "13779", + "name": "Rune halberd", + "examine": "A rune halberd.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "48,67,0,-4,0,-1,4,5,0,0,0,68,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "lendable": "true", + "render_anim": "28", + "two_handed": "true", + "weapon_interface": "15" + }, + { + "id": "13780", + "name": "Rune mace", + "examine": "A spiky mace.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "20,-2,39,0,0,0,0,0,0,0,0,36,0,4,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "weapon_interface": "8" + }, + { + "id": "13781", + "name": "Rune chainbody", + "examine": "A series of connected metal rings.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13782", + "name": "Rune boots", + "examine": "These will protect my feet.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2237", + "lendable": "true" + }, + { + "id": "13783", + "name": "Rune med helm", + "examine": "A medium sized helmet.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,22,23,21,-1,22,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13784", + "name": "Granite helm", + "examine": "A stone helmet.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-9,-7,31,33,29,-1,39,9,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13785", + "name": "Granite body", + "examine": "Provides excellent protection.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-22,-5,87,84,79,-6,97,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13786", + "name": "Granite legs", + "examine": "These look pretty heavy.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13787", + "name": "Rune sq shield", + "examine": "A medium square shield.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,38,40,36,0,38,35,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13788", + "name": "Granite shield", + "examine": "A solid stone shield.", + "absorb": "5,0,10", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-12,-8,40,42,38,0,65,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13789", + "name": "A powdered wig", + "examine": "A big 'do about nothing.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13790", + "name": "Flared trousers", + "examine": "These'll help me stay alive.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13791", + "name": "Pantaloons", + "examine": "Alas, someone has slashed my pantaloons.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13792", + "name": "Sleeping cap", + "examine": "A cap for wearing whil...zzzzzzzzz", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13793", + "name": "Rune cane", + "examine": "A diamond-topped cane.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,0,4,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true", + "weapon_interface": "8" + }, + { + "id": "13794", + "name": "Top hat", + "examine": "Comes with a free rabbit!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13795", + "name": "Sheep mask", + "examine": "Shear elegance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13796", + "name": "Penguin mask", + "examine": "I can get the look right, but can I do the waddle too?", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13797", + "name": "Bat mask", + "examine": "The bats have left the belltower.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13798", + "name": "Cat mask", + "examine": "It's the year of the cat!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13799", + "name": "Wolf mask", + "examine": "I'm hungry like the wolf.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13800", + "name": "Rune platebody (g)", + "examine": "Rune platebody with gold trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13801", + "name": "Rune platelegs (g)", + "examine": "Rune platelegs with gold trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13802", + "name": "Rune plateskirt (g)", + "examine": "Rune plateskirt with gold trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13803", + "name": "Rune full helm(g)", + "examine": "Rune full helmet with gold trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13804", + "name": "Rune kiteshield (g)", + "examine": "Rune kiteshield with gold trim", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13805", + "name": "Rune platebody (t)", + "examine": "Rune platebody with trim.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13806", + "name": "Rune platelegs (t)", + "examine": "Rune platelegs with trim!", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13807", + "name": "Rune plateskirt (t)", + "examine": "Rune plateskirt with trim.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13808", + "name": "Rune full helm (t)", + "examine": "Rune full helmet with trim.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13809", + "name": "Rune kiteshield (t)", + "examine": "A large, metal shield with a nice trim.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13810", + "name": "Highwayman mask", + "examine": "Your money or your life!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13811", + "name": "Blue beret", + "examine": "Parlez-vous francais?", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "hat": true, + "lendable": "true" + }, + { + "id": "13812", + "name": "Black beret", + "examine": "Parlez-vous francais?", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "hat": true, + "lendable": "true" + }, + { + "id": "13813", + "name": "White beret", + "examine": "Parlez-vous Francais?", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "hat": true, + "lendable": "true" + }, + { + "id": "13814", + "name": "Tan cavalier", + "examine": "All for one and one for all!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13815", + "name": "Dark cavalier", + "examine": "All for one and one for all!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13816", + "name": "Black cavalier", + "examine": "All for one and one for all!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13817", + "name": "Red headband", + "examine": "A minimalist's hat.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13818", + "name": "Black headband", + "examine": "A minimalist's hat.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13819", + "name": "Brown headband", + "examine": "A minimalist's hat.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13820", + "name": "Zamorak platebody", + "examine": "Rune platebody in the colours of Zamorak.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13821", + "name": "Zamorak platelegs", + "examine": "Rune platelegs in the colours of Zamorak.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13822", + "name": "Zamorak plateskirt", + "examine": "Rune plateskirt in the colours of Zamorak.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13823", + "name": "Zamorak full helm", + "examine": "A full helmet in the colours of Zamorak.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13824", + "name": "Zamorak kiteshield", + "examine": "A Rune kiteshield in the colours of Zamorak.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13825", + "name": "Saradomin platebody", + "examine": "Rune platebody in the colours of Saradomin.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13826", + "name": "Saradomin platelegs", + "examine": "Rune platelegs in the colours of Saradomin.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13827", + "name": "Saradomin plateskirt", + "examine": "Rune plateskirt in the colours of Saradomin.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13828", + "name": "Saradomin full helm", + "examine": "Rune full helmet in the colours of Saradomin.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13829", + "name": "Saradomin kiteshield", + "examine": "Rune kiteshield in the colours of Saradomin.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13830", + "name": "Guthix platebody", + "examine": "Rune platebody in the colours of Guthix.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13831", + "name": "Guthix platelegs", + "examine": "Rune platelegs in the colours of Guthix.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13832", + "name": "Guthix plateskirt", + "examine": "Rune plateskirt in the colours of Guthix.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13833", + "name": "Guthix full helm", + "examine": "A rune full helmet in the colours of Guthix.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13834", + "name": "Guthix kiteshield", + "examine": "Rune kiteshield in the colours of Guthix.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13835", + "name": "Gilded platebody", + "examine": "Rune platebody with complete gold trim & plating.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13836", + "name": "Gilded platelegs", + "examine": "Rune platelegs with gold plate.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13837", + "name": "Gilded plateskirt", + "examine": "Rune plateskirt with gold plate.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13838", + "name": "Gilded full helm", + "examine": "Rune full helmet with gold plate.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "13839", + "name": "Gilded kiteshield", + "examine": "Rune kiteshield with gold plate.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "lendable": "true" + }, + { + "id": "13845", + "name": "Brawling gloves (melee)", + "examine": "A pair of gloves that gives you bonus Atk, Def, Str and LP xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13846", + "name": "Brawling gloves (ranged)", + "examine": "A pair of gloves that gives you bonus Ranged xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13847", + "name": "Brawling gloves (magic)", + "examine": "A pair of gloves that gives you bonus Magic xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13848", + "name": "Brawling gloves (prayer)", + "examine": "A pair of gloves that gives you bonus Prayer xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13849", + "name": "Brawling gloves (agility)", + "examine": "A pair of gloves that gives you bonus Agility xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13850", + "name": "Brawling gloves (wc)", + "examine": "A pair of gloves that gives you bonus Woodcutting xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13851", + "name": "Brawling gloves (fm)", + "examine": "A pair of gloves that gives you bonus Firemaking xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13852", + "name": "Brawling gloves (mining)", + "examine": "A pair of gloves that gives you bonus Mining xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13853", + "name": "Brawling gloves (hunter)", + "examine": "A pair of gloves that gives you bonus Hunting xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13854", + "name": "Brawling gloves (thieving)", + "examine": "A pair of gloves that gives you bonus Thieving xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13855", + "name": "Brawling gloves (smithing)", + "examine": "A pair of gloves that gives you bonus Smithing xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13856", + "name": "Brawling gloves (fishing)", + "examine": "A pair of gloves that gives you bonus Fishing xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13857", + "name": "Brawling gloves (cooking)", + "examine": "A pair of gloves that gives you bonus Cooking xp.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Your brawling gloves are fragile and will disappear if you drop them. Are you sure you want to drop them?", + "durability": null, + "equipment_slot": "9", + "shop_price": "50000" + }, + { + "id": "13858", + "name": "Zuriel's robe top", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "12,6,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}-{6,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13859", + "name": "Zuriel's robe top", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}-{6,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13860", + "name": "Zuriel's robe top (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "12,6,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}-{6,78}", + "shop_price": "300000" + }, + { + "id": "13861", + "name": "Zuriel's robe bottom", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "8,4,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}-{6,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13862", + "name": "Zuriel's robe bottom", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}-{6,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13863", + "name": "Zuriel's robe bottom (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "8,4,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}-{6,78}", + "shop_price": "300000" + }, + { + "id": "13864", + "name": "Zuriel's hood", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}-{6,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13865", + "name": "Zuriel's hood", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}-{6,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13866", + "name": "Zuriel's hood (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}-{6,78}", + "shop_price": "150000" + }, + { + "id": "13867", + "name": "Zuriel's staff", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", + "defence_anim": "420", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,78}-{6,78}", + "run_anim": "1210", + "shop_price": "300000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "13868", + "name": "Zuriel's staff", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,78}-{6,78}", + "shop_price": "300000", + "tradeable": "true" + }, + { + "id": "13869", + "name": "Zuriel's staff (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", + "defence_anim": "420", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "rare_item": "true", + "render_anim": "28", + "requirements": "{0,78}-{6,78}", + "run_anim": "1210", + "shop_price": "300000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1" + }, + { + "id": "13870", + "name": "Morrigan's leather body", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,12,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}-{4,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13871", + "name": "Morrigan's leather body", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}-{4,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13872", + "name": "Morrigan's leather body (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,12,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}-{4,78}", + "shop_price": "300000" + }, + { + "id": "13873", + "name": "Morrigan's leather chaps", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,8,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop, Destroy after degrading.", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}-{4,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13874", + "name": "Morrigan's leather chaps", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop, Destroy after degrading.", + "durability": null, + "requirements": "{1,78}-{4,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13875", + "name": "Morrigan's leather chaps (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,8,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop, Destroy after degrading.", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}-{4,78}", + "shop_price": "300000" + }, + { + "id": "13876", + "name": "Morrigan's coif", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}-{4,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13877", + "name": "Morrigan's coif", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}-{4,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13878", + "name": "Morrigan's coif (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}-{4,78}", + "shop_price": "150000" + }, + { + "id": "13879", + "name": "Morrigan's javelin", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1381", + "requirements": "{4,78}", + "shop_price": "4000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "13880", + "name": "Morrigan's javelin(p)", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1381", + "requirements": "{4,78}", + "shop_price": "10000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "13881", + "name": "Morrigan's javelin(p+)", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1381", + "requirements": "{4,78}", + "shop_price": "10000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "13882", + "name": "Morrigan's javelin(p++)", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1381", + "requirements": "{4,78}", + "shop_price": "10000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "13883", + "name": "Morrigan's throwing axe", + "examine": "A vicious throwing axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,117", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "requirements": "{4,78}", + "shop_price": "4000", + "tradeable": "true", + "weapon_interface": "18" + }, + { + "id": "13884", + "name": "Statius's platebody", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,5,7,-30,-10,154,145,121,-6,157,60,5,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13885", + "name": "Statius's platebody", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13886", + "name": "Statius's platebody (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,5,7,-30,-10,154,145,121,-6,157,60,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}", + "shop_price": "300000" + }, + { + "id": "13887", + "name": "Vesta's chainbody", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0", + "destroy": "false", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13888", + "name": "Vesta's chainbody", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13889", + "name": "Vesta's chainbody (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0", + "destroy": "true", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,78}", + "shop_price": "300000" + }, + { + "id": "13890", + "name": "Statius's platelegs", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13891", + "name": "Statius's platelegs", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13892", + "name": "Statius's platelegs (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,78}", + "shop_price": "300000" + }, + { + "id": "13893", + "name": "Vesta's plateskirt", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13894", + "name": "Vesta's plateskirt", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,78}", + "shop_price": "500000", + "tradeable": "true" + }, + { + "id": "13895", + "name": "Vesta's plateskirt (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,78}", + "shop_price": "300000" + }, + { + "id": "13896", + "name": "Statius's full helm", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13897", + "name": "Statius's full helm", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "requirements": "{1,78}", + "shop_price": "250000", + "tradeable": "true" + }, + { + "id": "13898", + "name": "Statius' full helm (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,78}", + "shop_price": "150000" + }, + { + "id": "13899", + "name": "Vesta's longsword", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", + "defence_anim": "397", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "has_special": "true", + "rare_item": "true", + "render_anim": "1426", + "requirements": "{0,78}", + "run_anim": "824", + "shop_price": "300000", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "6" + }, + { + "id": "13900", + "name": "Vesta's longsword", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, "requirements": "{0,78}", "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "two_handed": "true", - "destroy_message": "Drop", - "grand_exchange_price": "5264612", - "name": "Vesta's spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13906" + "tradeable": "true" }, { + "id": "13901", + "name": "Vesta's longsword (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "has_special": "true", + "rare_item": "true", + "render_anim": "1426", + "requirements": "{0,78}", + "run_anim": "824", + "shop_price": "300000", + "stand_turn_anim": "823", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "6" + }, + { + "id": "13902", + "name": "Statius's warhammer", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0", + "defence_anim": "397", + "destroy": "false", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "has_special": "true", + "requirements": "{0,78}", + "run_anim": "1210", + "shop_price": "300000", + "stand_anim": "808", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10" + }, + { + "id": "13903", + "name": "Statius's warhammer", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "requirements": "{0,78}", + "shop_price": "300000", + "tradeable": "true" + }, + { + "id": "13904", + "name": "Statius' warhammer (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,395,395", + "attack_audios": "2504,0,0,0", + "attack_speed": "6", + "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0", + "destroy": "true", + "destroy_message": "Drop, Destroy once it has been degraded.", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1426", + "run_anim": "1210", + "shop_price": "300000", + "stand_anim": "808", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "10" + }, + { + "id": "13905", + "name": "Vesta's spear", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "133,113,120,0,0,18,21,21,0,0,0,122,0,0,0", + "defence_anim": "2079", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", + "requirements": "{0,78}", + "run_anim": "1210", + "shop_price": "300000", + "stand_anim": "813", + "stand_turn_anim": "1209", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14", + "weight": "4.2" + }, + { + "id": "13906", + "name": "Vesta's spear", + "examine": "This item degrades in combat, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,78}", + "shop_price": "300000", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "13907", + "name": "Vesta's spear (deg)", + "examine": "This item degrades in combat, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "133,113,120,0,0,18,21,21,0,0,0,122,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "has_special": "true", + "render_anim": "28", "requirements": "{0,78}", "shop_price": "180000", - "examine": "This item degrades in combat, and will turn to dust.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "5", + "turn90ccw_anim": "1208", "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "5264612", - "name": "Vesta's spear (deg)", - "archery_ticket_price": "0", - "id": "13907", - "bonuses": "133,113,120,0,0,18,21,21,0,0,0,122,0,0,0" + "walk_anim": "1205", + "weapon_interface": "14" }, { - "requirements": "{1,78}", - "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", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1897306", - "name": "Corrupt statius's platebody", - "archery_ticket_price": "0", "id": "13908", - "bonuses": "5,5,7,-10,-30,154,145,121,-6,157,60,5,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "1897306", - "durability": null, "name": "Corrupt statius's platebody", - "tradeable": "true", - "destroy": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,0,12", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13909" + "bonuses": "5,5,7,-10,-30,154,145,121,-6,157,60,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}", + "shop_price": "125000" }, { - "requirements": "{1,78}", - "shop_price": "75000", + "id": "13909", + "name": "Corrupt statius's platebody", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,0,12", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "1897306", - "name": "Corrupt statius's platebody (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13910", - "bonuses": "5,5,7,-10,-30,154,145,121,-6,157,60,5,0,0,0" + "name": "Corrupt statius's platebody (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,5,7,-10,-30,154,145,121,-6,157,60,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}}", + "shop_price": "75000" }, { - "requirements": "{1,78}", - "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", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "2546175", - "name": "Corrupt vesta's chainbody", - "archery_ticket_price": "0", "id": "13911", - "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "2546175", - "durability": null, "name": "Corrupt vesta's chainbody", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13912" - }, - { - "requirements": "{1,78}", - "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", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "2546175", - "name": "Corrupt vesta's chainbody (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}", + "shop_price": "125000" + }, + { + "id": "13912", + "name": "Corrupt vesta's chainbody", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13913", - "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0" + "name": "Corrupt vesta's chainbody (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,0,12", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "5,7,7,-15,0,120,131,145,-3,140,60,6,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}", + "shop_price": "75000" }, { - "requirements": "{1,78}", - "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", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1734527", - "name": "Corrupt statius's platelegs", - "archery_ticket_price": "0", "id": "13914", - "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "1734527", - "durability": null, "name": "Corrupt statius's platelegs", - "tradeable": "true", - "destroy": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,8", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13915" + "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,20}", + "shop_price": "125000" }, { - "requirements": "{1,78}", - "shop_price": "75000", + "id": "13915", + "name": "Corrupt statius's platelegs", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1734527", - "name": "Corrupt statius's platelegs (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13916", - "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0" + "name": "Corrupt statius's platelegs (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,5,-21,-7,110,106,97,-4,121,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,20}", + "shop_price": "75000" }, { - "requirements": "{1,78}", - "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", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1942627", - "name": "Corrupt vesta's plateskirt", - "archery_ticket_price": "0", "id": "13917", - "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "1942627", - "durability": null, "name": "Corrupt vesta's plateskirt", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13918" - }, - { - "requirements": "{1,78}", - "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", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1942627", - "name": "Corrupt vesta's plateskirt (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}", + "shop_price": "125000" + }, + { + "id": "13918", + "name": "Corrupt vesta's plateskirt", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13919", - "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0" + "name": "Corrupt vesta's plateskirt (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,8", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}", + "shop_price": "75000" }, { - "remove_head": "true", - "requirements": "{1,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "1062835", - "name": "Corrupt statius's full helm", - "archery_ticket_price": "0", "id": "13920", - "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "1062835", - "durability": null, "name": "Corrupt statius's full helm", - "tradeable": "true", - "destroy": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13921" + "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}", + "shop_price": "50000" }, { - "remove_head": "true", - "requirements": "{1,78}", - "shop_price": "30000", + "id": "13921", + "name": "Corrupt statius's full helm", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "1062835", - "name": "Corrupt statius' full helm (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}", + "shop_price": "50000", + "tradeable": "true" + }, + { "id": "13922", - "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" + "name": "Corrupt statius' full helm (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}", + "shop_price": "30000" }, { - "requirements": "{0,78}", - "shop_price": "125000", - "turn90cw_anim": "1207", - "examine": "This item degrades while worn, and will turn to dust.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "5", - "turn180_anim": "1206", - "absorb": "0,0,0", - "render_anim": "1426", - "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "destroy_message": "Drop", - "grand_exchange_price": "3739998", - "name": "Corrupt vesta's longsword", - "run_anim": "824", - "archery_ticket_price": "0", "id": "13923", - "stand_turn_anim": "823", - "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0" - }, - { - "requirements": "{0,78}", - "shop_price": "125000", - "turn90cw_anim": "1207", - "examine": "This item degrades while worn, and will turn to dust.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "1208", - "attack_speed": "4", - "turn180_anim": "1206", - "render_anim": "1426", - "attack_anims": "381,390,390,390", - "destroy_message": "Drop", - "grand_exchange_price": "3739998", "name": "Corrupt vesta's longsword", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "13924", - "stand_turn_anim": "823" - }, - { - "requirements": "{0,78}", - "shop_price": "75000", - "turn90cw_anim": "1207", "examine": "This item degrades while worn, and will turn to dust.", - "walk_anim": "1205", - "durability": null, - "destroy": "true", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "5", - "turn180_anim": "1206", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "381,390,390,390", + "attack_speed": "5", + "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", "render_anim": "1426", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "3739998", - "name": "C. vesta's longsword (deg)", + "requirements": "{0,20}", "run_anim": "824", - "archery_ticket_price": "0", - "id": "13925", + "shop_price": "125000", "stand_turn_anim": "823", - "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5" }, { - "requirements": "{0,78}", - "shop_price": "125000", + "id": "13924", + "name": "Corrupt vesta's longsword", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "8", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "3033516", - "attack_audios": "395,395,395,395", - "name": "Corrupt statius's warhammer", "archery_ticket_price": "0", + "attack_anims": "381,390,390,390", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "1426", + "requirements": "{0,20}", + "run_anim": "824", + "shop_price": "125000", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205" + }, + { + "id": "13925", + "name": "C. vesta's longsword (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "106,121,-2,0,0,1,4,3,0,0,0,118,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1426", + "requirements": "{0,20}", + "run_anim": "824", + "shop_price": "75000", + "stand_turn_anim": "823", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5" + }, + { "id": "13926", - "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{0,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "3033516", - "durability": null, "name": "Corrupt statius's warhammer", - "tradeable": "true", - "destroy": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13927" + "attack_audios": "395,395,395,395", + "attack_speed": "6", + "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,114,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,20}", + "shop_price": "125000", + "weapon_interface": "8" }, { - "requirements": "{0,78}", - "shop_price": "75000", + "id": "13927", + "name": "Corrupt statius's warhammer", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "8", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "3033516", - "name": "C. statius's warhammer (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13928", - "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,0,0,0,0" + "name": "C. statius's warhammer (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "-4,-4,123,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2233", + "equipment_slot": "3", + "requirements": "{0,20}", + "shop_price": "75000", + "weapon_interface": "8" }, { - "requirements": "{0,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "grand_exchange_price": "343474", - "name": "Corrupt vesta's spear", - "archery_ticket_price": "0", "id": "13929", - "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0" - }, - { - "requirements": "{0,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "destroy_message": "Drop", - "grand_exchange_price": "343474", "name": "Corrupt vesta's spear", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13930" + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,20}", + "shop_price": "125000", + "two_handed": "true", + "weapon_interface": "14" }, { - "requirements": "{0,78}", - "shop_price": "75000", + "id": "13930", + "name": "Corrupt vesta's spear", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "343474", - "name": "Corrupt vesta's spear (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,20}", + "shop_price": "125000", + "tradeable": "true", + "two_handed": "true" + }, + { "id": "13931", - "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0" + "name": "Corrupt vesta's spear (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "133,113,120,0,0,18,21,21,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{0,20}", + "shop_price": "75000", + "two_handed": "true", + "weapon_interface": "14" }, { - "requirements": "{1,78}-{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "12,6,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "253230", - "name": "Corrupt zuriel's robe top", - "archery_ticket_price": "0", "id": "13932", - "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "253230", - "durability": null, "name": "Corrupt zuriel's robe top", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13933" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", "absorb": "12,6,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "253230", - "name": "Corrupt zuriel's robe top (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,20}", + "shop_price": "125000" + }, + { + "id": "13933", + "name": "Corrupt zuriel's robe top", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{6,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13934", - "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0" + "name": "Corrupt zuriel's robe top (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "12,6,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,35,-10,63,45,74,35,0,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,20}", + "shop_price": "75000" }, { - "requirements": "{1,78}-{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "8,4,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "121290", - "name": "Corrupt zuriel's robe bottom", - "archery_ticket_price": "0", "id": "13935", - "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "121290", - "durability": null, "name": "Corrupt zuriel's robe bottom", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13936" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", "absorb": "8,4,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "121290", - "name": "Corrupt zuriel's robe bottom (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}-{6,20}", + "shop_price": "125000" + }, + { + "id": "13936", + "name": "Corrupt zuriel's robe bottom", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{6,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { "id": "13937", - "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0" + "name": "Corrupt zuriel's robe bottom (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "8,4,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}-{6,20}", + "shop_price": "75000" }, { - "remove_head": "true", - "requirements": "{1,78}-{6,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "43778", - "name": "Corrupt zuriel's hood", - "archery_ticket_price": "0", "id": "13938", - "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{6,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "43778", - "durability": null, "name": "Corrupt zuriel's hood", - "tradeable": "true", - "destroy": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,3,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13939" + "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}-{6,20}", + "shop_price": "50000" }, { - "remove_head": "true", - "requirements": "{1,78}-{6,78}", - "shop_price": "30000", + "id": "13939", + "name": "Corrupt zuriel's hood", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "43778", - "name": "Corrupt zuriel's hood (deg)", "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{6,20}", + "shop_price": "50000", + "tradeable": "true" + }, + { "id": "13940", - "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" - }, - { - "requirements": "{6,78}", - "shop_price": "125000", + "name": "Corrupt zuriel's hood (deg)", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "attack_anims": "2555,0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "219418", - "attack_audios": "2555,0,0,0", - "name": "Corrupt zuriel's staff", - "archery_ticket_price": "0", - "id": "13941", - "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10" - }, - { - "destroy_message": "Drop", - "requirements": "{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "219418", - "durability": null, - "name": "Corrupt zuriel's staff", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13942" - }, - { - "requirements": "{6,78}", - "shop_price": "70000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "219418", - "name": "Corrupt zuriel's staff (deg)", - "archery_ticket_price": "0", - "id": "13943", - "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,12,6", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "387212", - "name": "Corrupt morrigan's leather body", - "archery_ticket_price": "0", - "id": "13944", - "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "387212", - "durability": null, - "name": "Corrupt morrigan's leather body", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13945" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "75000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,12,6", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "387212", - "name": "Corrupt morrigan's leather body (deg)", - "archery_ticket_price": "0", - "id": "13946", - "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,8,4", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "84592", - "name": "Corrupt morrigan's leather chaps", - "archery_ticket_price": "0", - "id": "13947", - "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "84592", - "durability": null, - "name": "Corrupt morrigan's leather chaps", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13948" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "75000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,8,4", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "84592", - "name": "Corrupt morrigan's leather chaps (deg)", - "archery_ticket_price": "0", - "id": "13949", - "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{4,78}", - "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", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "63502", - "name": "Corrupt morrigan's coif", - "archery_ticket_price": "0", - "id": "13950", - "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "requirements": "{1,78}-{4,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "63502", - "durability": null, - "name": "Corrupt morrigan's coif", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13951" - }, - { - "remove_head": "true", - "requirements": "{1,78}-{4,78}", - "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", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "63502", - "name": "Corrupt morrigan's coif (deg)", - "archery_ticket_price": "0", - "id": "13952", - "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "1000", - "examine": "A vicious javelin.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "6752", - "name": "Corrupt morrigan's javelin", - "archery_ticket_price": "0", - "id": "13953", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "6752", - "name": "C. morrigan's javelin (p)", - "archery_ticket_price": "0", - "id": "13954", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "6752", - "name": "C. morrigan's javelin (p+)", - "archery_ticket_price": "0", - "id": "13955", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "18", - "absorb": "0,0,0", - "render_anim": "1381", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "6752", - "name": "C. morrigan's javelin (p++)", - "archery_ticket_price": "0", - "id": "13956", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145" - }, - { - "requirements": "{4,78}", - "shop_price": "1000", - "examine": "A vicious throwing axe.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "18", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "5388", - "name": "C. morrigan's throwing axe", - "archery_ticket_price": "0", - "id": "13957", - "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,117" - }, - { - "requirements": "{0,78}-{4,78}", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "168278", - "name": "Corrupt dragon chainbody", - "archery_ticket_price": "0", - "id": "13958", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "168278", - "durability": null, - "name": "Corrupt dragon chainbody", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13959" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "168278", - "name": "Corrupt dragon chainbody (deg)", - "archery_ticket_price": "0", - "id": "13960", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "121835", - "name": "Corrupt dragon med helm", - "archery_ticket_price": "0", - "id": "13961", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "121835", - "durability": null, - "name": "Corrupt dragon med helm", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13962" - }, - { - "remove_head": "true", - "shop_price": "36000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "121835", - "name": "Corrupt dragon med helm (deg)", - "archery_ticket_price": "0", - "id": "13963", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "5,0,11", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "79703", - "name": "Corrupt dragon sq shield", - "archery_ticket_price": "0", - "id": "13964", - "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "79703", - "durability": null, - "name": "Corrupt dragon sq shield", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13965" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "5,0,11", - "equipment_slot": "5", - "destroy_message": "Drop", - "grand_exchange_price": "79703", - "name": "Corrupt dragon sq shield (deg)", - "archery_ticket_price": "0", - "id": "13966", - "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "7", - "destroy_message": "Destroy", - "grand_exchange_price": "71066", - "name": "Corrupt dragon plateskirt", - "archery_ticket_price": "0", - "id": "13967", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "destroy_message": "Destroy", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "71066", - "durability": null, - "name": "Corrupt dragon plateskirt", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13968" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "7", - "destroy_message": "Destroy", - "grand_exchange_price": "71066", - "name": "Corrupt dragon plateskirt (deg)", - "archery_ticket_price": "0", - "id": "13969", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "87957", - "name": "Corrupt dragon platelegs", - "archery_ticket_price": "0", - "id": "13970", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "87957", - "durability": null, - "name": "Corrupt dragon platelegs", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13971" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "87957", - "name": "Corrupt dragon platelegs (deg)", - "archery_ticket_price": "0", - "id": "13972", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "2.7", - "attack_speed": "6", - "weapon_interface": "2", - "absorb": "0,0,0", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "destroy_message": "Drop", - "grand_exchange_price": "332229", - "name": "Corrupt dragon battleaxe", - "archery_ticket_price": "0", - "id": "13973", - "bonuses": "-2,70,65,0,0,0,0,0,-1,-1,0,85,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "2.7", - "attack_speed": "4", - "render_anim": "2586", - "defence_anim": "397", - "attack_anims": "395,395,395,395", - "destroy_message": "Drop", - "grand_exchange_price": "332229", - "name": "Corrupt dragon battleaxe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13974", - "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "2.7", - "attack_speed": "6", - "weapon_interface": "2", - "absorb": "0,0,0", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "395,395,395,395", - "destroy_message": "Drop", - "grand_exchange_price": "332229", - "name": "C. dragon battleaxe (deg)", - "archery_ticket_price": "0", - "id": "13975", - "bonuses": "-2,70,65,0,0,0,0,0,-1,-1,0,85,0,0,0" - }, - { - "shop_price": "30000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "grand_exchange_price": "33358", - "attack_audios": "2517,2517,2500,2517", - "name": "Corrupt dragon dagger", - "archery_ticket_price": "0", - "id": "13976", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "shop_price": "30000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "defence_anim": "397", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "grand_exchange_price": "33358", - "attack_audios": "2517,2517,2500,2517", - "name": "Corrupt dragon dagger", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13977" - }, - { - "shop_price": "18000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "396,396,395,396", - "destroy_message": "Drop", - "grand_exchange_price": "33358", - "attack_audios": "2517,2517,2500,2517", - "name": "C. dragon dagger (deg)", - "archery_ticket_price": "0", - "id": "13978", - "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "100000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1928", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "grand_exchange_price": "338644", - "attack_audios": "2500,0,2517,0", - "name": "Corrupt dragon scimitar", - "archery_ticket_price": "0", - "id": "13979", - "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "338644", - "durability": null, - "name": "Corrupt dragon scimitar", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13980" - }, - { - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1928", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "338644", - "name": "C. dragon scimitar (deg)", - "archery_ticket_price": "0", - "id": "13981", - "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "100000", - "ge_buy_limit": "10", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "1582", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "grand_exchange_price": "207053", - "attack_audios": "2500,2500,2517,2500", - "name": "Corrupt dragon longsword", - "archery_ticket_price": "0", - "id": "13982", - "bonuses": "58,69,-2,0,0,0,3,2,1,0,0,71,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100000", - "examine": "This item degrades while worn, and will turn to dust.", - "grand_exchange_price": "207053", - "durability": null, - "name": "Corrupt dragon longsword", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13983" - }, - { - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "1582", - "equipment_slot": "3", - "destroy_message": "Drop", - "grand_exchange_price": "207053", - "name": "C. dragon longsword (deg)", - "archery_ticket_price": "0", - "id": "13984", - "bonuses": "58,69,-2,0,0,0,3,2,1,0,0,71,0,0,0" - }, - { - "requirements": "{0,60}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "8", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "grand_exchange_price": "50096", - "name": "Corrupt dragon mace", - "archery_ticket_price": "0", - "id": "13985", - "bonuses": "40,-2,60,1,0,0,0,0,0,0,0,55,0,5,0" - }, - { - "requirements": "{0,60}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "4", - "defence_anim": "397", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "grand_exchange_price": "50096", - "name": "Corrupt dragon mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13986" - }, - { - "requirements": "{0,60}", - "shop_price": "30000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "8", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "grand_exchange_price": "50096", - "name": "Corrupt dragon mace (deg)", - "archery_ticket_price": "0", - "id": "13987", - "bonuses": "40,-2,60,1,0,0,0,0,0,0,0,55,0,5,0" - }, - { - "shop_price": "40000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "grand_exchange_price": "920922", - "name": "Corrupt dragon spear", - "archery_ticket_price": "0", - "id": "13988", - "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" - }, - { - "shop_price": "40000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "defence_anim": "2079", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "grand_exchange_price": "920922", - "name": "Corrupt dragon spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13989" - }, - { - "shop_price": "24000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "28", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "destroy_message": "Drop", - "grand_exchange_price": "920922", - "name": "Corrupt dragon spear (deg)", - "archery_ticket_price": "0", - "id": "13990", - "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.", - "shop_price": "5", - "examine": "A book about the Wilderness.", - "durability": null, - "name": "Pvp worlds manual", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14056", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "25", - "examine": "A threat to dusty corners everywhere.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "8", - "absorb": "0,0,0", - "render_anim": "1072", - "equipment_slot": "3", - "destroy_message": "You can obtain another by talking to Maggie the witch.", - "name": "Broomstick", - "archery_ticket_price": "0", - "id": "14057", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you drop the goulash, it will be destroyed. You can get some more by dipping a bowl into Maggie's cauldron.", - "shop_price": "2", - "examine": "What a curious and ghoulish smell!", - "durability": null, - "name": "Goulash", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14058", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "You can get another flask of magic unguent from the chest in Betty's basement.", - "shop_price": "2", - "examine": "Purple and pungent.", - "durability": null, - "name": "Magic unguent", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14061", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another flask of broom ointment from Hetty in Rimmington.", - "shop_price": "2", - "examine": "To be used on brooms.", - "durability": null, - "name": "Broom ointment", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14062", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you release this newt, you can get another from the crate in Hetty's basement in Rimmington.", - "shop_price": "5", - "examine": "A newt from Hetty's basement.", - "durability": null, - "name": "Newt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14064", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "You can get another newt label from Gus in Hetty's basement in Rimmington.", - "shop_price": "1", - "examine": "A label for a shipping crate.", - "durability": null, - "name": "Newt label", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14065", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another toad label from Gus in Hetty's basement in Rimmington.", - "shop_price": "1", - "examine": "A label for a shipping crate.", - "durability": null, - "name": "Toad label", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14066", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another newt-and-toad label from Gus in Hetty's basement in Rimmington.", - "shop_price": "1", - "examine": "A label for a shipping crate.", - "durability": null, - "name": "Newts and toads label", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14067", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can obtain another wand from the chest in Betty's basement.", - "shop_price": "15", - "examine": "Warm to the touch.", - "durability": null, - "name": "Betty's wand", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14068", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you destroy this item, you can get another from Lottie in Betty's basement.", - "shop_price": "2", - "examine": "What a view!", - "durability": null, - "name": "Magic slate", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14069", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you release this creature, you will have to restart the puzzle in Betty's basement.", - "shop_price": "1", - "examine": "Desert dweller.", - "durability": null, - "name": "Reptile", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14070", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "If you release this creature, you will have to restart the puzzle in Betty's basement.", - "shop_price": "1", - "examine": "Bleak and beaky.", - "durability": null, - "name": "Blackbird", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14071", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "An annoying flappy thing.", - "durability": null, - "name": "Bat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14072", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "It's an extremely small brown spider, probably very poisonous.", - "durability": null, - "name": "Spider", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14073", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "A popular dwarven delicacy.", - "durability": null, - "name": "Rat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14074", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "durability": null, - "name": "Snail", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14075", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "The height of warlock fashion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You can get another warlock top from Diango in Draynor Village.", - "remove_sleeves": "true", - "name": "Warlock top", - "archery_ticket_price": "0", - "id": "14076", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another set of warlock legs from Diango in Draynor Village.", - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "name": "Warlock legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14077", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "shop_price": "15", - "examine": "The height of witchly fashion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You can get another witch top from Diango in Draynor.", - "remove_sleeves": "true", - "name": "Witch top", - "archery_ticket_price": "0", - "id": "14078", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another witch top from Diango in Draynor.", - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "name": "Witch skirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14079", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You can get another witch cloak from Diango in Draynor.", - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "name": "Witch cloak", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14080", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "destroy_message": "You can get another warlock cloak from Diango in Draynor Village.", - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "name": "Warlock cloak", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14081", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "destroy_message": "Drop", - "shop_price": "1", - "examine": "A sticky orb of popped corn.", - "durability": null, - "name": "Popcorn ball", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14082", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", - "examine": "Chocolatey goodness.", - "durability": null, - "name": "Chocolate drop", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14083", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", - "examine": "Sugary bliss.", - "durability": null, - "name": "Wrapped candy", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14084", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1", - "examine": "An urgent communiqu?? revealing important information.", - "durability": null, - "name": "Armadyl communiqu?", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14085", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "The height of witchly fashion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "You can get another witch top from Diango in Draynor.", - "remove_sleeves": "true", - "name": "Witch top", - "archery_ticket_price": "0", - "id": "14086", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can get another witch top from Diango in Draynor.", - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "name": "Witch skirt", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14087", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You can get another witch cloak from Diango in Draynor.", - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "name": "Witch cloak", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14088", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "durability": null, - "name": "Pet kitten", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14089", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Pet cat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14090", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14091", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Overgrown cat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14092", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14093", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "43333", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Sacred clay platebody", - "archery_ticket_price": "0", - "id": "14094", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "42667", - "examine": "The sacred clay bottom has transformed into spiked, platemail legs.", - "durability": null, - "name": "Sacred clay platelegs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14095", - "absorb": "2,0,4", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "shop_price": "23467", - "examine": "The sacred clay hat has transformed into a spined helm.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Sacred clay helm", - "archery_ticket_price": "0", - "id": "14096", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "shop_price": "17066", - "examine": "The sacred clay weapon has transformed into a sharp scimitar.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay scimitar", - "archery_ticket_price": "0", - "id": "14097", - "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "This can turn into a variety of tools.", - "durability": null, - "name": "Volatile clay tool", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14098", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "21333", - "examine": "Your volatile tool wants to mine some rocks.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Volatile clay pickaxe", - "archery_ticket_price": "0", - "id": "14099", - "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "shop_price": "21333", - "examine": "Your volatile tool wants to cut some wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Volatile clay hatchet", - "archery_ticket_price": "0", - "id": "14100", - "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "Your volatile tool wants to spear some big fish.", - "durability": null, - "name": "Volatile clay harpoon", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14101", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "21333", - "examine": "Your volatile tool wants to catch some butterflies.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Volatile clay butterfly net", - "archery_ticket_price": "0", - "id": "14102", - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "Your volatile tool wants to work metal or build furniture.", - "durability": null, - "name": "Volatile clay hammer", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14104", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "Your volatile tool wants to make some leather armour.", - "durability": null, - "name": "Volatile clay needle", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14105", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "This can turn into a variety of tools.", - "durability": null, - "name": "Proto-tool", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14106", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "21333", - "examine": "Your sacred clay tool has transformed into a pickaxe.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay pickaxe", - "archery_ticket_price": "0", - "id": "14107", - "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "shop_price": "21333", - "examine": "Your sacred clay tool has transformed into a hatchet.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay hatchet", - "archery_ticket_price": "0", - "id": "14108", - "bonuses": "-2,4,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "Your sacred clay tool has transformed into a harpoon.", - "durability": null, - "name": "Sacred clay harpoon", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14109", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "1", - "examine": "Your sacred clay tool has transformed into a butterfly net.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "5", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay butterfly net", - "archery_ticket_price": "0", - "id": "14110", - "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "The sacred clay has transformed into a knife.", - "durability": null, - "name": "Sacred clay fletching knife", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14111", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "Your sacred clay tool has transformed into a hammer.", - "durability": null, - "name": "Sacred clay hammer", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14112", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "21333", - "examine": "The sacred clay has transformed into a needle.", - "durability": null, - "name": "Sacred clay needle", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14113", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "43333", - "examine": "The sacred clay top has transformed into a heavy, magical robe.", - "durability": null, - "destroy": "true", - "attack_speed": "4", "absorb": "6,3,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Sacred clay robe top", - "archery_ticket_price": "0", - "id": "14114", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "42667", - "examine": "The sacred clay bottom has transformed into a heavy, magical robe.", - "durability": null, - "name": "Sacred clay robe bottom", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14115", - "absorb": "4,2,0", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", - "equipment_slot": "7" - }, - { + "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0", + "destroy": "true", "destroy_message": "Drop", - "shop_price": "23467", - "examine": "The sacred clay hat has transformed into a wizard hat.", "durability": null, - "name": "Sacred clay hat", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14116", - "absorb": "3,1,0", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", - "equipment_slot": "0" + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}-{6,20}", + "shop_price": "30000" }, { - "shop_price": "17066", - "examine": "The sacred clay weapon has transformed into a magical staff.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", + "id": "13941", + "name": "Corrupt zuriel's staff", + "examine": "This item degrades while worn, and will turn to dust.", "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay staff", "archery_ticket_price": "0", - "id": "14117", - "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0" + "attack_anims": "2555,0,0,0", + "attack_audios": "2555,0,0,0", + "attack_speed": "6", + "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,20}", + "shop_price": "125000", + "weapon_interface": "1" }, { - "shop_price": "43333", - "examine": "The sacred clay top has transformed into a leather body.", - "durability": null, - "destroy": "true", + "id": "13942", + "name": "Corrupt zuriel's staff", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{6,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { + "id": "13943", + "name": "Corrupt zuriel's staff (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "requirements": "{6,20}", + "shop_price": "70000", + "weapon_interface": "1" + }, + { + "id": "13944", + "name": "Corrupt morrigan's leather body", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,12,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{4,20}", + "shop_price": "125000" + }, + { + "id": "13945", + "name": "Corrupt morrigan's leather body", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{4,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { + "id": "13946", + "name": "Corrupt morrigan's leather body (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,12,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,36,61,53,66,75,62,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{4,20}", + "shop_price": "75000" + }, + { + "id": "13947", + "name": "Corrupt morrigan's leather chaps", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,8,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}-{4,20}", + "shop_price": "125000" + }, + { + "id": "13948", + "name": "Corrupt morrigan's leather chaps", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{4,20}", + "shop_price": "125000", + "tradeable": "true" + }, + { + "id": "13949", + "name": "Corrupt morrigan's leather chaps (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,8,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}-{4,20}", + "shop_price": "75000" + }, + { + "id": "13950", + "name": "Corrupt morrigan's coif", + "examine": "This item degrades while worn, and will turn to dust.", "absorb": "0,6,3", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Sacred clay body", - "archery_ticket_price": "0", - "id": "14118", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "42667", - "examine": "The sacred clay bottom has been transformed into a set of chaps.", - "durability": null, - "name": "Sacred clay chaps", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14119", - "absorb": "0,4,2", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", - "equipment_slot": "7" - }, - { - "remove_head": "true", - "shop_price": "23467", - "examine": "The sacred clay hat has transformed into a coif.", - "durability": null, + "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0", "destroy": "true", - "attack_speed": "4", - "absorb": "0,3,1", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2238", "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Sacred clay coif", - "archery_ticket_price": "0", - "id": "14120", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,5,0,0,0,0" + "remove_head": "true", + "requirements": "{1,20}-{4,20}", + "shop_price": "50000" }, { - "shop_price": "17066", - "examine": "The sacred clay weapon has transformed into a bow.", - "durability": null, - "destroy": "true", + "id": "13951", + "name": "Corrupt morrigan's coif", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "16", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{1,20}-{4,20}", + "shop_price": "50000", + "tradeable": "true" + }, + { + "id": "13952", + "name": "Corrupt morrigan's coif (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,20}-{4,20}", + "shop_price": "30000" + }, + { + "id": "13953", + "name": "Corrupt morrigan's javelin", + "examine": "A vicious javelin.", "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Sacred clay bow", "archery_ticket_price": "0", - "id": "14121", - "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14122", - "bonuses": "10,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "name": "Pickaxe (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14123" - }, - { - "shop_price": "60", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14124", - "bonuses": "15,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "name": "Pickaxe (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14125" - }, - { - "shop_price": "90", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14126", - "bonuses": "20,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "name": "Pickaxe (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14127" - }, - { - "shop_price": "120", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14128", - "bonuses": "25,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "name": "Pickaxe (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14129" - }, - { - "shop_price": "150", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14130", - "bonuses": "30,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "name": "Pickaxe (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14131" - }, - { - "shop_price": "30", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14132", - "bonuses": "0,10,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "name": "Hatchet (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14133" - }, - { - "shop_price": "60", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14134", - "bonuses": "0,15,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "name": "Hatchet (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14135" - }, - { - "shop_price": "90", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14136", - "bonuses": "0,20,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "name": "Hatchet (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14137" - }, - { - "shop_price": "120", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14138", - "bonuses": "0,25,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "name": "Hatchet (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14139" - }, - { - "shop_price": "150", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14140", - "bonuses": "0,30,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "name": "Hatchet (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14141" - }, - { - "shop_price": "30", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Harpoon (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14142", - "bonuses": "10,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "name": "Harpoon (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14143" - }, - { - "shop_price": "60", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Harpoon (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14144", - "bonuses": "15,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "name": "Harpoon (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14145" - }, - { - "shop_price": "90", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Harpoon (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14146", - "bonuses": "20,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "name": "Harpoon (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14147" - }, - { - "shop_price": "120", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Harpoon (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14148", - "bonuses": "25,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "name": "Harpoon (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14149" - }, - { - "shop_price": "150", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Harpoon (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14150", - "bonuses": "30,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has become long enough to reach the depths of pools.", - "durability": null, - "name": "Harpoon (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14151" - }, - { - "shop_price": "30", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Butterfly net (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14152", - "bonuses": "0,0,10,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "name": "Butterfly net (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14153" - }, - { - "shop_price": "60", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Butterfly net (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14154", - "bonuses": "0,0,15,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "name": "Butterfly net (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14155" - }, - { - "shop_price": "90", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Butterfly net (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14156", - "bonuses": "0,0,20,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "name": "Butterfly net (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14157" - }, - { - "shop_price": "120", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Butterfly net (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14158", - "bonuses": "0,0,25,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "name": "Butterfly net (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14159" - }, - { - "shop_price": "150", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "14", - "absorb": "0,0,0", - "render_anim": "158", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Butterfly net (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14160", - "bonuses": "0,0,30,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has become light enough to swing at floating shards.", - "durability": null, - "name": "Butterfly net (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14161" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14162", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14163" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14164", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14165" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14166", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14167" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14168", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14169" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14170", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into something bland, but edible.", - "durability": null, - "name": "Food (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14171" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14172", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14173" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14174", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14175" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14176", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14177" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14178", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14179" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14180", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The clay has transformed into an expandable wall.", - "durability": null, - "name": "Barrier (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14181" - }, - { - "destroy_message": "Drop", - "shop_price": "15", - "examine": "Very low quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14182", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "15", - "examine": "Very low quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14183" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "Low quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14184", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "Low quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14185" - }, - { - "destroy_message": "Drop", - "shop_price": "45", - "examine": "Medium quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14186", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "45", - "examine": "Medium quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14187" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "High quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14188", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "High quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14189" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Very high quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14190", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "75", - "examine": "Very high quality sacred clay.", - "durability": null, - "name": "Sacred clay (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14191" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14192", - "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "name": "Bow (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14193" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14194", - "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "name": "Bow (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14195" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14196", - "bonuses": "0,0,0,0,135,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "name": "Bow (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14197" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14198", - "bonuses": "0,0,0,0,180,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "name": "Bow (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14199" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14200", - "bonuses": "0,0,0,0,225,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a wood-like substance.", - "durability": null, - "name": "Bow (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14201" - }, - { - "shop_price": "2", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "name": "Arrows (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14202", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,15" - }, - { - "shop_price": "4", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "name": "Arrows (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14203", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,30" - }, - { - "shop_price": "6", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "name": "Arrows (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14204", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,45" - }, - { - "shop_price": "8", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "name": "Arrows (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14205", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60" - }, - { - "shop_price": "10", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "13", - "destroy_message": "Drop", - "name": "Arrows (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14206", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75" - }, - { - "shop_price": "150", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14207", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "3 doses of Prayer restore potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Prayer potion(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14208" - }, - { - "shop_price": "120", - "examine": "4 doses of Prayer restore potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of Prayer restore potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Prayer potion(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14210" - }, - { - "shop_price": "90", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14211", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of Prayer restore potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Prayer potion(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14212" - }, - { - "shop_price": "60", - "examine": "2 doses of Prayer restore potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14213", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of Prayer restore potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Prayer potion(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14214" - }, - { - "shop_price": "30", - "examine": "1 dose of Prayer restore potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14215", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of Prayer restore potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Prayer potion(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14216" - }, - { - "shop_price": "150", - "examine": "4 doses of energy potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14217", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "4 doses of energy potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Energy potion (5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14218" - }, - { - "shop_price": "120", - "examine": "4 doses of energy potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14219", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of energy potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Energy potion (4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14220" - }, - { - "shop_price": "90", - "examine": "3 doses of energy potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14221", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of energy potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Energy potion (3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14222" - }, - { - "shop_price": "60", - "examine": "2 doses of energy potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14223", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of energy potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Energy potion (2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14224" - }, - { - "shop_price": "30", - "examine": "1 dose of energy potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14225", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of energy potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Energy potion (1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14226" - }, - { - "shop_price": "150", - "examine": "5 doses of super Attack potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14227", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "5 doses of super Attack potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super attack(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14228" - }, - { - "shop_price": "120", - "examine": "4 doses of super Attack potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14229", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of super Attack potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super attack(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14230" - }, - { - "shop_price": "90", - "examine": "3 doses of super Attack potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14231", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of super Attack potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super attack(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14232" - }, - { - "shop_price": "60", - "examine": "2 doses of super Attack potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14233", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of super Attack potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super attack(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14234" - }, - { - "shop_price": "30", - "examine": "1 dose of super Attack potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14235", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of super Attack potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super attack(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14236" - }, - { - "shop_price": "150", - "examine": "5 doses of super Strength potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14237", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "5 doses of super Strength potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super strength(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14238" - }, - { - "shop_price": "120", - "examine": "4 doses of super Strength potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14239", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of super Strength potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super strength(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14240" - }, - { - "shop_price": "90", - "examine": "3 doses of super Strength potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14241", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of super Strength potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super strength(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14242" - }, - { - "shop_price": "60", - "examine": "2 doses of super Strength potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14243", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of super Strength potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super strength(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14244" - }, - { - "shop_price": "30", - "examine": "1 dose of super Strength potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14245", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of super Strength potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Super strength(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14246" - }, - { - "shop_price": "150", - "examine": "4 doses of ranging potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14247", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "5 doses of ranging potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Ranging potion(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14248" - }, - { - "shop_price": "120", - "examine": "4 doses of ranging potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14249", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of ranging potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Ranging potion(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14250" - }, - { - "shop_price": "90", - "examine": "3 doses of ranging potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14251", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of ranging potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Ranging potion(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14252" - }, - { - "shop_price": "60", - "examine": "2 doses of ranging potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14253", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of ranging potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Ranging potion(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14254" - }, - { - "shop_price": "30", - "examine": "1 dose of ranging potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14255", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of ranging potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Ranging potion(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14256" - }, - { - "shop_price": "150", - "examine": "3 doses of Defence Potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14257", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "3 doses of Defence Potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Defence potion(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14258" - }, - { - "shop_price": "120", - "examine": "4 doses of Defence Potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14259", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of Defence Potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Defence potion(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14260" - }, - { - "shop_price": "90", - "examine": "3 doses of Defence Potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14261", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of Defence Potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Defence potion(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14262" - }, - { - "shop_price": "60", - "examine": "2 doses of Defence Potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14263", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of Defence Potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Defence potion(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14264" - }, - { - "shop_price": "30", - "examine": "1 dose of Defence Potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14265", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of Defence Potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Defence potion(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14266" - }, - { - "shop_price": "150", - "examine": "4 doses of Magic potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14267", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "5 doses of Magic potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Magic potion(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14268" - }, - { - "shop_price": "120", - "examine": "4 doses of Magic potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14269", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of Magic potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Magic potion(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14270" - }, - { - "shop_price": "90", - "examine": "3 doses of Magic potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14271", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of Magic potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Magic potion(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14272" - }, - { - "shop_price": "60", - "examine": "2 doses of Magic potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14273", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of Magic potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Magic potion(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14274" - }, - { - "shop_price": "30", - "examine": "1 dose of Magic potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14275", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of Magic potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Magic potion(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14276" - }, - { - "shop_price": "150", - "examine": "3 doses of Summoning potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14277", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "3 doses of Summoning potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Summoning potion(5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14278" - }, - { - "shop_price": "120", - "examine": "4 doses of Summoning potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14279", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "4 doses of Summoning potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Summoning potion(4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14280" - }, - { - "shop_price": "90", - "examine": "3 doses of Summoning potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14281", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "3 doses of Summoning potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Summoning potion(3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14282" - }, - { - "shop_price": "60", - "examine": "2 doses of Summoning potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14283", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "2 doses of Summoning potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Summoning potion(2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14284" - }, - { - "shop_price": "30", - "examine": "1 dose of Summoning potion.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14285", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "1 dose of Summoning potion.", - "grand_exchange_price": "4", - "durability": null, - "name": "Summoning potion(1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14286" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "attack_audios": "2500,0,2517,0", - "name": "Scimitar (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14287", - "bonuses": "0,55,0,0,0,0,0,0,0,0,0,15,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "name": "Scimitar (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14288" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "attack_audios": "2500,0,2517,0", - "name": "Scimitar (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14289", - "bonuses": "0,90,0,0,0,0,0,0,0,0,0,30,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "name": "Scimitar (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14290" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "attack_audios": "2500,0,2517,0", - "name": "Scimitar (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14291", - "bonuses": "0,145,0,0,0,0,0,0,0,0,0,45,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "name": "Scimitar (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14292" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "attack_audios": "2500,0,2517,0", - "name": "Scimitar (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14293", - "bonuses": "0,180,0,0,0,0,0,0,0,0,0,60,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "name": "Scimitar (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14294" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "6", - "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "Drop", - "attack_audios": "2500,0,2517,0", - "name": "Scimitar (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14295", - "bonuses": "0,225,0,0,0,0,0,0,0,0,0,75,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "name": "Scimitar (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14296" - }, - { - "shop_price": "30", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "3", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dagger (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14297", - "bonuses": "41,0,0,0,0,0,0,0,0,0,0,15,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "name": "Dagger (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14298" - }, - { - "shop_price": "60", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "3", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dagger (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14299", - "bonuses": "68,0,0,0,0,0,0,0,0,0,0,30,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "name": "Dagger (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14300" - }, - { - "shop_price": "90", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "3", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dagger (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14301", - "bonuses": "101,0,0,0,0,0,0,0,0,0,0,45,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "name": "Dagger (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14302" - }, - { - "shop_price": "120", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "3", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dagger (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14303", - "bonuses": "135,0,0,0,0,0,0,0,0,0,0,60,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "name": "Dagger (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14304" - }, - { - "shop_price": "150", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "destroy": "true", - "attack_speed": "3", - "weapon_interface": "5", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "400,400,401,400", - "destroy_message": "Drop", - "attack_audios": "2517,2517,2500,2517", - "name": "Dagger (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14305", - "bonuses": "168,0,0,0,0,0,0,0,0,0,0,75,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay transformed into a sharp dagger.", - "durability": null, - "name": "Dagger (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14306" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Warhammer (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14307", - "bonuses": "0,0,83,0,0,0,0,0,0,0,0,15,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "name": "Warhammer (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14308" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Warhammer (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14309", - "bonuses": "0,0,135,0,0,0,0,0,0,0,0,30,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "name": "Warhammer (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14310" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Warhammer (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14311", - "bonuses": "0,0,202,0,0,0,0,0,0,0,0,45,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "name": "Warhammer (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14312" - }, - { - "shop_price": "120", - "examine": "The Sacred Clay has transformed into a heavy warhammer.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Warhammer (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14313", - "bonuses": "0,0,270,0,0,0,0,0,0,0,0,60,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The Sacred Clay has transformed into a heavy warhammer.", - "durability": null, - "name": "Warhammer (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14314" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "destroy": "true", - "attack_speed": "6", - "weapon_interface": "10", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Warhammer (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14315", - "bonuses": "0,0,338,0,0,0,0,0,0,0,0,75,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a heavy warhammer.", - "durability": null, - "name": "Warhammer (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14316" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Robe top (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14317", - "bonuses": "0,0,0,0,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe top (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14318" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Robe top (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14319", - "bonuses": "0,0,0,0,0,0,0,0,40,0,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "name": "Robe top (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14320" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Robe top (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14321", - "bonuses": "0,0,0,0,0,0,0,0,60,0,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "name": "Robe top (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14322" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Robe top (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14323", - "bonuses": "0,0,0,0,0,0,0,0,80,0,80,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "name": "Robe top (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14324" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Robe top (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14325", - "bonuses": "0,0,0,0,0,0,0,0,100,0,100,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like substance.", - "durability": null, - "name": "Robe top (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14326" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Robe bottom (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14327", - "bonuses": "0,0,0,0,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14328" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Robe bottom (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14329", - "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14330" - }, - { - "shop_price": "90", - "examine": "The sacred clay transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Robe bottom (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14331", - "bonuses": "0,0,0,0,0,0,0,0,45,0,45,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14332" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Robe bottom (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14333", - "bonuses": "0,0,0,0,0,0,0,0,60,0,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14334" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Robe bottom (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14335", - "bonuses": "0,0,0,0,0,0,0,0,75,0,75,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14336" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Hat (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14337", - "bonuses": "0,0,0,0,0,0,0,0,10,0,10,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14338" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Hat (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14339", - "bonuses": "0,0,0,0,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14340" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Hat (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14341", - "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14342" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Hat (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14343", - "bonuses": "0,0,0,0,0,0,0,0,40,0,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14344" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Hat (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14345", - "bonuses": "0,0,0,0,0,0,0,0,50,0,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14346" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Platebody (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14347", - "bonuses": "0,0,0,-30,-10,20,20,20,-10,10,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platebody (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14348" - }, - { - "shop_price": "60", - "examine": "The sacred clay transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Platebody (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14349", - "bonuses": "0,0,0,-30,-10,40,40,40,-10,20,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay transformed into a hard, metallic substance.", - "durability": null, - "name": "Platebody (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14350" - }, - { - "shop_price": "90", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Platebody (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14351", - "bonuses": "0,0,0,-30,-10,60,60,60,-10,30,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "name": "Platebody (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14352" - }, - { - "shop_price": "120", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Platebody (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14353", - "bonuses": "0,0,0,-30,-10,80,80,80,-10,40,80,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "name": "Platebody (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14354" - }, - { - "shop_price": "150", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Platebody (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14355", - "bonuses": "0,0,0,-30,-10,100,100,100,-10,50,100,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay top has transformed into a spiked, platemail body.", - "durability": null, - "name": "Platebody (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14356" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Platelegs (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14357", - "bonuses": "0,0,0,-30,-10,15,15,15,-10,7,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14358" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Platelegs (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14359", - "bonuses": "0,0,0,-30,-10,30,30,30,-10,15,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14360" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Platelegs (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14361", - "bonuses": "0,0,0,-30,-10,45,45,45,-10,23,45,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14362" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Platelegs (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14363", - "bonuses": "0,0,0,-30,-10,60,60,60,-10,30,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14364" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Platelegs (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14365", - "bonuses": "0,0,0,-30,-10,75,75,75,-10,37,75,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14366" - }, - { - "remove_head": "true", - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Helm (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14367", - "bonuses": "0,0,0,-30,-10,10,10,10,-10,5,10,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Helm (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14368" - }, - { - "remove_head": "true", - "shop_price": "60", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Helm (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14369", - "bonuses": "0,0,0,-30,-10,20,20,20,-10,10,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Helm (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14370" - }, - { - "remove_head": "true", - "shop_price": "90", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Helm (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14371", - "bonuses": "0,0,0,-30,-10,30,30,30,-10,15,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Helm (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14372" - }, - { - "remove_head": "true", - "shop_price": "120", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Helm (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14373", - "bonuses": "0,0,0,-30,-10,40,40,40,-10,20,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Helm (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14374" - }, - { - "remove_head": "true", - "shop_price": "150", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Helm (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14375", - "bonuses": "0,0,0,-30,-10,50,50,50,-10,25,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Helm (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14376" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a light weight, magical stick.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Staff (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14377", - "bonuses": "0,0,0,55,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a light weight, magical stick.", - "durability": null, - "name": "Staff (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14378" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a light weight, magical stick.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Staff (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14379", - "bonuses": "0,0,0,90,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a light weight, magical stick.", - "durability": null, - "name": "Staff (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14380" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Staff (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14381", - "bonuses": "0,0,0,135,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "name": "Staff (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14382" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Staff (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14383", - "bonuses": "0,0,0,180,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "name": "Staff (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14384" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Staff (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14385", - "bonuses": "0,0,0,225,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "name": "Staff (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14386" - }, - { - "durability": null, - "name": "Cape", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14387", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "durability": null, - "name": "Cape", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14389", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", - "equipment_slot": "1" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Leather body (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14391", - "bonuses": "0,0,0,0,0,10,10,10,10,20,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "name": "Leather body (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14392" - }, - { - "shop_price": "60", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Leather body (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14393", - "bonuses": "0,0,0,0,0,20,20,20,20,40,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "name": "Leather body (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14394" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Leather body (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14395", - "bonuses": "0,0,0,0,0,30,30,30,30,60,60,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "name": "Leather body (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14396" - }, - { - "shop_price": "120", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Leather body (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14397", - "bonuses": "0,0,0,0,0,40,40,40,40,80,80,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "name": "Leather body (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14398" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Leather body (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14399", - "bonuses": "0,0,0,0,0,50,50,50,50,100,100,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a flexible mesh.", - "durability": null, - "name": "Leather body (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14400" - }, - { - "shop_price": "30", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Chaps (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14401", - "bonuses": "0,0,0,0,0,7,7,7,0,15,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14402" - }, - { - "shop_price": "60", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Chaps (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14403", - "bonuses": "0,0,0,0,0,15,15,15,0,30,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14404" - }, - { - "shop_price": "90", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Chaps (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14405", - "bonuses": "0,0,0,0,0,23,23,23,0,45,45,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14406" - }, - { - "shop_price": "120", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Chaps (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14407", - "bonuses": "0,0,0,0,0,30,30,30,0,60,60,0,0,1,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14408" - }, - { - "shop_price": "150", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Chaps (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14409", - "bonuses": "0,0,0,0,0,37,37,37,0,75,75,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14410" - }, - { - "remove_head": "true", - "shop_price": "30", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Coif (class 1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14411", - "bonuses": "0,0,0,0,0,5,5,5,0,10,10,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "name": "Coif (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14412" - }, - { - "remove_head": "true", - "shop_price": "60", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Coif (class 2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14413", - "bonuses": "0,0,0,0,0,10,10,10,0,20,20,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "name": "Coif (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14414" - }, - { - "remove_head": "true", - "shop_price": "90", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Coif (class 3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14415", - "bonuses": "0,0,0,0,0,15,15,15,0,30,30,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "name": "Coif (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14416" - }, - { - "remove_head": "true", - "shop_price": "120", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Coif (class 4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14417", - "bonuses": "0,0,0,0,0,20,20,20,0,40,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "name": "Coif (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14418" - }, - { - "remove_head": "true", - "shop_price": "150", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Coif (class 5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14419", - "bonuses": "0,0,0,0,0,25,25,25,0,50,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a flexible, protective mesh.", - "durability": null, - "name": "Coif (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14420" - }, - { - "shop_price": "30", - "durability": null, - "name": "Clay deposit scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14421", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14422", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 1)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14423" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14424", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 2)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14425" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14426", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 3)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14427" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14428", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 4)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14429" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14430", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "This scroll will make your familiar deposit its items at your base.", - "durability": null, - "name": "Sacred clay pouch (class 5)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14431" - }, - { - "durability": null, - "name": "Null sacred clay", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14432", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "5", - "examine": "A vine flower grown through magical means.", - "durability": null, - "name": "Vine flower", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14458", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You caught this broav west of the Hunting Expert's home. You can catch another one with a trap baited with mort Myre Fungus.", - "examine": "Its unconscious, poor thing.", - "durability": null, - "name": "Unconscious broav", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14459", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got these clothes from a washerman who lives near the Khazard battle arena.", - "shop_price": "10", - "examine": "Some dirty clothes that apparently belong to Movario.", - "durability": null, - "name": "Dirty laundry", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14460", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "destroy_message": "You found this basket in Movario's base.", - "examine": "A receptacle for rubbish.", - "durability": null, - "name": "Waste-paper basket", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14461", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this key attached to the bottom of the bin in Movario's base.", - "examine": "A superbly made key with a fine ruby inserted into it. (While Guthix Sleeps)", - "durability": null, - "name": "Ruby key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14462", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found these notes in Movario's basement.", - "examine": "A collection of notes made by an absent-minded fellow.", - "durability": null, - "name": "Notes on pressure", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14463", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found these notes loosely piled up on ", - "examine": "A loose-leaf collection of research notes, found on Movario's study desk.", - "durability": null, - "name": "Movario's notes (volume 1)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14464", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found these notes in Movario's bedchest, in his base of operations near the Khazard Battlefield.", - "examine": "A loose-leaf collection of research notes, found in Movario's bed chest.", - "durability": null, - "name": "Movario's notes (volume 2)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14465", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A 1kg weight.", - "durability": null, - "name": "Weight (1kg)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14466", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A 2kg weight.", - "durability": null, - "name": "Weight (2kg)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14467", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "examine": "A 5kg weight.", - "durability": null, - "name": "Weight (5kg)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14468", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can find another under the bed on the top level of Movario's base", - "examine": "The loop half of a strange key.", - "durability": null, - "name": "Strange key loop", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14469", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You can find another under the bed on the top level of Movario's base", - "examine": "The teeth half of a strange key.", - "durability": null, - "name": "Strange key teeth", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14470", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You fused together two parts of a Dragonkin key which you obtained from Movario's base.", - "examine": "The completed dragonkin key.", - "durability": null, - "name": "Dragonkin key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14471", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30000", - "examine": "A badly damaged lump of dragon metal.", - "durability": null, - "rare_item": "true", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "955683", - "name": "Ruined dragon armour lump", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14472", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30000", - "examine": "A badly damaged lump of dragon metal.", - "grand_exchange_price": "955683", - "durability": null, - "name": "Ruined dragon armour lump", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14473" - }, - { - "shop_price": "30000", - "examine": "A badly damaged slice of dragon metal.", - "durability": null, - "rare_item": "true", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "1761141", - "name": "Ruined dragon armour slice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14474", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30000", - "examine": "A badly damaged slice of dragon metal.", - "grand_exchange_price": "1761141", - "durability": null, - "name": "Ruined dragon armour slice", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14475" - }, - { - "shop_price": "30000", - "examine": "A badly damaged shard of dragon metal.", - "durability": null, - "rare_item": "true", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "1090493", - "name": "Ruined dragon armour shard", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14476", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "30000", - "examine": "A badly damaged shard of dragon metal.", - "grand_exchange_price": "1090493", - "durability": null, - "name": "Ruined dragon armour shard", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14477" - }, - { - "destroy_message": "Drop", - "examine": "A very powerful Smithing hammer.", - "durability": null, - "name": "Blast fusion hammer", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14478", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" - }, - { - "requirements": "{1,60}", - "shop_price": "1760000", - "examine": "Provides excellent protection.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "equipment_slot": "4", - "lendable": "true", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "4453643", - "name": "Dragon platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14479", - "bonuses": "0,0,0,-30,0,109,107,97,-6,106,50,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "1760000", - "examine": "Provides excellent protection.", - "grand_exchange_price": "4453643", - "durability": null, - "name": "Dragon platebody", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14480" - }, - { - "requirements": "{1,60}", - "examine": "Provides excellent protection.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "4453643", - "name": "Dragon platebody", - "archery_ticket_price": "0", - "id": "14481", - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "67500", - "examine": "A set of fighting claws.", - "grand_exchange_price": "1472441", - "durability": null, - "name": "Dragon claws", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "two_handed": "true", - "id": "14485" - }, - { - "requirements": "{0,60}", - "examine": "A set of fighting claws.", - "has_special": "true", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "absorb": "0,0,0", - "equip_audio": "1003", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1472441", - "name": "Dragon claws", - "archery_ticket_price": "0", - "id": "14486", - "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0" - }, - { - "destroy_message": "You grew this herb by planting an enriched snapdragon seed in the special herb patch on top of Falador castle.", - "examine": "An enriched snapdragon herb.", - "durability": null, - "name": "Enriched snapdragon", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14487", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You made this serum by mixing an enriched snapdragon into one of Betty's truth serums.", - "examine": "Fluid sloshes innocently in this vial.", - "durability": null, - "name": "Super truth serum", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14488", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You gained this item by having papyrus and charcoal in your inventory while interrogating a spy in Falador castle.", - "examine": "A sketch of the suspect known as Dark Squall.", - "durability": null, - "name": "Suspect sketch", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14489", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{1,40}", - "shop_price": "65000", - "examine": "These look pretty heavy, but very elite.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "41559", - "name": "Elite black platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14490", - "bonuses": "0,0,0,-14,-6,52,50,25,-1,49,15,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "65000", - "examine": "These look pretty heavy, but very elite.", - "grand_exchange_price": "41559", - "durability": null, - "name": "Elite black platelegs", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14491" - }, - { - "requirements": "{1,40}", - "shop_price": "64000", - "examine": "Provides excellent protection, and is elite.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "destroy_message": "Drop", - "remove_sleeves": "true", - "grand_exchange_price": "38275", - "name": "Elite black platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14492", - "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "64000", - "examine": "Provides excellent protection, and is elite.", - "grand_exchange_price": "38275", - "durability": null, - "name": "Elite black platebody", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14493" - }, - { - "remove_head": "true", - "requirements": "{1,40}", - "shop_price": "35200", - "examine": "A full face helmet, and elite.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "30542", - "name": "Elite black full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14494", - "bonuses": "0,0,0,-4,-2,31,33,15,0,30,7,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "35200", - "examine": "A full face helmet, and elite.", - "grand_exchange_price": "30542", - "durability": null, - "name": "Elite black full helm", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14495" - }, - { - "destroy_message": "You got this key from the key rack in Dark Squall's base.", - "examine": "Opens the cells beneath the Black Knights' Fortress. (While Guthix Sleeps)", - "durability": null, - "name": "Cell key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14496", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "120000", - "examine": "A robe worn by members of the Dagon'hai.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "3,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "71098", - "name": "Dagon'hai robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14497", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,2,0" - }, - { - "shop_price": "120000", - "examine": "A robe worn by members of the Dagon'hai.", - "grand_exchange_price": "71098", - "durability": null, - "name": "Dagon'hai robe top", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14498" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "15000", - "examine": "A hat worn by members of the Dagon'hai.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "1,0,0", - "equipment_slot": "0", - "grand_exchange_price": "9580", - "name": "Dagon'hai hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14499", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "shop_price": "15000", - "examine": "A hat worn by members of the Dagon'hai.", - "grand_exchange_price": "9580", - "durability": null, - "name": "Dagon'hai hat", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14500" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "80000", - "examine": "A robe worn by members of the Dagon'hai.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "2,1,0", - "equipment_slot": "7", - "grand_exchange_price": "45691", - "name": "Dagon'hai robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14501", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,2,0" - }, - { - "shop_price": "80000", - "examine": "A robe worn by members of the Dagon'hai.", - "grand_exchange_price": "45691", - "durability": null, - "name": "Dagon'hai robe bottom", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14502" - }, - { - "shop_price": "1", - "examine": "Elite Black Knight armour adds to his stealthiness.", - "durability": null, - "name": "Silif", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14503", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "examine": "Elite Black Knight armour adds to his stealthiness.", - "durability": null, - "name": "Silif", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14504", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Teleorb", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14505", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "5" - }, - { - "destroy_message": "You created this by focusing the light onto a snapdragon seed from a rose tinted lens while in Betty's shop.", - "examine": "An enriched snapdragon seed. The herb that grows from this seed will be extra potent!", - "durability": null, - "name": "Enriched snapdragon seed", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14506", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14507", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14508", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14509", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14510", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14511", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14512", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14513", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", - "examine": "Part of a stone circle.", - "durability": null, - "name": "Dolmen", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14514", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an air-orb on an etched wall section.", - "examine": "An elemental key of air.", - "durability": null, - "name": "Air key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14515", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an earth-orb on an etched wall section.", - "examine": "An elemental key of earth.", - "durability": null, - "name": "Earth key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14516", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an", - "examine": "An elemental key of fire.", - "durability": null, - "name": "Fire key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14517", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an", - "examine": "An elemental key of water.", - "durability": null, - "name": "Water key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14518", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an air-orb on an etched wall section.", - "examine": "An elemental key of air.", - "durability": null, - "name": "Air key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14519", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an earth-orb on an etched wall section.", - "examine": "An elemental key of earth.", - "durability": null, - "name": "Earth key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14520", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an", - "examine": "An elemental key of fire.", - "durability": null, - "name": "Fire key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14521", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You found this in the Chasm of Tears by using an", - "examine": "An elemental key of water.", - "durability": null, - "name": "Water key", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14522", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Grand Exchange set containing a hat, top and bottoms.", - "grand_exchange_price": "193541", - "durability": null, - "name": "Dagon'hai robes set", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14525", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Grand Exchange set containing a hat, top and bottoms.", - "grand_exchange_price": "193541", - "durability": null, - "name": "Dagon'hai robes set", - "tradeable": "true", - "destroy": "false", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14526" - }, - { - "shop_price": "20", - "examine": "Grand Exchange set containing an elite black full helm, platebody and legs.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "6,0,13", - "destroy_message": "Drop", - "grand_exchange_price": "140536", - "name": "Elite black armour set", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14527", - "bonuses": "0,0,0,-38,-16,163,168,80,-3,159,62,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Grand Exchange set containing an elite black full helm, platebody and legs.", - "grand_exchange_price": "140536", - "durability": null, - "name": "Elite black armour set", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14528" - }, - { - "shop_price": "20", - "examine": "Grand Exchange set containing a dragon full helm, platebody, legs.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "9,0,19", - "destroy_message": "Drop", - "grand_exchange_price": "10629814", - "name": "Dragon plate armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14529", - "bonuses": "0,0,0,-57,-9,222,221,201,-11,217,82,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Grand Exchange set containing a dragon full helm, platebody, legs.", - "grand_exchange_price": "10629814", - "durability": null, - "name": "Dragon plate armour set (l)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14530" - }, - { - "shop_price": "20", - "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "9,0,19", - "destroy_message": "Drop", - "grand_exchange_price": "12243786", - "name": "Dragon plate armour set (sk)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14531", - "bonuses": "0,0,0,-57,-9,222,221,201,-11,217,82,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "20", - "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", - "grand_exchange_price": "12243786", - "durability": null, - "name": "Dragon plate armour set (sk)", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14532" - }, - { - "durability": null, - "name": "Broav", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14533", - "absorb": "0,0,0", - "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.", - "examine": "This orb can used to teleport people...somehow.", - "durability": null, - "name": "Strange teleorb", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14534", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will not be able to get the book back if you destroy it.", - "examine": "Notes on the locations of turkeys.", - "durability": null, - "name": "Turkey book", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14536", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", - "durability": null, - "name": "Cornucopia", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14537", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", - "durability": null, - "name": "Cornucopia", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14538", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "9", - "durability": null, - "name": "Raw turkey", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14539", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "4", - "examine": "Mmm, this looks tasty.", - "durability": null, - "name": "Cooked turkey", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14540", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "durability": null, - "name": "Burnt turkey", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14541", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "9", - "examine": "I need to cook this first.", - "durability": null, - "name": "Raw turkey drumstick", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14542", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "4", - "examine": "Mmm, this looks tasty.", - "durability": null, - "name": "Cooked turkey drumstick", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14543", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1", - "durability": null, - "name": "Burnt turkey drumstick", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14544", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", - "durability": null, - "name": "Cornucopia", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14570", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Not-so-sturdy boots for northern winters.", - "durability": null, - "name": "Fremennik sea boots 1", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14571", - "absorb": "0,0,0", - "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", - "equipment_slot": "10" - }, - { - "destroy_message": "You can claim replacement Fremennik sea boots from Yrsa in Rellekka.", - "examine": "Sturdy boots for northern winters.", - "durability": null, - "name": "Fremennik sea boots 2", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14572", - "absorb": "0,0,0", - "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "Very sturdy boots for northern winters.", - "durability": null, - "name": "Fremennik sea boots 3", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14573", - "absorb": "0,0,0", - "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0", - "equipment_slot": "10" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14574", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14575", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14576", - "absorb": "0,0,0", - "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" - }, - { - "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" - }, - { - "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" - }, - { + "render_anim": "1381", + "requirements": "{4,20}", "shop_price": "1000", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14580", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "weapon_interface": "18" }, { - "shop_price": "5000", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14581", + "id": "13954", + "name": "C. morrigan's javelin (p)", + "examine": "A vicious javelin.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "1381", + "requirements": "{4,20}", "shop_price": "10000", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", + "weapon_interface": "18" + }, + { + "id": "13955", + "name": "C. morrigan's javelin (p+)", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14582", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Harvest (turns to ", - "examine": "A white lily seed - plant in a flower patch.", - "durability": null, - "name": "White lily", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14583", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will not be able to replace this voucher - it is a one-time offer only!", - "shop_price": "500000", - "examine": "I can take this to the Herald to get my money crest back for free.", - "durability": null, - "name": "Money crest voucher", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14584", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1250", - "examine": "A white lily seed - plant in a flower patch.", - "durability": null, - "name": "White lily seed", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14589", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "1381", + "requirements": "{4,20}", + "shop_price": "10000", + "weapon_interface": "18" + }, + { + "id": "13956", + "name": "C. morrigan's javelin (p++)", + "examine": "A vicious javelin.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,145", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", + "equipment_slot": "3", + "render_anim": "1381", + "requirements": "{4,20}", + "shop_price": "10000", + "weapon_interface": "18" + }, + { + "id": "13957", + "name": "C. morrigan's throwing axe", + "examine": "A vicious throwing axe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,117", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "requirements": "{4,20}", "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", - "durability": null, - "name": "Enchanted lyre(5)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14590", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" + "weapon_interface": "18" }, { - "destroy_message": "Drop", - "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", - "durability": null, - "name": "Enchanted lyre(6)", - "destroy": "true", + "id": "13958", + "name": "Corrupt dragon chainbody", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,9", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14591", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "3" + "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{0,20}-{4,20}", + "shop_price": "120000" }, { - "lendable": "true", - "destroy_message": "Drop", - "examine": "These look pretty heavy, but very elite.", - "grand_exchange_price": "41559", - "durability": null, - "name": "Elite black platelegs", - "destroy": "true", + "id": "13959", + "name": "Corrupt dragon chainbody", + "examine": "This item degrades while worn, and will turn to dust.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14592", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "13960", + "name": "Corrupt dragon chainbody (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "72000" + }, + { + "id": "13961", + "name": "Corrupt dragon med helm", + "examine": "This item degrades while worn, and will turn to dust.", "absorb": "2,0,4", - "bonuses": "0,0,0,-14,-6,52,50,25,-1,49,15,0,0,0,0" - }, - { - "lendable": "true", - "destroy_message": "Drop", - "examine": "Provides excellent protection, and is elite.", - "grand_exchange_price": "38275", - "durability": null, - "name": "Elite black platebody", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14593", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "60000" + }, + { + "id": "13962", + "name": "Corrupt dragon med helm", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60000", + "tradeable": "true" + }, + { + "id": "13963", + "name": "Corrupt dragon med helm (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "36000" + }, + { + "id": "13964", + "name": "Corrupt dragon sq shield", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "shop_price": "120000" + }, + { + "id": "13965", + "name": "Corrupt dragon sq shield", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "13966", + "name": "Corrupt dragon sq shield (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "5,0,11", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "shop_price": "72000" + }, + { + "id": "13967", + "name": "Corrupt dragon plateskirt", + "examine": "This item degrades while worn, and will turn to dust.", "absorb": "3,0,6", - "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0" + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Destroy", + "durability": null, + "equipment_slot": "7", + "shop_price": "120000" }, { - "lendable": "true", + "id": "13968", + "name": "Corrupt dragon plateskirt", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Destroy", + "durability": null, + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "13969", + "name": "Corrupt dragon plateskirt (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Destroy", + "durability": null, + "equipment_slot": "7", + "shop_price": "72000" + }, + { + "id": "13970", + "name": "Corrupt dragon platelegs", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", "destroy_message": "Drop", - "examine": "A full face helmet, and elite.", - "grand_exchange_price": "30542", "durability": null, - "name": "Elite black full helm", - "destroy": "true", + "equip_audio": "2242", + "equipment_slot": "7", + "shop_price": "120000" + }, + { + "id": "13971", + "name": "Corrupt dragon platelegs", + "examine": "This item degrades while worn, and will turn to dust.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14594", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "13972", + "name": "Corrupt dragon platelegs (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "shop_price": "72000" + }, + { + "id": "13973", + "name": "Corrupt dragon battleaxe", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,70,65,0,0,0,0,0,-1,-1,0,85,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "render_anim": "2586", + "requirements": "{0,60}", + "shop_price": "120000", + "weapon_interface": "2", + "weight": "2.7" + }, + { + "id": "13974", + "name": "Corrupt dragon battleaxe", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "4", + "bonuses": "-2,70,65,0,0,0,0,0,0,-1,0,85,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "render_anim": "2586", + "requirements": "{0,60}", + "shop_price": "120000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "13975", + "name": "C. dragon battleaxe (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "395,395,401,395", + "attack_audios": "2498,2498,2497,2498", + "attack_speed": "6", + "bonuses": "-2,70,65,0,0,0,0,0,-1,-1,0,85,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "render_anim": "2586", + "requirements": "{0,60}", + "shop_price": "72000", + "weapon_interface": "2", + "weight": "2.7" + }, + { + "id": "13976", + "name": "Corrupt dragon dagger", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "shop_price": "30000", + "weapon_interface": "5" + }, + { + "id": "13977", + "name": "Corrupt dragon dagger", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "13978", + "name": "C. dragon dagger (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "396,396,395,396", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "40,25,-4,1,0,0,0,0,1,0,0,40,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "shop_price": "18000", + "weapon_interface": "5" + }, + { + "id": "13979", + "name": "Corrupt dragon scimitar", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1928", + "requirements": "{0,60}", + "shop_price": "100000", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "id": "13980", + "name": "Corrupt dragon scimitar", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "shop_price": "100000", + "tradeable": "true" + }, + { + "id": "13981", + "name": "C. dragon scimitar (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "8,67,-2,0,0,0,1,0,0,0,0,66,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1928", + "shop_price": "60000", + "weapon_interface": "6" + }, + { + "id": "13982", + "name": "Corrupt dragon longsword", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", + "attack_speed": "5", + "bonuses": "58,69,-2,0,0,0,3,2,1,0,0,71,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "ge_buy_limit": "10", + "render_anim": "1582", + "requirements": "{0,60}", + "shop_price": "100000", + "weapon_interface": "5", + "weight": "1.8" + }, + { + "id": "13983", + "name": "Corrupt dragon longsword", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100000", + "tradeable": "true" + }, + { + "id": "13984", + "name": "C. dragon longsword (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "58,69,-2,0,0,0,3,2,1,0,0,71,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1582", + "shop_price": "60000", + "weapon_interface": "5" + }, + { + "id": "13985", + "name": "Corrupt dragon mace", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "40,-2,60,1,0,0,0,0,0,0,0,55,0,5,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "requirements": "{0,60}", + "shop_price": "50000", + "weapon_interface": "8", + "weight": "1.8" + }, + { + "id": "13986", + "name": "Corrupt dragon mace", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "4", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,60}", + "shop_price": "50000", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "13987", + "name": "Corrupt dragon mace (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", + "attack_speed": "5", + "bonuses": "40,-2,60,1,0,0,0,0,0,0,0,55,0,5,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "requirements": "{0,60}", + "shop_price": "30000", + "weapon_interface": "8", + "weight": "1.8" + }, + { + "id": "13988", + "name": "Corrupt dragon spear", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "40000", + "two_handed": "true", + "weapon_interface": "14" + }, + { + "id": "13989", + "name": "Corrupt dragon spear", + "examine": "This item degrades while worn, and will turn to dust.", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "4", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "40000", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "13990", + "name": "Corrupt dragon spear (deg)", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "2080,2081,2082,2080", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "24000", + "two_handed": "true", + "weapon_interface": "14" + }, + { + "id": "14056", + "name": "Pvp worlds manual", + "examine": "A book about the Wilderness.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another copy by searching bookshelves around 2009Scape.", + "durability": null, + "shop_price": "5" + }, + { + "id": "14057", + "name": "Broomstick", + "examine": "A threat to dusty corners everywhere.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can obtain another by talking to Maggie the witch.", + "durability": null, + "equipment_slot": "3", + "render_anim": "1072", + "shop_price": "25", + "weapon_interface": "8" + }, + { + "id": "14058", + "name": "Goulash", + "examine": "What a curious and ghoulish smell!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you drop the goulash, it will be destroyed. You can get some more by dipping a bowl into Maggie's cauldron.", + "durability": null, + "equipment_slot": "3", + "shop_price": "2" + }, + { + "id": "14061", + "name": "Magic unguent", + "examine": "Purple and pungent.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another flask of magic unguent from the chest in Betty's basement.", + "durability": null, + "shop_price": "2" + }, + { + "id": "14062", + "name": "Broom ointment", + "examine": "To be used on brooms.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another flask of broom ointment from Hetty in Rimmington.", + "durability": null, + "shop_price": "2" + }, + { + "id": "14064", + "name": "Newt", + "examine": "A newt from Hetty's basement.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you release this newt, you can get another from the crate in Hetty's basement in Rimmington.", + "durability": null, + "equipment_slot": "3", + "shop_price": "5" + }, + { + "id": "14065", + "name": "Newt label", + "examine": "A label for a shipping crate.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another newt label from Gus in Hetty's basement in Rimmington.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14066", + "name": "Toad label", + "examine": "A label for a shipping crate.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another toad label from Gus in Hetty's basement in Rimmington.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14067", + "name": "Newts and toads label", + "examine": "A label for a shipping crate.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another newt-and-toad label from Gus in Hetty's basement in Rimmington.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14068", + "name": "Betty's wand", + "examine": "Warm to the touch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can obtain another wand from the chest in Betty's basement.", + "durability": null, + "shop_price": "15" + }, + { + "id": "14069", + "name": "Magic slate", + "examine": "What a view!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you destroy this item, you can get another from Lottie in Betty's basement.", + "durability": null, + "shop_price": "2" + }, + { + "id": "14070", + "name": "Reptile", + "examine": "Desert dweller.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you release this creature, you will have to restart the puzzle in Betty's basement.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14071", + "name": "Blackbird", + "examine": "Bleak and beaky.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "If you release this creature, you will have to restart the puzzle in Betty's basement.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14072", + "name": "Bat", + "examine": "An annoying flappy thing.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14073", + "name": "Spider", + "examine": "It's an extremely small brown spider, probably very poisonous.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14074", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14075", + "name": "Snail", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14076", + "name": "Warlock top", + "examine": "The height of warlock fashion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another warlock top from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "15" + }, + { + "id": "14077", + "name": "Warlock legs", + "examine": "Tattered but trendy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another set of warlock legs from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "7", + "shop_price": "15" + }, + { + "id": "14078", + "name": "Witch top", + "examine": "The height of witchly fashion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch top from Diango in Draynor.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "15" + }, + { + "id": "14079", + "name": "Witch skirt", + "examine": "Tattered but trendy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch top from Diango in Draynor.", + "durability": null, + "equipment_slot": "7", + "shop_price": "15" + }, + { + "id": "14080", + "name": "Witch cloak", + "examine": "Ragged but rugged.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch cloak from Diango in Draynor.", + "durability": null, + "equipment_slot": "1", + "shop_price": "15" + }, + { + "id": "14081", + "name": "Warlock cloak", + "examine": "Ragged but rugged.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another warlock cloak from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "1", + "shop_price": "15" + }, + { + "id": "14082", + "name": "Popcorn ball", + "examine": "A sticky orb of popped corn.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { + "id": "14083", + "name": "Chocolate drop", + "examine": "Chocolatey goodness.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { + "id": "14084", + "name": "Wrapped candy", + "examine": "Sugary bliss.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { + "id": "14085", + "name": "Armadyl communiqu?", + "examine": "An urgent communiqu?? revealing important information.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1" + }, + { + "id": "14086", + "name": "Witch top", + "examine": "The height of witchly fashion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch top from Diango in Draynor.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "15" + }, + { + "id": "14087", + "name": "Witch skirt", + "examine": "Tattered but trendy.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch top from Diango in Draynor.", + "durability": null, + "equipment_slot": "7", + "shop_price": "15" + }, + { + "id": "14088", + "name": "Witch cloak", + "examine": "Ragged but rugged.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another witch cloak from Diango in Draynor.", + "durability": null, + "equipment_slot": "1", + "shop_price": "15" + }, + { + "id": "14089", + "name": "Pet kitten", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14090", + "name": "Pet cat", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14091", + "name": "Lazy cat", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14092", + "name": "Overgrown cat", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14093", + "name": "Wily cat", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14094", + "name": "Sacred clay platebody", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "43333" + }, + { + "id": "14095", + "name": "Sacred clay platelegs", + "examine": "The sacred clay bottom has transformed into spiked, platemail legs.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "42667" + }, + { + "id": "14096", + "name": "Sacred clay helm", + "examine": "The sacred clay hat has transformed into a spined helm.", "absorb": "1,0,3", - "bonuses": "0,0,0,-4,-2,31,33,15,0,30,7,0,0,0,0" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "remove_sleeves": "true", - "examine": "A red and jolly top.", - "durability": null, - "name": "Santa costume top", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "23467" + }, + { + "id": "14097", + "name": "Sacred clay scimitar", + "examine": "The sacred clay weapon has transformed into a sharp scimitar.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "7,45,-2,0,0,0,1,0,0,0,0,44,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "17066", + "weapon_interface": "5" + }, + { + "id": "14098", + "name": "Volatile clay tool", + "examine": "This can turn into a variety of tools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14099", + "name": "Volatile clay pickaxe", + "examine": "Your volatile tool wants to mine some rocks.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333", + "weapon_interface": "4" + }, + { + "id": "14100", + "name": "Volatile clay hatchet", + "examine": "Your volatile tool wants to cut some wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-2,26,24,0,0,0,1,0,0,0,0,29,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333", + "weapon_interface": "2" + }, + { + "id": "14101", + "name": "Volatile clay harpoon", + "examine": "Your volatile tool wants to spear some big fish.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333" + }, + { + "id": "14102", + "name": "Volatile clay butterfly net", + "examine": "Your volatile tool wants to catch some butterflies.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "21333", + "weapon_interface": "5" + }, + { + "id": "14103", + "name": "Volatile clay fletching knife", + "examine": "Your volatile tool wants to make some bows or arrows.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "3", + "shop_price": "21333" + }, + { + "id": "14104", + "name": "Volatile clay hammer", + "examine": "Your volatile tool wants to work metal or build furniture.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333" + }, + { + "id": "14105", + "name": "Volatile clay needle", + "examine": "Your volatile tool wants to make some leather armour.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14106", + "name": "Proto-tool", + "examine": "This can turn into a variety of tools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14107", + "name": "Sacred clay pickaxe", + "examine": "Your sacred clay tool has transformed into a pickaxe.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333", + "weapon_interface": "4" + }, + { + "id": "14108", + "name": "Sacred clay hatchet", + "examine": "Your sacred clay tool has transformed into a hatchet.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "-2,4,2,0,0,0,1,0,0,0,0,5,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333", + "weapon_interface": "2" + }, + { + "id": "14109", + "name": "Sacred clay harpoon", + "examine": "Your sacred clay tool has transformed into a harpoon.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "21333" + }, + { + "id": "14110", + "name": "Sacred clay butterfly net", + "examine": "Your sacred clay tool has transformed into a butterfly net.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "5", + "bonuses": "4,-2,2,0,0,0,1,0,0,0,0,5,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "1", + "weapon_interface": "5" + }, + { + "id": "14111", + "name": "Sacred clay fletching knife", + "examine": "The sacred clay has transformed into a knife.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14112", + "name": "Sacred clay hammer", + "examine": "Your sacred clay tool has transformed into a hammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14113", + "name": "Sacred clay needle", + "examine": "The sacred clay has transformed into a needle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "21333" + }, + { + "id": "14114", + "name": "Sacred clay robe top", + "examine": "The sacred clay top has transformed into a heavy, magical robe.", + "absorb": "6,3,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "43333" + }, + { + "id": "14115", + "name": "Sacred clay robe bottom", + "examine": "The sacred clay bottom has transformed into a heavy, magical robe.", + "absorb": "4,2,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "42667" + }, + { + "id": "14116", + "name": "Sacred clay hat", + "examine": "The sacred clay hat has transformed into a wizard hat.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "23467" + }, + { + "id": "14117", + "name": "Sacred clay staff", + "examine": "The sacred clay weapon has transformed into a magical staff.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "17066", + "weapon_interface": "1" + }, + { + "id": "14118", + "name": "Sacred clay body", + "examine": "The sacred clay top has transformed into a leather body.", + "absorb": "0,6,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-15,15,40,32,45,20,40,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "43333" + }, + { + "id": "14119", + "name": "Sacred clay chaps", + "examine": "The sacred clay bottom has been transformed into a set of chaps.", + "absorb": "0,4,2", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "42667" + }, + { + "id": "14120", + "name": "Sacred clay coif", + "examine": "The sacred clay hat has transformed into a coif.", + "absorb": "0,3,1", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-1,2,4,6,8,4,4,5,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "23467" + }, + { + "id": "14121", + "name": "Sacred clay bow", + "examine": "The sacred clay weapon has transformed into a bow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,47,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "17066", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14122", + "name": "Pickaxe (class 1)", + "examine": "The sacred clay has become hard enough to chip stone.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "10,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "4" + }, + { + "id": "14123", + "name": "Pickaxe (class 1)", + "examine": "The sacred clay has become hard enough to chip stone.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14124", + "name": "Pickaxe (class 2)", + "examine": "The sacred clay has become hard enough to chip stone.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "15,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "4" + }, + { + "id": "14125", + "name": "Pickaxe (class 2)", + "examine": "The sacred clay has become hard enough to chip stone.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14126", + "name": "Pickaxe (class 3)", + "examine": "The sacred clay has become hard enough to chip stone.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "20,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "4" + }, + { + "id": "14127", + "name": "Pickaxe (class 3)", + "examine": "The sacred clay has become hard enough to chip stone.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14128", + "name": "Pickaxe (class 4)", + "examine": "The sacred clay has become hard enough to chip stone.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "25,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "4" + }, + { + "id": "14129", + "name": "Pickaxe (class 4)", + "examine": "The sacred clay has become hard enough to chip stone.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14130", + "name": "Pickaxe (class 5)", + "examine": "The sacred clay has become hard enough to chip stone.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "30,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "4" + }, + { + "id": "14131", + "name": "Pickaxe (class 5)", + "examine": "The sacred clay has become hard enough to chip stone.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14132", + "name": "Hatchet (class 1)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,10,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "2" + }, + { + "id": "14133", + "name": "Hatchet (class 1)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14134", + "name": "Hatchet (class 2)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,15,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "2" + }, + { + "id": "14135", + "name": "Hatchet (class 2)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14136", + "name": "Hatchet (class 3)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,20,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "2" + }, + { + "id": "14137", + "name": "Hatchet (class 3)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14138", + "name": "Hatchet (class 4)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,25,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "2" + }, + { + "id": "14139", + "name": "Hatchet (class 4)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14140", + "name": "Hatchet (class 5)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,30,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "2" + }, + { + "id": "14141", + "name": "Hatchet (class 5)", + "examine": "The sacred clay has become sharp enough to cut wood.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14142", + "name": "Harpoon (class 1)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "10,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14143", + "name": "Harpoon (class 1)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14144", + "name": "Harpoon (class 2)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "15,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14145", + "name": "Harpoon (class 2)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14146", + "name": "Harpoon (class 3)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "20,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14147", + "name": "Harpoon (class 3)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14148", + "name": "Harpoon (class 4)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "25,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14149", + "name": "Harpoon (class 4)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14150", + "name": "Harpoon (class 5)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "30,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14151", + "name": "Harpoon (class 5)", + "examine": "The sacred clay has become long enough to reach the depths of pools.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14152", + "name": "Butterfly net (class 1)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,10,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "14" + }, + { + "id": "14153", + "name": "Butterfly net (class 1)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14154", + "name": "Butterfly net (class 2)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,15,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "14" + }, + { + "id": "14155", + "name": "Butterfly net (class 2)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14156", + "name": "Butterfly net (class 3)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,20,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "14" + }, + { + "id": "14157", + "name": "Butterfly net (class 3)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14158", + "name": "Butterfly net (class 4)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,25,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "14" + }, + { + "id": "14159", + "name": "Butterfly net (class 4)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14160", + "name": "Butterfly net (class 5)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", + "attack_speed": "4", + "bonuses": "0,0,30,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "403", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "158", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "14" + }, + { + "id": "14161", + "name": "Butterfly net (class 5)", + "examine": "The sacred clay has become light enough to swing at floating shards.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14162", + "name": "Food (class 1)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14163", + "name": "Food (class 1)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14164", + "name": "Food (class 2)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14165", + "name": "Food (class 2)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14166", + "name": "Food (class 3)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14167", + "name": "Food (class 3)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14168", + "name": "Food (class 4)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14169", + "name": "Food (class 4)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14170", + "name": "Food (class 5)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14171", + "name": "Food (class 5)", + "examine": "The sacred clay has transformed into something bland, but edible.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14172", + "name": "Barrier (class 1)", + "examine": "The clay has transformed into an expandable wall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14173", + "name": "Barrier (class 1)", + "examine": "The clay has transformed into an expandable wall.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14174", + "name": "Barrier (class 2)", + "examine": "The clay has transformed into an expandable wall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14175", + "name": "Barrier (class 2)", + "examine": "The clay has transformed into an expandable wall.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14176", + "name": "Barrier (class 3)", + "examine": "The clay has transformed into an expandable wall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14177", + "name": "Barrier (class 3)", + "examine": "The clay has transformed into an expandable wall.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14178", + "name": "Barrier (class 4)", + "examine": "The clay has transformed into an expandable wall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14179", + "name": "Barrier (class 4)", + "examine": "The clay has transformed into an expandable wall.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14180", + "name": "Barrier (class 5)", + "examine": "The clay has transformed into an expandable wall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14181", + "name": "Barrier (class 5)", + "examine": "The clay has transformed into an expandable wall.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14182", + "name": "Sacred clay (class 1)", + "examine": "Very low quality sacred clay.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "14183", + "name": "Sacred clay (class 1)", + "examine": "Very low quality sacred clay.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "15", + "tradeable": "true" + }, + { + "id": "14184", + "name": "Sacred clay (class 2)", + "examine": "Low quality sacred clay.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14185", + "name": "Sacred clay (class 2)", + "examine": "Low quality sacred clay.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14186", + "name": "Sacred clay (class 3)", + "examine": "Medium quality sacred clay.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "45", + "tradeable": "true" + }, + { + "id": "14187", + "name": "Sacred clay (class 3)", + "examine": "Medium quality sacred clay.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "45", + "tradeable": "true" + }, + { + "id": "14188", + "name": "Sacred clay (class 4)", + "examine": "High quality sacred clay.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14189", + "name": "Sacred clay (class 4)", + "examine": "High quality sacred clay.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14190", + "name": "Sacred clay (class 5)", + "examine": "Very high quality sacred clay.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "75", + "tradeable": "true" + }, + { + "id": "14191", + "name": "Sacred clay (class 5)", + "examine": "Very high quality sacred clay.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "75", + "tradeable": "true" + }, + { + "id": "14192", + "name": "Bow (class 1)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14193", + "name": "Bow (class 1)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14194", + "name": "Bow (class 2)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,90,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14195", + "name": "Bow (class 2)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14196", + "name": "Bow (class 3)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,135,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14197", + "name": "Bow (class 3)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14198", + "name": "Bow (class 4)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,180,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14199", + "name": "Bow (class 4)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14200", + "name": "Bow (class 5)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,0,0,225,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "16" + }, + { + "id": "14201", + "name": "Bow (class 5)", + "examine": "The sacred clay has transformed into a wood-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14202", + "name": "Arrows (class 1)", + "examine": "The Sacred Clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,15", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "2", + "tradeable": "true" + }, + { + "id": "14203", + "name": "Arrows (class 2)", + "examine": "The Sacred Clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,30", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "4", + "tradeable": "true" + }, + { + "id": "14204", + "name": "Arrows (class 3)", + "examine": "The Sacred Clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,45", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "6", + "tradeable": "true" + }, + { + "id": "14205", + "name": "Arrows (class 4)", + "examine": "The Sacred Clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "8", + "tradeable": "true" + }, + { + "id": "14206", + "name": "Arrows (class 5)", + "examine": "The Sacred Clay has transformed into a wood-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "14207", + "name": "Prayer potion(5)", + "examine": "3 doses of Prayer restore potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14208", + "name": "Prayer potion(5)", + "examine": "3 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14209", + "name": "Prayer potion(4)", + "examine": "4 doses of Prayer restore potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14210", + "name": "Prayer potion(4)", + "examine": "4 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14211", + "name": "Prayer potion(3)", + "examine": "3 doses of Prayer restore potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14212", + "name": "Prayer potion(3)", + "examine": "3 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14213", + "name": "Prayer potion(2)", + "examine": "2 doses of Prayer restore potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14214", + "name": "Prayer potion(2)", + "examine": "2 doses of Prayer restore potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14215", + "name": "Prayer potion(1)", + "examine": "1 dose of Prayer restore potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14216", + "name": "Prayer potion(1)", + "examine": "1 dose of Prayer restore potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14217", + "name": "Energy potion (5)", + "examine": "4 doses of energy potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14218", + "name": "Energy potion (5)", + "examine": "4 doses of energy potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14219", + "name": "Energy potion (4)", + "examine": "4 doses of energy potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14220", + "name": "Energy potion (4)", + "examine": "4 doses of energy potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14221", + "name": "Energy potion (3)", + "examine": "3 doses of energy potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14222", + "name": "Energy potion (3)", + "examine": "3 doses of energy potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14223", + "name": "Energy potion (2)", + "examine": "2 doses of energy potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14224", + "name": "Energy potion (2)", + "examine": "2 doses of energy potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14225", + "name": "Energy potion (1)", + "examine": "1 dose of energy potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14226", + "name": "Energy potion (1)", + "examine": "1 dose of energy potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14227", + "name": "Super attack(5)", + "examine": "5 doses of super Attack potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14228", + "name": "Super attack(5)", + "examine": "5 doses of super Attack potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14229", + "name": "Super attack(4)", + "examine": "4 doses of super Attack potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14230", + "name": "Super attack(4)", + "examine": "4 doses of super Attack potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14231", + "name": "Super attack(3)", + "examine": "3 doses of super Attack potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14232", + "name": "Super attack(3)", + "examine": "3 doses of super Attack potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14233", + "name": "Super attack(2)", + "examine": "2 doses of super Attack potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14234", + "name": "Super attack(2)", + "examine": "2 doses of super Attack potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14235", + "name": "Super attack(1)", + "examine": "1 dose of super Attack potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14236", + "name": "Super attack(1)", + "examine": "1 dose of super Attack potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14237", + "name": "Super strength(5)", + "examine": "5 doses of super Strength potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14238", + "name": "Super strength(5)", + "examine": "5 doses of super Strength potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14239", + "name": "Super strength(4)", + "examine": "4 doses of super Strength potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14240", + "name": "Super strength(4)", + "examine": "4 doses of super Strength potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14241", + "name": "Super strength(3)", + "examine": "3 doses of super Strength potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14242", + "name": "Super strength(3)", + "examine": "3 doses of super Strength potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14243", + "name": "Super strength(2)", + "examine": "2 doses of super Strength potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14244", + "name": "Super strength(2)", + "examine": "2 doses of super Strength potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14245", + "name": "Super strength(1)", + "examine": "1 dose of super Strength potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14246", + "name": "Super strength(1)", + "examine": "1 dose of super Strength potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14247", + "name": "Ranging potion(5)", + "examine": "4 doses of ranging potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14248", + "name": "Ranging potion(5)", + "examine": "5 doses of ranging potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14249", + "name": "Ranging potion(4)", + "examine": "4 doses of ranging potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14250", + "name": "Ranging potion(4)", + "examine": "4 doses of ranging potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14251", + "name": "Ranging potion(3)", + "examine": "3 doses of ranging potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14252", + "name": "Ranging potion(3)", + "examine": "3 doses of ranging potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14253", + "name": "Ranging potion(2)", + "examine": "2 doses of ranging potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14254", + "name": "Ranging potion(2)", + "examine": "2 doses of ranging potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14255", + "name": "Ranging potion(1)", + "examine": "1 dose of ranging potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14256", + "name": "Ranging potion(1)", + "examine": "1 dose of ranging potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14257", + "name": "Defence potion(5)", + "examine": "3 doses of Defence Potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14258", + "name": "Defence potion(5)", + "examine": "3 doses of Defence Potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14259", + "name": "Defence potion(4)", + "examine": "4 doses of Defence Potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14260", + "name": "Defence potion(4)", + "examine": "4 doses of Defence Potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14261", + "name": "Defence potion(3)", + "examine": "3 doses of Defence Potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14262", + "name": "Defence potion(3)", + "examine": "3 doses of Defence Potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14263", + "name": "Defence potion(2)", + "examine": "2 doses of Defence Potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14264", + "name": "Defence potion(2)", + "examine": "2 doses of Defence Potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14265", + "name": "Defence potion(1)", + "examine": "1 dose of Defence Potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14266", + "name": "Defence potion(1)", + "examine": "1 dose of Defence Potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14267", + "name": "Magic potion(5)", + "examine": "4 doses of Magic potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14268", + "name": "Magic potion(5)", + "examine": "5 doses of Magic potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14269", + "name": "Magic potion(4)", + "examine": "4 doses of Magic potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14270", + "name": "Magic potion(4)", + "examine": "4 doses of Magic potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14271", + "name": "Magic potion(3)", + "examine": "3 doses of Magic potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14272", + "name": "Magic potion(3)", + "examine": "3 doses of Magic potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14273", + "name": "Magic potion(2)", + "examine": "2 doses of Magic potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14274", + "name": "Magic potion(2)", + "examine": "2 doses of Magic potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14275", + "name": "Magic potion(1)", + "examine": "1 dose of Magic potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14276", + "name": "Magic potion(1)", + "examine": "1 dose of Magic potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14277", + "name": "Summoning potion(5)", + "examine": "3 doses of Summoning potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14278", + "name": "Summoning potion(5)", + "examine": "3 doses of Summoning potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14279", + "name": "Summoning potion(4)", + "examine": "4 doses of Summoning potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14280", + "name": "Summoning potion(4)", + "examine": "4 doses of Summoning potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14281", + "name": "Summoning potion(3)", + "examine": "3 doses of Summoning potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14282", + "name": "Summoning potion(3)", + "examine": "3 doses of Summoning potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14283", + "name": "Summoning potion(2)", + "examine": "2 doses of Summoning potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14284", + "name": "Summoning potion(2)", + "examine": "2 doses of Summoning potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14285", + "name": "Summoning potion(1)", + "examine": "1 dose of Summoning potion.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14286", + "name": "Summoning potion(1)", + "examine": "1 dose of Summoning potion.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14287", + "name": "Scimitar (class 1)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "0,55,0,0,0,0,0,0,0,0,0,15,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "14288", + "name": "Scimitar (class 1)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14289", + "name": "Scimitar (class 2)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "0,90,0,0,0,0,0,0,0,0,0,30,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "14290", + "name": "Scimitar (class 2)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14291", + "name": "Scimitar (class 3)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "0,145,0,0,0,0,0,0,0,0,0,45,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "14292", + "name": "Scimitar (class 3)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14293", + "name": "Scimitar (class 4)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "0,180,0,0,0,0,0,0,0,0,0,60,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "14294", + "name": "Scimitar (class 4)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14295", + "name": "Scimitar (class 5)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", + "attack_audios": "2500,0,2517,0", + "attack_speed": "4", + "bonuses": "0,225,0,0,0,0,0,0,0,0,0,75,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "1629", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "6" + }, + { + "id": "14296", + "name": "Scimitar (class 5)", + "examine": "The sacred clay has transformed into a vicious, curved sword.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14297", + "name": "Dagger (class 1)", + "examine": "The sacred clay transformed into a sharp dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "3", + "bonuses": "41,0,0,0,0,0,0,0,0,0,0,15,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14298", + "name": "Dagger (class 1)", + "examine": "The sacred clay transformed into a sharp dagger.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14299", + "name": "Dagger (class 2)", + "examine": "The sacred clay transformed into a sharp dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "3", + "bonuses": "68,0,0,0,0,0,0,0,0,0,0,30,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14300", + "name": "Dagger (class 2)", + "examine": "The sacred clay transformed into a sharp dagger.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14301", + "name": "Dagger (class 3)", + "examine": "The sacred clay transformed into a sharp dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "3", + "bonuses": "101,0,0,0,0,0,0,0,0,0,0,45,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14302", + "name": "Dagger (class 3)", + "examine": "The sacred clay transformed into a sharp dagger.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14303", + "name": "Dagger (class 4)", + "examine": "The sacred clay transformed into a sharp dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "3", + "bonuses": "135,0,0,0,0,0,0,0,0,0,0,60,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14304", + "name": "Dagger (class 4)", + "examine": "The sacred clay transformed into a sharp dagger.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14305", + "name": "Dagger (class 5)", + "examine": "The sacred clay transformed into a sharp dagger.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "400,400,401,400", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "3", + "bonuses": "168,0,0,0,0,0,0,0,0,0,0,75,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "5" + }, + { + "id": "14306", + "name": "Dagger (class 5)", + "examine": "The sacred clay transformed into a sharp dagger.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14307", + "name": "Warhammer (class 1)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,83,0,0,0,0,0,0,0,0,15,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "3", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "10" + }, + { + "id": "14308", + "name": "Warhammer (class 1)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14309", + "name": "Warhammer (class 2)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,135,0,0,0,0,0,0,0,0,30,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "10" + }, + { + "id": "14310", + "name": "Warhammer (class 2)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14311", + "name": "Warhammer (class 3)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,202,0,0,0,0,0,0,0,0,45,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "10" + }, + { + "id": "14312", + "name": "Warhammer (class 3)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14313", + "name": "Warhammer (class 4)", + "examine": "The Sacred Clay has transformed into a heavy warhammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,270,0,0,0,0,0,0,0,0,60,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "10" + }, + { + "id": "14314", + "name": "Warhammer (class 4)", + "examine": "The Sacred Clay has transformed into a heavy warhammer.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14315", + "name": "Warhammer (class 5)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "6", + "bonuses": "0,0,338,0,0,0,0,0,0,0,0,75,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "10" + }, + { + "id": "14316", + "name": "Warhammer (class 5)", + "examine": "The sacred clay has transformed into a heavy warhammer.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14317", + "name": "Robe top (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14318", + "name": "Robe top (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14319", + "name": "Robe top (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,40,0,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14320", + "name": "Robe top (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14321", + "name": "Robe top (class 3)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14322", + "name": "Robe top (class 3)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14323", + "name": "Robe top (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,80,0,80,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14324", + "name": "Robe top (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14325", + "name": "Robe top (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,100,0,100,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14326", + "name": "Robe top (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14327", + "name": "Robe bottom (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,15,0,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14328", + "name": "Robe bottom (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14329", + "name": "Robe bottom (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14330", + "name": "Robe bottom (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14331", + "name": "Robe bottom (class 3)", + "examine": "The sacred clay transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,45,0,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14332", + "name": "Robe bottom (class 3)", + "examine": "The sacred clay transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14333", + "name": "Robe bottom (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14334", + "name": "Robe bottom (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14335", + "name": "Robe bottom (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,75,0,75,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14336", + "name": "Robe bottom (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14337", + "name": "Hat (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,10,0,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14338", + "name": "Hat (class 1)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14339", + "name": "Hat (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,20,0,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14340", + "name": "Hat (class 2)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14341", + "name": "Hat (class 3)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14342", + "name": "Hat (class 3)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14343", + "name": "Hat (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,40,0,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14344", + "name": "Hat (class 4)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14345", + "name": "Hat (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,50,0,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14346", + "name": "Hat (class 5)", + "examine": "The sacred clay has transformed into a light, cloth-like material.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14347", + "name": "Platebody (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,20,20,20,-10,10,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14348", + "name": "Platebody (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14349", + "name": "Platebody (class 2)", + "examine": "The sacred clay transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,40,40,40,-10,20,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14350", + "name": "Platebody (class 2)", + "examine": "The sacred clay transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14351", + "name": "Platebody (class 3)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,60,60,60,-10,30,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14352", + "name": "Platebody (class 3)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14353", + "name": "Platebody (class 4)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,80,80,80,-10,40,80,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14354", + "name": "Platebody (class 4)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14355", + "name": "Platebody (class 5)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,100,100,100,-10,50,100,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14356", + "name": "Platebody (class 5)", + "examine": "The sacred clay top has transformed into a spiked, platemail body.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14357", + "name": "Platelegs (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,15,15,15,-10,7,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14358", + "name": "Platelegs (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14359", + "name": "Platelegs (class 2)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,30,30,30,-10,15,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14360", + "name": "Platelegs (class 2)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14361", + "name": "Platelegs (class 3)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,45,45,45,-10,23,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14362", + "name": "Platelegs (class 3)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14363", + "name": "Platelegs (class 4)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,60,60,60,-10,30,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14364", + "name": "Platelegs (class 4)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14365", + "name": "Platelegs (class 5)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,75,75,75,-10,37,75,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14366", + "name": "Platelegs (class 5)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14367", + "name": "Helm (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,10,10,10,-10,5,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14368", + "name": "Helm (class 1)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14369", + "name": "Helm (class 2)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,20,20,20,-10,10,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14370", + "name": "Helm (class 2)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14371", + "name": "Helm (class 3)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,30,30,30,-10,15,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14372", + "name": "Helm (class 3)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14373", + "name": "Helm (class 4)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,40,40,40,-10,20,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14374", + "name": "Helm (class 4)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14375", + "name": "Helm (class 5)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,-10,50,50,50,-10,25,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14376", + "name": "Helm (class 5)", + "examine": "The sacred clay has transformed into a hard, metallic substance.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14377", + "name": "Staff (class 1)", + "examine": "The sacred clay has transformed into a light weight, magical stick.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,55,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "30", + "tradeable": "true", + "weapon_interface": "1" + }, + { + "id": "14378", + "name": "Staff (class 1)", + "examine": "The sacred clay has transformed into a light weight, magical stick.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14379", + "name": "Staff (class 2)", + "examine": "The sacred clay has transformed into a light weight, magical stick.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,90,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "60", + "tradeable": "true", + "weapon_interface": "1" + }, + { + "id": "14380", + "name": "Staff (class 2)", + "examine": "The sacred clay has transformed into a light weight, magical stick.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14381", + "name": "Staff (class 3)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,135,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "90", + "tradeable": "true", + "weapon_interface": "1" + }, + { + "id": "14382", + "name": "Staff (class 3)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14383", + "name": "Staff (class 4)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,180,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "120", + "tradeable": "true", + "weapon_interface": "1" + }, + { + "id": "14384", + "name": "Staff (class 4)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14385", + "name": "Staff (class 5)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "5", + "bonuses": "0,0,0,225,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "render_anim": "28", + "shop_price": "150", + "tradeable": "true", + "weapon_interface": "1" + }, + { + "id": "14386", + "name": "Staff (class 5)", + "examine": "The sacred clay has transformed into a lightweight, magical stick.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14387", + "name": "Cape", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1" + }, + { + "id": "14389", + "name": "Cape", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "durability": null, + "equipment_slot": "1" + }, + { + "id": "14391", + "name": "Leather body (class 1)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,10,10,20,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14392", + "name": "Leather body (class 1)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14393", + "name": "Leather body (class 2)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,20,40,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14394", + "name": "Leather body (class 2)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14395", + "name": "Leather body (class 3)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,30,30,30,60,60,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14396", + "name": "Leather body (class 3)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14397", + "name": "Leather body (class 4)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,40,40,40,40,80,80,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14398", + "name": "Leather body (class 4)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14399", + "name": "Leather body (class 5)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,50,50,50,100,100,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14400", + "name": "Leather body (class 5)", + "examine": "The sacred clay has transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14401", + "name": "Chaps (class 1)", + "examine": "The sacred clay transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,7,7,7,0,15,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14402", + "name": "Chaps (class 1)", + "examine": "The sacred clay transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14403", + "name": "Chaps (class 2)", + "examine": "The sacred clay transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,15,15,15,0,30,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14404", + "name": "Chaps (class 2)", + "examine": "The sacred clay transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14405", + "name": "Chaps (class 3)", + "examine": "The sacred clay transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,23,23,23,0,45,45,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14406", + "name": "Chaps (class 3)", + "examine": "The sacred clay transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14407", + "name": "Chaps (class 4)", + "examine": "The sacred clay transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,30,30,0,60,60,0,0,1,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14408", + "name": "Chaps (class 4)", + "examine": "The sacred clay transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14409", + "name": "Chaps (class 5)", + "examine": "The sacred clay transformed into a flexible mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,37,37,37,0,75,75,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "7", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14410", + "name": "Chaps (class 5)", + "examine": "The sacred clay transformed into a flexible mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14411", + "name": "Coif (class 1)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,5,0,10,10,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14412", + "name": "Coif (class 1)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14413", + "name": "Coif (class 2)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,10,0,20,20,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14414", + "name": "Coif (class 2)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14415", + "name": "Coif (class 3)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,15,15,15,0,30,30,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14416", + "name": "Coif (class 3)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14417", + "name": "Coif (class 4)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,40,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14418", + "name": "Coif (class 4)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14419", + "name": "Coif (class 5)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,25,25,25,0,50,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14420", + "name": "Coif (class 5)", + "examine": "The sacred clay has transformed into a flexible, protective mesh.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14421", + "name": "Clay deposit scroll", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14422", + "name": "Sacred clay pouch (class 1)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14422", + "name": "USDT Slot", + "examine": "You should not have this." + }, + { + "id": "14423", + "name": "Sacred clay pouch (class 1)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "14424", + "name": "Sacred clay pouch (class 2)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14424", + "name": "HDT Slot", + "examine": "You should not have this." + }, + { + "id": "14425", + "name": "Sacred clay pouch (class 2)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "60", + "tradeable": "true" + }, + { + "id": "14426", + "name": "Sacred clay pouch (class 3)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14426", + "name": "GDT Slot", + "examine": "You should not have this." + }, + { + "id": "14427", + "name": "Sacred clay pouch (class 3)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "90", + "tradeable": "true" + }, + { + "id": "14428", + "name": "Sacred clay pouch (class 4)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14428", + "name": "RSDT Slot", + "examine": "You should not have this." + }, + { + "id": "14429", + "name": "Sacred clay pouch (class 4)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "120", + "tradeable": "true" + }, + { + "id": "14430", + "name": "Sacred clay pouch (class 5)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14430", + "name": "ASDT Slot", + "examine": "You should not have this." + }, + { + "id": "14431", + "name": "Sacred clay pouch (class 5)", + "examine": "This scroll will make your familiar deposit its items at your base.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "14432", + "name": "Null sacred clay", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14458", + "name": "Vine flower", + "examine": "A vine flower grown through magical means.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "5" + }, + { + "id": "14459", + "name": "Unconscious broav", + "examine": "Its unconscious, poor thing.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You caught this broav west of the Hunting Expert's home. You can catch another one with a trap baited with mort Myre Fungus.", + "durability": null + }, + { + "id": "14460", + "name": "Dirty laundry", + "examine": "Some dirty clothes that apparently belong to Movario.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got these clothes from a washerman who lives near the Khazard battle arena.", + "durability": null, + "equipment_slot": "3", + "shop_price": "10" + }, + { + "id": "14461", + "name": "Waste-paper basket", + "examine": "A receptacle for rubbish.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this basket in Movario's base.", + "durability": null + }, + { + "id": "14462", + "name": "Ruby key", + "examine": "A superbly made key with a fine ruby inserted into it. (While Guthix Sleeps)", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this key attached to the bottom of the bin in Movario's base.", + "durability": null + }, + { + "id": "14463", + "name": "Notes on pressure", + "examine": "A collection of notes made by an absent-minded fellow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found these notes in Movario's basement.", + "durability": null + }, + { + "id": "14464", + "name": "Movario's notes (volume 1)", + "examine": "A loose-leaf collection of research notes, found on Movario's study desk.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found these notes loosely piled up on ", + "durability": null + }, + { + "id": "14465", + "name": "Movario's notes (volume 2)", + "examine": "A loose-leaf collection of research notes, found in Movario's bed chest.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found these notes in Movario's bedchest, in his base of operations near the Khazard Battlefield.", + "durability": null + }, + { + "id": "14466", + "name": "Weight (1kg)", + "examine": "A 1kg weight.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "14467", + "name": "Weight (2kg)", + "examine": "A 2kg weight.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "14468", + "name": "Weight (5kg)", + "examine": "A 5kg weight.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null + }, + { + "id": "14469", + "name": "Strange key loop", + "examine": "The loop half of a strange key.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can find another under the bed on the top level of Movario's base", + "durability": null + }, + { + "id": "14470", + "name": "Strange key teeth", + "examine": "The teeth half of a strange key.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can find another under the bed on the top level of Movario's base", + "durability": null + }, + { + "id": "14471", + "name": "Dragonkin key", + "examine": "The completed dragonkin key.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You fused together two parts of a Dragonkin key which you obtained from Movario's base.", + "durability": null + }, + { + "id": "14472", + "name": "Ruined dragon armour lump", + "examine": "A badly damaged lump of dragon metal.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14473", + "name": "Ruined dragon armour lump", + "examine": "A badly damaged lump of dragon metal.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14474", + "name": "Ruined dragon armour slice", + "examine": "A badly damaged slice of dragon metal.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14475", + "name": "Ruined dragon armour slice", + "examine": "A badly damaged slice of dragon metal.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14476", + "name": "Ruined dragon armour shard", + "examine": "A badly damaged shard of dragon metal.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "rare_item": "true", + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14477", + "name": "Ruined dragon armour shard", + "examine": "A badly damaged shard of dragon metal.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "30000", + "tradeable": "true" + }, + { + "id": "14478", + "name": "Blast fusion hammer", + "examine": "A very powerful Smithing hammer.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3" + }, + { + "id": "14479", + "name": "Dragon platebody", + "examine": "Provides excellent protection.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,0,109,107,97,-6,106,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,60}", + "shop_price": "1760000", + "tradeable": "true" + }, + { + "id": "14480", + "name": "Dragon platebody", + "examine": "Provides excellent protection.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "1760000", + "tradeable": "true" + }, + { + "id": "14481", + "name": "Dragon platebody", + "examine": "Provides excellent protection.", + "absorb": "4,0,9", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-30,0,109,107,97,-6,106,50,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "", + "lendable": "true", + "requirements": "{1,60}" + }, + { + "id": "14484", + "name": "Dragon claws", + "examine": "A set of fighting claws.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "393,393,1067,393", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", + "has_special": "true", + "lendable": "true", + "rare_item": "true", + "render_anim": "2583", + "requirements": "{0,60}", + "shop_price": "67500", + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "9" + }, + { + "id": "14485", + "name": "Dragon claws", + "examine": "A set of fighting claws.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "67500", + "tradeable": "true", + "two_handed": "true" + }, + { + "id": "14486", + "name": "Dragon claws", + "examine": "A set of fighting claws.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_audios": "2548,2548,2548,2548", + "attack_speed": "4", + "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "1003", + "has_special": "true", + "lendable": "true", + "requirements": "{0,60}", + "two_handed": "true", + "weapon_interface": "9" + }, + { + "id": "14487", + "name": "Enriched snapdragon", + "examine": "An enriched snapdragon herb.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You grew this herb by planting an enriched snapdragon seed in the special herb patch on top of Falador castle.", + "durability": null + }, + { + "id": "14488", + "name": "Super truth serum", + "examine": "Fluid sloshes innocently in this vial.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You made this serum by mixing an enriched snapdragon into one of Betty's truth serums.", + "durability": null + }, + { + "id": "14489", + "name": "Suspect sketch", + "examine": "A sketch of the suspect known as Dark Squall.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You gained this item by having papyrus and charcoal in your inventory while interrogating a spy in Falador castle.", + "durability": null + }, + { + "id": "14490", + "name": "Elite black platelegs", + "examine": "These look pretty heavy, but very elite.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-14,-6,52,50,25,-1,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "lendable": "true", + "requirements": "{1,40}", + "shop_price": "65000", + "tradeable": "true" + }, + { + "id": "14491", + "name": "Elite black platelegs", + "examine": "These look pretty heavy, but very elite.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "65000", + "tradeable": "true" + }, + { + "id": "14492", + "name": "Elite black platebody", + "examine": "Provides excellent protection, and is elite.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", + "shop_price": "64000", + "tradeable": "true" + }, + { + "id": "14493", + "name": "Elite black platebody", + "examine": "Provides excellent protection, and is elite.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "64000", + "tradeable": "true" + }, + { + "id": "14494", + "name": "Elite black full helm", + "examine": "A full face helmet, and elite.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-4,-2,31,33,15,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", + "shop_price": "35200", + "tradeable": "true" + }, + { + "id": "14495", + "name": "Elite black full helm", + "examine": "A full face helmet, and elite.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "35200", + "tradeable": "true" + }, + { + "id": "14496", + "name": "Cell key", + "examine": "Opens the cells beneath the Black Knights' Fortress. (While Guthix Sleeps)", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this key from the key rack in Dark Squall's base.", + "durability": null + }, + { + "id": "14497", + "name": "Dagon'hai robe top", + "examine": "A robe worn by members of the Dagon'hai.", + "absorb": "3,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,2,0,0", + "destroy": "false", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "14498", + "name": "Dagon'hai robe top", + "examine": "A robe worn by members of the Dagon'hai.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "120000", + "tradeable": "true" + }, + { + "id": "14499", + "name": "Dagon'hai hat", + "examine": "A hat worn by members of the Dagon'hai.", + "absorb": "1,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", + "destroy": "false", + "durability": null, + "equipment_slot": "0", + "requirements": "{1,20}-{6,40}", + "shop_price": "15000", + "tradeable": "true" + }, + { + "id": "14500", + "name": "Dagon'hai hat", + "examine": "A hat worn by members of the Dagon'hai.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "15000", + "tradeable": "true" + }, + { + "id": "14501", + "name": "Dagon'hai robe bottom", + "examine": "A robe worn by members of the Dagon'hai.", + "absorb": "2,1,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,2,0,0", + "destroy": "false", + "durability": null, + "equipment_slot": "7", + "requirements": "{1,20}-{6,40}", + "shop_price": "80000", + "tradeable": "true" + }, + { + "id": "14502", + "name": "Dagon'hai robe bottom", + "examine": "A robe worn by members of the Dagon'hai.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "80000", + "tradeable": "true" + }, + { + "id": "14503", + "name": "Silif", + "examine": "Elite Black Knight armour adds to his stealthiness.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14504", + "name": "Silif", + "examine": "Elite Black Knight armour adds to his stealthiness.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14505", + "name": "Teleorb", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "equipment_slot": "5" + }, + { + "id": "14506", + "name": "Enriched snapdragon seed", + "examine": "An enriched snapdragon seed. The herb that grows from this seed will be extra potent!", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You created this by focusing the light onto a snapdragon seed from a rose tinted lens while in Betty's shop.", + "durability": null + }, + { + "id": "14507", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14508", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14509", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14510", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14511", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14512", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14513", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14514", + "name": "Dolmen", + "examine": "Part of a stone circle.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You got this by solving a puzzle in the a Guthixian temple near the Chasm of Tears.", + "durability": null + }, + { + "id": "14515", + "name": "Air key", + "examine": "An elemental key of air.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an air-orb on an etched wall section.", + "durability": null + }, + { + "id": "14516", + "name": "Earth key", + "examine": "An elemental key of earth.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an earth-orb on an etched wall section.", + "durability": null + }, + { + "id": "14517", + "name": "Fire key", + "examine": "An elemental key of fire.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an", + "durability": null + }, + { + "id": "14518", + "name": "Water key", + "examine": "An elemental key of water.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an", + "durability": null + }, + { + "id": "14519", + "name": "Air key", + "examine": "An elemental key of air.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an air-orb on an etched wall section.", + "durability": null + }, + { + "id": "14520", + "name": "Earth key", + "examine": "An elemental key of earth.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an earth-orb on an etched wall section.", + "durability": null + }, + { + "id": "14521", + "name": "Fire key", + "examine": "An elemental key of fire.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an", + "durability": null + }, + { + "id": "14522", + "name": "Water key", + "examine": "An elemental key of water.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You found this in the Chasm of Tears by using an", + "durability": null + }, + { + "id": "14525", + "name": "Dagon'hai robes set", + "examine": "Grand Exchange set containing a hat, top and bottoms.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "false", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14526", + "name": "Dagon'hai robes set", + "examine": "Grand Exchange set containing a hat, top and bottoms.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "false", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14527", + "name": "Elite black armour set", + "examine": "Grand Exchange set containing an elite black full helm, platebody and legs.", + "absorb": "6,0,13", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-38,-16,163,168,80,-3,159,62,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14528", + "name": "Elite black armour set", + "examine": "Grand Exchange set containing an elite black full helm, platebody and legs.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14529", + "name": "Dragon plate armour set (l)", + "examine": "Grand Exchange set containing a dragon full helm, platebody, legs.", + "absorb": "9,0,19", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-57,-9,222,221,201,-11,217,82,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14530", + "name": "Dragon plate armour set (l)", + "examine": "Grand Exchange set containing a dragon full helm, platebody, legs.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14531", + "name": "Dragon plate armour set (sk)", + "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", + "absorb": "9,0,19", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-57,-9,222,221,201,-11,217,82,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14532", + "name": "Dragon plate armour set (sk)", + "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "20", + "tradeable": "true" + }, + { + "id": "14533", + "name": "Broav", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { + "id": "14534", + "name": "Strange teleorb", + "examine": "This orb can used to teleport people...somehow.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You gained this orb from Dark Squall's base. You can probably get another one from visiting the same place.", + "durability": null + }, + { + "id": "14536", + "name": "Turkey book", + "examine": "Notes on the locations of turkeys.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will not be able to get the book back if you destroy it.", + "durability": null + }, + { + "id": "14537", + "name": "Cornucopia", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", + "durability": null + }, + { + "id": "14538", + "name": "Cornucopia", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", + "durability": null + }, + { + "id": "14539", + "name": "Raw turkey", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "9" + }, + { + "id": "14540", + "name": "Cooked turkey", + "examine": "Mmm, this looks tasty.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "4" + }, + { + "id": "14541", + "name": "Burnt turkey", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14542", + "name": "Raw turkey drumstick", + "examine": "I need to cook this first.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "9" + }, + { + "id": "14543", + "name": "Cooked turkey drumstick", + "examine": "Mmm, this looks tasty.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "4" + }, + { + "id": "14544", + "name": "Burnt turkey drumstick", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1" + }, + { + "id": "14570", + "name": "Cornucopia", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will lose all food stored in the Cornucopia. You can get another empty one from Diango in Draynor Village.", + "durability": null + }, + { + "id": "14571", + "name": "Fremennik sea boots 1", + "examine": "Not-so-sturdy boots for northern winters.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10" + }, + { + "id": "14572", + "name": "Fremennik sea boots 2", + "examine": "Sturdy boots for northern winters.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0", + "destroy": "true", + "destroy_message": "You can claim replacement Fremennik sea boots from Yrsa in Rellekka.", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10" + }, + { + "id": "14573", + "name": "Fremennik sea boots 3", + "examine": "Very sturdy boots for northern winters.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0", + "destroy": "true", + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10" + }, + { + "id": "14574", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "14575", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "14576", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "14577", + "name": "Falador shield 1", + "examine": "A buckler shield from Falador.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,3,0,0", + "destroy": "true", + "destroy_message": "You will be able to get a replacement shield from Redbeard the Pirate in Port Sarim.", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "shop_price": "100" + }, + { + "id": "14578", + "name": "Falador shield 2", + "examine": "A kiteshield from Falador.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,5,0,0", + "destroy": "true", + "destroy_message": "You will be able to get a replacement shield from the chemist in Rimmington.", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "shop_price": "200" + }, + { + "id": "14579", + "name": "Falador shield 3", + "examine": "A tower shield from Falador.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,7,0,0", + "destroy": "true", + "destroy_message": "You will be able to get a replacement shield from Sir Vyvin's squire in the White Knights' Castle.", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "shop_price": "300" + }, + { + "id": "14580", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "shop_price": "1000", + "tradeable": "false" + }, + { + "id": "14581", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "shop_price": "5000", + "tradeable": "false" + }, + { + "id": "14582", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "shop_price": "10000", + "tradeable": "false" + }, + { + "id": "14583", + "name": "White lily", + "examine": "A white lily seed - plant in a flower patch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Harvest (turns to ", + "durability": null + }, + { + "id": "14584", + "name": "Money crest voucher", + "examine": "I can take this to the Herald to get my money crest back for free.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will not be able to replace this voucher - it is a one-time offer only!", + "durability": null, + "shop_price": "500000" + }, + { + "id": "14589", + "name": "White lily seed", + "examine": "A white lily seed - plant in a flower patch.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "1250" + }, + { + "id": "14590", + "name": "Enchanted lyre(5)", + "examine": "This will teleport me to Rellekka when I play it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "1000" + }, + { + "id": "14591", + "name": "Enchanted lyre(6)", + "examine": "This will teleport me to Rellekka when I play it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "3", + "shop_price": "1000" + }, + { + "id": "14592", + "name": "Elite black platelegs", + "examine": "These look pretty heavy, but very elite.", + "absorb": "2,0,4", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-14,-6,52,50,25,-1,49,15,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "14593", + "name": "Elite black platebody", + "examine": "Provides excellent protection, and is elite.", + "absorb": "3,0,6", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { + "id": "14594", + "name": "Elite black full helm", + "examine": "A full face helmet, and elite.", + "absorb": "1,0,3", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,-4,-2,31,33,15,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "lendable": "true" + }, + { "id": "14595", + "name": "Santa costume top", + "examine": "A red and jolly top.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "examine": "A spikey amulet made of ice crystals. It has been enchanted so it will not melt.", - "durability": null, - "name": "Ice amulet", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" + }, + { "id": "14596", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "2" - }, - { - "destroy_message": "You can probably get more in the last room of the yeti cave, in the Land of Snow.", - "shop_price": "1", - "durability": null, - "name": "Stones", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14597", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You will have to fetch more in the last room of the yeti cave, then get them enchanted by snow imps.", - "shop_price": "1", - "examine": "According to the snow imps, this is enchanted, frozen yeti dung.", - "durability": null, - "name": "Enchanted stones", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14598", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A spikey amulet made of ice crystals. It has been enchanted so it will not melt.", - "durability": null, "name": "Ice amulet", - "destroy": "true", + "examine": "A spikey amulet made of ice crystals. It has been enchanted so it will not melt.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14599", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, "equipment_slot": "2" }, { - "destroy_message": "You can get another from Diango in Draynor Village.", - "remove_sleeves": "true", - "examine": "A red and jolly top.", - "durability": null, - "name": "Santa costume top", - "destroy": "true", + "id": "14597", + "name": "Stones", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can probably get more in the last room of the yeti cave, in the Land of Snow.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14598", + "name": "Enchanted stones", + "examine": "According to the snow imps, this is enchanted, frozen yeti dung.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You will have to fetch more in the last room of the yeti cave, then get them enchanted by snow imps.", + "durability": null, + "shop_price": "1" + }, + { + "id": "14599", + "name": "Ice amulet", + "examine": "A spikey amulet made of ice crystals. It has been enchanted so it will not melt.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "2" + }, + { "id": "14600", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "remove_sleeves": "true", - "examine": "A red and jolly top.", - "durability": null, "name": "Santa costume top", - "destroy": "true", + "examine": "A red and jolly top.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14601", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "4" + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "destroy_message": "You can get another pair from Diango in Draynor Village.", - "examine": "Some black and jolly gloves.", - "durability": null, - "name": "Santa costume gloves", - "destroy": "true", + "id": "14601", + "name": "Santa costume top", + "examine": "A red and jolly top.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14602", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" + }, + { + "id": "14602", + "name": "Santa costume gloves", + "examine": "Some black and jolly gloves.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another pair from Diango in Draynor Village.", + "durability": null, "equipment_slot": "9" }, { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "Some red and jolly legs.", - "durability": null, - "name": "Santa costume legs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "14603", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "7" - }, - { - "destroy_message": "You can get another from Diango in Draynor Village.", - "examine": "Some red and jolly legs.", - "durability": null, "name": "Santa costume legs", - "destroy": "true", + "examine": "Some red and jolly legs.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14604", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, "equipment_slot": "7" }, { - "destroy_message": "You can get another pair from Diango in Draynor Village.", - "examine": "Some black and jolly boots.", - "durability": null, - "name": "Santa costume boots", - "destroy": "true", + "id": "14604", + "name": "Santa costume legs", + "examine": "Some red and jolly legs.", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14605", - "absorb": "0,0,0", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "7" + }, + { + "id": "14605", + "name": "Santa costume boots", + "examine": "Some black and jolly boots.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another pair from Diango in Draynor Village.", + "durability": null, "equipment_slot": "10" }, { - "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Aromatic twigs from a cinnamon tree.", - "durability": null, - "name": "Cinnamon twigs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "14606", + "name": "Cinnamon twigs", + "examine": "Aromatic twigs from a cinnamon tree.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Aromatic twigs from a sassafras tree.", - "durability": null, - "name": "Sassafras twigs", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14607", + "name": "Sassafras twigs", + "examine": "Aromatic twigs from a sassafras tree.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Aromatic twigs from a ailanthus tree.", - "durability": null, - "name": "Ailanthus twigs", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14608", + "name": "Ailanthus twigs", + "examine": "Aromatic twigs from a ailanthus tree.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Aromatic twigs from a cedar tree.", - "durability": null, - "name": "Cedar twigs", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14609", + "name": "Cedar twigs", + "examine": "Aromatic twigs from a cedar tree.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Aromatic twigs from a mastic tree.", - "durability": null, - "name": "Mastic twigs", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", + "durability": null + }, + { "id": "14610", + "name": "Mastic twigs", + "examine": "Aromatic twigs from a mastic tree.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Cinnamon twigs, fletched into ribbons of wood and ready to be woven.", - "durability": null, - "name": "Cinnamon weaving ribbon", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You harvested these from a tree in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14611", + "name": "Cinnamon weaving ribbon", + "examine": "Cinnamon twigs, fletched into ribbons of wood and ready to be woven.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Sassafras twigs, fletched into ribbons of wood and ready to be woven.", - "durability": null, - "name": "Sassafras weaving ribbon", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14612", + "name": "Sassafras weaving ribbon", + "examine": "Sassafras twigs, fletched into ribbons of wood and ready to be woven.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Ailanthus twigs, fletched into ribbons of wood and ready to be woven.", - "durability": null, - "name": "Ailanthus weaving ribbon", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14613", + "name": "Ailanthus weaving ribbon", + "examine": "Ailanthus twigs, fletched into ribbons of wood and ready to be woven.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Cedar twigs, fletched into ribbons of wood and ready to be woven.", - "durability": null, - "name": "Cedar weaving ribbon", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", + "durability": null + }, + { "id": "14614", + "name": "Cedar weaving ribbon", + "examine": "Cedar twigs, fletched into ribbons of wood and ready to be woven.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", - "examine": "Mastic twigs, fletched into ribbons of wood and ready to be woven.", - "durability": null, - "name": "Mastic weaving ribbon", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", + "durability": null + }, + { "id": "14615", + "name": "Mastic weaving ribbon", + "examine": "Mastic twigs, fletched into ribbons of wood and ready to be woven.", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You fletched this from twigs found in the phoenix's lair. You need one to complete the dungeon.", + "durability": null }, { - "shop_price": "100", - "examine": "A feather from a phoenix. Used in Summoning (72).", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "3431", - "name": "Phoenix quill", - "tradeable": "true", - "archery_ticket_price": "0", "id": "14616", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "100", - "examine": "A feather from a phoenix. Used in Summoning (72).", - "grand_exchange_price": "3431", - "durability": null, "name": "Phoenix quill", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14617" - }, - { - "durability": null, - "name": "Pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14620", + "examine": "A feather from a phoenix. Used in Summoning (72).", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "durability": null, - "name": "Pouch", - "tradeable": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14621" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "true" }, { + "id": "14617", + "name": "Phoenix quill", + "examine": "A feather from a phoenix. Used in Summoning (72).", + "archery_ticket_price": "0", + "attack_speed": "4", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100", + "tradeable": "true" + }, + { + "id": "14620", + "name": "Pouch", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "tradeable": "true" + }, + { + "id": "14621", + "name": "Pouch", + "archery_ticket_price": "0", + "attack_speed": "4", + "durability": null, + "tradeable": "true" + }, + { + "id": "14622", + "name": "Rise from the ashes scroll", + "examine": "A scroll for a phoenix familiar.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, "requirements": "{23,72}", "shop_price": "498", - "examine": "A scroll for a phoenix familiar.", - "durability": null, - "name": "Rise from the ashes scroll", - "tradeable": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14622", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "tradeable": "true" }, { - "shop_price": "4986", - "durability": null, - "name": "Phoenix pouch", - "tradeable": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "14623", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "4986", - "durability": null, "name": "Phoenix pouch", - "tradeable": "true", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14624" + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null, + "shop_price": "4986", + "tradeable": "true" }, { - "durability": null, - "name": "Pouch", + "id": "14624", + "name": "Phoenix pouch", "archery_ticket_price": "0", "attack_speed": "4", + "durability": null, + "shop_price": "4986", + "tradeable": "true" + }, + { "id": "14625", + "name": "Pouch", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A baby phoenix and it's bad to the bone!", - "durability": null, - "name": "Phoenix eggling", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "14626", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A baby phoenix and it's bad to the bone!", - "durability": null, "name": "Phoenix eggling", + "examine": "A baby phoenix and it's bad to the bone!", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "14627", + "name": "Phoenix eggling", + "examine": "A baby phoenix and it's bad to the bone!", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Unknown ", - "examine": "I found this egg in the Phoenix's Lair, and clapped at it!", - "durability": null, - "name": "Phoenix egg", - "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "durability": null + }, + { "id": "14629", + "name": "Phoenix egg", + "examine": "I found this egg in the Phoenix's Lair, and clapped at it!", "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", "destroy_message": "Unknown ", - "examine": "I found this egg in the Phoenix's Lair, and kicked it!", - "durability": null, - "name": "Cracked phoenix egg", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14630", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "durability": null }, { - "destroy_message": "A player can get a new one by talking to any seer around Seer's village.", - "examine": "A headband with an eye embroidered on it.", - "durability": null, - "name": "Seer's headband", - "destroy": "true", + "id": "14630", + "name": "Cracked phoenix egg", + "examine": "I found this egg in the Phoenix's Lair, and kicked it!", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "Unknown ", + "durability": null + }, + { "id": "14631", + "name": "Seer's headband", + "examine": "A headband with an eye embroidered on it.", "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,3,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "A player can get a new one by talking to any seer around Seer's village.", + "durability": null, "equipment_slot": "0" }, { - "turn90cw_anim": "821", - "examine": "This used to belong to King Arthur and has since been improved.", - "walk_anim": "819", - "has_special": "true", - "turn90ccw_anim": "822", - "attack_speed": "5", - "turn180_anim": "820", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "destroy_message": "You can retrieve the Enhanced Excalibur from the Lady of the Lake for 500 coins.", - "stand_anim": "808", - "run_anim": "824", - "archery_ticket_price": "0", "id": "14632", - "stand_turn_anim": "823", - "bonuses": "38,47,-2,0,0,0,3,2,1,0,0,45,0,0,0", - "requirements": "{0,30}", - "shop_price": "200", - "durability": null, - "destroy": "true", - "weapon_interface": "6", - "render_anim": "1", + "name": "Enhanced excalibur", + "examine": "This used to belong to King Arthur and has since been improved.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "390,390,381,390", "attack_audios": "2500,2500,2517,2500", - "name": "Enhanced excalibur" + "attack_speed": "5", + "bonuses": "38,47,-2,0,0,0,3,2,1,0,0,45,0,0,0", + "defence_anim": "397", + "destroy": "true", + "destroy_message": "You can retrieve the Enhanced Excalibur from the Lady of the Lake for 500 coins.", + "durability": null, + "equipment_slot": "3", + "has_special": "true", + "render_anim": "1", + "requirements": "{0,30}", + "run_anim": "824", + "shop_price": "200", + "stand_anim": "808", + "stand_turn_anim": "823", + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "819", + "weapon_interface": "6" }, { - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", "id": "14633", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14634", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14635", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "650", - "examine": "You don't want to wear it inside-out.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Slayer helmet (e)", - "archery_ticket_price": "0", - "id": "14636", - "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "650", - "examine": "You don't want to wear it inside-out.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "remove_beard": "true", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Slayer helmet (charged)", - "archery_ticket_price": "0", - "id": "14637", - "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "examine": "It can be charged at the Fountain of Rune.", - "grand_exchange_price": "26000", - "durability": null, - "name": "Ring of wealth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14638", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "examine": "It can be charged at the Fountain of Rune.", - "grand_exchange_price": "29800", - "durability": null, - "name": "Ring of wealth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14639" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with one teleport charge.", - "grand_exchange_price": "27000", - "durability": null, - "name": "Ring of wealth(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14640", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with one teleport charge.", - "grand_exchange_price": "29800", - "durability": null, - "name": "Ring of wealth(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14641" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with two teleport charges.", - "grand_exchange_price": "28000", - "durability": null, - "name": "Ring of wealth(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14642", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with two teleport charges.", - "grand_exchange_price": "29800", - "durability": null, - "name": "Ring of wealth(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14643" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with three teleport charges.", - "grand_exchange_price": "30000", - "durability": null, - "name": "Ring of wealth(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14644", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with three teleport charges.", - "grand_exchange_price": "29800", - "durability": null, - "name": "Ring of wealth(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14645" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with four teleport charges.", - "grand_exchange_price": "35000", - "durability": null, - "name": "Ring of wealth(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14646", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "100", - "examine": "An enchanted ring with four teleport charges.", - "grand_exchange_price": "29800", - "durability": null, - "name": "Ring of wealth(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14647" - }, - { - "shop_price": "0", - "ge_buy_limit": "0", - "examine": "A totally alive monkey friend for your back!", - "durability": null, - "destroy": "true", - "weight": "0.0", - "equipment_slot": "1", - "grand_exchange_price": "0", - "name": "Jangles the Monkey Backpack", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "14648", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "0", - "examine": "A ring for only the truest of cave goblins. And Logg too.", - "grand_exchange_price": "0", - "durability": null, - "name": "Zanik ring", - "tradeable": "false", - "archery_ticket_price": "0", - "destroy": "true", - "id": "14649", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "0", - "examine": "A standard issue wizard hat.", - "durability": null, - "weight": "0.1", - "destroy": "true", - "equipment_slot": "0", - "grand_exchange_price": "0", - "name": "Wizard hat", - "tradeable": "false", - "archery_ticket_price": "0", - "hat": "true", - "id": "14650", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0" - }, - { - "destroy_message": " WARNING: You will have to reobtain this item the hard way.", - "examine": "Mysterious blueprints written in an alien language.", - "durability": null, - "name": "Ancient Blueprints", - "destroy": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "14651" - }, - { - "destroy_message": "You can get another by bringing the materials along with the blueprint to a Star Sprite.", - "examine": "A stardust-infused dragonstone ring.", - "durability": null, - "name": "Ring of the Star Sprite", - "destroy": "true", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "14652", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "equipment_slot": "12" - }, - { - "examine": "A chunk of rock.", - "name": "Rock", - "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", + "durability": null, + "tradeable": "false" + }, + { + "id": "14634", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "14635", + "name": "Antique lamp", + "examine": "I wonder what happens if I rub it.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "14636", + "name": "Slayer helmet (e)", + "examine": "You don't want to wear it inside-out.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "650" + }, + { + "id": "14637", + "name": "Slayer helmet (charged)", + "examine": "You don't want to wear it inside-out.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,32,27,0,30,7,0,0,0,0", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "shop_price": "650" + }, + { + "id": "14638", + "name": "Ring of wealth", + "examine": "It can be charged at the Fountain of Rune.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14639", + "name": "Ring of wealth", + "examine": "It can be charged at the Fountain of Rune.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14640", + "name": "Ring of wealth(1)", + "examine": "An enchanted ring with one teleport charge.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14641", + "name": "Ring of wealth(1)", + "examine": "An enchanted ring with one teleport charge.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14642", + "name": "Ring of wealth(2)", + "examine": "An enchanted ring with two teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14643", + "name": "Ring of wealth(2)", + "examine": "An enchanted ring with two teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14644", + "name": "Ring of wealth(3)", + "examine": "An enchanted ring with three teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14645", + "name": "Ring of wealth(3)", + "examine": "An enchanted ring with three teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14646", + "name": "Ring of wealth(4)", + "examine": "An enchanted ring with four teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14647", + "name": "Ring of wealth(4)", + "examine": "An enchanted ring with four teleport charges.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "14648", + "name": "Jangles the Monkey Backpack", + "examine": "A totally alive monkey friend for your back!", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "0", + "shop_price": "0", + "tradeable": "false", + "weight": "0.0" + }, + { + "id": "14649", + "name": "Zanik ring", + "examine": "A ring for only the truest of cave goblins. And Logg too.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "0", + "tradeable": "false" + }, + { + "id": "14650", + "name": "Wizard hat", + "examine": "A standard issue wizard hat.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", + "destroy": "true", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "0", + "hat": "true", + "tradeable": "false", + "weight": "0.1" + }, + { + "id": "14651", + "name": "Ancient Blueprints", + "examine": "Mysterious blueprints written in an alien language.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": " WARNING: You will have to reobtain this item the hard way.", + "durability": null, + "weight": "0.1" + }, + { + "id": "14652", + "name": "Ring of the Star Sprite", + "examine": "A stardust-infused dragonstone ring.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "destroy": "true", + "destroy_message": "You can get another by bringing the materials along with the blueprint to a Star Sprite.", + "durability": null, + "equipment_slot": "12", + "weight": "0.1" + }, + { "id": "14654", + "name": "Staff of the raven", + "examine": "A staff with a spooky raven head attached.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "420", + "destroy": "true", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "200", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "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": "14655", - "stand_turn_anim": "1209", - "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", - "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", + "examine": "A staff with a spooky raven head attached.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "420", + "destroy": "true", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "14656", + "shop_price": "200", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Item container for C. Ele Minor Drop Table. You should not be able to obtain this item.", - "name": "C. Ele Minor Drop Table", - "id": "799" - }, - { - "examine": "This will unlock something. (Waterfall Quest)", - "name": "A key", "tradeable": "false", - "weight": "0.01", - "archery_ticket_price": "0", - "id": "293" + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" }, { - "examine": "This will unlock something. (Waterfall dungeon)", - "name": "A key", + "id": "14656", + "name": "Staff of the raven", + "examine": "A staff with a spooky raven head attached.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "defence_anim": "420", + "destroy": "true", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "shop_price": "200", + "stand_anim": "813", + "stand_turn_anim": "1209", "tradeable": "false", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2.2" + }, + { + "id": "14658", + "name": "Bunny ears", + "examine": "A rabbit-like adornment, back in black!", "archery_ticket_price": "0", - "weight": "0.01", - "id": "298" - }, - { - "examine": "You should not have this.", - "name": "USDT Slot", - "id": "14422" - }, - { - "examine": "You should not have this.", - "name": "HDT Slot", - "id": "14424" - }, - { - "examine": "You should not have this.", - "name": "GDT Slot", - "id": "14426" - }, - { - "examine": "You should not have this.", - "name": "RSDT Slot", - "id": "14428" - }, - { - "examine": "You should not have this.", - "name": "ASDT Slot", - "id": "14430" + "destroy": "true", + "destroy_message": "This item can not be reclaimed.", + "equipment_slot": "0", + "shop_price": "1", + "tradeable": "false", + "weight": "0.2" } ] \ 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..ef23c3ecf --- /dev/null +++ b/Server/data/configs/music_regions.json @@ -0,0 +1,2574 @@ +[ + { + "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": "10391", + "id": "478" + }, + { + "region": "10393", + "id": "346" + }, + { + "region": "10394", + "id": "109" + }, + { + "region": "10536", + "id": "588" + }, + { + "region": "10537", + "id": "587" + }, + { + "region": "10542", + "id": "71" + }, + { + "region": "10544", + "id": "161" + }, + { + "region": "10545", + "id": "167" + }, + { + "region": "10546", + "id": "70" + }, + { + "region": "10547", + "id": "99" + }, + { + "region": "10548", + "id": "81" + }, + { + "region": "10549", + "id": "60" + }, + { + "region": "10550", + "id": "140" + }, + { + "region": "10551", + "id": "20" + }, + { + "region": "10552", + "id": "290" + }, + { + "region": "10553", + "id": "289" + }, + { + "region": "10554", + "id": "289" + }, + { + "region": "10555", + "id": "225" + }, + { + "region": "10558", + "id": "217" + }, + { + "region": "10569", + "id": "107" + }, + { + "region": "10571", + "id": "143" + }, + { + "region": "10575", + "id": "393" + }, + { + "region": "10577", + "id": "568" + }, + { + "region": "10642", + "id": "71" + }, + { + "region": "10644", + "id": "363" + }, + { + "region": "10645", + "id": "363" + }, + { + "region": "10646", + "id": "481" + }, + { + "region": "10647", + "id": "169" + }, + { + "region": "10648", + "id": "63" + }, + { + "region": "10649", + "id": "63" + }, + { + "region": "10650", + "id": "63" + }, + { + "region": "10652", + "id": "316" + }, + { + "region": "10653", + "id": "295" + }, + { + "region": "10794", + "id": "306" + }, + { + "region": "10795", + "id": "305" + }, + { + "region": "10801", + "id": "164" + }, + { + "region": "10802", + "id": "115" + }, + { + "region": "10803", + "id": "91" + }, + { + "region": "10804", + "id": "192" + }, + { + "region": "10805", + "id": "184" + }, + { + "region": "10806", + "id": "7" + }, + { + "region": "10807", + "id": "21" + }, + { + "region": "10808", + "id": "293" + }, + { + "region": "10809", + "id": "291" + }, + { + "region": "10810", + "id": "291" + }, + { + "region": "10811", + "id": "291" + }, + { + "region": "10820", + "id": "563" + }, + { + "region": "10821", + "id": "282" + }, + { + "region": "10822", + "id": "278" + }, + { + "region": "10823", + "id": "278" + }, + { + "region": "10827", + "id": "146" + }, + { + "region": "10828", + "id": "378" + }, + { + "region": "10829", + "id": "378" + }, + { + "region": "10831", + "id": "393" + }, + { + "region": "10833", + "id": "249" + }, + { + "region": "10834", + "id": "240" + }, + { + "region": "10838", + "id": "442" + }, + { + "region": "10839", + "id": "442" + }, + { + "region": "10840", + "id": "444" + }, + { + "region": "10841", + "id": "444" + }, + { + "region": "10842", + "id": "444" + }, + { + "region": "10894", + "id": "312" + }, + { + "region": "10895", + "id": "311" + }, + { + "region": "10899", + "id": "362" + }, + { + "region": "10900", + "id": "362" + }, + { + "region": "10901", + "id": "364" + }, + { + "region": "10903", + "id": "17" + }, + { + "region": "10904", + "id": "192" + }, + { + "region": "10905", + "id": "63" + }, + { + "region": "10906", + "id": "88" + }, + { + "region": "10907", + "id": "340" + }, + { + "region": "10908", + "id": "340" + }, + { + "region": "10910", + "id": "274" + }, + { + "region": "10911", + "id": "548" + }, + { + "region": "11051", + "id": "303" + }, + { + "region": "11053", + "id": "129" + }, + { + "region": "11054", + "id": "114" + }, + { + "region": "11055", + "id": "117" + }, + { + "region": "11056", + "id": "58" + }, + { + "region": "11057", + "id": "55" + }, + { + "region": "11058", + "id": "6" + }, + { + "region": "11059", + "id": "347" + }, + { + "region": "11060", + "id": "324" + }, + { + "region": "11061", + "id": "74" + }, + { + "region": "11062", + "id": "104" + }, + { + "region": "11065", + "id": "356" + }, + { + "region": "11066", + "id": "4" + }, + { + "region": "11068", + "id": "335" + }, + { + "region": "11077", + "id": "277" + }, + { + "region": "11078", + "id": "282" + }, + { + "region": "11081", + "id": "5" + }, + { + "region": "11083", + "id": "65" + }, + { + "region": "11084", + "id": "378" + }, + { + "region": "11085", + "id": "378" + }, + { + "region": "11096", + "id": "444" + }, + { + "region": "11097", + "id": "444" + }, + { + "region": "11098", + "id": "444" + }, + { + "region": "11151", + "id": "307" + }, + { + "region": "11153", + "id": "265" + }, + { + "region": "11154", + "id": "114" + }, + { + "region": "11157", + "id": "248" + }, + { + "region": "11161", + "id": "19" + }, + { + "region": "11164", + "id": "341" + }, + { + "region": "11165", + "id": "357" + }, + { + "region": "11166", + "id": "261" + }, + { + "region": "11309", + "id": "172" + }, + { + "region": "11310", + "id": "90" + }, + { + "region": "11311", + "id": "165" + }, + { + "region": "11312", + "id": "162" + }, + { + "region": "11313", + "id": "172" + }, + { + "region": "11314", + "id": "170" + }, + { + "region": "11317", + "id": "119" + }, + { + "region": "11318", + "id": "87" + }, + { + "region": "11319", + "id": "9" + }, + { + "region": "11320", + "id": "187" + }, + { + "region": "11321", + "id": "257" + }, + { + "region": "11322", + "id": "292" + }, + { + "region": "11323", + "id": "294" + }, + { + "region": "11324", + "id": "569" + }, + { + "region": "11331", + "id": "553" + }, + { + "region": "11332", + "id": "564" + }, + { + "region": "11335", + "id": "504" + }, + { + "region": "11339", + "id": "52" + }, + { + "region": "11343", + "id": "491" + }, + { + "region": "11352", + "id": "444" + }, + { + "region": "11353", + "id": "444" + }, + { + "region": "11354", + "id": "444" + }, + { + "region": "11356", + "id": "442" + }, + { + "region": "11408", + "id": "365" + }, + { + "region": "11410", + "id": "90" + }, + { + "region": "11412", + "id": "162" + }, + { + "region": "11413", + "id": "336" + }, + { + "region": "11414", + "id": "25" + }, + { + "region": "11416", + "id": "179" + }, + { + "region": "11417", + "id": "19" + }, + { + "region": "11418", + "id": "100" + }, + { + "region": "11419", + "id": "100" + }, + { + "region": "11421", + "id": "261" + }, + { + "region": "11422", + "id": "369" + }, + { + "region": "11423", + "id": "396" + }, + { + "region": "11562", + "id": "304" + }, + { + "region": "11565", + "id": "78" + }, + { + "region": "11566", + "id": "94" + }, + { + "region": "11567", + "id": "89" + }, + { + "region": "11568", + "id": "479" + }, + { + "region": "11569", + "id": "92" + }, + { + "region": "11570", + "id": "138" + }, + { + "region": "11571", + "id": "107" + }, + { + "region": "11572", + "id": "186" + }, + { + "region": "11573", + "id": "18" + }, + { + "region": "11574", + "id": "77" + }, + { + "region": "11575", + "id": "149" + }, + { + "region": "11576", + "id": "258" + }, + { + "region": "11578", + "id": "292" + }, + { + "region": "11587", + "id": "564" + }, + { + "region": "11588", + "id": "564" + }, + { + "region": "11589", + "id": "524" + }, + { + "region": "11591", + "id": "242" + }, + { + "region": "11595", + "id": "57" + }, + { + "region": "11599", + "id": "482" + }, + { + "region": "11605", + "id": "232" + }, + { + "region": "11606", + "id": "442" + }, + { + "region": "11608", + "id": "442" + }, + { + "region": "11612", + "id": "442" + }, + { + "region": "11665", + "id": "30" + }, + { + "region": "11666", + "id": "103" + }, + { + "region": "11668", + "id": "129" + }, + { + "region": "11670", + "id": "138" + }, + { + "region": "11671", + "id": "53" + }, + { + "region": "11672", + "id": "173" + }, + { + "region": "11673", + "id": "178" + }, + { + "region": "11676", + "id": "153" + }, + { + "region": "11677", + "id": "264" + }, + { + "region": "11678", + "id": "395" + }, + { + "region": "11821", + "id": "162" + }, + { + "region": "11823", + "id": "166" + }, + { + "region": "11824", + "id": "515" + }, + { + "region": "11825", + "id": "180" + }, + { + "region": "11826", + "id": "12" + }, + { + "region": "11827", + "id": "127" + }, + { + "region": "11828", + "id": "72" + }, + { + "region": "11829", + "id": "54" + }, + { + "region": "11831", + "id": "34" + }, + { + "region": "11832", + "id": "435" + }, + { + "region": "11833", + "id": "183" + }, + { + "region": "11834", + "id": "43" + }, + { + "region": "11835", + "id": "37" + }, + { + "region": "11837", + "id": "52" + }, + { + "region": "11853", + "id": "402" + }, + { + "region": "11864", + "id": "443" + }, + { + "region": "11865", + "id": "443" + }, + { + "region": "11866", + "id": "443" + }, + { + "region": "11924", + "id": "576" + }, + { + "region": "11925", + "id": "108" + }, + { + "region": "11926", + "id": "490" + }, + { + "region": "11929", + "id": "325" + }, + { + "region": "11930", + "id": "582" + }, + { + "region": "11936", + "id": "52" + }, + { + "region": "11937", + "id": "52" + }, + { + "region": "12078", + "id": "549" + }, + { + "region": "12079", + "id": "62" + }, + { + "region": "12080", + "id": "62" + }, + { + "region": "12081", + "id": "105" + }, + { + "region": "12082", + "id": "35" + }, + { + "region": "12083", + "id": "49" + }, + { + "region": "12084", + "id": "15" + }, + { + "region": "12085", + "id": "310" + }, + { + "region": "12086", + "id": "102" + }, + { + "region": "12087", + "id": "96" + }, + { + "region": "12088", + "id": "160" + }, + { + "region": "12089", + "id": "66" + }, + { + "region": "12090", + "id": "159" + }, + { + "region": "12091", + "id": "42" + }, + { + "region": "12092", + "id": "475" + }, + { + "region": "12093", + "id": "334" + }, + { + "region": "12100", + "id": "236" + }, + { + "region": "12102", + "id": "633" + }, + { + "region": "12105", + "id": "505" + }, + { + "region": "12107", + "id": "412" + }, + { + "region": "12108", + "id": "412" + }, + { + "region": "12109", + "id": "402" + }, + { + "region": "12111", + "id": "403" + }, + { + "region": "12115", + "id": "86" + }, + { + "region": "12117", + "id": "304" + }, + { + "region": "12120", + "id": "443" + }, + { + "region": "12121", + "id": "443" + }, + { + "region": "12122", + "id": "443" + }, + { + "region": "12181", + "id": "529" + }, + { + "region": "12184", + "id": "325" + }, + { + "region": "12186", + "id": "570" + }, + { + "region": "12187", + "id": "585" + }, + { + "region": "12192", + "id": "26" + }, + { + "region": "12193", + "id": "68" + }, + { + "region": "12335", + "id": "62" + }, + { + "region": "12336", + "id": "62" + }, + { + "region": "12337", + "id": "85" + }, + { + "region": "12338", + "id": "3" + }, + { + "region": "12339", + "id": "151" + }, + { + "region": "12340", + "id": "333" + }, + { + "region": "12341", + "id": "141" + }, + { + "region": "12342", + "id": "98" + }, + { + "region": "12343", + "id": "113" + }, + { + "region": "12344", + "id": "8" + }, + { + "region": "12345", + "id": "176" + }, + { + "region": "12346", + "id": "435" + }, + { + "region": "12349", + "id": "13" + }, + { + "region": "12354", + "id": "494" + }, + { + "region": "12367", + "id": "441" + }, + { + "region": "12376", + "id": "443" + }, + { + "region": "12377", + "id": "443" + }, + { + "region": "12378", + "id": "443" + }, + { + "region": "12436", + "id": "85" + }, + { + "region": "12437", + "id": "144" + }, + { + "region": "12438", + "id": "361" + }, + { + "region": "12440", + "id": "338" + }, + { + "region": "12441", + "id": "141" + }, + { + "region": "12442", + "id": "98" + }, + { + "region": "12589", + "id": "352" + }, + { + "region": "12590", + "id": "263" + }, + { + "region": "12591", + "id": "79" + }, + { + "region": "12593", + "id": "64" + }, + { + "region": "12594", + "id": "327" + }, + { + "region": "12595", + "id": "163" + }, + { + "region": "12596", + "id": "116" + }, + { + "region": "12597", + "id": "175" + }, + { + "region": "12598", + "id": "496" + }, + { + "region": "12599", + "id": "56" + }, + { + "region": "12600", + "id": "10" + }, + { + "region": "12601", + "id": "476" + }, + { + "region": "12602", + "id": "67" + }, + { + "region": "12603", + "id": "449" + }, + { + "region": "12604", + "id": "332" + }, + { + "region": "12605", + "id": "106" + }, + { + "region": "12610", + "id": "493" + }, + { + "region": "12610", + "id": "493" + }, + { + "region": "12614", + "id": "572" + }, + { + "region": "12615", + "id": "214" + }, + { + "region": "12619", + "id": "418" + }, + { + "region": "12623", + "id": "441" + }, + { + "region": "12625", + "id": "272" + }, + { + "region": "12693", + "id": "389" + }, + { + "region": "12694", + "id": "343" + }, + { + "region": "12696", + "id": "528" + }, + { + "region": "12697", + "id": "330" + }, + { + "region": "12698", + "id": "144" + }, + { + "region": "12843", + "id": "383" + }, + { + "region": "12846", + "id": "267" + }, + { + "region": "12848", + "id": "124" + }, + { + "region": "12849", + "id": "145" + }, + { + "region": "12850", + "id": "76" + }, + { + "region": "12851", + "id": "2" + }, + { + "region": "12852", + "id": "106" + }, + { + "region": "12853", + "id": "125" + }, + { + "region": "12854", + "id": "177" + }, + { + "region": "12855", + "id": "169" + }, + { + "region": "12856", + "id": "337" + }, + { + "region": "12857", + "id": "332" + }, + { + "region": "12858", + "id": "120" + }, + { + "region": "12861", + "id": "127" + }, + { + "region": "12880", + "id": "441" + }, + { + "region": "12888", + "id": "445" + }, + { + "region": "12889", + "id": "445" + }, + { + "region": "12890", + "id": "445" + }, + { + "region": "12944", + "id": "606" + }, + { + "region": "12945", + "id": "359" + }, + { + "region": "12946", + "id": "266" + }, + { + "region": "12948", + "id": "397" + }, + { + "region": "12949", + "id": "108" + }, + { + "region": "12950", + "id": "46" + }, + { + "region": "12952", + "id": "106" + }, + { + "region": "12953", + "id": "125" + }, + { + "region": "12956", + "id": "569" + }, + { + "region": "13099", + "id": "383" + }, + { + "region": "13100", + "id": "387" + }, + { + "region": "13102", + "id": "174" + }, + { + "region": "13104", + "id": "69" + }, + { + "region": "13105", + "id": "50" + }, + { + "region": "13106", + "id": "36" + }, + { + "region": "13107", + "id": "123" + }, + { + "region": "13108", + "id": "111" + }, + { + "region": "13109", + "id": "157" + }, + { + "region": "13110", + "id": "93" + }, + { + "region": "13111", + "id": "121" + }, + { + "region": "13113", + "id": "14" + }, + { + "region": "13114", + "id": "182" + }, + { + "region": "13115", + "id": "182" + }, + { + "region": "13116", + "id": "331" + }, + { + "region": "13117", + "id": "329" + }, + { + "region": "13131", + "id": "428" + }, + { + "region": "13133", + "id": "552" + }, + { + "region": "13135", + "id": "441" + }, + { + "region": "13141", + "id": "272" + }, + { + "region": "13142", + "id": "459" + }, + { + "region": "13144", + "id": "445" + }, + { + "region": "13145", + "id": "445" + }, + { + "region": "13146", + "id": "445" + }, + { + "region": "13199", + "id": "388" + }, + { + "region": "13200", + "id": "213" + }, + { + "region": "13203", + "id": "168" + }, + { + "region": "13206", + "id": "407" + }, + { + "region": "13209", + "id": "594" + }, + { + "region": "13356", + "id": "505" + }, + { + "region": "13357", + "id": "267" + }, + { + "region": "13358", + "id": "351" + }, + { + "region": "13359", + "id": "174" + }, + { + "region": "13361", + "id": "50" + }, + { + "region": "13362", + "id": "47" + }, + { + "region": "13363", + "id": "122" + }, + { + "region": "13364", + "id": "75" + }, + { + "region": "13365", + "id": "20" + }, + { + "region": "13366", + "id": "93" + }, + { + "region": "13368", + "id": "179" + }, + { + "region": "13369", + "id": "326" + }, + { + "region": "13373", + "id": "586" + }, + { + "region": "13400", + "id": "445" + }, + { + "region": "13401", + "id": "445" + }, + { + "region": "13402", + "id": "445" + }, + { + "region": "13456", + "id": "452" + }, + { + "region": "13457", + "id": "464" + }, + { + "region": "13458", + "id": "540" + }, + { + "region": "13459", + "id": "540" + }, + { + "region": "13461", + "id": "462" + }, + { + "region": "13463", + "id": "534" + }, + { + "region": "13464", + "id": "45" + }, + { + "region": "13465", + "id": "45" + }, + { + "region": "13611", + "id": "451" + }, + { + "region": "13613", + "id": "447" + }, + { + "region": "13614", + "id": "465" + }, + { + "region": "13616", + "id": "377" + }, + { + "region": "13617", + "id": "36" + }, + { + "region": "13618", + "id": "286" + }, + { + "region": "13619", + "id": "154" + }, + { + "region": "13620", + "id": "245" + }, + { + "region": "13621", + "id": "84" + }, + { + "region": "13622", + "id": "48" + }, + { + "region": "13623", + "id": "339" + }, + { + "region": "13642", + "id": "411" + }, + { + "region": "13712", + "id": "452" + }, + { + "region": "13718", + "id": "11" + }, + { + "region": "13720", + "id": "246" + }, + { + "region": "13722", + "id": "156" + }, + { + "region": "13723", + "id": "514" + }, + { + "region": "13872", + "id": "377" + }, + { + "region": "13873", + "id": "501" + }, + { + "region": "13875", + "id": "286" + }, + { + "region": "13876", + "id": "241" + }, + { + "region": "13877", + "id": "244" + }, + { + "region": "13878", + "id": "61" + }, + { + "region": "13879", + "id": "344" + }, + { + "region": "13899", + "id": "146" + }, + { + "region": "13968", + "id": "539" + }, + { + "region": "13972", + "id": "260" + }, + { + "region": "13975", + "id": "287" + }, + { + "region": "13977", + "id": "319" + }, + { + "region": "13979", + "id": "342" + }, + { + "region": "14131", + "id": "380" + }, + { + "region": "14134", + "id": "288" + }, + { + "region": "14155", + "id": "146" + }, + { + "region": "14157", + "id": "379" + }, + { + "region": "14158", + "id": "379" + }, + { + "region": "14167", + "id": "562" + }, + { + "region": "14231", + "id": "381" + }, + { + "region": "14234", + "id": "345" + }, + { + "region": "14391", + "id": "353" + }, + { + "region": "14413", + "id": "379" + }, + { + "region": "14414", + "id": "379" + }, + { + "region": "14486", + "id": "197" + }, + { + "region": "14488", + "id": "521" + }, + { + "region": "14638", + "id": "530" + }, + { + "region": "14646", + "id": "355" + }, + { + "region": "14746", + "id": "354" + }, + { + "region": "14747", + "id": "471" + }, + { + "region": "14894", + "id": "631" + }, + { + "region": "14994", + "id": "632" + }, + { + "region": "14995", + "id": "632" + }, + { + "region": "15148", + "id": "238" + }, + { + "region": "15150", + "id": "611" + }, + { + "region": "15151", + "id": "610" + }, + { + "region": "15159", + "id": "358" + }, + { + "region": "15248", + "id": "238" + }, + { + "region": "15251", + "id": "632" + } +] \ No newline at end of file diff --git a/Server/data/configs/music_tiles.json b/Server/data/configs/music_tiles.json new file mode 100644 index 000000000..7b901f974 --- /dev/null +++ b/Server/data/configs/music_tiles.json @@ -0,0 +1,62 @@ +[ + { + "id": "98", + "borders": "{3072,3456,3135,3519,[3076,3456,3085,3458]~,[3082,3459,3085,3460]}" + }, + { + "id": "141", + "borders": "{3072,3392,3135,3455,[3076,3452,3085,3455]}" + }, + { + "id": "228", + "borders": "{2896,5446,2919,5462}" + }, + { + "id": "229", + "borders": "{2921,5456,2937,5479}" + }, + { + "id": "230", + "borders": "{2904,5481,2927,5497}" + }, + { + "id": "231", + "borders": "{2886,5464,2902,5487}" + }, + { + "id": "386", + "borders": "{2820,5312,2849,5369}-{2849,5349,2880,5374}" + }, + { + "id": "391", + "borders": "{2879,5340,2944,5366}-{2909,5316,2944,5340}" + }, + { + "id": "399", + "borders": "{2816,5248,2943,5375,[2823,5250,2844,5310]~,[2844,5250,2878,5280]~,[2879,5340,2944,5366]~,[2909,5316,2944,5340]~,[2820,5312,2849,5369]~,[2849,5349,2880,5374]~,[2885,5253,2934,5278]~,[2913,5278,2937,5306]}" + }, + { + "id": "404", + "borders": "{2823,5250,2844,5310}-{2844,5250,2878,5276}" + }, + { + "id": "408", + "borders": "{2885,5253,2934,5278}-{2913,5278,2937,5306}" + }, + { + "id": "459", + "borders": "{3137,5442,3192,5564}-{3193,5442,3198,5472}-{3193,5507,3198,5564}" + }, + { + "id": "467", + "borders": "{3199,5441,3262,5564}-{3193,5482,3198,5497}" + }, + { + "id": "488", + "borders": "{3263,5441,3327,5566}" + }, + { + "id": "492", + "borders": "{3076,3452,3085,3460,[3076,3459,3081,3460]}" + } +] \ No newline at end of file diff --git a/Server/data/configs/npc_configs.json b/Server/data/configs/npc_configs.json index 326a072fb..44c10bdcb 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -1,71215 +1,88129 @@ [ { - "examine": "Servant of the Duke of Lumbridge.", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "22", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Hans", - "defence_level": "14", - "safespot": "0", - "lifepoints": "12", - "strength_level": "17", "id": "0", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "One of Gielinor's many citizens.", + "name": "Hans", + "examine": "Servant of the Duke of Lumbridge.", + "attack_level": "17", + "attack_speed": "4", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "14", + "lifepoints": "12", + "magic_animation": "422", + "magic_level": "22", "melee_animation": "422", "range_animation": "422", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "6", - "respawn_delay": "18", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "422", - "death_animation": "9055", - "name": "Man", - "defence_level": "5", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "range_level": "1", + "safespot": "0", + "strength_level": "17" + }, + { "id": "1", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", - "death_animation": "9055", "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "422", + "attack_level": "5", + "attack_speed": "4", + "clue_level": "0", "combat_audio": "511,513,512", - "attack_speed": "4", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "422", "death_animation": "9055", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of Gielinor's many citizens.", - "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", - "name": "Woman", - "defence_level": "3", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of Gielinor's many citizens.", - "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", - "name": "Woman", - "defence_level": "3", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "1", - "id": "6", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "He grows the crops in this area.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Farmer", "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "7", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "6", + "melee_animation": "422", + "range_animation": "422", "range_level": "1", - "attack_level": "5" + "respawn_delay": "18", + "safespot": null, + "slayer_exp": "0", + "strength_level": "2", + "weakness": "9" }, { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "284", - "range_animation": "0", + "id": "2", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Thief", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "8", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "381", - "range_animation": "0", + "clue_level": "0", "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "1156", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "9", - "clue_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", "range_level": "1", - "attack_level": "13" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" }, { + "id": "3", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "4", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "slayer_exp": "0", + "strength_level": "1", + "weakness": "9" + }, + { + "id": "4", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "4", + "clue_level": "0", + "combat_audio": "511,506,505", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "4", + "clue_level": "0", + "combat_audio": "511,506,505", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "6", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "7", + "name": "Farmer", + "examine": "He grows the crops in this area.", + "attack_level": "5", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "9" + }, + { + "id": "8", + "name": "Thief", + "examine": "Known for his light-fingered qualities.", + "attack_level": "10", + "attack_speed": "4", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "10", + "weakness": "9" + }, + { + "id": "9", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "13", + "attack_speed": "4", + "clue_level": "1", + "combat_audio": "2549,1979,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "400", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "9" + }, + { + "id": "10", + "name": "Schoolgirl", "examine": "She looks happy.", + "attack_level": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "1142", - "defence_animation": "285", - "magic_animation": "0", - "death_animation": "287", - "name": "Schoolgirl", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "11", + "name": "Tramp", "examine": "A man down on his luck.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tramp", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "11", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Not very civilised looking.", - "name": "Barbarian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "12", + "name": "Barbarian", + "examine": "Not very civilised looking.", + "attack_level": "1", "clue_level": "0", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Slightly magical.", - "start_gfx": "2728", - "combat_style": "2", - "melee_animation": "2791", - "range_animation": "711", - "combat_audio": "511,513,512", - "magic_level": "21", - "respawn_delay": "12", - "end_gfx": "2737", - "defence_animation": "404", - "weakness": "3", - "slayer_exp": "0", - "magic_animation": "711", - "death_animation": "9055", - "name": "Wizard", - "defence_level": "12", - "safespot": null, - "lifepoints": "14", - "strength_level": "1", "id": "13", + "name": "Wizard", + "examine": "Slightly magical.", + "attack_level": "1", "bonuses": "23,7,8,2,1,8,12,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "2729", - "attack_level": "1" - }, - { - "examine": "Loves nature.", "combat_audio": "511,513,512", - "range_animation": "0", - "melee_animation": "422", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", + "combat_style": "2", + "death_animation": "9055", "defence_animation": "404", + "defence_level": "12", + "end_gfx": "2737", + "lifepoints": "14", "magic_animation": "711", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "14", + "magic_level": "21", + "melee_animation": "2791", + "projectile": "2729", + "range_animation": "711", "range_level": "1", - "attack_level": "28" + "respawn_delay": "12", + "safespot": null, + "slayer_exp": "0", + "start_gfx": "2728", + "strength_level": "1", + "weakness": "3" }, { + "id": "14", + "name": "Druid", + "examine": "Loves nature.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "defence_animation": "404", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28" + }, + { + "id": "15", + "name": "Warrior woman", "examine": "Not very fashion conscious.", + "attack_level": "22", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "20", + "magic_animation": "0", "melee_animation": "386", "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Warrior woman", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "22", - "id": "15", "range_level": "1", - "attack_level": "22" + "safespot": null, + "strength_level": "22", + "weakness": "7" }, { + "id": "16", + "name": "Man", "examine": "One of the citizens of Al Kharid.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", "melee_animation": "422", "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "16", - "clue_level": "0", "range_level": "1", - "attack_level": "3" + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "7" }, { + "id": "17", + "name": "Schoolgirl" + }, + { + "id": "18", + "name": "Al-Kharid warrior", "examine": "Part of Al-Kharid's elite fighting force.", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "10,10,10,0,0,12,15,10,-1,12,0,9,0,0,0", + "clue_level": "0", + "combat_audio": "2500,1976,512", + "death_animation": "836", + "defence_animation": "387", + "defence_level": "4", + "lifepoints": "19", + "magic_animation": "390", "melee_animation": "390", "range_animation": "390", - "combat_audio": "2500,1976,512", - "attack_speed": "4", - "respawn_delay": "25", - "defence_animation": "387", - "weakness": "7", - "magic_animation": "390", - "death_animation": "836", - "name": "Al-Kharid warrior", - "defence_level": "4", - "safespot": null, - "lifepoints": "19", - "strength_level": "5", - "id": "18", - "clue_level": "0", - "bonuses": "10,10,10,0,0,12,15,10,-1,12,0,9,0,0,0", "range_level": "1", - "attack_level": "7" + "respawn_delay": "25", + "safespot": null, + "strength_level": "5", + "weakness": "7" }, { + "id": "19", + "name": "White Knight", "examine": "Shiny armour!", + "attack_level": "25", + "attack_speed": "8", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", "melee_animation": "7042", "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "White Knight", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "19", "range_level": "1", - "attack_level": "25" + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" }, { - "examine": "A holy warrior.", - "melee_animation": "390", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Paladin", - "defence_level": "54", - "safespot": null, - "lifepoints": "66", - "strength_level": "54", "id": "20", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Heroic!", - "melee_animation": "390", - "range_animation": "0", + "name": "Paladin", + "examine": "A holy warrior.", + "attack_level": "54", "attack_speed": "5", - "respawn_delay": "60", + "combat_audio": "511,513,512", + "death_animation": "836", "defence_animation": "425", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Hero", "defence_level": "54", + "lifepoints": "66", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "82", - "strength_level": "55", + "strength_level": "54" + }, + { "id": "21", + "name": "Hero", + "examine": "Heroic!", + "attack_level": "54", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "54", + "lifepoints": "82", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", "range_level": "1", - "attack_level": "54" + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "7" }, { - "examine": "They love the forests.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "18", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Forester", - "defence_level": "8", - "safespot": null, - "lifepoints": "17", - "strength_level": "13", "id": "22", - "range_level": "1", - "attack_level": "14" - }, - { - "examine": "A member of Ardougne's militia.", - "melee_animation": "390", - "range_animation": "0", + "name": "Forester", + "examine": "They love the forests.", + "attack_level": "14", + "attack_speed": "4", "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Knight of Ardougne", - "defence_level": "31", - "safespot": null, - "lifepoints": "52", - "strength_level": "40", - "id": "23", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "24", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "25", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "To protect and serve the populace of Ardougne.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Knight of Ardougne", - "defence_level": "31", - "safespot": null, - "lifepoints": "52", - "strength_level": "40", - "id": "26", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "Good with arrows.", - "start_gfx": "19", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "12", - "defence_animation": "404", - "magic_animation": "426", "death_animation": "9055", - "name": "Archer", - "defence_level": "20", - "safespot": null, - "lifepoints": "50", - "strength_level": "20", - "id": "27", - "range_level": "40", - "projectile": "10", - "attack_level": "20" - }, - { - "examine": "Enjoys locking up animals in small pens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", + "defence_animation": "404", + "defence_level": "8", + "lifepoints": "17", "magic_animation": "0", - "death_animation": "0", - "name": "Zoo keeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "28", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's a lumberjack, and he's ok.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chuck", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "29", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Doesn't water down the beer too much.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "30", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "31", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the peace... kind of.", - "melee_animation": "401", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "22", - "strength_level": "18", - "id": "32", - "clue_level": "1", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "What a boring job he has.", - "name": "Door man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "33", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Watches stuff. But who watches him?", "melee_animation": "422", "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Watchman", - "defence_level": "31", - "safespot": null, - "lifepoints": "22", - "strength_level": "31", - "id": "34", "range_level": "1", - "attack_level": "31" + "respawn_delay": "18", + "safespot": null, + "strength_level": "13" }, { - "examine": "A soldier of the town of Yanille.", + "id": "23", + "name": "Knight of Ardougne", + "examine": "A member of Ardougne's militia.", + "attack_level": "38", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "52", + "magic_animation": "0", "melee_animation": "390", "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "24", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,513,512", "death_animation": "836", - "name": "Soldier", - "defence_level": "26", - "safespot": null, - "lifepoints": "22", - "strength_level": "25", - "id": "35", - "range_level": "1", - "attack_level": "26" - }, - { - "examine": "The head gardener.", - "melee_animation": "0", - "range_animation": "0", "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", "magic_animation": "0", - "death_animation": "0", - "name": "Wyson the gardener", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "36", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bold knight famed for his travels.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigbert the Adventurer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "37", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Builds ships for a living.", "melee_animation": "422", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Shipyard worker", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "38", "range_level": "1", - "attack_level": "10" + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" }, { - "examine": "Builds ships for a living.", - "melee_animation": "401", + "id": "25", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Shipyard worker", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "39", "range_level": "1", - "attack_level": "10" + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" }, { - "examine": "Let's not go skinny dipping eh?", + "id": "26", + "name": "Knight of Ardougne", + "examine": "To protect and serve the populace of Ardougne.", + "attack_level": "38", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "52", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "27", + "name": "Archer", + "examine": "Good with arrows.", + "attack_level": "20", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "20", + "lifepoints": "50", + "magic_animation": "426", + "melee_animation": "426", + "projectile": "10", + "range_animation": "426", + "range_level": "40", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "19", + "strength_level": "20" + }, + { + "id": "28", + "name": "Zoo keeper", + "examine": "Enjoys locking up animals in small pens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shark", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "40", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "29", + "name": "Chuck", + "examine": "He's a lumberjack, and he's ok.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "30", + "name": "Barman", + "examine": "Doesn't water down the beer too much.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "31", + "name": "Priest", + "examine": "A holy man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "32", + "name": "Guard", + "examine": "Keeps the peace... kind of.", + "attack_level": "17", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "22", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "18", + "weakness": "9" + }, + { + "id": "33", + "name": "Door man", + "examine": "What a boring job he has.", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "34", + "name": "Watchman", + "examine": "Watches stuff. But who watches him?", + "attack_level": "31", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "22", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "31", + "weakness": "7" + }, + { + "id": "35", + "name": "Soldier", + "examine": "A soldier of the town of Yanille.", + "attack_level": "26", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "22", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "8" + }, + { + "id": "36", + "name": "Wyson the gardener", + "examine": "The head gardener.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "37", + "name": "Sigbert the Adventurer", + "examine": "A bold knight famed for his travels.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "38", + "name": "Shipyard worker", + "examine": "Builds ships for a living.", + "attack_level": "10", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "10", + "weakness": "6" + }, + { + "id": "39", + "name": "Shipyard worker", + "examine": "Builds ships for a living.", + "attack_level": "10", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "10", + "weakness": "6" + }, + { + "id": "40", + "name": "Shark", + "examine": "Let's not go skinny dipping eh?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "41", + "name": "Chicken", "examine": "Yep. Definitely a chicken.", - "slayer_task": "7", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", "melee_animation": "5387", "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", + "range_level": "1", "respawn_delay": "18", - "defence_animation": "5388", - "weakness": "9", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", "safespot": null, - "lifepoints": "3", "strength_level": "1", - "id": "41", - "range_level": "1", - "attack_level": "1" + "weakness": "9" }, { - "examine": "Freshly sheared.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "42", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and fluffy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Sheep", + "examine": "Freshly sheared.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "43", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", + "name": "Sheep", + "examine": "White and fluffy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "44", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "45", + "examine": "He can look after my money.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Quackers.", - "slayer_task": "7", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "3468", - "name": "Duck", - "defence_level": "1", - "water_npc": "true", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "46", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "45", + "name": "Banker", + "examine": "She can look after my money.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "46", + "name": "Duck", + "examine": "Quackers.", + "attack_level": "1", + "death_animation": "3468", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "water_npc": "true", + "weakness": "8" + }, + { + "id": "47", + "name": "Rat", "examine": "A popular dwarven delicacy.", - "slayer_task": "67", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,-42,-53,0,0,0", + "combat_audio": "3102,3104,3103", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "2705", + "magic_level": "1", "melee_animation": "2705", "range_animation": "2705", - "combat_audio": "3102,3104,3103", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "3", - "defence_animation": "2706", - "slayer_exp": "2", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "47", - "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,-42,-53,0,0,0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "3", + "safespot": null, + "slayer_exp": "2", + "strength_level": "1" }, { + "id": "48", + "name": "Oomlie bird", "examine": "A jungle version of the chicken, but more vicious.", - "slayer_task": "7", + "attack_level": "40", + "attack_speed": "4", + "death_animation": "5389", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "40", + "magic_animation": "0", "melee_animation": "5387", "range_animation": "0", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5389", - "name": "Oomlie bird", - "defence_level": "40", - "safespot": null, - "lifepoints": "40", - "strength_level": "40", - "id": "48", "range_level": "1", - "attack_level": "40" + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "9" }, { + "id": "49", + "name": "Hellhound", "examine": "Hello, nice doggy...", - "slayer_task": "43", + "aggressive": "true", + "attack_level": "105", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "3717,3719,3718", + "death_animation": "6564", + "defence_animation": "6563", + "defence_level": "102", + "lifepoints": "116", + "magic_animation": "6562", "melee_animation": "6562", "range_animation": "6562", - "combat_audio": "3717,3719,3718", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "6563", - "weakness": "1", - "slayer_exp": "116", - "magic_animation": "6562", - "death_animation": "6564", - "name": "Hellhound", - "defence_level": "102", - "safespot": null, - "lifepoints": "116", - "strength_level": "104", - "id": "49", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "105" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "116", + "strength_level": "104", + "weakness": "1" }, { - "agg_radius": "64", - "examine": "The biggest, meanest dragon around.", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "magic_level": "240", - "respawn_delay": "50", - "defence_animation": "89", - "weakness": "4", - "slayer_exp": "258", - "magic_animation": "80", - "death_animation": "92", - "name": "King Black Dragon", - "defence_level": "240", - "safespot": null, - "lifepoints": "240", - "strength_level": "240", "id": "50", + "name": "King Black Dragon", + "examine": "The biggest, meanest dragon around.", + "agg_radius": "64", "aggressive": "true", + "attack_level": "240", "bonuses": "0,0,0,0,0,70,90,90,80,70,0,0,0,0,0", "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "240", + "lifepoints": "240", + "magic_animation": "80", + "magic_level": "240", + "melee_animation": "80", + "range_animation": "80", "range_level": "1", - "attack_level": "240" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "258", + "strength_level": "240", + "weakness": "4" }, { - "examine": "Young but still dangerous.", - "combat_audio": "407,405,406", - "magic_level": "116", - "respawn_delay": "25", - "weakness": "0", - "name": "Baby dragon", - "defence_level": "112", - "safespot": null, - "lifepoints": "160", - "strength_level": "103", "id": "51", - "aggressive": "true", - "bonuses": "124,130,137,184,176,128,168,145,104,54,0,0,0,0,0", - "range_level": "80", - "attack_level": "102" - }, - { + "name": "Baby dragon", "examine": "Young but still dangerous.", - "slayer_task": "11", - "combat_audio": "405,407,406", - "melee_animation": "25", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "26", - "weakness": "3", - "slayer_exp": "50", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby blue dragon", - "defence_level": "40", + "aggressive": "true", + "attack_level": "102", + "bonuses": "124,130,137,184,176,128,168,145,104,54,0,0,0,0,0", + "combat_audio": "407,405,406", + "defence_level": "112", + "lifepoints": "160", + "magic_level": "116", + "range_level": "80", + "respawn_delay": "25", "safespot": null, - "lifepoints": "50", - "strength_level": "40", + "strength_level": "103", + "weakness": "0" + }, + { "id": "52", + "name": "Baby blue dragon", + "examine": "Young but still dangerous.", "aggressive": "true", + "attack_level": "40", + "attack_speed": "4", "bonuses": "0,0,0,0,0,30,50,50,40,30,0,0,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "40", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "1", + "melee_animation": "25", "range_level": "1", - "attack_level": "40" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "50", + "strength_level": "40", + "weakness": "3" }, { - "examine": "A big powerful dragon.", - "slayer_task": "68", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "143", - "magic_animation": "80", - "death_animation": "92", - "name": "Red dragon", - "defence_level": "130", - "safespot": null, - "lifepoints": "140", - "strength_level": "130", "id": "53", + "name": "Red dragon", + "examine": "A big powerful dragon.", "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", "clue_level": "2", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "A fierce dragon with black scales!", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "200", - "magic_animation": "80", "death_animation": "92", - "name": "Black dragon", - "defence_level": "200", - "safespot": null, - "lifepoints": "190", - "strength_level": "200", - "id": "54", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", + "defence_animation": "89", + "defence_level": "130", + "lifepoints": "140", + "magic_animation": "80", "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "55", + "slayer_exp": "143", + "strength_level": "130", + "weakness": "3" + }, + { + "id": "54", + "name": "Black dragon", + "examine": "A fierce dragon with black scales!", "aggressive": "true", + "attack_level": "200", + "attack_speed": "4", "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "200", + "lifepoints": "190", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", "range_level": "1", - "attack_level": "95" + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "200", + "strength_level": "200", + "weakness": "3" }, { - "examine": "A wood nymph.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dryad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "56", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A delicate creature from this strange realm.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "57", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is it a spider or is it a shadow?", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "55", - "magic_animation": "0", - "death_animation": "5329", - "name": "Shadow spider", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "strength_level": "42", - "id": "58", + "id": "55", + "name": "Blue dragon", + "examine": "A mother dragon.", "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", "range_level": "1", - "attack_level": "44" + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" }, { - "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", + "id": "56", + "name": "Dryad", + "examine": "A wood nymph.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "57", + "name": "Fairy", + "examine": "A delicate creature from this strange realm.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "58", + "name": "Shadow spider", + "examine": "Is it a spider or is it a shadow?", + "aggressive": "true", + "attack_level": "44", + "attack_speed": "4", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "55", + "magic_animation": "0", "melee_animation": "5327", "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "5", - "magic_animation": "0", - "death_animation": "5329", - "name": "Giant spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "59", - "aggressive": "false", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "55", + "strength_level": "42", + "weakness": "2" }, { + "id": "59", + "name": "Giant spider", "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", + "aggressive": "false", + "attack_level": "1", + "attack_speed": "4", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "5327", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "5", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "60", + "name": "Giant spider", + "examine": "I think this spider has been genetically modified.", + "aggressive": "true", + "attack_level": "20", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "21", + "lifepoints": "32", + "magic_animation": "5327", "melee_animation": "5327", "range_animation": "5327", - "combat_audio": "537,539,538", - "defence_animation": "5328", - "weakness": "2", - "slayer_exp": "33", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Giant spider", - "defence_level": "21", - "safespot": null, - "lifepoints": "32", - "strength_level": "24", - "id": "60", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "20" + "safespot": null, + "slayer_exp": "33", + "strength_level": "24", + "weakness": "2" }, { + "id": "61", + "name": "Spider", "examine": "Incey wincey.", - "slayer_task": "76", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6251", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "0", "melee_animation": "6249", "range_animation": "0", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", + "safespot": null, "slayer_exp": "2", - "magic_animation": "0", - "death_animation": "6251", - "name": "Spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", "strength_level": "1", - "id": "61", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" + "weakness": "2" }, { - "examine": "A barely visible deadly jungle spider.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "5327", - "combat_audio": "537,539,538", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "5328", - "weakness": "2", - "slayer_exp": "50", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "35", - "poison_immune": "false", - "safespot": null, - "lifepoints": "50", - "strength_level": "37", "id": "62", + "name": "Jungle spider", + "examine": "A barely visible deadly spider.", "aggressive": "true", + "attack_level": "35", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "5327", + "magic_level": "1", + "melee_animation": "5327", + "poison_immune": "false", + "range_animation": "5327", "range_level": "1", - "attack_level": "35" + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "50", + "strength_level": "37", + "weakness": "2" }, { + "id": "63", + "name": "Deadly red spider", "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", + "attack_level": "30", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,15,16,7,12,16,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "30", + "lifepoints": "35", + "magic_animation": "5327", + "magic_level": "1", "melee_animation": "5327", "range_animation": "5327", - "combat_audio": "537,539,538", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "5328", - "weakness": "2", - "slayer_exp": "35", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Deadly red spider", - "defence_level": "30", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "63", - "bonuses": "0,0,0,0,0,0,15,16,7,12,16,0,0,0,0", "range_level": "1", - "attack_level": "30" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "25", + "weakness": "2" }, { + "id": "64", + "name": "Ice spider", "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,17,12,13,13,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "65", + "magic_animation": "0", "melee_animation": "5327", "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", + "safespot": null, "slayer_exp": "65", - "magic_animation": "0", - "death_animation": "5329", - "name": "Ice spider", - "defence_level": "43", - "safespot": null, - "lifepoints": "65", "strength_level": "55", - "id": "64", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,20,17,12,13,13,0,0,0,0", - "range_level": "1", - "attack_level": "50" + "weakness": "2" }, { - "examine": "A funny little man normally associated with rainbows.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Leprechaun", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "65", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Like a mini man!", + "name": "Leprechaun", + "examine": "A funny little man normally associated with rainbows.", + "attack_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "1", "melee_animation": "422", "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Gnome", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "3", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "66", + "name": "Gnome", + "examine": "Like a mini man!", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "422", + "magic_level": "1", + "melee_animation": "190", + "range_animation": "190", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Like a mini man!", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", "id": "67", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Like a mini man!", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", "name": "Gnome", + "examine": "Like a mini man!", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", "defence_level": "1", - "safespot": null, "lifepoints": "3", - "strength_level": "1", + "magic_animation": "422", + "magic_level": "1", + "melee_animation": "190", + "range_animation": "190", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "68", + "name": "Gnome", + "examine": "Like a mini man!", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "422", + "magic_level": "1", + "melee_animation": "190", + "range_animation": "190", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A scaly reptilian creature.", - "slayer_task": "", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Lizard man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "69", + "name": "Lizard man", + "examine": "A scaly reptilian creature.", "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideous malformed elf.", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "1", "melee_animation": "422", "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Orc", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "70", + "name": "Orc" + }, + { "id": "71", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideously deformed creature.", + "name": "Orc", + "examine": "A hideous malformed elf.", + "attack_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "1", "melee_animation": "422", "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Troll", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "72", + "name": "Troll", + "examine": "A hideously deformed creature.", + "attack_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "422", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Dead man walking.", - "slayer_task": "93", - "melee_animation": "5567", - "combat_audio": "931,923,922", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "5568", - "slayer_exp": "22", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "10", - "safespot": null, - "lifepoints": "22", - "strength_level": "9", "id": "73", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Dead woman walking.", - "slayer_task": "93", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "4", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "24", - "magic_animation": "5578", - "death_animation": "5580", "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5568", "defence_level": "10", - "safespot": null, - "lifepoints": "24", - "strength_level": "13", - "id": "74", - "aggressive": "true", + "lifepoints": "22", + "melee_animation": "5567", "range_level": "1", - "attack_level": "13" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "22", + "strength_level": "9" }, { - "examine": "The walking dead.", - "slayer_task": "93", + "id": "74", + "name": "Zombie", + "examine": "Dead woman walking.", + "aggressive": "true", + "attack_level": "13", + "attack_speed": "4", + "combat_audio": "931,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "10", + "lifepoints": "24", + "magic_animation": "5578", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "4", + "range_level": "1", "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "16", "safespot": null, - "lifepoints": "30", - "strength_level": "21", + "slayer_exp": "24", + "strength_level": "13" + }, + { "id": "75", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "The walking dead.", - "slayer_task": "93", - "melee_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", "name": "Zombie", - "defence_level": "16", - "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "id": "76", + "examine": "The walking dead.", "aggressive": "true", + "attack_level": "19", + "attack_speed": "4", + "combat_audio": "931,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "16", + "lifepoints": "30", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", "range_level": "1", - "attack_level": "19" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "21" }, { + "id": "76", + "name": "Zombie", + "examine": "The walking dead.", + "aggressive": "true", + "attack_level": "19", + "attack_speed": "4", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "16", + "lifepoints": "30", + "melee_animation": "5571", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "21" + }, + { + "id": "77", + "name": "Summoned Zombie", "examine": "The living dead.", - "slayer_task": "93", + "attack_level": "8", + "attack_speed": "4", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "22", + "magic_animation": "0", "melee_animation": "5568", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "22", - "magic_animation": "0", - "death_animation": "5569", - "name": "Summoned Zombie", - "defence_level": "10", - "safespot": null, - "lifepoints": "22", - "strength_level": "9", - "id": "77", "range_level": "1", - "attack_level": "8" + "safespot": null, + "slayer_exp": "22", + "strength_level": "9", + "weakness": "9" }, { + "id": "78", + "name": "Giant bat", "examine": "An annoying flappy thing.", - "slayer_task": "5", + "aggressive": "true", + "attack_level": "22", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", + "combat_audio": "292,294,293", + "death_animation": "4917", + "defence_animation": "4916", + "defence_level": "22", + "lifepoints": "32", + "magic_animation": "4915", + "magic_level": "1", "melee_animation": "4915", "range_animation": "4915", - "combat_audio": "292,294,293", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "4916", - "weakness": "4", - "slayer_exp": "32", - "magic_animation": "4915", - "death_animation": "4917", - "name": "Giant bat", - "defence_level": "22", - "safespot": null, - "lifepoints": "32", - "strength_level": "22", - "id": "78", - "aggressive": "true", - "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", "range_level": "1", - "attack_level": "22" + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "32", + "strength_level": "22", + "weakness": "4" }, { + "id": "79", + "name": "Death wing", "examine": "A shadowy, barely visible flying entity from some evil place.", - "slayer_task": "5", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "death_animation": "4917", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "80", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "4915", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "80", - "magic_animation": "0", - "death_animation": "4917", - "name": "Death wing", - "defence_level": "70", - "safespot": null, - "lifepoints": "80", - "strength_level": "70", - "id": "79", - "aggressive": "true", "range_level": "1", - "attack_level": "70" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "80", + "strength_level": "70", + "weakness": "3" }, { + "id": "80", + "name": "Camel", "examine": "Oh, it's a camel.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "80", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "81", + "name": "Cow", "examine": "Converts grass to beef.", - "slayer_task": "20", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "369,371,370", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", "melee_animation": "5849", "range_animation": "5849", - "combat_audio": "369,371,370", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "81", "range_level": "1", - "attack_level": "1" + "safespot": null, + "slayer_exp": "8", + "strength_level": "1", + "weakness": "8" }, { + "id": "82", + "name": "Lesser demon", "examine": "Lesser, but still pretty big.", - "slayer_task": "56", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", "melee_animation": "4630", "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "82", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", "range_level": "1", - "attack_level": "68" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" }, { - "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "32", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "87", - "magic_animation": "64", - "death_animation": "68", - "name": "Greater demon", - "defence_level": "81", - "safespot": null, - "lifepoints": "89", - "strength_level": "78", "id": "83", + "name": "Greater demon", + "examine": "Big, red, and incredibly evil.", "aggressive": "true", + "attack_level": "76", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", "clue_level": "2", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", + "combat_audio": "400,404,403", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "81", + "lifepoints": "89", + "magic_animation": "64", + "magic_level": "1", "melee_animation": "64", "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "157", - "facing_booth": "", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", - "strength_level": "148", - "id": "84", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", "range_level": "1", - "attack_level": "145" + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "87", + "strength_level": "78", + "weakness": "4" }, { + "id": "84", + "name": "Black demon", + "examine": "A big, scary, jet-black demon.", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "facing_booth": "", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", + "melee_animation": "64", + "range_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "157", + "strength_level": "148", + "weakness": "4" + }, + { + "id": "85", + "name": "Golem", "examine": "A creature made from clay.", + "attack_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "magic_level": "1", "melee_animation": "422", "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Golem", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "85", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "86", + "name": "Giant rat", "examine": "Overgrown vermin.", - "slayer_task": "67", + "aggressive": "true", + "attack_level": "2", + "attack_speed": "4", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "2", + "lifepoints": "5", + "magic_animation": "4933", "melee_animation": "4933", "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "4", - "defence_animation": "4934", - "weakness": "8", - "slayer_exp": "5", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "2", - "safespot": null, - "lifepoints": "5", - "strength_level": "3", - "id": "86", - "aggressive": "true", "range_level": "1", - "attack_level": "2" + "safespot": null, + "slayer_exp": "5", + "strength_level": "3", + "weakness": "8" }, { + "id": "87", + "name": "Giant rat", "examine": "Overgrown vermin.", - "slayer_task": "67", + "aggressive": "true", + "attack_level": "6", + "attack_speed": "4", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "2", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "4933", "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "4", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", + "safespot": null, "slayer_exp": "10", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "2", - "safespot": null, - "lifepoints": "10", "strength_level": "5", - "id": "87", - "aggressive": "true", - "range_level": "1", - "attack_level": "6" + "weakness": "8" }, { - "examine": "A dirty rat.", - "slayer_task": "67", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "slayer_exp": "12", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "10", - "safespot": null, - "lifepoints": "12", - "strength_level": "10", "id": "88", + "name": "Dungeon rat", + "examine": "A dirty rat.", "aggressive": "true", + "attack_level": "10", + "attack_speed": "4", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "12", + "magic_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "10" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "12", + "strength_level": "10", + "weakness": "8" }, { + "id": "89", + "name": "Unicorn", "examine": "Horse with a horn.", + "attack_level": "11", + "attack_speed": "4", + "combat_audio": "369,371,370", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "13", + "lifepoints": "19", + "magic_animation": "6376", + "magic_level": "1", "melee_animation": "6376", "range_animation": "6376", - "combat_audio": "369,371,370", - "attack_speed": "4", - "magic_level": "1", + "range_level": "1", "respawn_delay": "90", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn", - "defence_level": "13", "safespot": null, - "lifepoints": "19", "strength_level": "13", - "id": "89", - "range_level": "1", - "attack_level": "11" + "weakness": "3" }, { - "examine": "Not man's best friend.", - "slayer_task": "92", - "melee_animation": "6559", - "combat_audio": "481,491,490", + "id": "90", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "15", "attack_speed": "4", - "defence_animation": "6557", - "slayer_exp": "69", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "52", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "17", + "lifepoints": "29", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "respawn_delay": "70", "safespot": null, - "lifepoints": "69", - "strength_level": "55", + "slayer_exp": "29", + "strength_level": "18", + "weakness": "8" + }, + { + "id": "91", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "17", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "17", + "lifepoints": "24", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "slayer_exp": "24", + "strength_level": "17", + "weakness": "8" + }, + { + "id": "92", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "24", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "24", + "lifepoints": "17", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "slayer_exp": "17", + "strength_level": "24", + "weakness": "8" + }, + { + "id": "93", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "32", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "36", + "lifepoints": "59", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "slayer_exp": "59", + "strength_level": "35", + "weakness": "8" + }, + { + "id": "94", + "name": "Skeleton Mage" + }, + { "id": "95", + "name": "Wolf", + "examine": "Not man's best friend.", + "attack_level": "50", + "attack_speed": "4", + "combat_audio": "481,491,490", + "death_animation": "6558", + "defence_animation": "6557", + "defence_level": "52", + "lifepoints": "69", + "melee_animation": "6559", "range_level": "1", - "attack_level": "50" + "safespot": null, + "slayer_exp": "69", + "strength_level": "55" }, { - "examine": "A vicious mountain wolf.", - "slayer_task": "92", - "melee_animation": "6579", - "range_animation": "6579", - "attack_speed": "4", - "defence_animation": "6578", - "weakness": "9", - "slayer_exp": "34", - "magic_animation": "6579", - "death_animation": "6576", - "name": "White wolf", - "defence_level": "22", - "safespot": null, - "lifepoints": "34", - "strength_level": "16", "id": "96", - "aggressive": "true", - "range_level": "1", - "attack_level": "20" - }, - { + "name": "White wolf", "examine": "A vicious mountain wolf.", - "slayer_task": "92", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "4", + "combat_audio": "909,912,911", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "22", + "lifepoints": "34", + "magic_animation": "6579", "melee_animation": "6579", "range_animation": "6579", - "attack_speed": "4", - "defence_animation": "6578", - "weakness": "7", - "slayer_exp": "44", - "magic_animation": "6579", - "death_animation": "6576", - "name": "White wolf", - "defence_level": "32", - "safespot": null, - "lifepoints": "44", - "strength_level": "31", - "id": "97", - "aggressive": "true", "range_level": "1", - "attack_level": "30" + "safespot": null, + "slayer_exp": "34", + "strength_level": "16", + "weakness": "9" }, { + "id": "97", + "name": "White wolf", + "examine": "A vicious mountain wolf.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "4", + "combat_audio": "909,912,911", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "32", + "lifepoints": "44", + "magic_animation": "6579", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "safespot": null, + "slayer_exp": "44", + "strength_level": "31", + "weakness": "7" + }, + { + "id": "98", + "name": "Dog", "examine": "Bow wow.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "98", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "99", + "name": "Guard dog", "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", + "aggressive": "true", + "attack_level": "35", + "attack_speed": "4", + "death_animation": "6564", + "defence_animation": "6563", + "defence_level": "37", + "lifepoints": "49", + "magic_animation": "6562", "melee_animation": "6562", "range_animation": "6562", - "attack_speed": "4", + "range_level": "1", "respawn_delay": "15", - "defence_animation": "6563", - "weakness": "7", + "safespot": null, "slayer_exp": "49", - "magic_animation": "6562", - "death_animation": "6564", - "name": "Guard dog", - "defence_level": "37", - "safespot": null, - "lifepoints": "49", "strength_level": "36", - "id": "99", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" + "weakness": "7" }, { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "4", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", "id": "100", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", "name": "Goblin", - "defence_level": "3", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "101", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", + "attack_level": "1", + "attack_speed": "4", + "clue_level": "0", "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", "death_animation": "6182", - "name": "Goblin", + "defence_animation": "6183", "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "102", - "clue_level": "0", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", "range_level": "1", - "attack_level": "1" + "safespot": null, + "slayer_exp": "5", + "strength_level": "1" }, { - "agg_radius": "", + "id": "101", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "102", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "2" + }, + { + "id": "103", + "name": "Ghost", "examine": "Eeek! A ghost!", - "slayer_task": "36", + "agg_radius": "", + "aggressive": "true", + "attack_level": "13", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,5,-5,5,0,0,0,0,0", + "combat_audio": "436,439,438", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "5540", + "magic_level": "1", "melee_animation": "5540", "range_animation": "5540", - "combat_audio": "436,439,438", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "40", - "defence_animation": "5541", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "5540", - "death_animation": "5542", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "13", - "id": "103", - "aggressive": "true", - "bonuses": "0,0,0,0,0,5,5,5,-5,5,0,0,0,0,0", "range_level": "1", - "attack_level": "13" + "respawn_delay": "40", + "safespot": null, + "slayer_exp": "25", + "strength_level": "13", + "weakness": "5" }, { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5540", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "40", - "defence_animation": "5541", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5542", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "13", "id": "104", + "name": "Ghost", + "examine": "Eeek! A ghost!", "aggressive": "true", + "attack_level": "13", + "attack_speed": "4", "bonuses": "0,0,0,0,0,5,5,5,-5,5,0,0,0,0,0", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "300,302,301", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "27", - "magic_animation": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "15", - "safespot": null, - "lifepoints": "27", - "strength_level": "18", - "id": "105", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "300,302,301", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "4929", - "name": "Black bear", - "defence_level": "13", - "safespot": null, + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "18", "lifepoints": "25", - "strength_level": "16", - "id": "106", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "An extremely vicious scorpion.", - "melee_animation": "6254", - "combat_audio": "3611,3612,3610", - "respawn_delay": "60", - "defence_animation": "6255", - "slayer_exp": "17", - "death_animation": "6256", - "name": "Scorpion", - "defence_level": "1", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "107", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It has a very vicious looking tail.", - "slayer_task": "71", - "melee_animation": "6254", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5532", "range_animation": "0", - "combat_audio": "3611,3612,3610", + "range_level": "1", + "respawn_delay": "40", + "safespot": null, + "slayer_exp": "25", + "strength_level": "13", + "weakness": "5" + }, + { + "id": "105", + "name": "Grizzly bear", + "examine": "Eek! A bear!", + "aggressive": "true", + "attack_level": "17", "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "6255", - "weakness": "0", - "slayer_exp": "23", - "poison_amount": "3", - "magic_animation": "0", - "death_animation": "6256", - "name": "Poison Scorpion", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "300,302,301", + "death_animation": "4929", + "defence_animation": "0", "defence_level": "15", - "safespot": null, - "lifepoints": "23", - "strength_level": "17", - "id": "108", - "aggressive": "true", - "bonuses": "0,0,0,0,0,5,15,15,0,5,0,0,0,0,0", + "lifepoints": "27", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4925", + "range_animation": "0", "range_level": "1", - "attack_level": "16" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "27", + "strength_level": "18", + "weakness": "7" }, { + "id": "106", + "name": "Black bear", + "examine": "Eek! A bear!", + "aggressive": "false", + "attack_level": "15", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "300,302,301", + "death_animation": "4929", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4925", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "25", + "strength_level": "16", + "weakness": "7" + }, + { + "id": "107", + "name": "Scorpion", + "examine": "An extremely vicious scorpion.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "6255", + "defence_level": "1", + "lifepoints": "17", + "melee_animation": "6254", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "17", + "strength_level": "1" + }, + { + "id": "108", + "name": "Poison Scorpion", + "examine": "It has a very vicious looking tail.", + "aggressive": "true", + "attack_level": "16", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,15,15,0,5,0,0,0,0,0", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "6255", + "defence_level": "15", + "lifepoints": "23", + "magic_animation": "0", + "melee_animation": "6254", + "poison_amount": "15", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "23", + "strength_level": "17", + "weakness": "0" + }, + { + "id": "109", + "name": "Pit Scorpion", "examine": "Tiny", - "slayer_task": "71", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "6255", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", "melee_animation": "6254", "range_animation": "0", - "combat_audio": "3611,3612,3610", - "respawn_delay": "60", - "defence_animation": "6255", - "weakness": "0", - "magic_animation": "0", - "death_animation": "6256", - "name": "Pit Scorpion", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "1", - "id": "109", - "aggressive": "true", "range_level": "40", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" }, { + "id": "110", + "name": "Fire giant", "examine": "A very large elemental adversary.", - "slayer_task": "33", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", "melee_animation": "4666", "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "110", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", "range_level": "1", - "attack_level": "65" + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" }, { + "id": "111", + "name": "Ice giant", "examine": "He's got icicles in his beard.", - "slayer_task": "47", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4673", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "4672", "range_animation": "0", - "combat_audio": "448,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "70", - "magic_animation": "0", - "death_animation": "4673", - "name": "Ice giant", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "40", - "id": "111", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", "range_level": "1", - "attack_level": "40" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "70", + "strength_level": "40", + "weakness": "9" }, { + "id": "112", + "name": "Moss giant", "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "60", + "magic_animation": "4666", + "magic_level": "1", "melee_animation": "4666", "range_animation": "4666", - "combat_audio": "449,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "60", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "60", - "strength_level": "30", - "id": "112", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", "range_level": "1", - "attack_level": "30" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "60", + "strength_level": "30", + "weakness": "1" }, { + "id": "113", + "name": "Jogre", "examine": "An aggressive humanoid.", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "5", + "clue_level": "1", + "death_animation": "131", + "defence_animation": "129", + "defence_level": "41", + "lifepoints": "50", + "magic_animation": "128", "melee_animation": "128", "range_animation": "128", - "attack_speed": "5", - "defence_animation": "129", - "magic_animation": "128", - "death_animation": "131", - "name": "Jogre", - "defence_level": "41", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "113", - "aggressive": "true", - "clue_level": "1", "range_level": "1", - "attack_level": "41" + "safespot": null, + "strength_level": "36", + "weakness": "7" }, { - "examine": "Big, ugly, and smelly.", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", - "respawn_delay": "25", - "defence_animation": "360", - "magic_animation": "359", - "death_animation": "361", - "name": "Mogre", - "defence_level": "27", - "safespot": null, - "lifepoints": "60", - "strength_level": "27", "id": "114", - "aggressive": "true", - "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", - "range_level": "27", - "attack_level": "27" - }, - { - "examine": "A large dim looking humanoid.", - "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": "Ogre", - "defence_level": "43", - "safespot": null, - "lifepoints": "60", - "strength_level": "43", - "id": "115", - "aggressive": "true", - "bonuses": "22,22,22,0,0,0,0,0,0,0,0,20,0,0,0", - "range_level": "1", - "attack_level": "43" - }, - { + "name": "Mogre", "examine": "Big, ugly, and smelly.", - "slayer_task": "44", + "aggressive": "true", + "attack_level": "27", + "attack_speed": "6", + "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "27", + "lifepoints": "60", + "magic_animation": "359", + "magic_level": "27", "melee_animation": "359", "range_animation": "359", - "attack_speed": "4", - "magic_level": "1", + "range_level": "27", "respawn_delay": "25", - "defence_animation": "360", - "weakness": "8", - "slayer_exp": "75", - "magic_animation": "359", - "death_animation": "361", - "name": "Cyclops", - "defence_level": "35", "safespot": null, - "lifepoints": "75", - "strength_level": "65", - "id": "116", + "slayer_exp": "", + "strength_level": "27" + }, + { + "id": "115", + "name": "Ogre", + "examine": "A large dim looking humanoid.", "aggressive": "true", + "attack_level": "43", + "attack_speed": "6", + "bonuses": "22,22,22,0,0,0,0,0,0,0,0,20,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "43", + "lifepoints": "60", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "43" + }, + { + "id": "116", + "name": "Cyclops", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "35", + "lifepoints": "75", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", "range_level": "1", - "attack_level": "65" + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "75", + "strength_level": "65", + "weakness": "8" }, { + "id": "117", + "name": "Hill Giant", "examine": "A very large foe.", - "slayer_task": "44", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", "melee_animation": "4652", "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", + "range_level": "1", "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", + "safespot": null, "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", "strength_level": "22", - "id": "117", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" + "weakness": "6" }, { - "examine": "A short angry guy.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "7", "id": "118", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "A dwarf gone bad.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "combat_audio": "2567,419,418", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "150", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "61", - "magic_animation": "0", - "death_animation": "102", - "name": "Chaos dwarf", - "defence_level": "28", - "safespot": null, - "lifepoints": "61", - "strength_level": "42", - "id": "119", - "aggressive": "true", - "bonuses": "13,13,13,0,0,40,34,25,10,35,0,9,0,0,0", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "A mountain dwelling short angry guy.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "attack_speed": "6", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", "name": "Dwarf", - "defence_level": "16", - "safespot": null, - "lifepoints": "26", - "strength_level": "16", - "id": "120", - "range_level": "1", - "attack_level": "15" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, + "examine": "A short angry guy.", + "attack_level": "7", + "attack_speed": "7", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "7", "lifepoints": "10", - "strength_level": "1", - "id": "121", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly smelly creature.", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "3", + "respawn_delay": "60", "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "122", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" + "slayer_exp": "16", + "strength_level": "7", + "weakness": "6" }, { - "examine": "An ugly smelly creature.", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "3520,472,471", + "id": "119", + "name": "Chaos dwarf", + "examine": "A dwarf gone bad.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "4", + "bonuses": "13,13,13,0,0,40,34,25,10,35,0,9,0,0,0", + "combat_audio": "2567,419,418", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "61", + "strength_level": "42", + "weakness": "6" + }, + { + "id": "120", + "name": "Dwarf", + "examine": "A mountain dwelling short angry guy.", + "attack_level": "15", "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "16", + "lifepoints": "26", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "16", + "weakness": "6" + }, + { + "id": "121", + "name": "Dwarf", + "attack_level": "1", "defence_level": "1", + "lifepoints": "10", + "range_level": "1", "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "123", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" + "slayer_exp": "16", + "strength_level": "1" }, { + "id": "122", + "name": "Hobgoblin", + "examine": "An ugly smelly creature.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "3", + "lifepoints": "29", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "123", + "name": "Hobgoblin", + "examine": "An ugly smelly creature.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "3520,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "1", + "lifepoints": "29", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "124", + "name": "Earth warrior", "examine": "A strange, inhuman, elemental warrior.", - "slayer_task": "30", + "aggressive": "true", + "attack_level": "42", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "54", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "393", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "54", - "magic_animation": "0", - "death_animation": "836", - "name": "Earth warrior", - "defence_level": "42", - "safespot": null, - "lifepoints": "54", - "strength_level": "42", - "id": "124", - "aggressive": "true", - "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", "range_level": "1", - "attack_level": "42" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "54", + "strength_level": "42", + "weakness": "7" }, { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "combat_audio": "2500,530,529", - "attack_speed": "4", - "respawn_delay": "30", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "59", - "magic_animation": "451", - "death_animation": "843", - "name": "Ice warrior", - "defence_level": "47", - "safespot": null, - "lifepoints": "59", - "strength_level": "47", "id": "125", + "name": "Ice warrior", + "examine": "A cold-hearted elemental warrior.", "aggressive": "true", + "attack_level": "47", + "attack_speed": "4", "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", "clue_level": "1", + "combat_audio": "2500,530,529", + "death_animation": "843", + "defence_animation": "404", + "defence_level": "47", + "lifepoints": "59", + "magic_animation": "451", + "melee_animation": "451", + "range_animation": "451", "range_level": "1", - "attack_level": "47" - }, - { - "examine": "Looks pretty otherworldly to me!", - "slayer_task": "65", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "45", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "name": "Otherworldly being", - "defence_level": "45", + "respawn_delay": "30", "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "126", - "range_level": "1", - "attack_level": "1" + "slayer_exp": "59", + "strength_level": "47", + "weakness": "9" }, { + "id": "126", + "name": "Otherworldly being", + "examine": "Looks pretty otherworldly to me!", + "attack_level": "1", + "attack_speed": "5", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "magic_level": "45", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "127", + "name": "Magic axe", "examine": "A magic axe with a mind of its own.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,5,15,5,10,0,0,0,0,0", + "combat_audio": "2498,12,1976", + "death_animation": "188", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "44", + "magic_animation": "0", "melee_animation": "185", "range_animation": "0", - "combat_audio": "2498,12,1976", - "attack_speed": "4", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "188", - "name": "Magic axe", - "defence_level": "29", - "safespot": null, - "lifepoints": "44", - "strength_level": "38", - "id": "127", - "aggressive": "true", - "bonuses": "0,0,0,0,0,10,5,15,5,10,0,0,0,0,0", "range_level": "1", - "attack_level": "38" + "respawn_delay": "30", + "safespot": null, + "strength_level": "38", + "weakness": "6" }, { + "id": "128", + "name": "Snake", "examine": "A slithering serpent.", + "aggressive": "true", + "attack_level": "4", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "278", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "6", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "275", "range_animation": "0", - "combat_audio": "3609,3608,3610", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "278", - "name": "Snake", - "defence_level": "3", - "safespot": null, - "lifepoints": "6", - "strength_level": "5", - "id": "128", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "4" + "respawn_delay": "30", + "safespot": null, + "strength_level": "5", + "weakness": "8" }, { + "id": "129", + "name": "Skavid" + }, + { + "id": "130", + "name": "Yeti", + "examine": "Stinky! Poor guy." + }, + { + "id": "131", + "name": "Penguin", "examine": "An inhabitant of icy regions.", - "slayer_task": "7", + "attack_level": "6", + "death_animation": "5671", + "defence_animation": "0", + "defence_level": "6", + "lifepoints": "8", + "magic_animation": "0", "melee_animation": "5669", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5671", - "name": "Penguin", - "defence_level": "6", - "safespot": null, - "lifepoints": "8", - "strength_level": "6", - "id": "131", "range_level": "1", - "attack_level": "6" + "safespot": null, + "strength_level": "6", + "weakness": "9" }, { + "id": "132", + "name": "Monkey", "examine": "Perhaps our oldest relatives?", - "slayer_task": "61", + "aggressive": "false", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "629,631,630", + "death_animation": "223", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "6", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "220", "range_animation": "0", - "combat_audio": "629,631,630", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "37", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "223", - "name": "Monkey", - "defence_level": "2", - "safespot": null, - "lifepoints": "6", - "strength_level": "3", - "id": "132", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "2" + "respawn_delay": "37", + "safespot": null, + "strength_level": "3", + "weakness": "8" }, { + "id": "133", + "name": "Black unicorn", "examine": "A unicorn with a blackened heart.", + "aggressive": "false", + "attack_level": "21", + "attack_speed": "4", + "combat_audio": "369,371,370", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "23", + "lifepoints": "29", + "magic_animation": "6376", + "magic_level": "1", "melee_animation": "6376", "range_animation": "6376", - "combat_audio": "369,371,370", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "90", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Black unicorn", - "defence_level": "23", - "safespot": null, - "lifepoints": "29", - "strength_level": "23", - "id": "133", - "aggressive": "false", "range_level": "1", - "attack_level": "21" + "respawn_delay": "90", + "safespot": null, + "strength_level": "23", + "weakness": "3" }, { - "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "30", - "defence_animation": "5328", - "weakness": "2", - "poison_amount": "6", - "magic_animation": "0", - "death_animation": "5329", + "id": "134", "name": "Poison spider", - "safespot": null, + "examine": "A nasty, poisonous arachnid.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", "defence_level": "52", "lifepoints": "64", - "strength_level": "65", - "id": "134", - "aggressive": "true", + "magic_animation": "0", + "melee_animation": "5327", + "poison_amount": "30", + "poisonous": "true", + "range_animation": "0", "range_level": "1", - "attack_level": "50" + "respawn_delay": "30", + "safespot": null, + "strength_level": "65", + "weakness": "2" }, { + "id": "135", + "name": "Mammoth" + }, + { + "id": "136", + "name": "Mounted terrorchick gnome" + }, + { + "id": "137", + "name": "Terrorchick gnome", "examine": "Aaw", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorchick gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "137", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "138", + "name": "Terrorbird", "examine": "A giant raptor.", - "slayer_task": "7", + "aggressive": "false", + "attack_level": "23", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "832,834,833", + "death_animation": "1013", + "defence_animation": "1012", + "defence_level": "19", + "lifepoints": "34", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "1010", "range_animation": "0", - "combat_audio": "832,834,833", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "1012", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "19", - "safespot": null, - "lifepoints": "34", - "strength_level": "23", - "id": "138", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "23" + "respawn_delay": "30", + "safespot": null, + "strength_level": "23", + "weakness": "6" }, { - "examine": "A giant raptor.", - "melee_animation": "1010", - "combat_audio": "832,834,833", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "1012", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "19", - "safespot": null, - "lifepoints": "34", - "strength_level": "23", "id": "139", + "name": "Terrorbird", + "examine": "A giant raptor.", "aggressive": "false", + "attack_level": "23", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "832,834,833", + "death_animation": "1013", + "defence_animation": "1012", + "defence_level": "19", + "lifepoints": "34", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1010", "range_level": "1", - "attack_level": "23" + "respawn_delay": "30", + "safespot": null, + "strength_level": "23" }, { + "id": "140", + "name": "Souless", "examine": "A servant to Iban.", + "aggressive": "true", + "attack_level": "33", + "death_animation": "340", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", "melee_animation": "338", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "340", - "name": "Souless", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "140", - "aggressive": "true", "range_level": "1", - "attack_level": "33" + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "7" }, { + "id": "141", + "name": "Big Wolf", "examine": "Must be the pack leader.", - "slayer_task": "92", + "aggressive": "true", + "attack_level": "31", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "44", + "magic_animation": "0", "melee_animation": "6559", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "74", - "magic_animation": "0", - "death_animation": "6558", - "name": "Big Wolf", - "defence_level": "31", - "safespot": null, - "lifepoints": "44", - "strength_level": "31", - "id": "141", - "aggressive": "true", "range_level": "1", - "attack_level": "31" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "74", + "strength_level": "31", + "weakness": "7" }, { - "slayer_exp": "34", - "death_animation": "6558", + "id": "142", "name": "Wolf", + "attack_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6558", "defence_level": "1", - "safespot": null, "lifepoints": "69", "melee_animation": "6559", - "combat_audio": "481,491,490", - "strength_level": "1", - "id": "142", "range_level": "1", - "attack_level": "1" + "safespot": null, + "slayer_exp": "34", + "strength_level": "1" }, { + "id": "143", + "name": "Jungle Wolf", "examine": "A rare jungle wolf - specific to the Kharazi jungle.", - "slayer_task": "92", + "aggressive": "true", + "attack_level": "63", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", "melee_animation": "6559", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "69", - "magic_animation": "0", - "death_animation": "6558", - "name": "Jungle Wolf", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "143", - "aggressive": "true", "range_level": "1", - "attack_level": "63" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "69", + "strength_level": "63", + "weakness": "7" }, { + "id": "144", + "name": "King Scorpion", "examine": "Wow! Scorpions shouldn't grow that big.", - "slayer_task": "71", + "aggressive": "true", + "attack_level": "21", + "attack_speed": "5", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", "melee_animation": "6254", "range_animation": "0", - "combat_audio": "3611,3612,3610", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "17", - "magic_animation": "0", - "death_animation": "6256", - "name": "King Scorpion", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "21", - "id": "144", - "aggressive": "true", "range_level": "28", - "attack_level": "21" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "17", + "strength_level": "21", + "weakness": "2" }, { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "combat_audio": "2500,530,529", - "attack_speed": "4", - "respawn_delay": "30", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "59", - "magic_animation": "451", - "death_animation": "843", - "name": "Ice warrior", - "defence_level": "47", - "safespot": null, - "lifepoints": "59", - "strength_level": "47", "id": "145", + "name": "Ice warrior", + "examine": "A cold-hearted elemental warrior.", "aggressive": "true", + "attack_level": "47", + "attack_speed": "4", "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", "clue_level": "1", + "combat_audio": "2500,530,529", + "death_animation": "843", + "defence_animation": "404", + "defence_level": "47", + "lifepoints": "59", + "magic_animation": "451", + "melee_animation": "451", + "range_animation": "451", "range_level": "1", - "attack_level": "47" + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "59", + "strength_level": "47", + "weakness": "9" }, { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gull", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "146", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Gull", "examine": "A sea bird.", - "slayer_task": "7", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cormorant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "147", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Cormorant", "examine": "A sea bird.", - "slayer_task": "7", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pelican", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "148", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These look much better in the wild.", + "name": "Pelican", + "examine": "A sea bird.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Butterfly", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "149", + "name": "Gull" + }, + { + "id": "150", + "name": "Gull" + }, + { + "id": "151", + "name": "Fly trap" + }, + { + "id": "152", + "name": "Butterfly" + }, + { "id": "153", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I love butterflies.", + "name": "Butterfly", + "examine": "These look much better in the wild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Butterfly", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "154", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "154", + "name": "Butterfly", + "examine": "I love butterflies.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "155", + "name": "Butterfly" + }, + { + "id": "156", + "name": "Butterfly" + }, + { + "id": "157", + "name": "Butterfly" + }, + { + "id": "158", + "name": "Shadow warrior", "examine": "A fighter from the supernatural world. He's a shadow of his former self.", - "slayer_task": "74", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "5", + "death_animation": "843", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", "melee_animation": "394", "range_animation": "0", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "843", - "name": "Shadow warrior", - "defence_level": "47", "safespot": null, - "lifepoints": "67", "strength_level": "47", - "id": "158", - "aggressive": "true", - "range_level": "1", - "attack_level": "47" + "weakness": "6" }, { - "examine": "Small", - "melee_animation": "191", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome child", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "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.", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "0", "melee_animation": "191", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome child", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "161", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1", + "weakness": "9" }, { + "id": "160", + "name": "Gnome child", + "examine": "Small, even by gnome standards.", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "191", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "161", + "name": "Gnome child", + "examine": "Small, even by gnome standards.", + "attack_level": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "191", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "162", + "name": "Gnome trainer", "examine": "He can advise on training.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome trainer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "162", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "163", + "name": "Gnome guard", "examine": "A tree gnome guard.", + "attack_level": "38", + "death_animation": "1224", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", "melee_animation": "1218", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1224", - "name": "Gnome guard", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "163", "range_level": "1", - "attack_level": "38" + "safespot": null, + "strength_level": "38", + "weakness": "9" }, { + "id": "164", + "name": "Gnome guard", "examine": "A tree gnome guard.", + "attack_level": "38", + "death_animation": "196", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", "melee_animation": "192", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome guard", - "defence_level": "38", + "range_level": "1", "safespot": null, - "lifepoints": "54", "strength_level": "38", - "id": "164", - "range_level": "1", - "attack_level": "38" + "weakness": "9" + }, + { + "id": "165", + "name": "Gnome shop keeper" + }, + { + "id": "166", + "name": "Gnome banker", + "examine": "Banks gnomish things." + }, + { + "id": "167", + "name": "Gnome baller" }, { - "examine": "A female gnome.", - "combat_style": "1", - "melee_animation": "191", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome woman", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", "id": "168", - "range_level": "1", - "attack_level": "30" - }, - { + "name": "Gnome woman", "examine": "A female gnome.", + "attack_level": "30", "combat_style": "1", + "death_animation": "196", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", "melee_animation": "191", "range_animation": "0", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome woman", - "defence_level": "30", "safespot": null, - "lifepoints": "42", "strength_level": "30", + "weakness": "7" + }, + { "id": "169", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "One of Gielinor's many citizens", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", + "name": "Gnome woman", + "examine": "A female gnome.", + "attack_level": "30", + "combat_style": "1", + "death_animation": "196", "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "170", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "He works evil magic.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "27", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "27", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "172", - "aggressive": "true", - "range_level": "1", - "attack_level": "1", - "prj_height": "3" - }, - { - "examine": "An evil user of Magic powers", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "836", - "name": "Invrigar the Necromancer", "defence_level": "30", - "safespot": null, "lifepoints": "42", - "strength_level": "1", - "id": "173", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He works evil magic.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "24", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "24", - "safespot": null, - "lifepoints": "12", - "strength_level": "1", - "id": "174", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "2", - "examine": "He jumps out and attacks people.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", "magic_animation": "0", - "death_animation": "836", - "name": "Mugger", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "175", - "aggressive": "true", + "melee_animation": "191", + "range_animation": "0", "range_level": "1", - "attack_level": "11" + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "7" }, { + "id": "170", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" + }, + { + "id": "171", + "name": "Brimstail" + }, + { + "id": "172", + "name": "Dark wizard", + "examine": "He works evil magic.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "27", + "lifepoints": "24", + "magic_animation": "711", + "magic_level": "27", + "melee_animation": "422", + "prj_height": "3", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "173", + "name": "Invrigar the Necromancer", + "examine": "An evil user of Magic powers", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "magic_level": "30", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "174", + "name": "Dark wizard", + "examine": "He works evil magic.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "24", + "lifepoints": "12", + "magic_animation": "711", + "magic_level": "24", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "175", + "name": "Mugger", + "examine": "He jumps out and attacks people.", + "agg_radius": "2", + "aggressive": "true", + "attack_level": "11", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "176", + "name": "Witch", "examine": "The hat's a dead give away.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "magic_level": "22", "melee_animation": "423", "range_animation": "0", - "magic_level": "22", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "836", - "name": "Witch", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "1", - "id": "176", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1", + "weakness": "4" }, { + "id": "177", + "name": "Witch" + }, + { + "id": "178", + "name": "Black Knight", "examine": "A dark-hearted knight.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", + "bonuses": "18,18,18,0,0,73,76,70,-11,72,0,16,0,0,0", + "combat_audio": "2503,15,512", + "death_animation": "836", + "defence_animation": "388", + "defence_level": "25", + "lifepoints": "42", + "magic_animation": "0", "melee_animation": "390", "range_animation": "0", - "combat_audio": "2503,15,512", - "attack_speed": "5", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "25", - "safespot": null, - "lifepoints": "42", - "strength_level": "25", - "id": "178", - "aggressive": "true", - "bonuses": "18,18,18,0,0,73,76,70,-11,72,0,16,0,0,0", "range_level": "1", - "attack_level": "25" + "respawn_delay": "25", + "safespot": null, + "strength_level": "25", + "weakness": "7" }, { - "examine": "A dark-hearted knight.", - "melee_animation": "390", - "combat_audio": "2503,15,512", - "attack_speed": "5", - "respawn_delay": "25", - "defence_animation": "388", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "25", - "safespot": null, - "lifepoints": "42", - "strength_level": "25", "id": "179", + "name": "Black Knight", + "examine": "A dark-hearted knight.", "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", "bonuses": "18,18,18,0,0,73,76,70,-11,72,0,16,0,0,0", + "combat_audio": "2503,15,512", + "death_animation": "836", + "defence_animation": "388", + "defence_level": "25", + "lifepoints": "42", + "melee_animation": "390", "range_level": "1", - "attack_level": "25" + "respawn_delay": "25", + "safespot": null, + "strength_level": "25" }, { + "id": "180", + "name": "Highwayman", "examine": "He holds up passers by.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "8", + "lifepoints": "13", + "magic_animation": "799", "melee_animation": "799", "range_animation": "799", - "combat_audio": "511,513,512", - "attack_speed": "5", - "defence_animation": "404", - "weakness": "7", - "magic_animation": "799", - "death_animation": "9055", - "name": "Highwayman", - "defence_level": "8", - "safespot": null, - "lifepoints": "13", - "strength_level": "8", - "id": "180", - "aggressive": "true", "range_level": "1", - "attack_level": "8" + "safespot": null, + "strength_level": "8", + "weakness": "7" }, { - "examine": "A crazy, evil druid.", - "start_gfx": "105", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "10", - "end_gfx": "107", - "respawn_delay": "25", - "defence_animation": "404", - "weakness": "3", - "slayer_exp": "0", - "magic_animation": "422", - "death_animation": "9055", - "name": "Chaos druid", - "defence_level": "12", - "safespot": null, - "lifepoints": "20", - "strength_level": "8", "id": "181", + "name": "Chaos druid", + "examine": "A crazy, evil druid.", "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "106", - "attack_level": "8" - }, - { - "melee_animation": "422", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "182", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "183", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "184", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "185", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "186", - "aggressive": "true", - "clue_level": "0", - "bonuses": "5,5,5,0,0,2,3,3,0,0,0,5,0,0,0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "25", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "5", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "18", - "id": "188", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "18" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "25", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "5", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "12", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "189", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "8" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "40", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "5", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "42", - "safespot": null, - "lifepoints": "40", - "strength_level": "38", - "id": "190", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "38" - }, - { - "examine": "A primitive warrior.", - "melee_animation": "428", - "range_animation": "0", - "attack_speed": "4", - "poisonous": "true", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "8", - "poison_amount": "11", - "magic_animation": "0", - "death_animation": "836", - "name": "Tribesman", - "defence_level": "26", - "safespot": null, - "lifepoints": "39", - "strength_level": "27", - "id": "191", - "aggressive": "true", - "clue_level": "1", - "bonuses": "8,8,8,0,0,4,6,6,0,0,0,5,0,0,0", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A warrior of Darkness.", - "melee_animation": "451", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "8", "death_animation": "9055", - "name": "Dark warrior", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "192", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A crazy evil druid.", - "start_gfx": "105", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "10", - "respawn_delay": "25", - "end_gfx": "107", "defence_animation": "404", - "weakness": "3", - "magic_animation": "422", - "death_animation": "836", - "name": "Chaos druid warrior", "defence_level": "12", - "safespot": null, + "end_gfx": "107", "lifepoints": "20", - "strength_level": "8", - "id": "193", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "106", - "attack_level": "8" - }, - { - "examine": "A crazy evil necromancer.", + "magic_animation": "422", + "magic_level": "10", "melee_animation": "422", - "range_animation": "0", - "magic_level": "28", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "836", - "name": "Necromancer", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "194", + "projectile": "106", + "range_animation": "422", "range_level": "1", - "attack_level": "1" + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "0", + "start_gfx": "105", + "strength_level": "8", + "weakness": "3" }, { - "examine": "Bandit Camp guard.", + "id": "182", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "183", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "184", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "185", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "186", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,2,3,3,0,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "386", "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard Bandit", - "defence_level": "20", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "196", - "range_level": "20", - "attack_level": "1" + "strength_level": "5", + "weakness": "9" }, { + "id": "187", + "name": "Rogue", + "examine": "Rogueish.", + "aggressive": "false", + "attack_level": "7", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "7", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "7", + "weakness": "9" + }, + { + "id": "188", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "22", + "end_gfx": "101", + "lifepoints": "20", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "18", + "weakness": "5" + }, + { + "id": "189", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "12", + "end_gfx": "101", + "lifepoints": "10", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "8", + "weakness": "5" + }, + { + "id": "190", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "42", + "end_gfx": "101", + "lifepoints": "40", + "magic_animation": "711", + "magic_level": "40", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "38", + "weakness": "5" + }, + { + "id": "191", + "name": "Tribesman", + "examine": "A primitive warrior.", + "aggressive": "true", + "attack_level": "23", + "attack_speed": "4", + "bonuses": "8,8,8,0,0,4,6,6,0,0,0,5,0,0,0", + "clue_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "39", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "428", + "poison_amount": "55", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "27", + "weakness": "8" + }, + { + "id": "192", + "name": "Dark warrior", + "examine": "A warrior of Darkness.", + "attack_level": "10", + "attack_speed": "5", + "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "10", + "lifepoints": "10", + "melee_animation": "451", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "10", + "weakness": "8" + }, + { + "id": "193", + "name": "Chaos druid warrior", + "examine": "A crazy evil druid.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "12", + "end_gfx": "107", + "lifepoints": "20", + "magic_animation": "422", + "magic_level": "10", + "melee_animation": "422", + "projectile": "106", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "105", + "strength_level": "8", + "weakness": "3" + }, + { + "id": "194", + "name": "Necromancer", + "examine": "A crazy evil necromancer.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "magic_level": "28", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "195", + "name": "Bandit" + }, + { + "id": "196", + "name": "Guard Bandit", + "examine": "Bandit Camp guard.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "20", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "197", + "name": "Barbarian guard" + }, + { + "id": "198", + "name": "Guildmaster", "examine": "Master of the Champions' Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guildmaster", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "198", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "199", + "name": "Gunthor the Brave", "examine": "A mighty warrior!", + "attack_level": "29", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "41", + "magic_animation": "0", "melee_animation": "395", "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gunthor the Brave", - "defence_level": "29", + "range_level": "1", "safespot": null, - "lifepoints": "41", "strength_level": "29", - "id": "199", - "range_level": "1", - "attack_level": "29" + "weakness": "8" + }, + { + "id": "200", + "name": "Lord Daquarius" }, { - "examine": "Guards prisoners for the black knights.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Jailer", - "defence_level": "40", - "safespot": null, - "lifepoints": "47", - "strength_level": "40", "id": "201", + "name": "Jailer", + "examine": "Guards prisoners for the black knights.", "aggressive": "false", + "attack_level": "40", + "attack_speed": "4", "bonuses": "0,0,0,0,0,79,63,47,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Has a fearsome scowl.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", "death_animation": "836", - "name": "Black Heather", - "defence_level": "28", - "lifepoints": "40", - "strength_level": "28", - "id": "202", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "1595", - "range_animation": "1595", - "combat_audio": "729,731,730", - "attack_speed": "5", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "47", + "magic_animation": "0", "magic_level": "1", - "defence_animation": "1596", - "weakness": "7", - "magic_animation": "1595", - "death_animation": "1597", - "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1631", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "1595", - "range_animation": "1595", - "combat_audio": "729,731,730", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "1596", - "weakness": "7", - "magic_animation": "1595", - "death_animation": "1597", - "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1632", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "Has a fearsome posture.", - "melee_animation": "390", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Donny the Lad", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "203", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Nice hair.", - "melee_animation": "390", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Speedy Keith", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "204", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A crazy evil druid.", "melee_animation": "422", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Salarin the Twisted", - "defence_level": "62", - "safespot": null, - "lifepoints": "70", - "strength_level": "58", - "id": "205", - "aggressive": "true", - "bonuses": "45,50,43,55,39,31,19,28,33,22,22,15,13,9,8", "range_level": "1", - "attack_level": "58" + "respawn_delay": "50", + "safespot": null, + "strength_level": "40", + "weakness": "8" }, { + "id": "202", + "name": "Black Heather", + "examine": "Has a fearsome scowl.", + "attack_level": "28", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "strength_level": "28", + "weakness": "8" + }, + { + "id": "203", + "name": "Donny the Lad", + "examine": "Has a fearsome posture.", + "attack_level": "28", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "204", + "name": "Speedy Keith", + "examine": "Nice hair.", + "attack_level": "28", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "28", + "weakness": "7" + }, + { + "id": "205", + "name": "Salarin the Twisted", + "examine": "A crazy evil druid.", + "aggressive": "true", + "attack_level": "58", + "bonuses": "45,50,43,55,39,31,19,28,33,22,22,15,13,9,8", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "62", + "lifepoints": "70", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "58" + }, + { + "id": "206", + "name": "Guard", "examine": "A dwarven guard.", - "slayer_task": "29", + "aggressive": "false", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", + "magic_animation": "0", "melee_animation": "99", "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "102", - "name": "Guard", - "defence_level": "12", + "range_level": "1", "safespot": null, - "lifepoints": "17", "strength_level": "12", - "id": "206", - "aggressive": "false", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" + "weakness": "6" + }, + { + "id": "207", + "name": "Lollk" }, { - "examine": "The head honcho around here.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Lawgof", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "208", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks serene.", + "name": "Captain Lawgof", + "examine": "The head honcho around here.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grail Maiden", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "209", + "name": "Nulodion" + }, + { "id": "210", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A former Knight of the Round Table.", + "name": "Grail Maiden", + "examine": "She looks serene.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Percival", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "211", + "name": "Sir Percival" + }, + { "id": "212", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks unhappy...", + "name": "King Percival", + "examine": "A former Knight of the Round Table.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peasant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "213", + "name": "Merlin" + }, + { "id": "214", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "High Priest of Entrana.", + "name": "Peasant", + "examine": "He looks unhappy...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "High Priest", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "215", + "name": "Peasant" + }, + { "id": "216", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a fisherman.", + "name": "High Priest", + "examine": "High Priest of Entrana.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fisherman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "217", + "name": "Crone" + }, + { + "id": "218", + "name": "Galahad" + }, + { "id": "219", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very well...", + "name": "Fisherman", + "examine": "It's a fisherman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Fisher King", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "220", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "220", + "name": "The Fisher King", + "examine": "He doesn't look very well...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "221", + "name": "Black Knight Titan", "examine": "He looks mean and powerful.", + "aggressive": "true", + "attack_level": "91", + "attack_speed": "7", + "bonuses": "27,27,27,0,0,18,27,18,1000,1000,0,0,0,0,0", + "death_animation": "131", + "defence_animation": "0", + "defence_level": "91", + "lifepoints": "142", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "128", "range_animation": "0", - "attack_speed": "7", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "131", - "name": "Black Knight Titan", - "defence_level": "91", - "safespot": null, - "lifepoints": "142", - "strength_level": "100", - "id": "221", - "aggressive": "true", - "bonuses": "27,27,27,0,0,18,27,18,1000,1000,0,0,0,0,0", "range_level": "1", - "attack_level": "91" + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "10" }, { + "id": "222", + "name": "Monk", "examine": "A holy man.", + "attack_level": "7", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "magic_level": "10", "melee_animation": "422", "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "10", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "836", - "name": "Monk", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "7", - "id": "222", "range_level": "1", - "attack_level": "7" + "safespot": null, + "strength_level": "7", + "weakness": "5" }, { + "id": "223", + "name": "Brother Kojo", "examine": "A peaceful monk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Kojo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "223", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "224", + "name": "Dungeon rat", "examine": "A dirty rat.", - "slayer_task": "67", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", "melee_animation": "4933", "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "8", "safespot": null, - "lifepoints": "11", "strength_level": "8", - "id": "224", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" + "weakness": "9" + }, + { + "id": "225", + "name": "Bonzo" + }, + { + "id": "226", + "name": "Morris" + }, + { + "id": "228", + "name": "Big Dave" + }, + { + "id": "229", + "name": "Joshua" }, { - "examine": "He looks elderly.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grandpa Jack", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "230", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to cut down trees.", + "name": "Grandpa Jack", + "examine": "He looks elderly.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Forester", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "231", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I can fish here.", + "name": "Forester", + "examine": "He likes to cut down trees.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishing spot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "233", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "232", + "name": "Austri" + }, + { + "id": "233", + "name": "Fishing spot", + "examine": "I can fish here.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "234", + "name": "Fishing spot" + }, + { + "id": "235", + "name": "Fishing spot" + }, + { + "id": "236", + "name": "Fishing spot" + }, + { + "id": "237", + "name": "Renegade Knight", "examine": "He isn't very friendly.", + "aggressive": "true", + "attack_level": "15", + "death_animation": "6490", + "defence_animation": "0", + "defence_level": "15", + "lifepoints": "42", + "magic_animation": "0", "melee_animation": "6489", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6490", - "name": "Renegade Knight", - "defence_level": "15", - "safespot": null, - "lifepoints": "42", - "strength_level": "15", - "id": "237", - "aggressive": "true", "range_level": "1", - "attack_level": "15" + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "7" }, { + "id": "238", + "name": "Thrantax the Mighty", "examine": "A terrifying spirit.", + "attack_level": "50", + "death_animation": "5542", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "142", + "magic_animation": "0", "melee_animation": "5540", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5542", - "name": "Thrantax the Mighty", - "defence_level": "50", - "safespot": null, - "lifepoints": "142", - "strength_level": "50", - "id": "238", "range_level": "1", - "attack_level": "50" + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "7" }, { + "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": "246", + "name": "Teapotspout", "examine": "Specialist meat transporter.", + "attack_level": "1", + "death_animation": "2301", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2301", - "name": "Teapotspout", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "246", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "6490", + "id": "247", "name": "Sir Mordred", + "attack_level": "1", + "death_animation": "6490", + "defence_animation": "6488", "defence_level": "1", - "safespot": null, "lifepoints": "50", "melee_animation": "6489", - "strength_level": "1", - "id": "247", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6488" + "safespot": null, + "strength_level": "1" + }, + { + "id": "248", + "name": "Morgan Le Faye" + }, + { + "id": "249", + "name": "Merlin" + }, + { + "id": "250", + "name": "The Lady of the Lake" }, { - "examine": "Legendary King of the Britons.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Arthur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "251", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the door.", + "name": "King Arthur", + "examine": "Legendary King of the Britons.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Khazard Guard", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "252", + "name": "Beggar" + }, + { "id": "253", + "name": "Khazard Guard", + "examine": "It's one of General Khazard's guards.", + "attack_level": "22", + "attack_speed": "6", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "397", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "395", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" }, { - "examine": "He's been guarding the tavern for a bit too long.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Khazard Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "254", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His armour indicates he's a Khazard Guard.", + "name": "Khazard Guard", + "examine": "He's been guarding the tavern for a bit too long.", + "attack_level": "22", + "attack_speed": "6", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "397", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", "melee_animation": "395", "range_animation": "0", - "attack_speed": "6", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard Guard", - "defence_level": "22", "safespot": null, - "lifepoints": "31", "strength_level": "22", + "weakness": "6" + }, + { "id": "255", - "range_level": "1", - "attack_level": "22" - }, - { + "name": "Khazard Guard", "examine": "His armour indicates he's a Khazard Guard.", + "attack_level": "22", + "attack_speed": "6", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "397", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", "melee_animation": "395", "range_animation": "0", - "attack_speed": "6", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard Guard", - "defence_level": "22", "safespot": null, - "lifepoints": "31", "strength_level": "22", + "weakness": "6" + }, + { "id": "256", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A slave fighter. He looks mistreated and weak.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fightslave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "262", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "6581", - "respawn_delay": "60", - "defence_animation": "6578", - "death_animation": "6576", - "name": "Bouncer", - "defence_level": "1", - "safespot": null, - "lifepoints": "116", - "strength_level": "1", - "id": "269", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Khazard's strongest ogre warrior.", - "melee_animation": "2100", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8576", - "name": "Khazard Ogre", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "46", - "id": "270", - "aggressive": "true", - "range_level": "1", - "attack_level": "46" - }, - { - "examine": "A large", - "melee_animation": "6254", - "range_animation": "0", - "combat_audio": "3611,3612,3610", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "6256", - "name": "Khazard Scorpion", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "27", - "id": "271", - "aggressive": "true", - "range_level": "36", - "attack_level": "27" - }, - { - "examine": "A guard who has devoted their life to Armadyl.", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "name": "Guardian of Armadyl", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "274", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A guard who has devoted their life to Armadyl.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "name": "Guardian of Armadyl", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "275", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Intimidating!", - "combat_style": "2", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "836", - "name": "Fire Warrior of Lesarkus", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "277", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Lumbridge Castle's head cook.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "278", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old monk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Omad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "279", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old drunk monk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Cedric", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "280", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An Ardougne Monk.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Monk", - "defence_level": "4", - "safespot": null, - "lifepoints": "5", - "strength_level": "4", - "id": "281", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "A dastardly blanket thief.", - "melee_animation": "386", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "15", - "safespot": null, - "lifepoints": "21", - "strength_level": "15", - "id": "282", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "The head of the treacherous blanket stealing gang.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Head Thief", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "283", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "A dwarf smith. Quite handy with a hammer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Doric", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "284", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She doesn't look too happy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Veronica", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "285", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", + "name": "Khazard Guard", + "examine": "His armour indicates he's a Khazard Guard.", + "attack_level": "22", "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "288", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "397", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "395", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" }, { - "examine": "An official of Ardougne.", + "id": "257", + "name": "Khazard Guard", + "examine": "He takes the slaves to the arena. He rarely takes them back..", + "attack_level": "22", + "attack_speed": "6", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "397", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "395", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "258", + "name": "General Khazard", + "examine": "He looks really nasty. Smells bad too.", + "attack_level": "75", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "7050", + "defence_level": "80", + "lifepoints": "170", + "magic_animation": "0", + "melee_animation": "7049", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "78", + "weakness": "6" + }, + { + "id": "259", + "name": "Khazard barman", + "examine": "A tough looking barman.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "397", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Councillor Halgrive", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "289", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "6" }, { - "examine": "A serious-looking doctor.", + "id": "260", + "name": "Kelvin" + }, + { + "id": "261", + "name": "Joe" + }, + { + "id": "262", + "name": "Fightslave", + "examine": "A slave fighter. He looks mistreated and weak.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Doctor Orbon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "290", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A farmer who's seen happier times.", + "id": "263", + "name": "Hengrad" + }, + { + "id": "264", + "name": "Lady Servil", + "examine": "She looks wealthy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "265", + "name": "Jeremy Servil", + "examine": "A young squire. He looks worse for wear.", + "attack_level": "0", + "clue_level": "0", "death_animation": "0", - "name": "Farmer Brumty", - "defence_level": "1", - "safespot": null, + "defence_level": "0", "lifepoints": "10", - "strength_level": "1", - "id": "291", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "7218", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", "magic_animation": "0", - "death_animation": "7219", - "name": "Guard", - "defence_level": "25", + "range_level": "0", "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "296", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" + "strength_level": "0" }, { - "examine": "He tries to keep order around here.", - "melee_animation": "7218", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7220", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "297", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "7218", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7219", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "298", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "583", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7213", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "299", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "An old motherly witch with a curious smile and a hooked nose.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", + "id": "266", + "name": "Jeremy Servil", + "examine": "A young squire. He looks worse for wear.", + "attack_level": "0", + "clue_level": "0", "death_animation": "0", - "name": "Hetty", - "defence_level": "1", - "safespot": null, + "defence_level": "0", "lifepoints": "10", - "strength_level": "1", - "id": "307", + "magic_animation": "0", + "range_level": "0", + "safespot": null, + "strength_level": "0" + }, + { + "id": "267", + "name": "Justin Servil", + "examine": "Jeremy father, Sir Servil.", + "attack_level": "0", + "clue_level": "0", + "death_animation": "0", + "defence_level": "0", + "lifepoints": "10", + "magic_animation": "0", + "range_level": "0", + "safespot": null, + "strength_level": "0" + }, + { + "id": "268", + "name": "Local", + "examine": "A scruffy looking chap.", + "attack_level": "0", + "clue_level": "0", + "death_animation": "0", + "defence_level": "0", + "lifepoints": "10", + "magic_animation": "0", + "range_level": "0", + "safespot": null, + "strength_level": "0" + }, + { + "id": "269", + "name": "Bouncer", "aggressive": "true", + "attack_level": "120", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "120", + "lifepoints": "116", + "melee_animation": "6581", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "120" }, { - "examine": "The man in charge of the fishing guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master fisher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "308", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He has a colourful personality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Da Vinci", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "336", + "id": "270", + "name": "Khazard Ogre", + "examine": "Khazard's strongest ogre warrior.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's ready for a bet.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chancy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "338", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's ready for a bet.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chancy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "339", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hops", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "340", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hops", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "341", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks rather concerned.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guidor's wife", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "342", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "344", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "345", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "346", - "clue_level": "1", - "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "351", - "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": "352", - "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": "353", - "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": "354", - "clue_level": "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", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "355", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "358", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Man...", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "359", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "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": "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", - "id": "363", - "clue_level": "0", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "King Lathas of East Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Lathas", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "364", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy warrior.", - "name": "Paladin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "365", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Smells very chemically...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chemist", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "367", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "368", - "clue_level": "1", - "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", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Nurse Sarah", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "373", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big, ugly, and smelly.", - "slayer_task": "64", + "attack_level": "46", + "attack_speed": "6", + "bonuses": "22,22,22,0,0,0,0,0,0,0,0,20,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "359", + "magic_level": "1", "melee_animation": "359", "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", + "range_level": "1", "respawn_delay": "25", - "defence_animation": "360", - "weakness": "8", - "magic_animation": "359", - "death_animation": "361", - "name": "Ogre", - "defence_level": "27", "safespot": null, - "lifepoints": "60", - "strength_level": "27", - "id": "374", + "strength_level": "46" + }, + { + "id": "271", + "name": "Khazard Scorpion", + "examine": "A large angry scorpion", "aggressive": "true", + "attack_level": "27", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "6254", + "range_animation": "0", + "range_level": "36", + "safespot": null, + "strength_level": "27", + "weakness": "2" + }, + { + "id": "272", + "name": "Lucien", + "examine": "He walks with a slight limp.", + "aggressive": "", + "attack_level": "12", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "10", + "lifepoints": "17", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "11", + "weakness": "" + }, + { + "id": "273", + "name": "Lucien", + "examine": "He walks with a slight limp.", + "aggressive": "", + "attack_level": "12", + "combat_audio": "", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "", + "weakness": "" + }, + { + "id": "274", + "name": "Guardian of Armadyl", + "examine": "A guard who has devoted their life to Armadyl.", + "attack_level": "30", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "8" + }, + { + "id": "275", + "name": "Guardian of Armadyl", + "examine": "A guard who has devoted their life to Armadyl.", + "attack_level": "30", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "8" + }, + { + "id": "276", + "name": "Winelda" + }, + { + "id": "277", + "name": "Fire Warrior of Lesarkus", + "examine": "Intimidating!", + "attack_level": "55", + "attack_speed": "8", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "78", + "end_gfx": "101", + "lifepoints": "59", + "magic_animation": "711", + "magic_level": "62", + "melee_animation": "0", + "projectile": "127", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "start_height": "96", + "strength_level": "78", + "weakness": "3" + }, + { + "id": "278", + "name": "Cook", + "examine": "Lumbridge Castle's head cook.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "279", + "name": "Brother Omad", + "examine": "An old monk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "280", + "name": "Brother Cedric", + "examine": "An old drunk monk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "281", + "name": "Monk", + "examine": "An Ardougne Monk.", + "attack_level": "4", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "4", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "4", + "weakness": "9" + }, + { + "id": "282", + "name": "Thief", + "examine": "A dastardly blanket thief.", + "attack_level": "15", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "15", + "lifepoints": "21", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "9" + }, + { + "id": "283", + "name": "Head Thief", + "examine": "The head of the treacherous blanket stealing gang.", + "attack_level": "18", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "18", + "weakness": "9" + }, + { + "id": "284", + "name": "Doric", + "examine": "A dwarf smith. Quite handy with a hammer.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "285", + "name": "Veronica", + "examine": "She doesn't look too happy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "286", + "name": "Professor Oddenstein", + "examine": "A mad scientist if I ever saw one!" + }, + { + "id": "287", + "name": "Ernest" + }, + { + "id": "288", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "289", + "name": "Councillor Halgrive", + "examine": "An official of Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "290", + "name": "Doctor Orbon", + "examine": "A serious-looking doctor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "291", + "name": "Farmer Brumty", + "examine": "A farmer who's seen happier times.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "292", + "name": "Guard" + }, + { + "id": "296", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7219", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "7218", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "297", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7220", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "7218", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "298", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7219", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "7218", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "299", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7213", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "583", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "300", + "name": "Sedridor", + "examine": "Chief of research at Wizards Tower." + }, + { + "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": "307", + "name": "Hetty", + "examine": "An old motherly witch with a curious smile and a hooked nose.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "308", + "name": "Master fisher", + "examine": "The man in charge of the fishing guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "323", + "name": "Fishing spot", + "examine": "I can see fish swimming in the water." + }, + { + "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": "335", + "name": "Elena", + "examine": "She doesn't look too happy." + }, + { + "id": "336", + "name": "Da Vinci", + "examine": "He has a colourful personality.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "337", + "name": "Da Vinci" + }, + { + "id": "338", + "name": "Chancy", + "examine": "He's ready for a bet.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "339", + "name": "Chancy", + "examine": "He's ready for a bet.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "340", + "name": "Hops", + "examine": "He's drunk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "341", + "name": "Hops", + "examine": "He's drunk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "342", + "name": "Guidor's wife", + "examine": "She looks rather concerned.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "343", + "name": "Guidor" + }, + { + "id": "344", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "345", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "346", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "347", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "attack_level": "8", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "8", + "lifepoints": "18", + "melee_animation": "428", + "strength_level": "8" + }, + { + "id": "348", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "attack_level": "19", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "19", + "lifepoints": "30", + "melee_animation": "422", + "strength_level": "19" + }, + { + "id": "349", + "name": "Kilron" + }, + { + "id": "350", + "name": "Omart" + }, + { + "id": "351", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", + "clue_level": "", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "13", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "352", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", + "clue_level": "", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "353", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", + "clue_level": "", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "13", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "354", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,1,1,1,0,0,0,0,0,0,0", + "clue_level": "", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "13", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "355", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "magic_level": "", + "melee_animation": "", + "movement_radius": "", + "poison_amount": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "356", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "magic_level": "", + "melee_animation": "", + "movement_radius": "", + "poison_amount": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "357", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "attack_level": "14", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "14", + "lifepoints": "24", + "melee_animation": "428", + "strength_level": "14" + }, + { + "id": "358", + "name": "Priest", + "examine": "A holy man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "359", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "5", + "attack_speed": "4", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "422", + "magic_level": "6", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "2", + "weakness": "9" + }, + { + "id": "360", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "2", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "13", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "361", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "10", + "lifepoints": "13", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "10", + "weakness": "" + }, + { + "id": "362", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "2", + "lifepoints": "10", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "" + }, + { + "id": "363", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "10", + "lifepoints": "23", + "magic_animation": "", + "melee_animation": "422", + "range_animation": "", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "10", + "weakness": "" + }, + { + "id": "364", + "name": "King Lathas", + "examine": "King Lathas of East Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "365", + "name": "Paladin", + "examine": "A holy warrior.", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "366", + "name": "Jerico" + }, + { + "id": "367", + "name": "Chemist", + "examine": "Smells very chemically...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "368", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "369", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "attack_level": "19", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "19", + "lifepoints": "30", + "melee_animation": "422", + "strength_level": "19" + }, + { + "id": "370", + "name": "Mourner (boss)", + "examine": "A mourner, or plague healer.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,3,2,4,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "10", + "lifepoints": "19", + "melee_animation": "428", + "strength_level": "10" + }, + { + "id": "371", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "attack_level": "8", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "836", + "defence_level": "8", + "lifepoints": "20", + "melee_animation": "422", + "strength_level": "8" + }, + { + "id": "372", + "name": "Mourner", + "examine": "A mourner, or plague healer." + }, + { + "id": "373", + "name": "Nurse Sarah", + "examine": "She's quite a looker!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "374", + "name": "Ogre", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "27", + "attack_speed": "6", "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "27", + "lifepoints": "60", + "magic_animation": "359", + "magic_level": "27", + "melee_animation": "359", + "range_animation": "359", "range_level": "27", - "attack_level": "27" + "respawn_delay": "25", + "safespot": null, + "strength_level": "27", + "weakness": "8" }, { - "examine": "A pirate. Smells of brine and brimstone. Also vomit.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "498,500,499", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Redbeard Frank", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "375", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Inspects people's packages.", + "name": "Redbeard Frank", + "examine": "A pirate. Smells of brine and brimstone. Also vomit.", + "attack_level": "1", + "combat_audio": "498,500,499", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customs officer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "376", + "name": "Captain Tobias", + "examine": "An old sea dog." + }, + { + "id": "377", + "name": "Seaman Lorris", + "examine": "A young sailor." + }, + { + "id": "378", + "name": "Seaman Thresnor", + "examine": "A young sailor." + }, + { + "id": "379", + "name": "Luthas", + "examine": "Owner of the banana plantation." + }, + { "id": "380", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf who looks after the mining guild.", + "name": "Customs officer", + "examine": "Inspects people's packages.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "381", + "name": "Dwarf" + }, + { "id": "382", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", + "name": "Dwarf", + "examine": "A dwarf who looks after the mining guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barbarian guard", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "383", + "name": "Stankers" + }, + { "id": "384", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks vicious!", + "name": "Barbarian guard", + "examine": "Not very civilised looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "combat_audio": "3611,3612,3610", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Kharid Scorpion", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "385", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks vicious!", "name": "Kharid Scorpion", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "386", - "range_level": "1", - "attack_level": "1" - }, - { "examine": "Looks vicious!", - "name": "Kharid Scorpion", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", + "attack_level": "1", "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "387", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with a shave...", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "0", - "name": "Seer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "388", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { + "id": "386", + "name": "Kharid Scorpion", + "examine": "Looks vicious!", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "387", + "name": "Kharid Scorpion", + "examine": "Looks vicious!", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "388", + "name": "Seer", + "examine": "Could do with a shave...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "389", + "name": "Thormac", + "examine": "Nice hat." + }, + { + "id": "390", + "name": "Big fish", "examine": "A brightly coloured game bird.", + "attack_level": "3", + "death_animation": "2373", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "1", + "magic_animation": "0", "melee_animation": "2371", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2373", - "name": "Big fish", - "defence_level": "3", - "safespot": null, - "lifepoints": "1", - "strength_level": "3", - "id": "390", "range_level": "1", - "attack_level": "3" + "safespot": null, + "strength_level": "3" }, { + "id": "391", + "name": "River troll", "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "17", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "17", + "lifepoints": "20", + "magic_level": "1", + "melee_animation": "284", + "range_level": "1", + "respawn_delay": "", + "strength_level": "17" + }, + { + "id": "392", + "name": "River troll", + "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "32", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "284", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "285", - "magic_animation": "0", - "death_animation": "287", - "name": "River troll", - "defence_level": "32", - "safespot": null, - "lifepoints": "35", - "strength_level": "32", - "id": "392", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "32" + "safespot": null, + "strength_level": "32" }, { + "id": "393", + "name": "River troll", + "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "48", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "48", + "lifepoints": "48", + "magic_level": "1", + "melee_animation": "284", + "range_level": "1", + "strength_level": "48" + }, + { + "id": "394", + "name": "River troll", + "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "65", + "lifepoints": "85", + "magic_level": "1", + "melee_animation": "284", + "range_level": "1", + "strength_level": "65" + }, + { + "id": "395", + "name": "River troll", + "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "100", + "lifepoints": "120", + "magic_level": "1", + "melee_animation": "284", + "range_level": "1", + "strength_level": "100" + }, + { + "id": "396", + "name": "River troll", + "examine": "Likes fish... hates people.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "130", + "lifepoints": "170", + "magic_level": "1", + "melee_animation": "284", + "range_level": "1", + "strength_level": "130" + }, + { + "id": "397", + "name": "Cow", "examine": "Beefy.", - "slayer_task": "20", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "369,371,370", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", "melee_animation": "5849", "range_animation": "5849", - "combat_audio": "369,371,370", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", + "range_level": "1", + "safespot": null, "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", "strength_level": "1", - "id": "397", - "range_level": "1", - "attack_level": "1" + "weakness": "8" }, { - "examine": "A Legends Guild guard; he protects the entrance to the Legends Guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Legends guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "398", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He deals in exotic types of wood.", + "name": "Legends guard", + "examine": "A Legends Guild guard; he protects the entrance to the Legends Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle forester", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "399", + "name": "Legends guard" + }, + { + "id": "400", + "name": "Radimus Erkle" + }, + { "id": "401", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She deals in exotic types of wood.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Jungle forester", + "examine": "He deals in exotic types of wood.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "402", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Insects buzzing around.", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swarm", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "411", - "aggressive": "true", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "402", + "name": "Jungle forester", + "examine": "She deals in exotic types of wood.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "403", + "name": "Fishing spot" + }, + { + "id": "404", + "name": "Fishing spot" + }, + { + "id": "405", + "name": "Fishing spot" + }, + { + "id": "406", + "name": "Fishing spot" + }, + { + "id": "407", + "name": "Strange Plant", + "examine": "A very strange plant." + }, + { + "id": "408", + "name": "Strange Plant", + "examine": "A very strange plant.", + "death_animation": "355", + "defence_animation": "354", + "defence_level": "300", + "lifepoints": "100", + "magic_level": "200", + "melee_animation": "353" + }, + { + "id": "409", + "name": "Genie" + }, + { + "id": "410", + "name": "Mysterious Old Man" + }, + { + "id": "411", + "name": "Swarm", + "examine": "Insects buzzing around.", + "aggressive": "false", + "attack_level": "1", + "combat_audio": "819,821,820", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "412", + "name": "Bat", "examine": "An annoying flappy thing.", - "slayer_task": "5", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "4918", + "defence_animation": "4916", + "defence_level": "2", + "lifepoints": "8", + "magic_level": "10", "melee_animation": "4915", "range_animation": "4915", - "attack_speed": "5", - "magic_level": "10", + "range_level": "1", "respawn_delay": "30", - "defence_animation": "4916", - "weakness": "4", + "safespot": null, "slayer_exp": "8", - "death_animation": "4918", - "name": "Bat", - "defence_level": "2", - "safespot": null, - "lifepoints": "8", "strength_level": "1", - "id": "412", - "range_level": "1", - "attack_level": "1" + "weakness": "4" }, { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "5", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "20", - "safespot": null, - "lifepoints": "14", - "strength_level": "5", - "id": "419", - "range_level": "5", - "attack_level": "5" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "10", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "40", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "420", - "range_level": "10", - "attack_level": "10" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "15", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "50", - "safespot": null, - "lifepoints": "30", - "strength_level": "15", - "id": "421", - "range_level": "15", - "attack_level": "15" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "45", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "60", - "safespot": null, - "lifepoints": "79", - "strength_level": "45", - "id": "422", - "range_level": "45", - "attack_level": "45" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "35", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", - "id": "423", - "range_level": "45", - "attack_level": "45" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "magic_level": "45", - "defence_animation": "5574", - "magic_animation": "5578", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "90", - "safespot": null, - "lifepoints": "159", - "strength_level": "45", - "id": "424", - "range_level": "45", - "attack_level": "45" - }, - { - "examine": "Dead but not gone.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "7", - "safespot": null, - "lifepoints": "8", - "strength_level": "6", - "id": "425", - "range_level": "1", - "attack_level": "6" - }, - { - "examine": "Dead but not gone.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "30", - "safespot": null, - "lifepoints": "17", - "strength_level": "20", - "id": "426", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Dead but not gone.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "45", - "safespot": null, - "lifepoints": "28", - "strength_level": "43", - "id": "427", - "range_level": "1", - "attack_level": "43" - }, - { - "examine": "Dead but not gone.", - "slayer_task": "73", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "4.2", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "80", - "safespot": null, - "lifepoints": "42", - "strength_level": "74", - "id": "428", - "range_level": "1", - "attack_level": "74" - }, - { - "examine": "Dead but not gone.", - "slayer_task": "73", - "melee_animation": "390", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "4.2", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "130", - "safespot": null, - "lifepoints": "170", - "strength_level": "90", - "id": "429", + "id": "413", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "6", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "Dead but not gone.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Shade", - "defence_level": "105", - "safespot": null, - "lifepoints": "100", - "strength_level": "150", - "id": "430", - "range_level": "1", - "attack_level": "150" - }, - { - "examine": "I hope he's not dead!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fallen Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "435", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Entrance clerk for the Brimhaven Agility Arena.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "498,500,499", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cap'n Izzy No-Beard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "437", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "8", - "safespot": null, + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", "lifepoints": "25", - "strength_level": "8", - "id": "438", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "8" + "projectile": "968", + "range_animation": "5347", + "range_level": "15", + "strength_level": "8" }, { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "17", - "safespot": null, + "id": "414", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "17", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", "lifepoints": "40", - "strength_level": "17", - "id": "439", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "17" + "projectile": "968", + "range_animation": "5347", + "range_level": "30", + "strength_level": "17" }, { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "38", - "safespot": null, + "id": "415", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", "lifepoints": "60", - "strength_level": "38", - "id": "440", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "38" + "projectile": "968", + "range_animation": "5347", + "range_level": "40", + "strength_level": "36" }, { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "64", - "safespot": null, + "id": "416", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "64", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", "lifepoints": "86", - "strength_level": "64", - "id": "441", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "64" + "projectile": "968", + "range_animation": "5347", + "range_level": "60", + "strength_level": "64" }, { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "100", - "safespot": null, + "id": "417", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", "lifepoints": "120", - "strength_level": "100", - "id": "442", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" + "projectile": "968", + "range_animation": "5347", + "range_level": "75", + "strength_level": "100" }, { - "examine": "A very angry nymph.", - "melee_animation": "94", - "combat_audio": "498,500,499", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "95", - "death_animation": "97", - "name": "Tree spirit", + "id": "418", + "name": "Rock Golem", + "examine": "Rock with attitude.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", + "lifepoints": "166", + "projectile": "968", + "range_animation": "5347", + "range_level": "80", + "strength_level": "130" + }, + { + "id": "419", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "20", + "lifepoints": "14", + "magic_animation": "5578", + "magic_level": "5", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "5", + "safespot": null, + "strength_level": "5" + }, + { + "id": "420", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "10", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "40", + "lifepoints": "14", + "magic_animation": "5578", + "magic_level": "10", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "10", + "safespot": null, + "strength_level": "10" + }, + { + "id": "421", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "15", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "50", + "lifepoints": "30", + "magic_animation": "5578", + "magic_level": "15", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "15", + "safespot": null, + "strength_level": "15" + }, + { + "id": "422", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "45", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "60", + "lifepoints": "79", + "magic_animation": "5578", + "magic_level": "45", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "45", + "safespot": null, + "strength_level": "45" + }, + { + "id": "423", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "45", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "5578", + "magic_level": "35", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "45", + "safespot": null, + "strength_level": "45" + }, + { + "id": "424", + "name": "Zombie", + "examine": "Dead man walking.", + "attack_level": "45", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "90", + "lifepoints": "159", + "magic_animation": "5578", + "magic_level": "45", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "45", + "safespot": null, + "strength_level": "45" + }, + { + "id": "425", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "6", + "combat_audio": "511,439,438", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "7", + "lifepoints": "8", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "6", + "weakness": "9" + }, + { + "id": "426", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "20", + "combat_audio": "511,439,438", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "17", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "20", + "weakness": "9" + }, + { + "id": "427", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "43", + "combat_audio": "511,439,438", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "43", + "weakness": "9" + }, + { + "id": "428", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "74", + "combat_audio": "511,439,438", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "80", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "4.2", + "strength_level": "74", + "weakness": "9" + }, + { + "id": "429", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "130", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "511,439,438", + "death_animation": "836", + "defence_animation": "0", "defence_level": "130", - "safespot": null, "lifepoints": "170", - "strength_level": "130", - "id": "443", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "4.2", + "strength_level": "90", + "weakness": "9" + }, + { + "id": "430", + "name": "Shade", + "examine": "Dead but not gone.", + "attack_level": "150", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "105", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "150", + "weakness": "9" + }, + { + "id": "431", + "name": "Cyrisus" + }, + { + "id": "433", + "name": "Cyrisus" + }, + { + "id": "434", + "name": "Cyrisus" + }, + { + "id": "435", + "name": "Fallen Man", + "examine": "I hope he's not dead!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "436", + "name": "Fallen Man" + }, + { + "id": "437", + "name": "Cap'n Izzy No-Beard", + "examine": "Entrance clerk for the Brimhaven Agility Arena.", + "attack_level": "1", + "combat_audio": "498,500,499", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "438", + "name": "Tree spirit", + "examine": "A very angry nymph.", "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "8", + "lifepoints": "25", + "magic_level": "1", + "melee_animation": "94", "range_level": "1", - "attack_level": "130" + "safespot": null, + "strength_level": "8" }, { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", + "id": "439", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "17", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "17", + "lifepoints": "40", + "magic_level": "1", + "melee_animation": "94", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "strength_level": "17" + }, + { + "id": "440", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "38", + "lifepoints": "60", + "magic_level": "1", + "melee_animation": "94", + "range_level": "1", + "safespot": null, + "strength_level": "38" + }, + { + "id": "441", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "64", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "64", + "lifepoints": "86", + "magic_level": "1", + "melee_animation": "94", + "range_level": "1", + "safespot": null, + "strength_level": "64" + }, + { + "id": "442", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "100", + "lifepoints": "120", + "magic_level": "1", + "melee_animation": "94", + "range_level": "1", + "safespot": null, + "strength_level": "100" + }, + { + "id": "443", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "97", + "defence_animation": "95", + "defence_level": "130", + "lifepoints": "170", + "magic_level": "1", + "melee_animation": "94", + "range_level": "1", + "safespot": null, + "strength_level": "130" + }, + { "id": "444", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "445", - "clue_level": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" }, { + "id": "445", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "446", + "name": "Giant rat", "examine": "Overgrown vermin.", - "slayer_task": "67", + "aggressive": "true", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "2", + "lifepoints": "5", + "magic_animation": "0", "melee_animation": "4933", "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "2", "safespot": null, - "lifepoints": "5", "strength_level": "5", - "id": "446", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" + "weakness": "8" }, { - "examine": "I wonder who he's guarding?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Jail guard", - "defence_level": "21", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", "id": "447", + "name": "Jail guard", + "examine": "I wonder who he's guarding?", "aggressive": "true", + "attack_level": "19", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "21", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", "range_level": "1", - "attack_level": "19" + "respawn_delay": "60", + "safespot": null, + "strength_level": "23" }, { - "examine": "I wonder who he's guarding?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Jail guard", - "defence_level": "21", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", "id": "448", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "I wonder who he's guarding?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", "name": "Jail guard", - "defence_level": "21", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "449", + "examine": "I wonder who he's guarding?", "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A large well built farmer.", + "attack_level": "19", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "21", + "lifepoints": "32", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Seth Groats", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "452", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "23" }, { + "id": "449", + "name": "Jail guard", + "examine": "I wonder who he's guarding?", + "aggressive": "true", + "attack_level": "19", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "21", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23" + }, + { + "id": "450", + "name": "Gull", + "examine": "Flappy bird.", + "water_npc": "true" + }, + { + "id": "451", + "name": "Gull", + "examine": "Flappy bird.", + "water_npc": "true" + }, + { + "id": "452", + "name": "Seth Groats", + "examine": "A large well built farmer.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "453", + "name": "Suit of armour", "examine": "Aargh! It's alive!", + "attack_level": "16", + "attack_speed": "5", + "bonuses": "8,8,8,0,0,46,50,46,-12,45,0,10,0,0,0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "29", + "magic_animation": "0", "melee_animation": "390", "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Suit of armour", - "defence_level": "9", + "range_level": "1", "safespot": null, - "lifepoints": "29", "strength_level": "14", - "id": "453", - "bonuses": "8,8,8,0,0,46,50,46,-12,45,0,10,0,0,0", - "range_level": "1", - "attack_level": "16" + "weakness": "6" + }, + { + "id": "454", + "name": "Sanfew", + "examine": "An old druid." + }, + { + "id": "455", + "name": "Kaqemeex", + "examine": "A wise druid." }, { - "examine": "He seems to be very devout.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Aereck", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "456", + "name": "Father Aereck", + "examine": "He seems to be very devout.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Eek! A ghost!", - "name": "Restless ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", "id": "457", + "name": "Restless ghost", + "examine": "Eek! A ghost!", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Very holy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Urhney", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "458", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Wizard of the Magic Guild.", + "name": "Father Urhney", + "examine": "Very holy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Frumscone", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "460", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A Supplier of Magical items.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Magic Store owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "461", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Head of the Magic Guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Distentor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "462", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a gnome. He looks important.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Bolren", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "469", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tree gnome.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Commander Montai", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "470", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's one of General Khazard's warriors.", - "melee_animation": "401", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard trooper", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "475", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "An open eye of World-gorger.", - "magic_level": "5", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "name": "Khazard trooper", - "defence_level": "5", - "safespot": null, - "lifepoints": "11", - "strength_level": "1", - "id": "476", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks real nasty", - "melee_animation": "401", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard warlord", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "477", + "id": "459", + "name": "Skeleton", + "examine": "It rattles when it moves.", "aggressive": "true", + "attack_level": "3", + "combat_audio": "774,775,777", + "combat_style": "2", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "5", + "lifepoints": "14", + "magic_animation": "5485", + "magic_level": "5", + "melee_animation": "5485", + "range_animation": "5485", "range_level": "1", - "attack_level": "38" + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "3" }, { + "id": "460", + "name": "Wizard Frumscone", + "examine": "A Wizard of the Magic Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "461", + "name": "Magic Store owner", + "examine": "A Supplier of Magical items.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "462", + "name": "Wizard Distentor", + "examine": "Head of the Magic Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "469", + "name": "King Bolren", + "examine": "It's a gnome. He looks important.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "470", + "name": "Commander Montai", + "examine": "It's a tree gnome.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "471", + "name": "Bolkoy" + }, + { + "id": "472", + "name": "Remsai" + }, + { + "id": "473", + "name": "Khazard trooper" + }, + { + "id": "475", + "name": "Khazard trooper", + "examine": "It's one of General Khazard's warriors.", + "attack_level": "32", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "6" + }, + { + "id": "476", + "name": "Khazard trooper", + "examine": "An open eye of World-gorger.", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "11", + "magic_animation": "0", + "magic_level": "5", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "3" + }, + { + "id": "477", + "name": "Khazard warlord", + "examine": "He looks real nasty", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "38", + "weakness": "6" + }, + { + "id": "478", + "name": "Khazard commander", "examine": "It's one of General Khazard's commanders.", + "attack_level": "35", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", "melee_animation": "428", "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard commander", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "478", "range_level": "1", - "attack_level": "35" + "safespot": null, + "strength_level": "35", + "weakness": "6" }, { + "id": "479", + "name": "Gnome troop", "examine": "It's a tree gnome trooper.", + "attack_level": "24", + "death_animation": "196", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", "melee_animation": "190", "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome troop", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "24", - "id": "479", "range_level": "32", - "attack_level": "24" + "safespot": null, + "strength_level": "24", + "weakness": "1" + }, + { + "id": "480", + "name": "Gnome troop" }, { - "examine": "It's a gnome who specialises in covert operations.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tracker gnome 1", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "481", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Tracker gnome 1", "examine": "It's a gnome who specialises in covert operations.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tracker gnome 2", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "482", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Tracker gnome 2", "examine": "It's a gnome who specialises in covert operations.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tracker gnome 3", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a young tree gnome.", + "name": "Tracker gnome 3", + "examine": "It's a gnome who specialises in covert operations.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Local Gnome", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "484", + "name": "Local Gnome" + }, + { "id": "485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he wants to be more than just the muscle.", + "name": "Local Gnome", + "examine": "It's a young tree gnome.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Giant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "487", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "486", + "name": "Kalron" + }, + { + "id": "487", + "name": "Giant", + "examine": "I guess he wants to be more than just the muscle.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "488", + "name": "Observatory professor", + "examine": "A man, learned in the ways of the stars.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "489", + "name": "Goblin guard", "examine": "He doesn't look like he'd trust his own mother.", - "slayer_task": "38", + "attack_level": "32", + "combat_audio": "469,472,471", + "death_animation": "6190", + "defence_animation": "0", + "defence_level": "37", + "lifepoints": "43", + "magic_animation": "0", "melee_animation": "6199", "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6190", - "name": "Goblin guard", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "489", "range_level": "1", - "attack_level": "20" + "safespot": null, + "strength_level": "37", + "weakness": "7" }, { - "examine": "If the mummy is at school", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mummy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "490", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5540", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5541", - "slayer_exp": "25", - "death_animation": "5542", - "name": "Ghost", + "name": "Mummy", + "examine": "If the mummy is at school", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "491", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The essence of evil.", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spirit of Scorpius", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "492", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "491", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "5540", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "492", + "name": "Spirit of Scorpius", + "examine": "The essence of evil.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "493", + "name": "Grave scorpion", "examine": "A vicious", - "slayer_task": "71", + "aggressive": "true", + "attack_level": "5", + "combat_audio": "3611,3612,3610", + "death_animation": "6260", + "defence_animation": "0", + "defence_level": "7", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "6261", "range_animation": "0", - "combat_audio": "3611,3612,3610", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "6260", - "name": "Grave scorpion", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "5", - "id": "493", - "aggressive": "true", "range_level": "7", - "attack_level": "5" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "494", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Good with money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "495", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Manages money momentarily.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "498", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "499", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Undead one", - "defence_level": "32", "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "502", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" + "strength_level": "5", + "weakness": "2" }, { + "id": "494", + "name": "Banker", + "examine": "He can look after my money.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "495", + "name": "Banker", + "examine": "Good with money.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "496", + "name": "Banker", + "examine": "He can look after my money." + }, + { + "id": "497", + "name": "Banker", + "examine": "She can look after my money." + }, + { + "id": "498", + "name": "Banker", + "examine": "Manages money momentarily.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "499", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "500", + "name": "Mosol Rei", + "examine": "A warrior from Shilo Village." + }, + { + "id": "501", + "name": "Spirit of Zadimus", + "examine": "An unquiet soul." + }, + { + "id": "502", + "name": "Undead one", "examine": "A minion of Rashiliyia.", - "slayer_task": "75", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "5", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", "melee_animation": "5485", "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Undead one", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "503", "range_level": "1", - "attack_level": "33" + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "8" }, { + "id": "503", + "name": "Undead one", + "examine": "A minion of Rashiliyia.", + "attack_level": "33", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "33", + "weakness": "8" + }, + { + "id": "504", + "name": "Undead one", "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", "melee_animation": "5568", "range_animation": "0", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5569", - "name": "Undead one", - "defence_level": "32", "safespot": null, - "lifepoints": "45", "strength_level": "32", - "id": "504", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" + "weakness": "8" }, { - "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", - "melee_animation": "5571", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5575", - "name": "Undead one", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", "id": "505", + "name": "Undead one", + "examine": "The animated dead; one of Rashiliyia's minions.", "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "melee_animation": "5571", - "respawn_delay": "60", - "defence_animation": "5574", + "attack_level": "33", + "attack_speed": "5", "death_animation": "5575", - "name": "Nazastarool", - "defence_level": "1", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "5571", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "100", - "strength_level": "1", + "strength_level": "33", + "weakness": "8" + }, + { + "id": "506", + "name": "Rashiliyia ", + "examine": "The animated spirit of Rashiliyia the Zombie Queen." + }, + { "id": "507", + "name": "Nazastarool", "aggressive": "true", + "attack_level": "1", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "5571", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5507", - "respawn_delay": "60", - "defence_animation": "5508", - "death_animation": "5509", - "name": "Nazastarool", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", "id": "508", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5532", - "respawn_delay": "60", - "defence_animation": "5533", - "death_animation": "5534", "name": "Nazastarool", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5509", + "defence_animation": "5508", "defence_level": "1", + "lifepoints": "75", + "melee_animation": "5507", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "110", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "509", + "name": "Nazastarool", "aggressive": "true", + "attack_level": "1", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "5532", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "510", + "name": "Hajedy", + "examine": "A cart driver; it looks like he's quite experienced." + }, + { + "id": "511", + "name": "Vigroy", + "examine": "A cart driver; it looks like he's quite experienced." + }, + { + "id": "512", + "name": "Kaleb Paramaya", + "examine": "This is Kaleb Paramaya, a warm and friendly inn owner." + }, + { + "id": "513", + "name": "Yohnus", + "examine": "This is Yohnus - he runs the local blacksmiths." + }, + { + "id": "514", + "name": "Seravel", + "examine": "This is Seravel. He sells tickets for the 'Lady of the Waves.'" + }, + { + "id": "515", + "name": "Yanni Salika", + "examine": "Yanni Salika; he buys and sells antiques." + }, + { + "id": "516", + "name": "Obli", + "examine": "An intelligent-looking shop owner." + }, + { + "id": "517", + "name": "Fernahei", + "examine": "This is Fernahei; he owns the local fishing tackle shop." }, { - "examine": "He's the Captain of the 'Lady of the Waves'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Shanks", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "518", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on axes.", + "name": "Captain Shanks", + "examine": "He's the Captain of the 'Lady of the Waves'.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "519", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sells stuff.", + "name": "Bob", + "examine": "An expert on axes.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "520", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps sell stuff.", + "name": "Shopkeeper", + "examine": "Sells stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shop assistant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes people spending money.", + "name": "Shop assistant", + "examine": "Helps sell stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "522", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes helping sell stuff.", + "name": "Shopkeeper", + "examine": "Likes people spending money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shop assistant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A product of a consumerist society.", + "name": "Shop assistant", + "examine": "Likes helping sell stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes you more, the more you spend.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shop assistant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "525", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Shopkeeper", "examine": "A product of a consumerist society.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "526", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "525", + "name": "Shop assistant", "examine": "Likes you more, the more you spend.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shop assistant", - "defence_level": "1", + "range_level": "1", "safespot": null, + "strength_level": "1" + }, + { + "id": "526", + "name": "Shopkeeper", + "examine": "A product of a consumerist society.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an interesting assortment of items for sale.", + "name": "Shop assistant", + "examine": "Likes you more, the more you spend.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "528", + "name": "Shopkeeper", + "examine": "Sells stuff." + }, + { + "id": "529", + "name": "Shop assistant", + "examine": "Loves you more, the more you spend." + }, + { "id": "530", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works on commission.", + "name": "Shopkeeper", + "examine": "Has an interesting assortment of items for sale.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shop assistant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "531", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Like a grave keeper", - "range_animation": "0", - "magic_level": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Shopkeeper", - "defence_level": "60", - "safespot": null, - "lifepoints": "285", - "strength_level": "60", - "id": "532", - "aggressive": "true", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Sells stuff.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "534", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sells stuff.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy shop assistant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "535", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very snappily dressed.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "539", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Makes his money selling rocks.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "540", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the finest in armoured legware.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Louie Legs", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "542", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Ironically, makes a living from swords.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "551", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps the shopkeeper sell swords.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Shop assistant", + "examine": "Works on commission.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "532", + "name": "Shopkeeper Kofi", + "examine": "He clearly takes pride in his appearance.", + "aggressive": "true", + "attack_level": "60", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "285", + "magic_animation": "0", + "magic_level": "60", + "range_animation": "0", + "range_level": "60", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "533", + "name": "Shop assistant", + "examine": "Needs a haircut.", + "aggressive": "true", + "attack_level": "60", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "285", + "magic_animation": "0", + "magic_level": "60", + "range_animation": "0", + "range_level": "60", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "534", + "name": "Fairy shopkeeper", + "examine": "Sells stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "535", + "name": "Fairy shop assistant", + "examine": "Sells stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "536", + "name": "Valaine" + }, + { + "id": "537", + "name": "Scavvo" + }, + { + "id": "538", + "name": "Peksa", + "examine": "Always up-to-date with the latest helmet fashions." + }, + { + "id": "539", + "name": "Silk trader", + "examine": "Very snappily dressed.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "540", + "name": "Gem trader", + "examine": "Makes his money selling rocks.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "541", + "name": "Zeke", + "examine": "Sells superior scimitars." + }, + { + "id": "542", + "name": "Louie Legs", + "examine": "For the finest in armoured legware.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "543", + "name": "Karim", + "examine": "Kebabs are full of meaty goodness!" + }, + { + "id": "544", + "name": "Ranael", + "examine": "She's an expert on armoured skirts." + }, + { + "id": "545", + "name": "Dommik", + "examine": "If Crafting's your thing, he's your man." + }, + { + "id": "546", + "name": "Zaff", + "examine": "Sells superior staffs." + }, + { + "id": "547", + "name": "Baraek", + "examine": "Animal skins are a specialty." + }, + { + "id": "548", + "name": "Thessalia", + "examine": "Interesting assortment of clothes on offer..." + }, + { + "id": "549", + "name": "Horvik", + "examine": "The man with the armour." + }, + { + "id": "550", + "name": "Lowe", + "examine": "Sells arrows." + }, + { + "id": "551", + "name": "Shopkeeper", + "examine": "Ironically, makes a living from swords.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "552", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the interesting clothing items you just can't find elsewhere.", + "name": "Shop assistant", + "examine": "Helps the shopkeeper sell swords.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy-dress shop owner", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "553", + "name": "Aubury" + }, + { "id": "554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a fine moustache.", + "name": "Fancy-dress shop owner", + "examine": "For the interesting clothing items you just can't find elsewhere.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "555", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could stand to lose a few pounds.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Shopkeeper", + "examine": "Has a fine moustache.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "556", + "name": "Grum", + "examine": "Loves his gold!" + }, + { + "id": "557", + "name": "Wydin", + "examine": "Likes his food to be kept fresh." + }, + { + "id": "558", + "name": "Gerrant", + "examine": "A Fishing expert." + }, + { + "id": "559", + "name": "Brian", + "examine": "An expert on axes." + }, + { + "id": "560", + "name": "Jiminua", + "examine": "Goods for sale and trade." + }, + { "id": "561", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an odd smell about him.", + "name": "Shopkeeper", + "examine": "Could stand to lose a few pounds.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Candle-maker", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks strange and mysterious.", + "name": "Candle-maker", + "examine": "Has an odd smell about him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "563", + "name": "Arhein" + }, + { + "id": "564", + "name": "Jukat", + "examine": "A being from a mysterious realm." + }, + { + "id": "565", + "name": "Lunderwin" + }, + { + "id": "566", + "name": "Irksol", + "examine": "Is he invisible, or just floating clothing?" + }, + { "id": "567", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks fairly well fed.", + "name": "Fairy", + "examine": "Looks strange and mysterious.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silver merchant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "568", + "name": "Zambo", + "examine": "Appears slightly drunk." + }, + { "id": "569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems very well-off.", + "name": "Silver merchant", + "examine": "Looks fairly well fed.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem merchant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "570", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "So where are the butcher and the candlestick-maker?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "571", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a very exotic aroma about him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spice seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "572", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Knows how to keep warm in the winter.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fur trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "573", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Gem merchant", "examine": "Seems very well-off.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk merchant", - "defence_level": "1", + "range_level": "1", "safespot": null, + "strength_level": "1" + }, + { + "id": "571", + "name": "Baker", + "examine": "So where are the butcher and the candlestick-maker?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "572", + "name": "Spice seller", + "examine": "Has a very exotic aroma about him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "573", + "name": "Fur trader", + "examine": "Knows how to keep warm in the winter.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He runs a Mining store.", + "name": "Silk merchant", + "examine": "Seems very well-off.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drogo dwarf", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "575", + "name": "Hickton" + }, + { + "id": "576", + "name": "Harry" + }, + { + "id": "577", + "name": "Cassie", + "examine": "Nice eyes." + }, + { + "id": "578", + "name": "Frincos" + }, + { "id": "579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if he wants to buy my junk?", + "name": "Drogo dwarf", + "examine": "He runs a Mining store.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "580", + "name": "Flynn", + "examine": "The mace salesman." + }, + { + "id": "581", + "name": "Wayne", + "examine": "An armourer." + }, + { "id": "582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to sell tea.", + "name": "Dwarf", + "examine": "I wonder if he wants to buy my junk?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tea seller", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "583", + "name": "Betty", + "examine": "She seems like a nice sort of person." + }, + { + "id": "584", + "name": "Herquin" + }, + { + "id": "585", + "name": "Rommik" + }, + { + "id": "586", + "name": "Gaius", + "examine": "Ironically, makes a living from swords." + }, + { + "id": "587", + "name": "Jatix", + "examine": "He runs the Herblore Shop." + }, + { + "id": "588", + "name": "Davon", + "examine": "An amulet trader." + }, + { + "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": "595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pizza expert; in both making and eating.", + "name": "Tea seller", + "examine": "He seems to sell tea.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fat Tony", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "596", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Anyone fancy a trim?", + "name": "Fat Tony", + "examine": "A pizza expert; in both making and eating.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hairdresser", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "597", + "name": "Noterazzo" + }, + { "id": "598", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Master of the mystical make-over.", + "name": "Hairdresser", + "examine": "Anyone fancy a trim?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Make-over Mage", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "599", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Indentured servant of a Knight.", + "name": "Make-over Mage", + "examine": "Master of the mystical make-over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "606", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "600", + "name": "Hudo" + }, + { + "id": "601", + "name": "Rometti" + }, + { + "id": "602", + "name": "Gulluck" + }, + { + "id": "603", + "name": "Heckel Funch" + }, + { + "id": "604", + "name": "Thurgo", + "examine": "Dwarvish." + }, + { + "id": "605", + "name": "Sir Vyvin", + "examine": "An elderly White Knight." + }, + { + "id": "606", + "name": "Squire", + "examine": "Indentured servant of a Knight.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "607", + "name": "Gunnjorn" + }, + { + "id": "608", + "name": "Sir Amik Varze" + }, + { + "id": "609", + "name": "Fortress Guard", "examine": "A generic evil henchman.", + "attack_level": "26", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", "melee_animation": "428", "range_animation": "0", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Fortress Guard", - "defence_level": "26", "safespot": null, - "lifepoints": "37", "strength_level": "26", - "id": "609", - "range_level": "1", - "attack_level": "26" + "weakness": "9" }, { - "examine": "One of the Black Knights.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Black Knight Captain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "610", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Black Knights' resident witch.", + "name": "Black Knight Captain", + "examine": "One of the Black Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witch", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "611", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This person is working on the site.", + "name": "Witch", + "examine": "The Black Knights' resident witch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Digsite workman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "612", + "name": "Greldo" + }, + { "id": "613", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Digsite workman", "examine": "This person is working on the site.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Doug Deeping", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "614", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A student busy studying the digsite.", + "name": "Doug Deeping", + "examine": "This person is working on the site.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "615", "name": "Student", + "examine": "A student busy studying the digsite.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "616", + "name": "Student", + "examine": "A student busy studying the digsite.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "617", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Upon examining the examiner you examine it is indeed an examiner!", + "name": "Student", + "examine": "A student busy studying the digsite.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Examiner", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "618", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on archaeology.", + "name": "Examiner", + "examine": "Upon examining the examiner you examine it is indeed an examiner!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeological expert", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "619", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A specialist in panning for gold.", + "name": "Archaeological expert", + "examine": "An expert on archaeology.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Panning guide", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "620", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", + "name": "Panning guide", + "examine": "A specialist in panning for gold.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome baller", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "621", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Gnome baller", "examine": "A professional gnome baller.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome winger", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "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": "633", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the game fair.", + "name": "Gnome winger", + "examine": "A professional gnome baller.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome ball referee", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "634", + "name": "Gnome winger" + }, + { "id": "635", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dealer in potions.", + "name": "Gnome ball referee", + "examine": "Keeps the game fair.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apothecary", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "636", + "name": "Cheerleader" + }, + { + "id": "637", + "name": "Juliet", + "examine": "A tearful damsel, maybe I can help her?" + }, + { "id": "638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A religious man... And occasional drunk.", + "name": "Apothecary", + "examine": "A dealer in potions.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Lawrence", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "639", + "name": "Romeo", + "examine": "Rather dense and soppy looking." + }, + { "id": "640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks down on his luck.", + "name": "Father Lawrence", + "examine": "A religious man... And occasional drunk.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Charlie the Tramp", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "641", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "641", + "name": "Charlie the Tramp", + "examine": "Looks down on his luck.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "642", + "name": "Katrine" + }, + { + "id": "643", + "name": "Weaponsmaster", "examine": "Looks kind of obsessive...", + "attack_level": "15", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "15", + "lifepoints": "42", + "magic_animation": "0", "melee_animation": "390", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Weaponsmaster", - "defence_level": "15", - "safespot": null, - "lifepoints": "42", - "strength_level": "15", - "id": "643", "range_level": "1", - "attack_level": "15" + "safespot": null, + "strength_level": "15", + "weakness": "9" }, { + "id": "644", + "name": "Straven" + }, + { + "id": "645", + "name": "Jonny the beard", "examine": "Looks kind of shifty...", + "attack_level": "1", + "combat_audio": "498,500,499", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "0", "melee_animation": "422", "range_animation": "0", - "combat_audio": "498,500,499", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Jonny the beard", - "defence_level": "1", "safespot": null, - "lifepoints": "2", "strength_level": "1", - "id": "645", - "range_level": "1", - "attack_level": "1" + "weakness": "9" }, { - "examine": "Curator of the museum.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Curator Haig Halen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "646", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Varrock's resident monarch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Roald", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "648", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks quite experienced.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "649", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks big and dumb.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "650", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks holy.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "651", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks kind of puny...", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "652", - "range_level": "1", + "name": "Curator Haig Halen", + "examine": "Curator of the museum.", "attack_level": "1", - "prj_height": "3" + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" }, { + "id": "647", + "name": "King Roald" + }, + { + "id": "648", + "name": "King Roald", + "examine": "Varrock's resident monarch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "649", + "name": "Archer", + "examine": "She looks quite experienced.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "650", + "name": "Warrior", + "examine": "He looks big and dumb.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "651", + "name": "Monk", + "examine": "He looks holy.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "652", + "name": "Wizard", + "examine": "He looks kind of puny...", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "prj_height": "3", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "653", + "name": "Fairy Queen" + }, + { + "id": "654", + "name": "Shamus" + }, + { + "id": "655", + "name": "Tree spirit", "examine": "Guardian of the dramen tree.", + "attack_level": "45", + "attack_speed": "5", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", "melee_animation": "5532", "range_animation": "0", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5534", - "name": "Tree spirit", - "defence_level": "45", "safespot": null, - "lifepoints": "64", "strength_level": "45", - "id": "655", - "range_level": "1", - "attack_level": "45" + "weakness": "9" }, { - "examine": "Unsurprisingly monk like.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "656", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holy looking.", + "name": "Cave monk", + "examine": "Unsurprisingly monk like.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk of Entrana", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "657", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holy looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Monk of Entrana", + "examine": "Holy looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to paaaarty!", + "name": "Monk of Entrana", + "examine": "Holy looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Party Pete", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "659", + "name": "Party Pete", + "examine": "He likes to paaaarty!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", "id": "660", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", + "name": "Knight", + "attack_level": "1", "combat_audio": "511,513,512", - "strength_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "661", + "name": "Megan" + }, + { + "id": "662", + "name": "Lucy" + }, + { "id": "663", + "name": "Man", + "attack_level": "1", "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "664", + "name": "Boot" }, { - "examine": "A short and angry dwarf.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "665", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Chronozon the blood demon.", - "melee_animation": "64", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "death_animation": "67", - "name": "Chronozon", - "defence_level": "173", - "safespot": null, - "lifepoints": "60", - "strength_level": "172", - "id": "667", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "173" - }, - { - "examine": "An important looking gnome.", + "name": "Boot", + "examine": "A short and angry dwarf.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Narnode Shareen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "670", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "666", + "name": "Caleb" + }, + { + "id": "667", + "name": "Chronozon", + "examine": "Chronozon the blood demon.", + "aggressive": "true", + "attack_level": "173", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "173", + "lifepoints": "60", + "magic_level": "1", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "172" + }, + { + "id": "668", + "name": "Johnathon" + }, + { + "id": "669", + "name": "Hazelmere" + }, + { + "id": "670", + "name": "King Narnode Shareen", + "examine": "An important looking gnome.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "671", + "name": "Glough" + }, + { + "id": "672", + "name": "Anita" + }, + { + "id": "673", + "name": "Charlie" + }, + { + "id": "674", + "name": "Foreman", "examine": "The boss!", + "attack_level": "20", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", "melee_animation": "422", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Foreman", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "674", "range_level": "1", - "attack_level": "20" + "safespot": null, + "strength_level": "20", + "weakness": "9" }, { + "id": "675", + "name": "Shipyard worker", + "examine": "Builds ships for a living." + }, + { + "id": "676", + "name": "Femi" + }, + { + "id": "677", + "name": "Black demon", "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", "melee_animation": "64", "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", + "range_level": "1", "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", + "safespot": null, "slayer_exp": "157", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", "strength_level": "148", - "id": "677", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "145" + "weakness": "4" }, { - "examine": "Keeps order in the ranging guild.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "678", + "name": "Guard", + "examine": "Keeps order in the ranging guild.", + "attack_level": "1", "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The keeper of the gates to the ranging guild.", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranging Guild Doorman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "679", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert leatherworker.", + "name": "Ranging Guild Doorman", + "examine": "The keeper of the gates to the ranging guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leatherworker", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "680", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Mysterious swamp lights...", + "name": "Leatherworker", + "examine": "An expert leatherworker.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Held vampyre juvinate", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "681", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Rangers armour.", + "name": "Held vampyre juvinate", + "examine": "Mysterious swamp lights...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Armour salesman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Archery equipment.", + "name": "Armour salesman", + "examine": "Supplier of Rangers armour.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bow and Arrow salesman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tower keeper and competition judge.", + "name": "Bow and Arrow salesman", + "examine": "Supplier of Archery equipment.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tower Advisor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "684", + "name": "Tower Advisor", + "examine": "Tower keeper and competition judge.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "685", + "name": "Tower Advisor" + }, + { + "id": "686", + "name": "Tower Advisor" + }, + { + "id": "687", + "name": "Tower Advisor" }, { - "examine": "Defender of the north tower.", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower Archer", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "7", "id": "688", + "name": "Tower Archer", + "examine": "Defender of the north tower.", "aggressive": "true", + "attack_level": "7", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", "range_level": "10", - "attack_level": "7" + "respawn_delay": "60", + "safespot": null, + "strength_level": "7", + "weakness": "0" }, { - "examine": "Defender of the east tower.", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower Archer", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "15", "id": "689", + "name": "Tower Archer", + "examine": "Defender of the east tower.", "aggressive": "true", + "attack_level": "15", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", "range_level": "20", - "attack_level": "15" + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "0" }, { - "examine": "Defender of the south tower.", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower Archer", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "22", "id": "690", + "name": "Tower Archer", + "examine": "Defender of the south tower.", "aggressive": "true", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "Defender of the west tower.", + "attack_level": "22", "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", "melee_animation": "426", "range_animation": "426", + "range_level": "30", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower Archer", - "defence_level": "40", "safespot": null, - "lifepoints": "57", - "strength_level": "30", + "strength_level": "22", + "weakness": "0" + }, + { "id": "691", + "name": "Tower Archer", + "examine": "Defender of the west tower.", "aggressive": "true", + "attack_level": "30", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", "range_level": "40", - "attack_level": "30" + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "0" }, { - "examine": "Supplier of authentic throwing weapons.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tribal Weapon Salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "692", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sells equipment in exchange for archery tickets.", + "name": "Tribal Weapon Salesman", + "examine": "Supplier of authentic throwing weapons.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket Merchant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "693", + "name": "Competition Judge" + }, + { "id": "694", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Harlan, ready to teach swordplay.", + "name": "Ticket Merchant", + "examine": "Sells equipment in exchange for archery tickets.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Melee tutor", - "defence_level": "1", + "range_level": "1", "safespot": null, + "strength_level": "1" + }, + { + "id": "695", + "name": "Bailey" + }, + { + "id": "696", + "name": "Caroline", + "examine": "She looks very worried about something.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", "lifepoints": "10", - "strength_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "697", + "name": "Holgart" + }, + { + "id": "698", + "name": "Holgart", + "examine": "A very good sailor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old wizard.", + "name": "Melee tutor", + "examine": "Harlan, ready to teach swordplay.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Mizgog", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "706", + "name": "Wizard Mizgog", + "examine": "An old wizard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "707", + "name": "Wizard Grayzag", + "examine": "Master of imps." }, { - "examine": "A cheeky little imp.", - "melee_animation": "169", - "range_animation": "169", - "combat_audio": "534,536,535", - "attack_speed": "5", - "defence_animation": "170", - "weakness": "7", - "magic_animation": "169", - "death_animation": "172", - "name": "Imp", - "defence_level": "2", - "safespot": null, - "movement_radius": "25", - "lifepoints": "8", - "strength_level": "2", "id": "708", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "A cheeky little imp.", - "melee_animation": "169", - "range_animation": "169", - "combat_audio": "534,536,535", - "attack_speed": "5", - "defence_animation": "170", - "magic_animation": "169", - "death_animation": "172", "name": "Imp", - "defence_level": "1", - "safespot": null, - "movement_radius": "25", + "examine": "A cheeky little imp.", + "attack_level": "2", + "attack_speed": "5", + "combat_audio": "534,536,535", + "death_animation": "172", + "defence_animation": "170", + "defence_level": "2", "lifepoints": "8", - "strength_level": "1", + "magic_animation": "169", + "melee_animation": "169", + "movement_radius": "25", + "range_animation": "169", + "range_level": "1", + "safespot": null, + "strength_level": "2", + "weakness": "7" + }, + { "id": "709", + "name": "Imp", + "examine": "A cheeky little imp.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "534,536,535", + "death_animation": "172", + "defence_animation": "170", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "169", + "melee_animation": "169", + "movement_radius": "25", + "range_animation": "169", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "710", + "name": "Alrena" + }, + { + "id": "711", + "name": "Bravek" + }, + { + "id": "712", + "name": "Carla" }, { - "examine": "A bureaucratic administrator.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Clerk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "713", + "name": "Clerk", + "examine": "A bureaucratic administrator.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "slayer_exp": "0", - "examine": "A local civilian.", - "name": "Edmond", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "714", - "range_level": "1", - "attack_level": "1" + "name": "Edmond", + "examine": "A local civilian.", + "attack_level": "", + "combat_audio": "", + "defence_level": "", + "lifepoints": "", + "range_level": "", + "safespot": null, + "slayer_exp": "", + "strength_level": "" + }, + { + "id": "715", + "name": "Elena", + "examine": "She looks concerned." }, { - "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", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "716", - "range_level": "1", - "attack_level": "1" + "name": "Head Mourner", + "examine": "In charge of people with silly outfits." + }, + { + "id": "717", + "name": "Mourner", + "examine": "A mourner, or plague healer." + }, + { + "id": "718", + "name": "Mourner", + "examine": "A mourner, or plague healer." + }, + { + "id": "719", + "name": "Mourner", + "examine": "A mourner, or plague healer." }, { - "examine": "A member of the Ardougne Royal Army.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Recruiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "720", + "name": "Recruiter", + "examine": "A member of the Ardougne Royal Army.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "721", + "name": "Ted Rehnison" + }, + { + "id": "722", + "name": "Martha Rehnison" + }, + { + "id": "723", + "name": "Billy Rehnison" + }, + { + "id": "724", + "name": "Milli Rehnison" + }, + { + "id": "725", + "name": "Jethick" }, { - "examine": "One of Gielinor's many citizens.", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", "id": "726", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "One of Gielinor's many citizens.", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", "id": "727", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "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", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "728", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "", + "clue_level": "", + "combat_audio": "", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "protect_style": "", + "range_animation": "", + "range_level": "", + "respawn_delay": "", + "safespot": "", + "slayer_exp": "", + "strength_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", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "729", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Gielinor's many citizens.", "name": "Man", - "defence_level": "1", + "examine": "One of Gielinor's many citizens.", + "attack_level": "", + "clue_level": "", + "combat_audio": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", + "strength_level": "" + }, + { "id": "730", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "I could get a beer from him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "731", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Bartender", "examine": "I could get a beer from him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "732", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Bartender", "examine": "I could get a beer from him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "733", + "name": "Bartender", + "examine": "I could get a beer from him." + }, + { + "id": "734", + "name": "Bartender", + "examine": "I could get a beer from him." + }, + { "id": "735", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Bartender", "examine": "I could get a beer from him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "736", + "name": "Emily", + "examine": "Works in the Rising Sun." + }, + { "id": "737", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Bartender", "examine": "I could get a beer from him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "738", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I could get a beer from him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Bartender", + "examine": "I could get a beer from him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duke Horacio of Lumbridge.", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "739", + "name": "Bartender", + "examine": "I could get a beer from him.", + "attack_level": "1", "death_animation": "0", - "name": "Duke Horacio", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "741", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "Roar! A dragon!", - "melee_animation": "80", - "attack_speed": "4", - "magic_level": "70", - "respawn_delay": "35", - "defence_animation": "89", - "death_animation": "4642", - "name": "Elvarg", - "defence_level": "70", "safespot": null, - "lifepoints": "80", - "strength_level": "70", - "id": "742", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" + "strength_level": "1" }, { + "id": "740", + "name": "Trufitus", + "examine": "A wise old witch doctor." + }, + { + "id": "741", + "name": "Duke Horacio", + "examine": "Duke Horacio of Lumbridge.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "742", + "name": "Elvarg", + "examine": "Roar! A dragon!", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "death_animation": "4642", + "defence_animation": "89", + "defence_level": "70", + "lifepoints": "80", + "magic_level": "70", + "melee_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "strength_level": "70" + }, + { + "id": "743", + "name": "Ned", + "examine": "An old sailor." + }, + { + "id": "744", + "name": "Klarense", + "examine": "A young sailor." + }, + { + "id": "745", + "name": "Wormbrain", "examine": "A badly-behaved goblin.", - "slayer_task": "38", + "attack_level": "1", + "death_animation": "6204", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", "melee_animation": "6184", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6204", - "name": "Wormbrain", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "1", "strength_level": "1", - "id": "745", - "range_level": "1", - "attack_level": "1" + "weakness": "9" + }, + { + "id": "746", + "name": "Oracle" + }, + { + "id": "747", + "name": "Oziach", + "examine": "A strange little man." + }, + { + "id": "748", + "name": "Angry barbarian spirit" }, { - "examine": "He looks a tad upset.", - "melee_animation": "6726", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6727", - "name": "Angry barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "108", - "strength_level": "76", "id": "749", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { + "name": "Angry barbarian spirit", "examine": "He looks a tad upset.", + "aggressive": "true", + "attack_level": "76", + "death_animation": "6727", + "defence_animation": "0", + "defence_level": "76", + "lifepoints": "108", + "magic_animation": "0", "melee_animation": "6726", "range_animation": "0", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6727", - "name": "Enraged barbarian spirit", - "defence_level": "76", "safespot": null, - "lifepoints": "108", "strength_level": "76", + "weakness": "6" + }, + { "id": "750", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { + "name": "Enraged barbarian spirit", "examine": "He looks a tad upset.", - "melee_animation": "6726", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", + "aggressive": "true", + "attack_level": "76", "death_animation": "6727", - "name": "Berserk barbarian spirit", + "defence_animation": "0", "defence_level": "76", - "safespot": null, "lifepoints": "108", - "strength_level": "76", - "id": "751", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "He looks a tad upset.", + "magic_animation": "0", "melee_animation": "6726", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6727", - "name": "Ferocious barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "190", - "strength_level": "76", - "id": "752", - "aggressive": "true", - "bonuses": "80,100,60,70,85,70,70,90,60,100,50,90,90,0,0", "range_level": "1", - "attack_level": "76" + "respawn_delay": "60", + "safespot": null, + "strength_level": "76", + "weakness": "6" }, { + "id": "751", + "name": "Berserk barbarian spirit", + "examine": "He looks a tad upset.", + "aggressive": "true", + "attack_level": "76", + "death_animation": "6727", + "defence_animation": "0", + "defence_level": "76", + "lifepoints": "108", + "magic_animation": "0", + "melee_animation": "6726", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "76", + "weakness": "6" + }, + { + "id": "752", + "name": "Ferocious barbarian spirit", + "examine": "He looks a tad upset.", + "aggressive": "true", + "attack_level": "76", + "bonuses": "80,100,60,70,85,70,70,90,60,100,50,90,90,0,0", + "death_animation": "6727", + "defence_animation": "0", + "defence_level": "76", + "lifepoints": "190", + "magic_animation": "0", + "melee_animation": "6726", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "76", + "weakness": "6" + }, + { + "id": "753", + "name": "Melzar the Mad", "examine": "He looks totally insane!", + "aggressive": "true", + "attack_level": "28", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "magic_level": "28", "melee_animation": "423", "range_animation": "0", - "magic_level": "28", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Melzar the Mad", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "753", - "aggressive": "true", "range_level": "28", - "attack_level": "28" + "respawn_delay": "60", + "safespot": null, + "strength_level": "28", + "weakness": "10" }, { + "id": "754", + "name": "Peaceful barbarian spirit" + }, + { + "id": "755", + "name": "Morgan", + "examine": "He looks scared." + }, + { + "id": "756", + "name": "Dr Harlow", + "examine": "A retired vampyre hunter." + }, + { + "id": "757", + "name": "Count Draynor", "examine": "Stop looking and run!", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "5", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "20", + "lifepoints": "4", + "magic_animation": "422", "melee_animation": "422", "range_animation": "422", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Count Draynor", - "defence_level": "20", - "safespot": null, - "lifepoints": "4", - "strength_level": "1", - "id": "757", - "aggressive": "true", "range_level": "1", - "attack_level": "20" + "safespot": null, + "strength_level": "1" }, { - "examine": "A well-fed farmer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fred the Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "758", + "name": "Fred the Farmer", + "examine": "A well-fed farmer.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "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" }, { - "combat_style": "2", - "melee_animation": "192", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "194", - "death_animation": "196", - "name": "Crate", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", "id": "767", + "name": "Crate", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Overgrown cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "66", - "strength_level": "1", - "id": "778", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Civilian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "785", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Civilian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "786", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A citizen of Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Civilian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "787", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome who's supposed to be cleaning up a mess.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Letham", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "790", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Smartly dressed", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Alfonse the waiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "793", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Distinctly cook-like.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Charlie the cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "794", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A cold hearted lady.", - "melee_animation": "422", - "range_animation": "0", - "magic_level": "55", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Ice Queen", - "defence_level": "55", - "safespot": null, - "lifepoints": "157", - "strength_level": "55", - "id": "795", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "He looks cold and hungry.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Velrak the explorer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "798", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A morally ambiguous guard.", - "melee_animation": "390", - "range_animation": "0", + "attack_level": "1", "combat_audio": "703,705,704", + "combat_style": "2", + "death_animation": "196", + "defence_animation": "194", + "defence_level": "1", + "lifepoints": "88", + "melee_animation": "192", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Pirate Guard", - "defence_level": "30", "safespot": null, - "lifepoints": "42", - "strength_level": "30", + "strength_level": "1" + }, + { + "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": "778", + "name": "Overgrown cat", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "66", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "779", + "name": "Overgrown cat" + }, + { + "id": "780", + "name": "Gertrude" + }, + { + "id": "781", + "name": "Shilop", + "examine": "One of Gertrude's Sons." + }, + { + "id": "782", + "name": "Philop" + }, + { + "id": "783", + "name": "Wilough", + "examine": "One of Gertrude's Sons." + }, + { + "id": "784", + "name": "Kanel" + }, + { + "id": "785", + "name": "Civilian", + "examine": "A citizen of Ardougne.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "786", + "name": "Civilian", + "examine": "A citizen of Ardougne.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "787", + "name": "Civilian", + "examine": "A citizen of Ardougne.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "788", + "name": "Garv", + "examine": "A diligent guard." + }, + { + "id": "789", + "name": "Grubor", + "examine": "A rough looking thief." + }, + { + "id": "790", + "name": "Letham", + "examine": "A gnome who's supposed to be cleaning up a mess.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "791", + "name": "Seth" + }, + { + "id": "792", + "name": "Grip", + "examine": "Looks like he's been in the wars." + }, + { + "id": "793", + "name": "Alfonse the waiter", + "examine": "Smartly dressed, and ready to deliver food.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "794", + "name": "Charlie the cook", + "examine": "Distinctly cook-like.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "795", + "name": "Ice Queen", + "examine": "A cold hearted lady.", + "attack_level": "95", + "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "95", + "lifepoints": "104", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "94", + "weakness": "10" + }, + { + "id": "796", + "name": "Achietties", + "examine": "Distinctly heroic." + }, + { + "id": "797", + "name": "Helemos" + }, + { + "id": "798", + "name": "Velrak the explorer", + "examine": "He looks cold and hungry.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "799", + "name": "Pirate Guard", + "examine": "A morally ambiguous guard.", + "attack_level": "30", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", "range_level": "1", - "attack_level": "30" + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" }, { - "examine": "It looks like there might be eels swimming in the lava.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishing spot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "800", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", + "name": "Fishing spot", + "examine": "It looks like there might be eels swimming in the lava.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abbot Langley", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "801", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Abbot Langley", "examine": "A holy man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Jered", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "802", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Brother Jered", "examine": "A holy man.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", + "attack_level": "1", + "death_animation": "0", "defence_animation": "0", - "weakness": "9", + "defence_level": "1", + "lifepoints": "10", "magic_animation": "0", - "death_animation": "836", - "name": "Monk", - "defence_level": "12", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", "safespot": null, - "lifepoints": "17", - "strength_level": "12", + "strength_level": "1" + }, + { "id": "803", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "Manufacturer of fine leathers.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tanner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "804", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's in charge of the Crafting Guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master Crafter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "805", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks very tired...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Donovan the Family Handyman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "806", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An officer of the Law.", - "melee_animation": "0", - "range_animation": "0", + "name": "Monk", + "examine": "A holy man.", + "attack_level": "12", "combat_audio": "511,513,512", + "death_animation": "836", "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "812", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of those people who love to gossip!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gossip", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "813", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sure likes to sell stuff!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Poison Salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "820", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sinclair Guard dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "821", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It looks like he's been here a long time.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Male slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "825", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a bit aggressive.", "melee_animation": "422", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Rowdy slave", - "defence_level": "5", + "range_level": "1", "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "827", + "strength_level": "12", + "weakness": "9" + }, + { + "id": "804", + "name": "Tanner", + "examine": "Manufacturer of fine leathers.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "805", + "name": "Master Crafter", + "examine": "He's in charge of the Crafting Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "806", + "name": "Donovan the Family Handyman", + "examine": "He looks very tired...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "807", + "name": "Pierre", + "examine": "His job doesn't look very fun..." + }, + { + "id": "808", + "name": "Hobbes", + "examine": "He looks kind of stuck up..." + }, + { + "id": "809", + "name": "Louisa", + "examine": "She looks like she enjoys her job." + }, + { + "id": "810", + "name": "Mary", + "examine": "She looks very nervous..." + }, + { + "id": "811", + "name": "Stanford", + "examine": "He looks like he spends a lot of time outdoor." + }, + { + "id": "812", + "name": "Guard", + "examine": "An officer of the Law.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "813", + "name": "Gossip", + "examine": "One of those people who love to gossip!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "814", + "name": "Anna", + "examine": "She's dressed in a red top and green trousers." + }, + { + "id": "815", + "name": "Bob", + "examine": "He's dressed all in red." + }, + { + "id": "816", + "name": "Carol", + "examine": "She's wearing a blue top and red trousers." + }, + { + "id": "817", + "name": "David", + "examine": "He's dressed all in green." + }, + { + "id": "818", + "name": "Elizabeth", + "examine": "She's wearing a green top and blue trousers." + }, + { + "id": "819", + "name": "Frank", + "examine": "He's dressed all in blue." + }, + { + "id": "820", + "name": "Poison Salesman", + "examine": "He sure likes to sell stuff!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "821", + "name": "Sinclair Guard dog", + "examine": "Big, noisy, and scary looking!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "822", + "name": "Ana" + }, + { + "id": "823", + "name": "Ana" + }, + { + "id": "824", + "name": "Female slave" + }, + { + "id": "825", + "name": "Male slave", + "examine": "It looks like he's been here a long time.", "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "5" + "safespot": null, + "strength_level": "1" }, { + "id": "826", + "name": "Escaping slave" + }, + { + "id": "827", + "name": "Rowdy slave", "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", + "aggressive": "true", + "attack_level": "5", "death_animation": "836", - "name": "Mercenary Captain", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "830", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's in control of the whole mining camp.", - "melee_animation": "0", - "range_animation": "0", "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", "magic_animation": "0", - "death_animation": "0", - "name": "Captain Siad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "831", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Bedabin nomad", - "melee_animation": "0", + "melee_animation": "422", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bedabin Nomad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "833", "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Bedabin nomad guard - it looks like he's protecting an area.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bedabin Nomad Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "834", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He patrols the Shantay Pass.", - "melee_animation": "390", - "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Shantay Guard", - "defence_level": "11", "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "837", - "range_level": "1", - "attack_level": "11" + "strength_level": "5", + "weakness": "9" }, { - "examine": "He patrols the Shantay Pass.", + "id": "828", + "name": "Shanty Claws" + }, + { + "id": "829", + "name": "Mercenary Captain" + }, + { + "id": "830", + "name": "Mercenary Captain", + "examine": "He looks a bit aggressive.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "831", + "name": "Captain Siad", + "examine": "He's in control of the whole mining camp.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shantay Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "838", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "832", + "name": "Al Shabim" + }, + { + "id": "833", + "name": "Bedabin Nomad", + "examine": "A Bedabin nomad", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "834", + "name": "Bedabin Nomad Guard", + "examine": "A Bedabin nomad guard - it looks like he's protecting an area.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "835", + "name": "Irena", + "examine": "A resident of Al Kharid." + }, + { + "id": "836", + "name": "Shantay", + "examine": "He's in control of the Shantay Pass." + }, + { + "id": "837", + "name": "Shantay Guard", + "examine": "He patrols the Shantay Pass.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "838", + "name": "Shantay Guard", + "examine": "He patrols the Shantay Pass.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "839", + "name": "Desert Wolf", "examine": "A vicious desert wolf.", - "slayer_task": "92", + "attack_level": "11", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", "melee_animation": "6559", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6558", - "name": "Desert Wolf", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "839", "range_level": "1", - "attack_level": "11" + "safespot": null, + "strength_level": "11", + "weakness": "9" }, { - "death_animation": "9674", - "name": "Ugthanki", - "defence_level": "1", - "safespot": null, - "lifepoints": "46", - "strength_level": "1", - "attack_speed": "5", "id": "840", + "name": "Ugthanki", "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9674", + "defence_level": "1", + "lifepoints": "46", "range_level": "1", "respawn_delay": "60", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "841", + "name": "Mine cart driver", "examine": "He looks busy attending to his cart.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mine cart driver", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "841", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "842", + "name": "Rowdy Guard", "examine": "This pickaxe has been possessed by a dwarf ancestor spirit.", + "aggressive": "true", + "attack_level": "40", + "death_animation": "188", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "400", + "magic_animation": "0", "melee_animation": "185", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "188", - "name": "Rowdy Guard", - "defence_level": "40", - "safespot": null, - "lifepoints": "400", - "strength_level": "40", - "id": "842", - "aggressive": "true", "range_level": "1", - "attack_level": "40" + "respawn_delay": "60", + "safespot": null, + "strength_level": "40" }, { - "examine": "Inefficient looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "RPDT employee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "843", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Despite his name", + "name": "RPDT employee", + "examine": "Inefficient looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head chef", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "844", + "name": "Horacio" + }, + { + "id": "846", + "name": "Kangai Mau", + "examine": "A happening kind of guy!" + }, + { "id": "847", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The famous tree gnome chef.", + "name": "Head chef", + "examine": "Despite his name", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Aluft Gianne snr.", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "848", + "name": "Blurberry" + }, + { + "id": "849", + "name": "Barman" + }, + { "id": "850", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can serve you gnome food.", + "name": "Aluft Gianne snr.", + "examine": "The famous tree gnome chef.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Waiter", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "851", + "name": "Gnome Waiter", + "examine": "He can serve you gnome food.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Tough-looking.", - "slayer_task": "64", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "300", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre chieftain", - "defence_level": "75", - "safespot": null, - "lifepoints": "60", - "strength_level": "71", "id": "852", + "name": "Ogre chieftain", + "examine": "Tough-looking.", + "attack_level": "75", + "attack_speed": "4", "bonuses": "5,5,5,0,0,0,0,0,0,0,0,7,0,0,0", + "defence_animation": "0", + "defence_level": "75", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "1", + "range_animation": "0", "range_level": "1", - "attack_level": "75" + "respawn_delay": "300", + "safespot": null, + "strength_level": "71", + "weakness": "7" + }, + { + "id": "853", + "name": "Og" + }, + { + "id": "854", + "name": "Grew" + }, + { + "id": "855", + "name": "Toban" }, { - "name": "Gorad", - "defence_level": "1", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", "id": "856", + "name": "Gorad", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "81", "range_level": "1", "respawn_delay": "60", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "857", + "name": "Ogre guard" }, { - "examine": "These ogres protect the city.", - "slayer_task": "64", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "858", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ogre that guards.", - "slayer_task": "64", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", "name": "Ogre guard", + "examine": "These ogres protect the city.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, "strength_level": "1", + "weakness": "7" + }, + { "id": "859", + "name": "Ogre guard", + "examine": "An ogre that guards.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "860", + "name": "Ogre guard" + }, + { + "id": "861", + "name": "Ogre guard" }, { - "examine": "Tries to keep the peace.", - "slayer_task": "64", - "melee_animation": "2100", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "8576", - "name": "City guard", - "defence_level": "58", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", "id": "862", - "range_level": "1", - "attack_level": "58" - }, - { - "examine": "Frightened-looking.", - "melee_animation": "0", - "range_animation": "0", + "name": "City guard", + "examine": "Tries to keep the peace.", + "attack_level": "58", + "death_animation": "8576", "defence_animation": "0", + "defence_level": "58", + "lifepoints": "331", "magic_animation": "0", - "death_animation": "0", - "name": "Scared skavid", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "863", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks mad.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mad skavid", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "864", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big and ugly looking.", - "slayer_task": "64", "melee_animation": "2100", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "8576", - "name": "Enclave guard", - "defence_level": "58", + "range_level": "1", "safespot": null, - "lifepoints": "331", "strength_level": "58", + "weakness": "7" + }, + { + "id": "863", + "name": "Scared skavid", + "examine": "Frightened-looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "864", + "name": "Mad skavid", + "examine": "Looks mad.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "865", + "name": "Skavid" + }, + { + "id": "866", + "name": "Skavid" + }, + { + "id": "867", + "name": "Skavid" + }, + { + "id": "868", + "name": "Skavid" + }, + { + "id": "869", + "name": "Skavid" + }, + { "id": "870", + "name": "Enclave guard", + "examine": "Big and ugly looking.", + "attack_level": "58", + "death_animation": "8576", + "defence_animation": "0", + "defence_level": "58", + "lifepoints": "331", + "magic_animation": "0", + "melee_animation": "2100", + "range_animation": "0", "range_level": "1", - "attack_level": "58" + "respawn_delay": "60", + "safespot": null, + "strength_level": "58", + "weakness": "7" + }, + { + "id": "871", + "name": "Watchtower Wizard" }, { - "examine": "The hat is a dead giveaway.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Watchtower Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "872", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Funnily enough", - "slayer_task": "64", + "name": "Watchtower Wizard", + "examine": "The hat is a dead giveaway.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre trader", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "873", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Funnily enough", - "slayer_task": "64", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "874", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Funnily enough", - "slayer_task": "64", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Ogre trader", + "examine": "Funnily enough", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "875", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "874", + "name": "Ogre merchant", + "examine": "Funnily enough", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "875", + "name": "Ogre trader", + "examine": "Funnily enough", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "876", + "name": "Ogre trader" + }, + { + "id": "877", + "name": "Tower guard", "examine": "Tries to keep the peace.", + "attack_level": "17", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "17", + "lifepoints": "97", + "magic_animation": "0", "melee_animation": "386", "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower guard", - "defence_level": "17", + "range_level": "1", "safespot": null, - "lifepoints": "97", "strength_level": "17", - "id": "877", - "range_level": "1", - "attack_level": "17" + "weakness": "7" }, { - "melee_animation": "395", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Colonel Radick", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", "id": "878", + "name": "Colonel Radick", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "71", + "melee_animation": "395", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "death_animation": "67", + "id": "879", "name": "Delrith", + "attack_level": "1", + "death_animation": "67", + "defence_animation": "65", "defence_level": "1", - "safespot": null, "lifepoints": "7", "melee_animation": "64", - "strength_level": "1", - "id": "879", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "65" + "safespot": null, + "strength_level": "1" + }, + { + "id": "880", + "name": "Weakened Delrith" + }, + { + "id": "881", + "name": "Traiborn", + "examine": "An old wizard." + }, + { + "id": "882", + "name": "Gypsy Aris", + "examine": "An old gypsy lady." + }, + { + "id": "883", + "name": "Sir Prysin", + "examine": "One of the king's knights." }, { - "examine": "The head of the palace guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Rovin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "884", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Head of the Carnillean household.", + "name": "Captain Rovin", + "examine": "The head of the palace guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ceril Carnillean", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "885", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", + "name": "Ceril Carnillean", + "examine": "Head of the Carnillean household.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "887", - "clue_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "836", - "name": "Clivet", + "id": "886", + "name": "Claus the chef", + "examine": "The Carnillean family chef." + }, + { + "id": "887", + "name": "Guard", + "examine": "On special duty to protect the Canilleans.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", "defence_level": "1", + "lifepoints": "10", + "range_level": "1", "safespot": null, + "strength_level": "1" + }, + { + "id": "888", + "name": "Philipe Carnillean" + }, + { + "id": "889", + "name": "Henryeta Carnillean", + "examine": "Ceril Carnillean's wife." + }, + { + "id": "890", + "name": "Butler Jones", + "examine": "The Carnellian family butler." + }, + { + "id": "891", + "name": "Alomone", + "examine": "Leader of the Hazeel cult." + }, + { + "id": "892", + "name": "Hazeel" + }, + { + "id": "893", + "name": "Clivet", + "examine": "A member of the Hazeel cult.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", "lifepoints": "25", "melee_animation": "811", - "strength_level": "1", - "id": "893", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "1" }, { + "id": "894", + "name": "Hazeel Cultist", "examine": "A member of the Hazeel cult.", + "attack_level": "15", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "magic_level": "20", "melee_animation": "422", "range_animation": "0", - "magic_level": "20", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "836", - "name": "Hazeel Cultist", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "15", - "id": "894", "range_level": "1", - "attack_level": "15" + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "3" }, { - "examine": "Children are just like real people...just smaller.", + "id": "895", + "name": "Boy", + "examine": "A sad looking child.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "895", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "896", + "name": "Nora T. Hagg" + }, + { + "id": "897", + "name": "Witch's experiment", "examine": "Looks unnatural.", + "attack_level": "18", + "attack_speed": "4", + "death_animation": "258", + "defence_animation": "0", + "defence_level": "19", + "lifepoints": "21", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "255", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "258", - "name": "Witch's experiment", - "defence_level": "19", - "safespot": null, - "lifepoints": "21", - "strength_level": "10", - "id": "897", "range_level": "1", - "attack_level": "18" + "safespot": null, + "strength_level": "10", + "weakness": "7" }, { + "id": "898", + "name": "Witch's experiment (second form)", "examine": "Looks unnatural.", + "attack_level": "28", + "attack_speed": "4", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "31", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "5327", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5329", - "name": "Witch's experiment (second form)", - "defence_level": "29", - "safespot": null, - "lifepoints": "31", - "strength_level": "20", - "id": "898", "range_level": "1", - "attack_level": "28" + "safespot": null, + "strength_level": "20", + "weakness": "7" }, { + "id": "899", + "name": "Witch's experiment (third form)", "examine": "Looks unnatural.", + "attack_level": "38", + "attack_speed": "4", + "death_animation": "4929", + "defence_animation": "0", + "defence_level": "39", + "lifepoints": "41", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "4925", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4929", - "name": "Witch's experiment (third form)", - "defence_level": "39", - "safespot": null, - "lifepoints": "41", - "strength_level": "30", - "id": "899", "range_level": "1", - "attack_level": "38" + "safespot": null, + "strength_level": "30", + "weakness": "7" }, { + "id": "900", + "name": "Witch's experiment (fourth form)", "examine": "Looks unnatural.", + "attack_level": "48", + "attack_speed": "4", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "51", + "magic_animation": "0", + "magic_level": "1", "melee_animation": "6559", "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6558", - "name": "Witch's experiment (fourth form)", - "defence_level": "49", - "safespot": null, - "lifepoints": "51", - "strength_level": "40", - "id": "900", "range_level": "1", - "attack_level": "48" + "safespot": null, + "strength_level": "40", + "weakness": "7" }, { + "id": "901", + "name": "Mouse" + }, + { + "id": "902", + "name": "Gundai" + }, + { + "id": "903", + "name": "Lundail" + }, + { + "id": "904", + "name": "Chamber guardian", "examine": "He hasn't seen much sun lately.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chamber guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "904", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "slayer_exp": "0", - "examine": "Runs the Mage Arena.", - "name": "Kolodion", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", "id": "905", + "name": "Kolodion", + "examine": "Runs the Mage Arena.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "slayer_exp": "0", + "strength_level": "1" + }, + { + "id": "906", + "name": "Kolodion" }, { - "examine": "He's a shape-shifter", - "combat_style": "2", - "melee_animation": "811", - "attack_speed": "7", - "magic_level": "75", - "defence_animation": "404", - "slayer_exp": "0", - "death_animation": "1816", - "name": "Kolodion", - "defence_level": "20", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", "id": "907", + "name": "Kolodion", + "examine": "He's a shape-shifter", + "attack_level": "42", + "attack_speed": "7", + "combat_style": "2", + "death_animation": "1816", + "defence_animation": "404", + "defence_level": "20", + "lifepoints": "3", + "magic_level": "75", + "melee_animation": "811", "range_level": "1", - "attack_level": "42" + "safespot": null, + "slayer_exp": "0", + "strength_level": "1" }, { - "examine": "He's a shape-shifter", - "combat_style": "2", - "melee_animation": "132", - "attack_speed": "7", - "magic_level": "89", - "defence_animation": "404", - "slayer_exp": "0", - "death_animation": "133", - "name": "Kolodion", - "defence_level": "25", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", "id": "908", - "range_level": "1", - "attack_level": "74" - }, - { - "examine": "He's a shape-shifter", - "combat_style": "2", - "melee_animation": "5322", - "attack_speed": "7", - "magic_level": "89", - "defence_animation": "5320", - "slayer_exp": "0", - "death_animation": "5323", "name": "Kolodion", - "defence_level": "28", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "909", - "range_level": "1", - "attack_level": "74" - }, - { "examine": "He's a shape-shifter", - "combat_style": "2", - "melee_animation": "811", + "attack_level": "74", "attack_speed": "7", - "magic_level": "89", + "combat_style": "2", + "death_animation": "133", "defence_animation": "404", - "slayer_exp": "0", - "death_animation": "714", - "name": "Kolodion", - "defence_level": "30", - "safespot": null, - "lifepoints": "78", - "strength_level": "1", - "id": "910", + "defence_level": "25", + "lifepoints": "65", + "magic_level": "89", + "melee_animation": "132", "range_level": "1", - "attack_level": "74" + "safespot": null, + "slayer_exp": "0", + "strength_level": "1" }, { + "id": "909", + "name": "Kolodion", "examine": "He's a shape-shifter", - "melee_animation": "69", + "attack_level": "74", "attack_speed": "7", - "magic_level": "80", - "defence_animation": "65", + "combat_style": "2", + "death_animation": "5323", + "defence_animation": "5320", + "defence_level": "28", + "lifepoints": "65", + "magic_level": "89", + "melee_animation": "5322", + "range_level": "1", + "safespot": null, "slayer_exp": "0", - "death_animation": "68", + "strength_level": "1" + }, + { + "id": "910", "name": "Kolodion", - "defence_level": "105", - "safespot": null, - "lifepoints": "107", - "strength_level": "98", - "id": "911", - "range_level": "1", - "attack_level": "85" - }, - { - "examine": "He kills in the name of Zamorak.", + "examine": "He's a shape-shifter", + "attack_level": "74", + "attack_speed": "7", "combat_style": "2", + "death_animation": "714", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "78", + "magic_level": "89", "melee_animation": "811", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "50", - "respawn_delay": "150", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "836", - "name": "Battle mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "912", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He kills in the name of Saradomin.", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "50", - "respawn_delay": "150", - "defence_animation": "0", - "weakness": "3", + "safespot": null, "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Battle mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "913", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" + "strength_level": "1" }, { - "examine": "He kills in the name of Guthix.", + "id": "911", + "name": "Kolodion", + "examine": "He's a shape-shifter", + "attack_level": "85", + "attack_speed": "7", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "105", + "lifepoints": "107", + "magic_level": "80", + "melee_animation": "69", + "range_level": "1", + "safespot": null, + "slayer_exp": "0", + "strength_level": "98" + }, + { + "id": "912", + "name": "Battle mage", + "examine": "He kills in the name of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "120", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "811", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "150", + "safespot": null, + "strength_level": "1", + "weakness": "3" + }, + { + "id": "913", + "name": "Battle mage", + "examine": "He kills in the name of Saradomin.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "120", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "811", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "0", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "914", + "name": "Battle mage", + "examine": "He kills in the name of Guthix.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "196", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "120", + "magic_animation": "0", + "magic_level": "50", "melee_animation": "197", "range_animation": "0", - "attack_speed": "4", - "magic_level": "50", + "range_level": "1", "respawn_delay": "150", - "defence_animation": "0", - "weakness": "3", + "safespot": null, "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "196", - "name": "Battle mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", "strength_level": "1", - "id": "914", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" + "weakness": "3" }, { - "examine": "A crafter at the pinnacle of his art.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Joe", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "916", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder who he's guarding?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Jail guard", - "defence_level": "21", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "917", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "The emir's chief advisor and physician.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hassan", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "923", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Guards the border between Al Kharid and the Kingdom of Misthalin.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Border Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "925", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An aggressive native of the Kharazi Jungle.", - "melee_animation": "428", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Jungle savage", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "44", - "id": "931", - "aggressive": "true", - "range_level": "1", - "attack_level": "44" - }, - { - "melee_animation": "64", - "respawn_delay": "60", - "defence_animation": "65", - "death_animation": "67", - "name": "Nezikchened", - "defence_level": "1", - "safespot": null, - "lifepoints": "150", - "strength_level": "1", - "id": "934", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5485", - "respawn_delay": "60", - "defence_animation": "5489", - "death_animation": "5491", - "name": "Ranalph Devere", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "938", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder what this is doing here.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "939", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Must be related to Elvarg.", - "slayer_task": "41", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "68", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "75", - "magic_animation": "80", - "death_animation": "92", - "name": "Green dragon", - "defence_level": "68", - "safespot": null, - "lifepoints": "75", - "strength_level": "68", - "id": "941", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "An expert on all things culinary.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master chef", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "942", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very much an outdoors type.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Survival expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "943", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on all forms of combat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Combat instructor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "944", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your introduction to the world of Gielinor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "RuneScape guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "945", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master of Magic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Magic instructor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "946", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official representative from the First National Bank of Gielinor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Financial advisor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "947", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on Mining-related skills.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mining instructor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "948", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your introduction to the world of Gielinor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Quest guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "949", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "950", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "951", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Brace", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "954", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Converts grass to beef.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "369,371,370", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "955", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's had a fair bit to drink...", - "start_gfx": "33", - "combat_style": "1", - "melee_animation": "99", - "range_animation": "99", - "attack_speed": "5", - "magic_level": "20", - "defence_animation": "101", - "slayer_exp": "0", - "magic_animation": "99", - "death_animation": "102", - "name": "Drunken Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "5", - "id": "956", - "range_level": "6", - "projectile": "33", - "attack_level": "5", - "prj_height": "45" - }, - { - "examine": "This doctor really knows her stuff.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Surgeon General Tafani", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "961", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's one of Iban's pets.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "5329", - "name": "Blessed spider", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "22", - "id": "977", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Blessed giant rat", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "978", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "979", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "980", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "981", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "982", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "983", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "984", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wretched slave of Iban.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "985", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Precariously balanced...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "986", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "369,371,370", - "strength_level": "1", - "id": "987", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Sir Jerro", - "defence_level": "1", - "safespot": null, - "lifepoints": "57", - "melee_animation": "400", - "strength_level": "1", - "id": "988", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Sir Carl", - "defence_level": "1", - "safespot": null, - "lifepoints": "57", - "melee_animation": "400", - "strength_level": "1", - "id": "989", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Sir Harry", - "defence_level": "1", - "safespot": null, - "lifepoints": "57", - "melee_animation": "400", - "strength_level": "1", - "id": "990", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "A creature empty of emotion.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Half-soulless", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "991", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Curiosity is yet to kill this one...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witch's cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "993", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A giant spider.", - "melee_animation": "5319", - "respawn_delay": "60", - "defence_animation": "5320", - "death_animation": "5321", - "name": "Kalrag", - "defence_level": "1", - "safespot": null, - "lifepoints": "91", - "strength_level": "1", - "id": "997", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the guardians of Iban.", - "melee_animation": "64", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "65", - "death_animation": "67", - "name": "Othainian", - "defence_level": "1", - "safespot": null, - "lifepoints": "90", - "strength_level": "1", - "id": "998", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the guardians of Iban.", - "melee_animation": "64", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "65", - "death_animation": "67", - "name": "Doomion", - "defence_level": "1", - "safespot": null, - "lifepoints": "90", - "strength_level": "1", - "id": "999", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the guardians of Iban.", - "melee_animation": "64", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "65", - "death_animation": "67", - "name": "Holthion", - "defence_level": "1", - "safespot": null, - "lifepoints": "90", - "strength_level": "1", - "id": "1000", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A user of dark magic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Dark mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1001", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dark magic user.", - "melee_animation": "422", - "range_animation": "0", - "magic_level": "32", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "836", - "name": "Disciple of Iban", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "24", - "id": "1002", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "Incey wincey.", - "melee_animation": "6249", - "respawn_delay": "60", - "defence_animation": "6250", - "death_animation": "6251", - "name": "Spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "1004", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An annoying flappy thing.", - "combat_audio": "292,294,293", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "slayer_exp": "8", - "name": "Giant bat", - "defence_level": "22", - "safespot": null, - "lifepoints": "32", - "strength_level": "22", - "id": "1005", - "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A servant of Zamorak.", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "60", - "spell_id": "43", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Zamorak wizard", - "defence_level": "50", - "safespot": null, - "lifepoints": "73", - "strength_level": "50", - "id": "1007", - "aggressive": "true", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "30", - "defence_animation": "5328", - "weakness": "2", - "poison_amount": "6", - "magic_animation": "0", - "death_animation": "5329", - "name": "Poison spider", - "safespot": null, - "defence_level": "52", - "lifepoints": "64", - "strength_level": "65", - "id": "1009", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A green skinned croaker", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "34", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swamp toad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1013", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Useful for hitting rocks.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Iron pickaxe", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1015", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "slayer_task": "7", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "weakness": "9", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "1017", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He rules the", - "slayer_task": "7", - "melee_animation": "5387", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5389", - "name": "Rooster", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "1018", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "A fire elemental.", - "melee_animation": "1029", - "range_animation": "0", - "combat_audio": "1534,1536,1535", - "magic_level": "32", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1031", - "name": "Fire elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "24", - "id": "1019", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "An earth elemental.", - "melee_animation": "4868", - "range_animation": "0", - "combat_audio": "1531,1533,1532", - "attack_speed": "6", - "magic_level": "10", - "respawn_delay": "50", - "defence_animation": "4869", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "35", - "safespot": null, - "movement_radius": "4", - "lifepoints": "35", - "strength_level": "32", - "id": "1020", - "aggressive": "false", - "range_level": "30", - "attack_level": "20" - }, - { - "examine": "An air elemental.", - "melee_animation": "1040", - "range_animation": "0", - "combat_audio": "1527,1529,1528", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "1041", - "name": "Air elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "24", - "id": "1021", - "range_level": "32", - "attack_level": "24" - }, - { - "examine": "A water elemental.", - "melee_animation": "1044", - "range_animation": "0", - "combat_audio": "1546,1543,1545", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "1048", - "name": "Water elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "24", - "id": "1022", - "range_level": "32", - "attack_level": "24" - }, - { - "examine": "Could his name be 'Lurch'?", - "melee_animation": "7183", - "slayer_exp": "50", - "death_animation": "7185", - "name": "Vampire", - "defence_level": "52", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1023", - "clue_level": "1", - "range_level": "1", - "attack_level": "52" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "25", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "18", - "id": "1044", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "18" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "25", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "17", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "1045", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "8" - }, - { - "examine": "An evil human cleric.", - "start_gfx": "99", - "combat_style": "2", - "combat_audio": "511,513,512", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "40", - "end_gfx": "101", - "respawn_delay": "60", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "711", - "death_animation": "836", - "name": "Monk of Zamorak", - "defence_level": "25", - "safespot": null, - "lifepoints": "25", - "strength_level": "25", - "id": "1046", - "aggressive": "true", - "range_level": "1", - "projectile": "100", - "attack_level": "25" - }, - { - "examine": "Arrghhh... a Ghast.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "433,435,434", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1052", - "aggressive": "false", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arrghhh... A Ghast.", - "death_gfx": "265", - "range_animation": "0", - "melee_animation": "1087", - "combat_audio": "433,435,434", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "1088", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1089", - "name": "Ghast", - "defence_level": "18", - "safespot": null, - "lifepoints": "45", - "strength_level": "22", - "id": "1053", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Ticket trader for the Brimhaven Agility Arena.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Jackie the Fruit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1055", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the audience.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange watcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1057", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the audience.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange watcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1058", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the audience.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange watcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1059", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Commander of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Denulth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1060", - "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": "1061", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1063", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1064", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "strength_level": "44", - "id": "1065", - "range_level": "1", - "attack_level": "44" - }, - { - "examine": "A soldier of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1066", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1067", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1068", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1069", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "name": "Eadburg", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1072", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Burthorpe Castle Archer.", - "start_gfx": "19", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "magic_level": "1", - "respawn_delay": "12", - "defence_animation": "404", - "magic_animation": "426", - "death_animation": "9055", - "name": "Archer", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "strength_level": "1", - "id": "1073", - "bonuses": "54,68,75,72,67,81,54,35,35,0,0,0,0,0,0", - "range_level": "44", - "projectile": "10", - "attack_level": "1" - }, - { - "examine": "A Burthorpe Castle Archer.", - "start_gfx": "19", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "magic_level": "1", - "respawn_delay": "12", - "defence_animation": "404", - "magic_animation": "426", - "death_animation": "9055", - "name": "Archer", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "strength_level": "1", - "id": "1074", - "bonuses": "54,68,75,72,67,81,54,35,35,0,0,0,0,0,0", - "range_level": "44", - "projectile": "10", - "attack_level": "1" - }, - { - "examine": "A Burthorpe Castle guard.", - "melee_animation": "395", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "33", - "safespot": null, - "lifepoints": "44", - "strength_level": "33", - "id": "1076", - "clue_level": "1", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A Burthorpe Castle guard.", - "melee_animation": "395", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "33", - "safespot": null, - "lifepoints": "44", - "strength_level": "33", - "id": "1077", - "clue_level": "1", - "range_level": "1", - "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" - }, - { - "death_animation": "836", - "name": "Ocga", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", - "id": "1085", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1086", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Penda", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", - "id": "1087", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Hygd", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", - "id": "1088", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Ceolburg", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", - "id": "1089", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "The knight seems to be watching something.", - "melee_animation": "7042", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "White Knight", - "defence_level": "27", - "safespot": null, - "lifepoints": "38", - "strength_level": "27", - "id": "1092", - "range_level": "1", - "attack_level": "27" - }, - { - "examine": "The biggest and baddest troll.", - "slayer_task": "83", - "melee_animation": "6523", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Rock", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "1095", - "aggressive": "true", - "range_level": "1", - "attack_level": "63" - }, - { - "examine": "Careful, he is a big.", - "slayer_task": "83", - "range_animation": "0", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Stick", - "defence_level": "61", - "safespot": null, - "lifepoints": "87", - "strength_level": "61", - "id": "1096", - "aggressive": "true", - "range_level": "1", - "attack_level": "61" - }, - { - "name": "Pee Hat", - "defence_level": "1", - "safespot": null, - "lifepoints": "126", - "strength_level": "1", - "id": "1097", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Kraka", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "1098", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dung", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1099", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ash", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1100", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower Troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1101", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower Troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1102", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower Troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1103", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower Troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1104", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower Troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1105", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1106", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1107", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1108", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1109", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1110", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1111", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1112", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "One of the troll generals.", - "slayer_task": "83", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll general", - "defence_level": "65", - "safespot": null, - "lifepoints": "92", - "strength_level": "65", - "id": "1115", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "One of the troll generals.", - "slayer_task": "83", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll general", - "defence_level": "65", - "safespot": null, - "lifepoints": "92", - "strength_level": "65", - "id": "1116", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "One of the troll generals.", - "slayer_task": "83", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll general", - "defence_level": "65", - "safespot": null, - "lifepoints": "92", - "strength_level": "65", - "id": "1117", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1118", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1119", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1120", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1121", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1122", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1123", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "1124", - "range_level": "1", - "attack_level": "47" - }, - { - "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", - "name": "Dad", - "defence_level": "55", - "safespot": null, - "lifepoints": "157", - "strength_level": "55", - "id": "1125", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "He's guarding the cells.", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Twig", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "1126", - "range_level": "1", - "attack_level": "50" - }, - { - "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", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "1127", - "range_level": "1", - "attack_level": "50" - }, - { - "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", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "1128", - "range_level": "1", - "attack_level": "50" - }, - { - "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", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "1129", - "range_level": "1", - "attack_level": "50" - }, - { - "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", - "range_animation": "284", - "attack_speed": "7", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1130", - "aggressive": "true", - "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "range_animation": "284", - "attack_speed": "7", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1131", - "aggressive": "true", - "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "range_animation": "284", - "attack_speed": "7", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1132", - "aggressive": "true", - "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "range_animation": "284", - "attack_speed": "7", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1133", - "aggressive": "true", - "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "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", - "range_animation": "284", - "attack_speed": "7", - "respawn_delay": "35", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "284", - "death_animation": "287", - "name": "Thrower troll", - "defence_level": "30", - "safespot": null, - "lifepoints": "95", - "strength_level": "41", - "id": "1134", - "aggressive": "true", - "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "276", - "attack_level": "41" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1135", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1136", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1137", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", - "melee_animation": "284", - "range_animation": "284", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", - "safespot": null, - "lifepoints": "90", - "strength_level": "20", - "id": "1138", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "He's fast asleep.", - "slayer_task": "83", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Mushroom", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1139", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This beast doesn't need climbing boots.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1140", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This beast doesn't need climbing boots.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1141", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the storeroom.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1142", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the storeroom.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1143", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1144", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1145", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1146", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1147", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1148", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1149", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the goutweed.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1150", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Human is his speciality.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Burntmeat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1151", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What's he mumbling about?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Weird Old Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1152", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "6223", - "range_animation": "6223", - "combat_audio": "571,573,572", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "38", - "defence_animation": "6232", - "weakness": "7", - "slayer_exp": "40", - "magic_animation": "6223", - "death_animation": "6230", - "name": "Kalphite Worker", - "defence_level": "20", - "safespot": null, - "lifepoints": "40", - "strength_level": "20", - "id": "1153", - "bonuses": "0,0,0,0,0,5,5,1,10,10,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "6224", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "60", - "weakness": "7", - "slayer_exp": "90", - "poison_amount": "4", - "magic_animation": "0", - "death_animation": "6228", - "lifepoints": "90", - "id": "1154", - "aggressive": "true", - "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", - "range_animation": "0", - "combat_audio": "567,569,568", - "magic_level": "1", - "defence_animation": "6227", - "name": "Kalphite Soldier", - "defence_level": "70", - "safespot": null, - "strength_level": "70", - "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", - "poisonous": "true", - "magic_level": "1", - "respawn_delay": "65", - "defence_animation": "6232", - "weakness": "7", - "poison_amount": "6", - "magic_animation": "6223", - "death_animation": "6230", - "name": "Kalphite Guardian", - "defence_level": "110", - "safespot": null, - "lifepoints": "170", - "strength_level": "110", - "id": "1155", - "aggressive": "true", - "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "110" - }, - { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "6223", - "range_animation": "6223", - "combat_audio": "571,573,572", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "38", - "defence_animation": "6232", - "weakness": "7", - "slayer_exp": "40", - "magic_animation": "6223", - "death_animation": "6230", - "name": "Kalphite Worker", - "defence_level": "20", - "safespot": null, - "lifepoints": "40", - "strength_level": "20", - "id": "1156", - "bonuses": "0,0,0,0,0,5,5,1,10,10,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "6223", - "range_animation": "6223", - "attack_speed": "4", - "poisonous": "true", - "magic_level": "1", - "respawn_delay": "65", - "defence_animation": "6232", - "weakness": "7", - "poison_amount": "6", - "magic_animation": "6223", - "death_animation": "6230", - "name": "Kalphite Guardian", - "defence_level": "110", - "safespot": null, - "lifepoints": "170", - "strength_level": "110", - "id": "1157", - "aggressive": "true", - "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "110" - }, - { - "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", - "defence_animation": "6232", - "weakness": "10", - "magic_animation": "6241", - "death_animation": "6242", - "name": "Kalphite Queen", - "defence_level": "300", - "safespot": null, - "lifepoints": "255", - "strength_level": "300", - "id": "1158", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,50,10,100,100,0,0,0,0,0", - "range_level": "1000", - "attack_level": "300" - }, - { - "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", - "defence_animation": "6234", - "weakness": "10", - "magic_animation": "6235", - "death_animation": "6233", - "name": "Kalphite Queen", - "defence_level": "300", - "safespot": null, - "lifepoints": "255", - "strength_level": "300", - "id": "1160", - "aggressive": "true", - "bonuses": "0,0,0,0,0,100,100,100,10,10,0,0,0,0,0", - "range_level": "1000", - "attack_level": "300" - }, - { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Kalphite Larva", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1161", - "aggressive": "false", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge beast", - "melee_animation": "313", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "315", - "name": "The Shaikahan", - "defence_level": "50", - "safespot": null, - "lifepoints": "100", - "strength_level": "50", - "id": "1172", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", - "start_gfx": "250", - "combat_style": "1", - "melee_animation": "428", - "range_animation": "426", - "combat_audio": "425,427,426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "1", - "id": "1183", - "clue_level": "2", - "range_level": "72", - "projectile": "249", - "attack_level": "1" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "68", - "safespot": null, - "lifepoints": "85", - "strength_level": "68", - "id": "1184", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "An elven city guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Elven city guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1185", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A cute bunny rabbit.", - "melee_animation": "6090", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6092", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1192", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "6092", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "6090", - "strength_level": "1", - "id": "1193", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6091" - }, - { - "death_animation": "6092", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "6090", - "strength_level": "1", - "id": "1194", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6091" - }, - { - "examine": "It's an NPC.", - "melee_animation": "4921", - "combat_audio": "300,302,301", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4927", - "slayer_exp": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "35", - "safespot": null, - "lifepoints": "35", - "strength_level": "36", - "id": "1195", - "aggressive": "true", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Eek! A bear cub!", - "slayer_task": "6", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Grizzly bear cub", - "defence_level": "21", - "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "id": "1196", - "range_level": "1", - "attack_level": "21" - }, - { - "name": "Grizzly bear cub", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "1197", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "What big teeth you have.", - "slayer_task": "92", - "melee_animation": "6559", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6558", - "name": "Dire Wolf", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "1198", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "An elf tracker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Elf Tracker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1199", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of King Tyras's men.", - "melee_animation": "414", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Tyras guard", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "1200", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "I don't want to get on the wrong side of him.", - "start_gfx": "250", - "combat_style": "1", - "melee_animation": "426", - "combat_audio": "425,427,426", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "1201", - "clue_level": "2", - "range_level": "1", - "projectile": "249", - "attack_level": "1" - }, - { - "examine": "One of King Tyras's men.", - "melee_animation": "395", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Tyras guard", - "defence_level": "110", - "safespot": null, - "lifepoints": "110", - "strength_level": "105", - "id": "1203", - "range_level": "50", - "attack_level": "95" - }, - { - "examine": "One of King Tyras's men.", - "melee_animation": "395", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Tyras guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "112", - "strength_level": "1", - "id": "1204", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Responsible for the food and equipment of the troops.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Quartermaster", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1208", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Mysterious swamp lights...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Will o' the wisp", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1211", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems strangely familiar...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Parroty Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1216", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old gardener.", - "melee_animation": "433", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gardener", - "defence_level": "4", - "safespot": null, - "lifepoints": "5", - "strength_level": "4", - "id": "1217", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "A scary, man eating ghoul.", - "slayer_task": "37", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "442,444,443", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "50", - "magic_animation": "422", - "death_animation": "9055", - "name": "Ghoul", - "defence_level": "30", - "safespot": null, - "lifepoints": "50", - "strength_level": "40", - "id": "1218", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Yuck! It's all slimy!", - "melee_animation": "1273", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1272", - "name": "Leech", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "1219", - "aggressive": "true", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Could his name be 'Lurch'?", - "melee_animation": "7183", - "slayer_exp": "50", - "death_animation": "7185", - "name": "Vampire", - "defence_level": "52", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1220", - "clue_level": "1", - "range_level": "1", - "attack_level": "52" - }, - { - "slayer_exp": "40", - "name": "Vampire", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1223", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Vampire", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1225", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A marsh coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Myre Blamish Snail", - "defence_level": "22", - "safespot": null, - "lifepoints": "8", - "strength_level": "0", - "id": "1227", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "5", - "attack_level": "0" - }, - { - "examine": "A blood coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Blood Blamish Snail", - "defence_level": "45", - "safespot": null, - "lifepoints": "13", - "strength_level": "0", - "id": "1228", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "12", - "attack_level": "0" - }, - { - "examine": "A muddy coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Ochre Blamish Snail", - "defence_level": "18", - "safespot": null, - "lifepoints": "10", - "strength_level": "0", - "id": "1229", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "7", - "attack_level": "0" - }, - { - "examine": "A bruise blue coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Bruise Blamish Snail", - "defence_level": "40", - "safespot": null, - "lifepoints": "12", - "strength_level": "0", - "id": "1230", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "15", - "attack_level": "0" - }, - { - "agg_radius": "", - "examine": "A branch bark coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Bark Blamish Snail", - "defence_level": "20", - "safespot": null, - "lifepoints": "22", - "strength_level": "0", - "id": "1231", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "10", - "attack_level": "0" - }, - { - "examine": "A marsh coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Myre Blamish Snail", - "defence_level": "22", - "safespot": null, - "lifepoints": "8", - "strength_level": "0", - "id": "1232", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "5", - "attack_level": "0" - }, - { - "examine": "A blood coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Blood Blamish Snail", - "defence_level": "45", - "safespot": null, - "lifepoints": "13", - "strength_level": "0", - "id": "1233", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "12", - "attack_level": "0" - }, - { - "examine": "A muddy coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Ochre Blamish Snail", - "defence_level": "18", - "safespot": null, - "lifepoints": "10", - "strength_level": "0", - "id": "1234", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "7", - "attack_level": "0" - }, - { - "examine": "A bruise blue coloured blamish snail, these types are said to spit acid.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "8144", - "name": "Bruise Blamish Snail", - "defence_level": "40", - "safespot": null, - "lifepoints": "12", - "strength_level": "0", - "id": "1235", - "aggressive": "true", - "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", - "range_level": "15", - "attack_level": "0" - }, - { - "examine": "A Bedabin nomad fighter - a sandy swordsman.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Bedabin Nomad Fighter", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "1239", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A shadowy sort of entity", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Loar Shadow", - "defence_level": "26", - "safespot": null, - "lifepoints": "38", - "strength_level": "30", - "id": "1240", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", - "melee_animation": "1283", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "1284", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1308", - "name": "Loar Shade", - "defence_level": "26", - "safespot": null, - "lifepoints": "38", - "strength_level": "30", - "id": "1241", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "A shadowy sort of entity", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Phrin Shadow", - "defence_level": "42", - "safespot": null, - "lifepoints": "56", - "strength_level": "47", - "id": "1243", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", - "melee_animation": "1283", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1287", - "name": "Phrin Shade", - "defence_level": "42", - "safespot": null, - "lifepoints": "56", - "strength_level": "47", - "id": "1244", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "A shadowy sort of entity", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Riyl Shadow", - "defence_level": "60", - "safespot": null, - "lifepoints": "76", - "strength_level": "55", - "id": "1245", - "aggressive": "true", - "range_level": "1", - "attack_level": "88" - }, - { - "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", - "melee_animation": "1283", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1287", - "name": "Riyl Shade", - "defence_level": "60", - "safespot": null, - "lifepoints": "76", - "strength_level": "55", - "id": "1246", - "aggressive": "true", - "range_level": "1", - "attack_level": "88" - }, - { - "examine": "A shadowy sort of entity", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Asyn Shadow", - "defence_level": "70", - "safespot": null, - "lifepoints": "90", - "strength_level": "84", - "id": "1247", - "aggressive": "true", - "range_level": "1", - "attack_level": "102" - }, - { - "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", - "melee_animation": "1283", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1287", - "name": "Asyn Shade", - "defence_level": "70", - "safespot": null, - "lifepoints": "90", - "strength_level": "84", - "id": "1248", - "aggressive": "true", - "range_level": "1", - "attack_level": "102" - }, - { - "examine": "A shadowy sort of entity", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "0", - "name": "Fiyr Shadow", - "defence_level": "85", - "safespot": null, - "lifepoints": "110", - "strength_level": "100", - "id": "1249", - "aggressive": "true", - "range_level": "1", - "attack_level": "120" - }, - { - "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", - "melee_animation": "1283", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1287", - "name": "Fiyr Shade", - "defence_level": "85", - "safespot": null, - "lifepoints": "110", - "strength_level": "100", - "id": "1250", - "aggressive": "true", - "range_level": "1", - "attack_level": "120" - }, - { - "examine": "A local villager of Mort'ton.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Afflicted", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "1257", - "range_level": "1", - "attack_level": "43" - }, - { - "examine": "A local villager of Mort'ton.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Afflicted", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "44", - "id": "1258", - "range_level": "1", - "attack_level": "44" - }, - { - "examine": "A local villager of Mort'ton.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Afflicted", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "1261", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "A local villager of Mort'ton.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Afflicted", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "46", - "id": "1262", - "aggressive": "true", - "range_level": "1", - "attack_level": "46" - }, - { - "examine": "Slightly more magical.", - "name": "Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1263", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A follower of Saradomin.", - "melee_animation": "376", - "range_animation": "0", - "poisonous": "true", - "magic_level": "90", - "spell_id": "41", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Saradomin wizard", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "90", - "id": "1264", - "aggressive": "true", - "range_level": "70", - "attack_level": "90" - }, - { - "examine": "No one likes crabs...", - "melee_animation": "1312", - "range_animation": "1312", - "combat_audio": "717,723,722", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1265", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rocky outcrop.", - "melee_animation": "1312", - "range_animation": "1312", - "attack_speed": "5", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rocks", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1266", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No one likes crabs...", - "melee_animation": "1312", - "range_animation": "1312", - "combat_audio": "717,723,722", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "1313", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1267", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rocky outcrop.", - "melee_animation": "1312", - "range_animation": "1312", - "attack_speed": "5", - "defence_animation": "1313", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rocks", - "defence_level": "4", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1268", - "aggressive": "true", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "A Fremennik bard.", - "melee_animation": "1312", - "range_animation": "1312", - "attack_speed": "5", - "defence_animation": "1313", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Olaf the Bard", - "defence_level": "8", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1269", - "aggressive": "true", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Distinctly troll-shaped.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lalli", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1270", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Freshly shorn.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Golden sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1271", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Lovely thick wool.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Golden sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1272", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A powerful spirit that lives in this lake.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fossegrimen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1273", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like he's had a few drinks already.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ospak", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1274", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Doesn't look like the musical type.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Styrmir", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1275", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Waiting for the show.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Torbrund", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1276", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sits impatiently with his friends.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fridgeir", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1277", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's fat, he's round, he bounces on the ground. That's how he got the job.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Longhall Bouncer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1278", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fearful spirit of the drowned.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "The Draugen", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "1279", - "aggressive": "true", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A Fremennik hunter.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigli the Huntsman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1281", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik merchant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigmund The Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1282", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik navigator.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swensen the Navigator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1283", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Every innkeeper's best friend!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Manni the Reveller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1286", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supposedly fixes things around Gielinor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Council workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1287", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik riddler.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peer the Seer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1288", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik hero.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Thorvald the Warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1289", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your challenge awaits! ", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", - "safespot": null, - "lifepoints": "14", - "strength_level": "13", - "id": "1290", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He lives again!", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", - "safespot": null, - "lifepoints": "14", - "strength_level": "13", - "id": "1291", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He keeps on living!", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", - "safespot": null, - "lifepoints": "14", - "strength_level": "13", - "id": "1292", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He just keeps on going.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", - "safespot": null, - "lifepoints": "14", - "strength_level": "13", - "id": "1293", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "The Fremennik tribe's chieftain.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brundt the Chieftain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1294", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Stands around and looks at stuff all day.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1296", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1297", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What a rubbish job he has.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Town Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1298", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Longhall barkeep", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A bartender.", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1300", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Pretty shabbily dressed for a clothes shop owner.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Yrsa", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1301", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There's something fishy about this guy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fisherman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1302", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sells and makes weapons and armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skulgrimen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1303", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's strong to the finish", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sailor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1304", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Agnar", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1305", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Freidir", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1306", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "395", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Borrokar", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1307", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Lanzig", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1308", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pontak", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1309", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sassilik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1313", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Rellekka's many citizens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Inga", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1314", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Fish-tastic!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish monger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1315", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder what he does with all that fur?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fur trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1316", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the stalls secure.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Market Guard", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "1317", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A hardened Fremennik warrior.", - "melee_animation": "401", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Warrior", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "1318", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A cunning animal.", - "melee_animation": "6562", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6570", - "name": "Fox", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "1319", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Hoppity", - "melee_animation": "1245", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "1246", - "name": "Bunny", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1320", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "1246", - "name": "Bunny", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "1245", - "strength_level": "1", - "id": "1321", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1244" - }, - { - "examine": "Cute. But deadly.", - "slayer_task": "6", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Bear Cub", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "8", - "id": "1326", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Cute. But deadly.", - "name": "Bear Cub", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "1327", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Horned horsey.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "369,371,370", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "90", - "defence_animation": "6375", - "weakness": "7", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn Foal", - "defence_level": "10", - "safespot": null, - "lifepoints": "15", - "strength_level": "6", - "id": "1328", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Cute but evil.", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "369,371,370", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Black unicorn Foal", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "1329", - "aggressive": "false", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "Fearsome predator found only in the Fremennik Province", - "slayer_task": "92", - "melee_animation": "6559", - "range_animation": "0", - "combat_audio": "481,491,490", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "69", - "magic_animation": "0", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "35", - "safespot": null, - "lifepoints": "69", - "strength_level": "35", - "id": "1330", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "A horror from the ocean depths...", - "combat_style": "1", - "melee_animation": "1340", - "range_animation": "1340", - "attack_speed": "5", - "defence_animation": "1341", - "magic_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "70", - "safespot": null, - "lifepoints": "70", - "strength_level": "80", - "id": "1338", - "clue_level": "1", - "range_level": "85", - "projectile": "288", - "attack_level": "80" - }, - { - "examine": "A horror from the ocean depths...", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1339", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "288", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1340", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "288", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "1341", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "1342", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "1343", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1344", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1345", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1346", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1347", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths.", - "slayer_task": "24", - "melee_animation": "1341", - "range_animation": "0", - "magic_level": "60", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "60", - "safespot": null, - "lifepoints": "128", - "strength_level": "60", - "id": "1351", - "aggressive": "true", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "A horror from the ocean depths.", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1352", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths.", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1353", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths.", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1354", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths.", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1355", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horror from the ocean depths.", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1356", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems happy to see you.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1360", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Skraeling", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "401", - "strength_level": "1", - "id": "1367", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Skraeling", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "395", - "strength_level": "1", - "id": "1368", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "Hmm", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishmonger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1369", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "At least he eats his greens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Greengrocer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1370", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the throne room.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1374", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's cutting the wheat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1377", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No-one would mistake her for a duchess.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Flower Girl", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1378", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Alrik", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "422", - "strength_level": "1", - "id": "1381", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "He's strong to the finish, because he eats cabbage.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sailor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1385", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Rannveig", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "422", - "strength_level": "1", - "id": "1386", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Valgerd", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "422", - "strength_level": "1", - "id": "1388", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Skraeling", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "395", - "strength_level": "1", - "id": "1389", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Broddi", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "422", - "strength_level": "1", - "id": "1390", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Skraeling", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "395", - "strength_level": "1", - "id": "1391", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Ragnvald", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "810", - "strength_level": "1", - "id": "1392", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "Hmm", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishmonger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1393", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "At least he eats his greens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Greengrocer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1394", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's a lumberjack", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lumberjack Leif", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1395", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's just mining his own business.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Miner Magnus", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1396", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There's something fishy about him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fisherman Frodi", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1397", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She's looking a bit weedy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gardener Gunnhild", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1398", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "1401", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "1402", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "5389", - "name": "Rooster", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "5387", - "strength_level": "1", - "id": "1403", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5388" - }, - { - "death_animation": "6092", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "6090", - "strength_level": "1", - "id": "1404", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6091" - }, - { - "examine": "There to help me make my bids.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lumdo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1419", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official looking Gnome with small beady eyes.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "G.L.O. Caranock", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1427", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Duke", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1442", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Oipuis", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1443", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Uyoro", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1444", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1387", - "death_animation": "1384", - "name": "Ouhai", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1445", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Uodai", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1446", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Padulah", - "defence_level": "1", - "safespot": null, - "lifepoints": "133", - "strength_level": "1", - "id": "1447", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rather sleepy looking guard", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sleeping Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1451", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An adorable little monkey child.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1452", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks like the Monkey's Uncle.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Monkey's Uncle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1453", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks like the Monkey's Aunt.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Monkey's Aunt", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1454", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A scimitar wielding ninja monkey.", - "slayer_task": "61", - "melee_animation": "1392", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "1384", - "name": "Monkey Guard", - "defence_level": "66", - "safespot": null, - "lifepoints": "94", - "strength_level": "66", - "id": "1455", - "range_level": "1", - "attack_level": "66" - }, - { - "examine": "A bow wielding ninja monkey.", - "slayer_task": "61", - "combat_style": "1", - "melee_animation": "0", - "range_animation": "1394", - "combat_audio": "629,631,630", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey Archer", - "defence_level": "58", - "safespot": null, - "lifepoints": "82", - "strength_level": "43", - "id": "1456", - "aggressive": "true", - "range_level": "58", - "attack_level": "43" - }, - { - "combat_style": "1", - "melee_animation": "1394", - "combat_audio": "629,631,630", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Monkey Archer", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1457", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "1394", - "combat_audio": "629,631,630", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Monkey Archer", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1458", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", - "slayer_task": "61", - "melee_animation": "1402", - "range_animation": "1402", - "combat_audio": "629,631,630", - "attack_speed": "6", - "defence_animation": "1403", - "weakness": "8", - "magic_animation": "1402", - "death_animation": "1404", - "name": "Monkey Guard", - "defence_level": "90", - "safespot": null, - "lifepoints": "130", - "strength_level": "130", - "id": "1459", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", - "slayer_task": "61", - "melee_animation": "1402", - "range_animation": "0", - "combat_audio": "629,631,630", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "1404", - "name": "Monkey Guard", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "1460", - "aggressive": "true", - "range_level": "1", - "attack_level": "63" - }, - { - "examine": "A huge brutish gorilla stands here", - "slayer_task": "61", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Elder Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1461", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1463", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1464", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A large and lumbering undead monkey.", - "slayer_task": "61", - "melee_animation": "1383", - "range_animation": "0", - "combat_audio": "629,631,630", - "attack_speed": "4", - "magic_level": "60", - "respawn_delay": "50", - "defence_animation": "1393", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1384", - "name": "Monkey Zombie", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "60", - "id": "1465", - "aggressive": "true", - "bonuses": "35,35,35,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "150" - }, - { - "examine": "A large and lumbering undead monkey stands here, blocking the way.", - "melee_animation": "1392", - "combat_audio": "629,631,630", - "attack_speed": "4", - "magic_level": "90", - "respawn_delay": "13", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Monkey Zombie", - "defence_level": "90", - "safespot": null, - "lifepoints": "90", - "strength_level": "110", - "id": "1466", - "aggressive": "true", - "bonuses": "35,35,35,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "150" - }, - { - "examine": "A recently deceased monkey. Its flesh seems to be worse for the wear.", - "melee_animation": "1392", - "combat_audio": "629,631,630", - "attack_speed": "4", - "magic_level": "60", - "respawn_delay": "50", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Monkey Zombie", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "60", - "id": "1467", - "aggressive": "true", - "bonuses": "15,15,15,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "He looks like the type of guy who would mind monkeys.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey minder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1469", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Greater Jungle demon. A magical aura emanates from its hide.", - "range_animation": "0", - "combat_audio": "400,404,403", - "attack_speed": "2", - "magic_level": "50", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Jungle demon", - "defence_level": "50", - "safespot": null, - "lifepoints": "428", - "strength_level": "50", - "id": "1472", - "aggressive": "true", - "range_level": "50", - "attack_level": "50" - }, - { - "melee_animation": "6249", - "respawn_delay": "60", - "defence_animation": "6250", - "death_animation": "6251", - "name": "Spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "1473", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a brightly coloured bird of the jungle.", - "slayer_task": "7", - "melee_animation": "6775", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "6779", - "name": "Bird", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "1475", - "range_level": "1", - "attack_level": "5" - }, - { - "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", - "weakness": "8", - "magic_animation": "0", - "death_animation": "6777", - "name": "Bird", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "1476", - "range_level": "1", - "attack_level": "5" - }, - { - "slayer_exp": "17", - "name": "Scorpion", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "1477", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very dangerous looking spider", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "47", - "id": "1478", - "aggressive": "true", - "range_level": "63", - "attack_level": "47" - }, - { - "name": "Snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3609,3608,3610", - "strength_level": "1", - "id": "1479", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Small ninja monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1480", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Medium ninja monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1481", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Bearded gorilla", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "1483", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ancient monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1484", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Small zombie monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1485", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Large zombie monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1486", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1487", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge nail beast. Its nails appear very sharp.", - "melee_animation": "5989", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5990", - "name": "Nail beast", - "defence_level": "20", - "safespot": null, - "lifepoints": "550", - "strength_level": "150", - "id": "1521", - "range_level": "1", - "attack_level": "150" - }, - { - "examine": "He is one", - "melee_animation": "5970", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5972", - "name": "Undead Lumberjack", - "defence_level": "10", - "safespot": null, - "lifepoints": "100", - "strength_level": "73", - "id": "1524", - "range_level": "1", - "attack_level": "73" - }, - { - "examine": "His robes prominently display the star of Saradomin.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zealot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1528", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Imp", - "defence_level": "1", - "safespot": null, - "movement_radius": "25", - "lifepoints": "8", - "strength_level": "1", - "id": "1531", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Handy for hindering the enemy team's movement.", - "combat_style": "1", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "9658", - "name": "Barricade", - "defence_level": "1", - "poison_immune": "true", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1532", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Handy for hindering the enemy team's movement.", - "combat_style": "1", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "9658", - "name": "Barricade", - "defence_level": "1", - "poison_immune": "true", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1533", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Handy for hindering the enemy team's movement.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "9658", - "name": "Barricade", - "defence_level": "1", - "poison_immune": "true", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1534", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Handy for hindering the enemy team's movement.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "9658", - "name": "Barricade", - "defence_level": "1", - "poison_immune": "true", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1535", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "185", - "respawn_delay": "60", - "defence_animation": "186", - "death_animation": "188", - "name": "Possessed pickaxe", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "1536", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A strange disturbance in the air.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Haze", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1537", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Well", - "slayer_task": "75", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Corpse", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1538", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I don't think the pickaxe is for hitting rocks.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeletal miner", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "1539", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5540", - "respawn_delay": "60", - "defence_animation": "5541", - "death_animation": "5542", - "name": "Treus Dayth", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1540", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "25", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1541", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5540", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5541", - "slayer_exp": "25", - "death_animation": "5542", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "1549", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A large boisterous bird, a delicacy for ogres.", - "slayer_task": "7", - "melee_animation": "6761", - "range_animation": "0", - "combat_audio": "1449,1451,1450", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "6762", - "name": "Chompy bird", - "defence_level": "3", - "safespot": null, - "poison_immune": "true", - "lifepoints": "10", - "strength_level": "5", - "id": "1550", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "5" - }, - { - "name": "Mischievous ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1551", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ug", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1553", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Arrg", - "defence_level": "1", - "safespot": null, - "lifepoints": "150", - "strength_level": "1", - "attack_speed": "5", - "id": "1556", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Ug", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "1557", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not man's best friend.", - "slayer_task": "92", - "melee_animation": "6559", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6558", - "name": "Ice wolf", - "defence_level": "51", - "safespot": null, - "lifepoints": "72", - "strength_level": "51", - "id": "1558", - "aggressive": "true", - "range_level": "1", - "attack_level": "51" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1560", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1561", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1562", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1563", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1564", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1565", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1566", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A tall and imposing man who's more than familiar with boating.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cyreg Paddlehorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1567", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "1582", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "1583", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "1584", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "1585", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "1586", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "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", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "60", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "60", - "strength_level": "30", - "id": "1587", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "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", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "60", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "60", - "strength_level": "30", - "id": "1588", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Young but still dangerous.", - "slayer_task": "68", - "melee_animation": "25", - "range_animation": "25", - "combat_audio": "405,407,406", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "32", - "defence_animation": "26", - "weakness": "3", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby red dragon", - "defence_level": "42", - "safespot": null, - "lifepoints": "50", - "strength_level": "40", - "id": "1589", - "aggressive": "true", - "bonuses": "50,40,64,60,30,45,40,40,20,40,40,20,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Its scales seem to be made of bronze.", - "slayer_task": "13", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "40", - "defence_animation": "89", - "weakness": "7", - "slayer_exp": "125", - "magic_animation": "80", - "death_animation": "92", - "name": "Bronze dragon", - "defence_level": "112", - "safespot": null, - "lifepoints": "122", - "strength_level": "112", - "id": "1590", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "112" - }, - { - "examine": "Its scales seem to be made of iron.", - "slayer_task": "50", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "45", - "defence_animation": "89", - "weakness": "7", - "slayer_exp": "173", - "magic_animation": "80", - "death_animation": "92", - "name": "Iron dragon", - "defence_level": "165", - "safespot": null, - "lifepoints": "165", - "strength_level": "165", - "id": "1591", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "165" - }, - { - "examine": "Its scales seem to be made of steel.", - "slayer_task": "80", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "45", - "defence_animation": "89", - "weakness": "7", - "slayer_exp": "221", - "magic_animation": "80", - "death_animation": "92", - "name": "Steel dragon", - "defence_level": "215", - "safespot": null, - "lifepoints": "210", - "strength_level": "215", - "id": "1592", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "215" - }, - { - "examine": "An unsuitable pet.", - "slayer_task": "27", - "melee_animation": "6562", - "range_animation": "6562", - "magic_level": "20", - "respawn_delay": "20", - "defence_animation": "6563", - "weakness": "7", - "magic_animation": "6562", - "death_animation": "6564", - "name": "Wild dog", - "defence_level": "25", - "safespot": null, - "lifepoints": "62", - "strength_level": "25", - "id": "1593", - "aggressive": "true", - "bonuses": "10,15,5,10,30,5,30,30,30,10,30,5,40,5,5", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "Looks like it's got Rabies!", - "melee_animation": "6562", - "range_animation": "6562", - "magic_level": "20", - "respawn_delay": "20", - "defence_animation": "6563", - "magic_animation": "6562", - "death_animation": "6564", - "name": "Wild dog", - "defence_level": "25", - "safespot": null, - "lifepoints": "62", - "strength_level": "25", - "id": "1594", - "aggressive": "true", - "bonuses": "10,15,5,10,30,5,30,30,30,10,30,5,40,5,5", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "A spiky crawling critter. ", - "slayer_task": "16", - "melee_animation": "266", - "range_animation": "266", - "combat_audio": "341,343,342", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "267", - "weakness": "1", - "slayer_exp": "22", - "magic_animation": "266", - "death_animation": "265", - "name": "Cave crawler", - "defence_level": "30", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "1600", - "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", - "range_level": "38", - "attack_level": "28" - }, - { - "examine": "A spiky crawling critter. ", - "melee_animation": "266", - "range_animation": "266", - "combat_audio": "341,343,342", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "267", - "slayer_exp": "22", - "magic_animation": "266", - "death_animation": "265", - "name": "Cave crawler", - "defence_level": "30", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "1601", - "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A spiky crawling critter. ", - "melee_animation": "266", - "range_animation": "266", - "combat_audio": "341,343,342", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "267", - "slayer_exp": "22", - "magic_animation": "266", - "death_animation": "265", - "name": "Cave crawler", - "defence_level": "30", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "1602", - "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A spiky crawling critter. ", - "melee_animation": "266", - "range_animation": "266", - "combat_audio": "341,343,342", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "267", - "slayer_exp": "22", - "magic_animation": "266", - "death_animation": "265", - "name": "Cave crawler", - "defence_level": "30", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "1603", - "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A very smelly ghost.", - "slayer_task": "0", - "start_gfx": "334", - "combat_style": "2", - "melee_animation": "9466", - "attack_speed": "4", - "weakness": "5", - "slayer_exp": "90", - "magic_animation": "9466", - "death_animation": "9467", - "lifepoints": "90", - "id": "1604", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", - "range_animation": "1507", - "combat_audio": "272,275,274", - "magic_level": "105", - "end_gfx": "336", - "defence_animation": "9468", - "name": "Aberrant spectre", - "defence_level": "90", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "335", - "attack_level": "1" - }, - { - "examine": "A very smelly ghost.", - "slayer_task": "0", - "start_gfx": "334", - "combat_style": "2", - "melee_animation": "9466", - "attack_speed": "4", - "weakness": "5", - "slayer_exp": "90", - "magic_animation": "9466", - "death_animation": "9467", - "lifepoints": "90", - "id": "1605", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", - "range_animation": "1507", - "combat_audio": "272,275,274", - "magic_level": "105", - "end_gfx": "336", - "defence_animation": "9468", - "name": "Aberrant spectre", - "defence_level": "90", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "335", - "attack_level": "1" - }, - { - "examine": "A very smelly ghost.", - "slayer_task": "0", - "start_gfx": "334", - "combat_style": "2", - "melee_animation": "9466", - "attack_speed": "4", - "weakness": "5", - "slayer_exp": "90", - "magic_animation": "9466", - "death_animation": "9467", - "lifepoints": "90", - "id": "1606", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", - "range_animation": "1507", - "combat_audio": "272,275,274", - "magic_level": "105", - "end_gfx": "336", - "defence_animation": "9468", - "name": "Aberrant spectre", - "defence_level": "90", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "335", - "attack_level": "1" - }, - { - "examine": "A very smelly ghost.", - "slayer_task": "0", - "start_gfx": "334", - "combat_style": "2", - "melee_animation": "9466", - "attack_speed": "4", - "weakness": "5", - "slayer_exp": "90", - "magic_animation": "9466", - "death_animation": "9467", - "lifepoints": "90", - "id": "1607", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", - "range_animation": "1507", - "combat_audio": "272,275,274", - "magic_level": "105", - "end_gfx": "336", - "defence_animation": "9468", - "name": "Aberrant spectre", - "defence_level": "90", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "335", - "attack_level": "1" - }, - { - "examine": "Large, heavy, with sharp things attached to its head.", - "melee_animation": "9439", - "range_animation": "9439", - "combat_audio": "588,590,589", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9441", - "slayer_exp": "97", - "magic_animation": "9439", - "death_animation": "9440", - "name": "Kurask", - "defence_level": "105", - "safespot": null, - "lifepoints": "97", - "strength_level": "105", - "id": "1608", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "67" - }, - { - "examine": "Large, heavy, with sharp things attached to its head.", - "melee_animation": "9439", - "range_animation": "9439", - "combat_audio": "588,590,589", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9441", - "slayer_exp": "97", - "magic_animation": "9439", - "death_animation": "9440", - "name": "Kurask", - "defence_level": "105", - "safespot": null, - "lifepoints": "97", - "strength_level": "105", - "id": "1609", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "67" - }, - { - "examine": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "combat_audio": "428,430,429", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9455", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Gargoyle", - "defence_level": "107", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "1610", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "Flies like a rock.", - "slayer_task": "84", - "melee_animation": "1516", - "range_animation": "1516", - "attack_speed": "5", - "defence_animation": "0", - "weakness": "0", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Swarming turoth", - "defence_level": "42", - "safespot": null, - "lifepoints": "105", - "strength_level": "104", - "id": "1611", - "clue_level": "2", - "range_level": "49", - "attack_level": "45" - }, - { - "examine": "A tortured screaming soul.", - "melee_animation": "9449", - "range_animation": "1523", - "combat_audio": "284,286,285", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9451", - "slayer_exp": "22", - "magic_animation": "1523", - "death_animation": "9450", - "name": "Banshee", - "defence_level": "22", - "safespot": null, - "lifepoints": "22", - "strength_level": "15", - "id": "1612", - "aggressive": "true", - "clue_level": "0", - "bonuses": "0,0,0,0,0,5,5,5,0,5,0,0,0,0,0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An evil death demon.", - "melee_animation": "9487", - "range_animation": "1528", - "combat_audio": "645,647,646", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9489", - "magic_animation": "1528", - "death_animation": "9488", - "name": "Nechryael", - "defence_level": "105", - "safespot": null, - "lifepoints": "105", - "strength_level": "97", - "id": "1613", - "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "97" - }, - { - "examine": "An evil death spawn.", - "melee_animation": "1540", - "range_animation": "1540", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "1540", - "death_animation": "9460", - "name": "Death spawn", - "defence_level": "30", - "poison_immune": "true", - "safespot": null, - "lifepoints": "60", - "strength_level": "7", - "id": "1614", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", - "range_level": "1", - "attack_level": "67" - }, - { - "examine": "A denizen of the Abyss!", - "slayer_task": "1", - "melee_animation": "1537", - "range_animation": "1537", - "combat_audio": "276,278,277", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "150", - "magic_animation": "1537", - "death_animation": "1538", - "name": "Abyssal demon", - "defence_level": "135", - "safespot": null, - "lifepoints": "150", - "strength_level": "67", - "id": "1615", - "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "97" - }, - { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "combat_audio": "288,290,289", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "45", - "id": "1616", - "aggressive": "false", - "bonuses": "0,0,0,0,0,20,20,0,20,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "combat_audio": "288,290,289", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "45", - "id": "1617", - "aggressive": "false", - "bonuses": "0,0,0,0,0,20,20,0,20,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "combat_audio": "312,314,313", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "9132", - "slayer_exp": "120", - "magic_animation": "1552", - "death_animation": "9131", - "name": "Bloodveld", - "defence_level": "30", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", - "id": "1618", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "combat_audio": "312,314,313", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "9132", - "slayer_exp": "120", - "magic_animation": "1552", - "death_animation": "9131", - "name": "Bloodveld", - "defence_level": "30", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", - "id": "1619", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "The winged reptile.", - "slayer_task": "19", - "melee_animation": "7762", - "range_animation": "7762", - "combat_audio": "363,365,364", - "attack_speed": "5", - "defence_animation": "7761", - "weakness": "2", - "slayer_exp": "37", - "magic_animation": "7762", - "death_animation": "7763", - "name": "Cockatrice", - "defence_level": "35", - "safespot": null, - "lifepoints": "37", - "strength_level": "32", - "id": "1620", - "bonuses": "30,30,30,30,30,35,0,0,0,0,0,0,0,0,0", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "examine": "The winged reptile.", - "melee_animation": "7762", - "range_animation": "7762", - "combat_audio": "363,365,364", - "attack_speed": "5", - "defence_animation": "7761", - "slayer_exp": "37", - "magic_animation": "7762", - "death_animation": "7763", - "name": "Cockatrice", - "defence_level": "35", - "safespot": null, - "lifepoints": "37", - "strength_level": "32", - "id": "1621", - "bonuses": "30,30,30,30,30,35,0,0,0,0,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "He's one big leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "85", - "safespot": null, - "lifepoints": "78", - "strength_level": "85", - "id": "1622", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "He's one big leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "88", - "safespot": null, - "lifepoints": "81", - "strength_level": "88", - "id": "1623", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "58" - }, - { - "examine": "The vacuumed face of evil.", - "slayer_task": "28", - "melee_animation": "1557", - "range_animation": "1557", - "combat_audio": "414,416,415", - "attack_speed": "4", - "defence_animation": "1555", - "weakness": "2", - "slayer_exp": "105", - "magic_animation": "1557", - "death_animation": "1558", - "name": "Dust devil", - "defence_level": "40", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "1624", - "range_level": "1", - "attack_level": "105" - }, - { - "examine": "The cave-dwelling cousin of the dust devils.", - "slayer_task": "28", - "melee_animation": "1557", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "1558", - "name": "Smokedevil", - "defence_level": "61", - "safespot": null, - "lifepoints": "87", - "strength_level": "105", - "id": "1625", - "range_level": "61", - "attack_level": "105" - }, - { - "examine": "She's one leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "86", - "safespot": null, - "lifepoints": "79", - "strength_level": "86", - "id": "1626", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "56" - }, - { - "examine": "It's one leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "84", - "safespot": null, - "lifepoints": "77", - "strength_level": "84", - "id": "1627", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "It's one small leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "83", - "safespot": null, - "lifepoints": "76", - "strength_level": "83", - "id": "1628", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "He's one big leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "87", - "safespot": null, - "lifepoints": "80", - "strength_level": "87", - "id": "1629", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "57" - }, - { - "examine": "He's one big leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "combat_audio": "873,875,874", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9473", - "magic_animation": "9471", - "death_animation": "9472", - "name": "Turoth", - "defence_level": "87", - "safespot": null, - "lifepoints": "80", - "strength_level": "87", - "id": "1630", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "57" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "combat_audio": "696,698,697", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "60", - "defence_animation": "8084", - "weakness": "4", - "slayer_exp": "45", - "death_animation": "8078", - "name": "Pyrefiend", - "defence_level": "19", - "safespot": null, - "lifepoints": "45", - "strength_level": "29", - "id": "1633", - "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", - "clue_level": "1", - "range_level": "19", - "attack_level": "22" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "combat_audio": "696,698,697", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "60", - "defence_animation": "8084", - "weakness": "4", - "slayer_exp": "45", - "death_animation": "8078", - "name": "Pyrefiend", - "defence_level": "19", - "safespot": null, - "lifepoints": "45", - "strength_level": "29", - "id": "1634", - "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", - "clue_level": "1", - "range_level": "19", - "attack_level": "22" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "combat_audio": "696,698,697", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "60", - "defence_animation": "8084", - "weakness": "4", - "slayer_exp": "45", - "death_animation": "8078", - "name": "Pyrefiend", - "defence_level": "19", - "safespot": null, - "lifepoints": "45", - "strength_level": "29", - "id": "1635", - "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", - "clue_level": "1", - "range_level": "19", - "attack_level": "22" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "combat_audio": "696,698,697", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "60", - "defence_animation": "8084", - "weakness": "4", - "slayer_exp": "45", - "death_animation": "8078", - "name": "Pyrefiend", - "defence_level": "19", - "safespot": null, - "lifepoints": "45", - "strength_level": "29", - "id": "1636", - "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", - "clue_level": "1", - "range_level": "19", - "attack_level": "22" - }, - { - "examine": "It's a Jelly.", - "slayer_task": "51", - "melee_animation": "8569", - "range_animation": "8569", - "combat_audio": "547,550,549", - "attack_speed": "5", - "magic_level": "40", - "defence_animation": "8571", - "weakness": "2", - "slayer_exp": "75", - "magic_animation": "8569", - "death_animation": "8570", - "name": "Jelly", - "defence_level": "40", - "safespot": null, - "lifepoints": "75", - "strength_level": "45", - "id": "1637", - "clue_level": "2", - "range_level": "40", - "attack_level": "40" - }, - { - "examine": "Doesn't look so tough...", - "slayer_task": "51", - "melee_animation": "8569", - "range_animation": "0", - "combat_audio": "547,550,549", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "8570", - "name": "Jelly", - "defence_level": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1638", - "clue_level": "2", - "range_level": "49", - "attack_level": "1" - }, - { - "examine": "Wibbly.", - "slayer_task": "51", - "melee_animation": "8569", - "range_animation": "0", - "combat_audio": "547,550,549", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "8570", - "name": "Jelly", - "defence_level": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1639", - "clue_level": "2", - "range_level": "49", - "attack_level": "1" - }, - { - "examine": "There's always room for jelly.", - "slayer_task": "51", - "melee_animation": "8569", - "range_animation": "0", - "combat_audio": "547,550,549", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "8570", - "name": "Jelly", - "defence_level": "49", - "safespot": null, - "lifepoints": "140", - "strength_level": "1", - "id": "1640", - "clue_level": "2", - "range_level": "49", - "attack_level": "1" - }, - { - "melee_animation": "8569", - "combat_audio": "547,550,549", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8571", - "slayer_exp": "75", - "name": "Jelly", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "1641", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "8569", - "combat_audio": "547,550,549", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8571", - "slayer_exp": "75", - "name": "Jelly", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "1642", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil magic user.", - "slayer_task": "49", - "start_gfx": "99", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "magic_level": "75", - "respawn_delay": "12", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Infernal Mage", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1643", - "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "slayer_task": "49", - "start_gfx": "99", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "magic_level": "75", - "respawn_delay": "12", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Infernal Mage", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1644", - "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "examine": "An evil magic user.", - "slayer_task": "49", - "start_gfx": "99", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "magic_level": "75", - "respawn_delay": "12", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Infernal Mage", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1645", - "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "examine": "An evil magic user.", - "slayer_task": "49", - "start_gfx": "99", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "magic_level": "75", - "respawn_delay": "12", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Infernal Mage", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1646", - "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "examine": "An evil magic user.", - "slayer_task": "49", - "start_gfx": "99", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "magic_level": "75", - "respawn_delay": "12", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "811", - "death_animation": "836", - "name": "Infernal Mage", - "defence_level": "60", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1647", - "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "examine": "Now THAT'S handy.", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "9125", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "9127", - "weakness": "9", - "slayer_exp": "19", - "magic_animation": "9125", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "5", - "safespot": null, - "lifepoints": "16", - "strength_level": "5", - "id": "1648", - "bonuses": "5,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Gimmie five. Actually", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "19", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", - "safespot": null, - "lifepoints": "5", - "strength_level": "8", - "id": "1649", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Gimmie five. Actually", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", - "safespot": null, - "lifepoints": "5", - "strength_level": "8", - "id": "1650", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Gimmie five. Actually", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "19", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", - "safespot": null, - "lifepoints": "5", - "strength_level": "8", - "id": "1651", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Gimmie five. Actually", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "19", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", - "safespot": null, - "lifepoints": "5", - "strength_level": "8", - "id": "1652", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Now THAT's handy.", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "1653", - "range_level": "1", - "attack_level": "13" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "1654", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A big severed hand.", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "1655", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "Give the guy a big hand.....", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "1656", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A big severed hand.", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "combat_audio": "377,379,378", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "1657", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A Supplier of Magical robes.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Robe Store owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's in charge of the Skullball Course.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skullball Boss", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1660", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's in charge of the Agility Course.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Agility Boss", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1661", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A skullball guide.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skullball Trainer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1662", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A werewolf agility trainer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Agility Trainer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1663", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like he's guarding a trapdoor...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "1665", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Gardener Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1675", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His beard seems to have a life of its own.", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,451,450", - "magic_level": "1", - "defence_animation": "4665", - "weakness": "3", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "60", - "safespot": null, - "lifepoints": "120", - "strength_level": "60", - "id": "1681", - "aggressive": "true", - "bonuses": "66,62,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A ghost disciple.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost disciple", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1686", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader from across the eastern sea.", - "start_gfx": "0", - "start_height": "0", - "melee_animation": "386", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Ak-Haranu", - "defence_level": "1", - "movement_radius": "2", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1688", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's an undead cow.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "5", - "defence_animation": "5850", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5851", - "name": "Undead cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "3", - "id": "1691", - "aggressive": "false", - "bonuses": "-15,-15,-15,0,0,-21,-21,-21,-21,-21,0,-15,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yup, definitely a chicken... an undead chicken.", - "slayer_task": "7", - "melee_animation": "5387", - "range_animation": "0", - "combat_audio": "355,357,356", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5389", - "name": "Undead chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "1692", - "aggressive": "false", - "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,0,-42,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An extremely vicious lobster.", - "slayer_task": "71", - "melee_animation": "6265", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6267", - "name": "Giant lobster", - "defence_level": "30", - "safespot": null, - "lifepoints": "128", - "strength_level": "30", - "id": "1693", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "An old", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Old crone", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1695", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A creaky old man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Old man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1696", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A spooky ghost villager.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost villager", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1697", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "51", - "magic_animation": "0", - "death_animation": "836", - "name": "Tortured soul", - "defence_level": "38", - "safespot": null, - "lifepoints": "51", - "strength_level": "62", - "id": "1698", - "aggressive": "true", - "range_level": "1", - "attack_level": "52" - }, - { - "examine": "Beware the ghostly shopkeeper's wares!", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1699", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Doesn't look like the bar's open anymore.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost innkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1700", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ghost farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1701", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A ghost banker.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1702", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A ghost sailor.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost sailor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1703", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A ghostship captain.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost captain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1704", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ghost captain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This ghost guards the gates of Port Phasmatys.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1706", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ghost (?)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1707", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ghost (?)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "1708", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard for the humans against monster group.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "H.A.M. Guard", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "1710", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A guard for the humans against monster group.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "H.A.M. Guard", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "1711", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A guard for the humans against monster group.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "H.A.M. Guard", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "1712", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A deacon in the Humans Against Monsters group. A rather enthusiastic chap.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "H.A.M. Deacon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1713", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1714", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1715", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1716", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1717", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A young man with a dark and mysterious past.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jimmy the Chisel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1718", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Manages the mill.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Oak", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Sigmund's elite guards.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Yew", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1740", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Sigmund's elite guards.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evergreen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1741", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evergreen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1742", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evergreen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1743", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tree", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1744", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dead tree", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1745", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Achey Tree", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1746", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tree", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1747", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of Sigmund's H.A.M. splinter group.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tree", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1748", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A giant raptor.", - "melee_animation": "1010", - "combat_audio": "832,834,833", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "1012", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "19", - "safespot": null, - "lifepoints": "34", - "strength_level": "23", - "id": "1751", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "These gnomes know how to get around!", - "slayer_task": "7", - "melee_animation": "6790", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "33", - "safespot": null, - "lifepoints": "37", - "strength_level": "33", - "id": "1752", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A farmer's enemy.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Crow", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1754", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He grows the crops in this area.", - "melee_animation": "7181", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Farmer", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "1757", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He grows the crops in this area.", - "melee_animation": "7181", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Farmer", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "1758", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Farming.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1759", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He grows the crops in this area.", - "melee_animation": "7181", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7185", - "name": "Farmer", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "1760", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Farming the wheat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1761", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and fluffy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1763", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Young, but still beefy.", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "366,368,367", - "attack_speed": "5", - "defence_animation": "5850", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow calf", - "defence_level": "1", - "safespot": null, - "lifepoints": "6", - "strength_level": "1", - "id": "1766", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Where beef comes from.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "369,371,370", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1767", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Young, but still beefy.", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "366,368,367", - "attack_speed": "5", - "defence_animation": "5850", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow calf", - "defence_level": "1", - "safespot": null, - "lifepoints": "6", - "strength_level": "1", - "id": "1768", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1769", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1770", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1771", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1772", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1773", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1774", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1775", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "slayer_task": "38", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "1776", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks quite independent in an aggressive and business like way.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "498,500,499", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hammerspike Stoutbeard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1794", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A short stout menacing fellow.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf gang member", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "1795", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "A short stout menacing fellow.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf gang member", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "1796", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "A short stout menacing fellow.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf gang member", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "1797", - "range_level": "1", - "attack_level": "25" - }, - { - "melee_animation": "1750", - "respawn_delay": "60", - "defence_animation": "1751", - "death_animation": "1752", - "name": "Slagilith", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "1802", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pile of boulders.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rock pile", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1803", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to be guarding a pile of rocks. Interesting job.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1805", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to be guarding a single rock. Interesting job.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1806", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Chieftain of the mountain camp.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hamal the Chieftain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1807", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is that a bear... or a man?", - "melee_animation": "1760", - "range_animation": "0", - "magic_level": "23", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "1759", - "name": "The Kendal", - "defence_level": "23", - "safespot": null, - "lifepoints": "85", - "strength_level": "23", - "id": "1812", - "aggressive": "true", - "range_level": "23", - "attack_level": "23" - }, - { - "melee_animation": "1760", - "respawn_delay": "60", - "defence_animation": "1758", - "death_animation": "1759", - "name": "The Kendal", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "1813", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the inhabitants of the camp.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "1814", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "One of the inhabitants of the camp.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "1815", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "One of the inhabitants of the camp.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "1816", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "One of the inhabitants of the camp.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "1817", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "One of the inhabitants of the camp.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "1818", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Looks a little underfed.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1819", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This goat belongs to the mountain camp people.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1820", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", - "melee_animation": "6001", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1822", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", - "melee_animation": "6001", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1823", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", - "melee_animation": "6001", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1824", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", - "melee_animation": "6001", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "1825", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "combat_audio": "428,430,429", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9455", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Gargoyle", - "defence_level": "107", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "1827", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "It didn't get that big eating flies!", - "melee_animation": "1793", - "range_animation": "1793", - "attack_speed": "6", - "defence_animation": "1794", - "weakness": "1", - "magic_animation": "1793", - "death_animation": "1795", - "name": "Giant frog", - "defence_level": "60", - "safespot": null, - "lifepoints": "100", - "strength_level": "45", - "id": "1828", - "bonuses": "10,10,10,10,10,10,1,10,10,10,10,37,0,0,0", - "range_level": "32", - "attack_level": "45" - }, - { - "examine": "It didn't get that big eating flies.", - "melee_animation": "1793", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "1795", - "name": "Big frog", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "1", - "id": "1829", - "range_level": "26", - "attack_level": "1" - }, - { - "examine": "A foul-smelling blob of protoplasm.", - "slayer_task": "18", - "melee_animation": "1789", - "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "15", - "weakness": "1", - "slayer_exp": "25", - "poison_amount": "3", - "magic_animation": "0", - "death_animation": "1792", - "lifepoints": "25", - "id": "1831", - "aggressive": "false", - "range_animation": "0", - "combat_audio": "784,786,785", - "magic_level": "13", - "defence_animation": "0", - "name": "Cave slime", - "defence_level": "35", - "safespot": null, - "strength_level": "13", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "5", - "magic_animation": "0", - "death_animation": "6082", - "name": "Cave bug", - "defence_level": "6", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1832", - "range_level": "6", - "attack_level": "1" - }, - { - "examine": "A little", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave bug larva", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1833", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an odd smell about him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Candle seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1834", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A short angry guy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Engineer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1840", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The little guy is having trouble standing up right.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Khorvak, a dwarven engineer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1842", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's probably you who will be paying the ferryman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Ferryman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1843", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's probably you who will be paying the ferryman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Ferryman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1844", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It is the avatar of the Arzinian Being of Bordanzan", - "melee_animation": "1840", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "1841", - "name": "Arzinian Avatar of Strength", - "defence_level": "40", - "safespot": null, - "lifepoints": "114", - "strength_level": "40", - "id": "1850", - "range_level": "1", - "attack_level": "40" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Strength", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "1851", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Strength", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1852", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It is the avatar of the Arzinian Being of Bordanzan", - "melee_animation": "1843", - "range_animation": "1843", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "1841", - "name": "Arzinian Avatar of Ranging", - "defence_level": "40", - "safespot": null, - "lifepoints": "114", - "strength_level": "30", - "id": "1853", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Ranging", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "1854", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Ranging", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1855", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It is the avatar of the Arzinian Being of Bordanzan", - "melee_animation": "1844", - "range_animation": "0", - "magic_level": "40", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "1841", - "name": "Arzinian Avatar of Magic", - "defence_level": "40", - "safespot": null, - "lifepoints": "114", - "strength_level": "30", - "id": "1856", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Magic", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "1857", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1840", - "respawn_delay": "60", - "defence_animation": "1842", - "death_animation": "1843", - "name": "Arzinian Avatar of Magic", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "1858", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's the Arzinian Being", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Arzinian Being of Bordanzan", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1859", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sells ranging equipment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1866", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Nemarti looks ready to teach you about ranged combat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranged Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1861", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Drunk man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drunken Ali", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1863", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hassled looking barman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali The barman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1864", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A kebab seller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Kebab seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1865", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Ali the discount animal seller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Camel Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1867", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mischievous looking child.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Street urchin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1868", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old Hag named Alice.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Hag", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1871", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A snake charmer.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Snake Charmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1872", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A foul tempered ugly lumpy yellow horse prone to spitting.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1873", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A slithering serpent.", - "melee_animation": "275", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "278", - "name": "Desert snake", - "defence_level": "6", - "safespot": null, - "lifepoints": "8", - "strength_level": "4", - "id": "1874", - "range_level": "6", - "attack_level": "4" - }, - { - "examine": "A toothless old Snake.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1875", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very tough-looking bandit.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Bandit champion", - "defence_level": "39", - "safespot": null, - "lifepoints": "55", - "strength_level": "39", - "id": "1885", - "aggressive": "true", - "range_level": "1", - "attack_level": "39" - }, - { - "examine": "Probably the weakest bandit in the world ....ever.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cowardly Bandit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1886", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Smooth operator.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Operator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1902", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Menaphite thug.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "0", - "respawn_delay": "5", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Menaphite Thug", - "defence_level": "20", - "safespot": null, - "lifepoints": "60", - "strength_level": "50", - "id": "1904", - "aggressive": "false", - "bonuses": "4,4,4,0,0,9,8,10,0,0,0,9,0,0,0", - "range_level": "0", - "attack_level": "60" - }, - { - "examine": "Menaphite thug.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "0", - "respawn_delay": "5", - "defence_animation": "425", - "magic_animation": "0", - "death_animation": "836", - "name": "Menaphite Thug", - "defence_level": "20", - "safespot": null, - "lifepoints": "60", - "strength_level": "50", - "id": "1905", - "aggressive": "false", - "bonuses": "4,4,4,0,0,9,8,10,0,0,0,9,0,0,0", - "range_level": "0", - "attack_level": "60" - }, - { - "examine": "Tough looking Menaphite.", - "melee_animation": "395", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Tough Guy", - "defence_level": "39", - "safespot": null, - "lifepoints": "114", - "strength_level": "39", - "id": "1906", - "aggressive": "true", - "range_level": "1", - "attack_level": "39" - }, - { - "examine": "Definitely not a chicken.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Desert Phoenix", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1911", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A tough-looking criminal.", - "melee_animation": "412", - "range_animation": "412", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "412", - "death_animation": "9055", - "name": "Kamil", - "defence_level": "20", - "safespot": null, - "lifepoints": "50", - "strength_level": "55", - "id": "1913", - "range_level": "1", - "attack_level": "20" - }, - { - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Dessous", - "defence_level": "1", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "1914", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Dessous", - "defence_level": "1", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "1915", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he sells what he steals...?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bandit shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1917", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hardened by the cutthroat world of archaeology.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeologist", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1918", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very mysterious looking...", - "melee_animation": "377", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Stranger", - "defence_level": "58", - "safespot": null, - "lifepoints": "82", - "strength_level": "58", - "id": "1919", - "aggressive": "true", - "range_level": "1", - "attack_level": "58" - }, - { - "examine": "Looks like a rough-and-ready type.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bartender", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1921", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A tough-looking criminal.", - "melee_animation": "412", - "range_animation": "412", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "412", - "death_animation": "9055", - "name": "Bandit", - "defence_level": "30", - "safespot": null, - "lifepoints": "65", - "strength_level": "70", - "id": "1926", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "This is an NPC.", - "melee_animation": "412", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "100", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Bandit", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "1931", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ice troll.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ice troll", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1935", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1936", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1937", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1938", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1939", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1940", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1941", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "1942", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1951", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1952", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1953", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1954", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1955", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Not a man's best friend.", - "melee_animation": "6579", - "range_animation": "6579", - "magic_level": "30", - "respawn_delay": "28", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Ice wolf", - "defence_level": "60", - "safespot": null, - "lifepoints": "72", - "strength_level": "60", - "id": "1956", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Highly flammable!", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1958", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "A tightly-wrapped monster.", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1961", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Spooky", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "poison_immune": "true", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1962", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "A victim of poor first aid.", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "poison_immune": "true", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1963", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "But who's the daddy?", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1964", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "A tightly-wrapped monster.", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1965", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "A victim of poor first aid.", - "melee_animation": "5549", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "1967", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "I think they're some kind of beetle...", - "slayer_task": "70", - "melee_animation": "1948", - "range_animation": "0", - "poisonous": "true", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "1946", - "name": "Scarabs", - "defence_level": "62", - "safespot": null, - "lifepoints": "88", - "strength_level": "62", - "id": "1969", - "range_level": "1", - "attack_level": "62" - }, - { - "examine": "A wandering merchant.", - "name": "Rasolo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1972", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A giant skeleton.", - "slayer_task": "75", - "melee_animation": "5499", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5503", - "name": "Giant skeleton", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "1973", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Looks hungry!", - "slayer_task": "27", - "melee_animation": "6565", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6564", - "name": "Shadow Hound", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "1976", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "799", - "respawn_delay": "60", - "defence_animation": "434", - "death_animation": "836", - "name": "Fareed", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "1977", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A malnourished worker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1978", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A malnourished worker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1979", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A strange-smelling merchant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Embalmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1980", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A block of a man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Carpenter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1981", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Preach my brother!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1988", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He has a dangerous glint in his eye.", - "combat_style": "2", - "melee_animation": "429", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Possessed Priest", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "30", - "id": "1991", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" - }, - { - "melee_animation": "7588", - "combat_audio": "386,388,387", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "7590", - "slayer_exp": "62", - "death_animation": "7591", - "name": "Crocodile", - "defence_level": "1", - "safespot": null, - "lifepoints": "63", - "strength_level": "1", - "id": "1993", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He has had his day.", - "slayer_task": "27", - "melee_animation": "6568", - "range_animation": "0", - "combat_audio": "544,546,545", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6567", - "name": "Jackal", - "defence_level": "15", - "safespot": null, - "lifepoints": "27", - "strength_level": "18", - "id": "1994", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "Obnoxious", - "melee_animation": "2015", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "2013", - "name": "Locust", - "defence_level": "12", - "safespot": null, - "lifepoints": "34", - "strength_level": "12", - "id": "1995", - "aggressive": "true", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "A very smelly frog.", - "melee_animation": "1021", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1022", - "name": "Plague frog", - "defence_level": "15", - "safespot": null, - "lifepoints": "42", - "strength_level": "15", - "id": "1997", - "aggressive": "true", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "I might give the burgers a miss in this town.", - "slayer_task": "20", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "0", - "name": "Plague cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1998", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I don't fancy eating any part of this.", - "slayer_task": "20", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "0", - "name": "Plague cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1999", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I think they're some kind of beetle.", - "slayer_task": "70", - "melee_animation": "1948", - "range_animation": "0", - "attack_speed": "2", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1946", - "name": "Scarab swarm", - "defence_level": "9", - "safespot": null, - "lifepoints": "900", - "strength_level": "9", - "id": "2001", - "aggressive": "true", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "This mummy looks like it means business!", - "melee_animation": "5554", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "2015", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "An irate warrior-mummy.", - "melee_animation": "5554", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "2016", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "This mummy looks like it means business!", - "melee_animation": "5554", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "2017", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "An irate mummy.", - "melee_animation": "5554", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "2018", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A wizened old warrior.", - "melee_animation": "5554", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5555", - "name": "Mummy", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "2019", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A shimmering creature of blue light.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Light creature", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2021", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Wonder how long he's been here...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange Old Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2024", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "start_gfx": "155", - "combat_style": "2", - "start_height": "96", - "melee_animation": "1162", - "range_animation": "1162", - "attack_speed": "5", - "magic_level": "100", - "end_gfx": "157", - "defence_animation": "420", - "weakness": "3", - "magic_animation": "1162", - "death_animation": "7197", - "name": "Ahrim the Blighted", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "2025", - "aggressive": "true", - "bonuses": "68,68,68,73,-19,103,85,117,73,0,0,68,0,49,0", - "range_level": "1", - "projectile": "156", - "attack_level": "1" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "melee_animation": "2066", - "range_animation": "2066", - "attack_speed": "7", - "magic_level": "1", - "respawn_delay": "0", - "defence_animation": "2063", - "weakness": "9", - "magic_animation": "2066", - "death_animation": "7197", - "name": "Dharok the Wretched", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "100", - "id": "2026", - "aggressive": "true", - "bonuses": "105,105,105,-58,-18,252,250,244,-11,249,0,105,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "melee_animation": "2080", - "range_animation": "2080", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "0", - "defence_animation": "2079", - "weakness": "6", - "magic_animation": "2080", - "death_animation": "7197", - "name": "Guthan the Infested", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "100", - "id": "2027", - "aggressive": "true", - "bonuses": "75,75,75,-50,-19,259,247,241,-11,-250,0,75,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "combat_style": "1", - "melee_animation": "2075", - "range_animation": "2075", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "16", - "defence_animation": "424", - "weakness": "0", - "magic_animation": "2075", - "death_animation": "7197", - "name": "Karil the Tainted", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "2028", - "aggressive": "true", - "bonuses": "0,0,0,-26,134,79,71,90,106,100,0,0,0,0,61", - "range_level": "100", - "projectile": "27", - "attack_level": "1" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "melee_animation": "2068", - "range_animation": "2068", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "0", - "defence_animation": "404", - "weakness": "7", - "magic_animation": "2068", - "death_animation": "7197", - "name": "Torag the Corrupted", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "100", - "id": "2029", - "aggressive": "true", - "bonuses": "72,72,72,-33,-11,221,235,222,0,221,0,72,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A vengeful spirit corrupted by dark magic.", - "melee_animation": "2062", - "range_animation": "2062", - "combat_audio": "", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "2063", - "weakness": "8", - "magic_animation": "2062", - "death_animation": "7197", - "name": "Verac the Defiled", - "defence_level": "100", - "safespot": null, - "lifepoints": "100", - "strength_level": "100", - "id": "2030", - "aggressive": "true", - "bonuses": "72,72,72,-42,-14,227,230,221,0,225,72,78,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "I think I should keep my distance...", - "melee_animation": "2070", - "range_animation": "2070", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "26", - "defence_animation": "2072", - "weakness": "6", - "magic_animation": "2070", - "death_animation": "2073", - "name": "Bloodworm", - "defence_level": "35", - "safespot": null, - "lifepoints": "45", - "strength_level": "20", - "id": "2031", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A nasty little rodent.", - "slayer_task": "67", - "melee_animation": "240", - "range_animation": "240", - "combat_audio": "703,705,704", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "241", - "weakness": "9", - "magic_animation": "240", - "death_animation": "243", - "name": "Crypt rat", - "defence_level": "20", - "safespot": null, - "lifepoints": "35", - "strength_level": "20", - "id": "2032", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "A nasty overgrown rodent.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4934", - "weakness": "9", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Giant crypt rat", - "defence_level": "65", - "safespot": null, - "lifepoints": "70", - "strength_level": "50", - "id": "2033", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "Incey wincey.", - "slayer_task": "76", - "melee_animation": "6249", - "range_animation": "6249", - "combat_audio": "537,539,538", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "6250", - "weakness": "7", - "magic_animation": "6249", - "death_animation": "6251", - "name": "Crypt spider", - "defence_level": "45", - "safespot": null, - "lifepoints": "45", - "strength_level": "47", - "id": "2034", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,10,17,20,0,0,0,0,0", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "Not very incey wincey.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "5327", - "combat_audio": "537,539,538", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "5328", - "weakness": "7", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Giant crypt spider", - "defence_level": "65", - "safespot": null, - "lifepoints": "80", - "strength_level": "67", - "id": "2035", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "combat_audio": "", - "strength_level": "1", - "id": "2044", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2045", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2046", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2047", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2048", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2049", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Skogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2050", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2051", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2052", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2053", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2054", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2055", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Skogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2056", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Skogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "2057", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "What could be hiding in that crack in the wall?", - "slayer_task": "87", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "30", - "magic_animation": "0", - "death_animation": "0", - "name": "Hole in the wall", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "2058", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Slash Bash", - "defence_level": "1", - "safespot": null, - "lifepoints": "103", - "strength_level": "1", - "id": "2060", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "I can see fish swimming in the water.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishing spot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2067", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This one is slacking off.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "2069", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "This one is slacking off.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "2070", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "This one is slacking off.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "2071", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "This one is slacking off.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "2072", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He protects the miners.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin guard", - "defence_level": "15", - "safespot": null, - "lifepoints": "21", - "strength_level": "15", - "id": "2073", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "He protects the miners.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "6008", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Cave goblin guard", - "defence_level": "15", - "safespot": null, - "lifepoints": "21", - "strength_level": "15", - "id": "2074", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "He's working hard!", - "melee_animation": "6007", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2075", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's working hard!", - "melee_animation": "6007", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2076", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's working hard!", - "melee_animation": "6007", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2077", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's working hard!", - "melee_animation": "6007", - "combat_audio": "469,472,471", - "defence_animation": "6008", - "death_animation": "6003", - "name": "Cave goblin miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2078", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Advisor to the Duke of Lumbridge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigmund", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2082", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5327", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5328", - "death_animation": "5329", - "name": "Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2091", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Purple Pewter mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Purple Pewter Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2092", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Yellow Fortune mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Yellow Fortune Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2093", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Blue Opal mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Blue Opal Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2094", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Green Gemstone mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Green Gemstone Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2095", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the White Chisel mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "White Chisel Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2096", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Silver Cog mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silver Cog Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2097", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head secretary of the Brown Engine mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brown Engine Secretary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2098", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Purple Pewter Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2100", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Blue Opal Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2101", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Yellow Fortune Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2102", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Green Gemstone Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2103", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "White Chisel Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2104", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silver Cog Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2105", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the leaders of the business Consortium", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brown Engine Director", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2106", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Purple Pewter mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2109", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Purple Pewter mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2110", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Blue Opal mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2111", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Blue Opal mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2112", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Yellow Fortune mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2113", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Yellow Fortune mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2114", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Green Gemstone mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2115", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Green Gemstone mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2116", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the White Chisel mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2117", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the White Chisel mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2118", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Silver Cog mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2119", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Silver Cog mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2120", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Brown Engine mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2121", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A trader for the Brown Engine mining company.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2122", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He is regulating the flow of goods on the trade floor and maintaining order.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trade Referee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2127", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2130", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2131", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2132", - "range_level": "1", - "attack_level": "34" - }, - { - "slayer_exp": "16", - "name": "Black Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2133", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An elite member of the Black Guard.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "102", - "name": "Black Guard Berserker", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "2134", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "An elite member of the Black Guard.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "102", - "name": "Black Guard Berserker", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "2135", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "An elite member of the Black Guard.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "102", - "name": "Black Guard Berserker", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "2136", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "A gnome traveller, visiting Keldagrim.", - "name": "Gnome traveller", - "defence_level": "1", - "force_talk": "Buying low-level skilling items!", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2138", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very protective of his master... and his property.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dromund's cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2140", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Makes sculptures.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Blasidar the sculptor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2141", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rather personable banker lady.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2163", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rather serious old fella.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2164", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He takes care of the library and its many books.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Librarian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2165", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A customer looking for books.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2167", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like a human traveler visiting the library.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2168", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tends to the plants in the palace garden.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rind the gardener", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2170", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after the factory.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Factory Manager", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2171", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works hard at whatever it is he does.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Factory Worker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2172", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works hard at whatever it is he does.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Factory Worker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2173", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works hard at whatever it is he does.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Factory Worker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2174", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works hard at whatever it is he does.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Factory Worker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2175", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These Dwarf ladies are so attractive!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barmaid", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2178", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes sure the carts don't run anyone over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2180", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes sure the carts don't run anyone over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2181", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes sure the carts don't run anyone over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2182", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes sure the carts don't run anyone over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2183", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes sure the carts don't run anyone over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2184", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Remember: don't drink and ride in mine carts.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2185", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the line clear of traffic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cart conductor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2186", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A loud", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rowdy dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2187", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "You cannot see his ship", - "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": "2205", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He carries a heavy load.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2207", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master at farming.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2234", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master at farming.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2235", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He guards the Draynor Market stalls from thieves.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Market Guard", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "2236", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A local farmer. He may be able to provide some useful information.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2237", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There to help me make my bids.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pig", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2240", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There to help me make my bids.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Piglet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2241", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Porcine.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Piglet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2242", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He provides new players with useful information.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lumbridge Guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2244", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's one of General Khazard's warriors.", - "melee_animation": "401", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Khazard trooper", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "2245", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's one of General Khazard's warriors.", - "melee_animation": "429", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Khazard trooper", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "2246", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "It's a tree gnome trooper.", - "combat_style": "1", - "melee_animation": "190", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "193", - "death_animation": "196", - "name": "Gnome troop", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "2247", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tree gnome trooper.", - "combat_style": "1", - "melee_animation": "190", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "193", - "death_animation": "196", - "name": "Gnome troop", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "2248", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Paladin", - "defence_level": "1", - "safespot": null, - "lifepoints": "66", - "strength_level": "1", - "id": "2256", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An initiate of Zamorak.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dark mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2262", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A blood-drinking denizen of the abyss.", - "melee_animation": "2181", - "range_animation": "0", - "combat_audio": "232,231,230", - "attack_speed": "3", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "0", - "magic_animation": "0", - "death_animation": "2183", - "name": "Abyssal leech", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "2263", - "aggressive": "true", - "range_level": "52", - "attack_level": "1" - }, - { - "examine": "It seems to have eyes in the back of its head...", - "melee_animation": "2186", - "range_animation": "2186", - "attack_speed": "6", - "magic_level": "57", - "defence_animation": "2188", - "weakness": "3", - "slayer_exp": "0", - "magic_animation": "2186", - "death_animation": "2189", - "name": "Abyssal guardian", - "defence_level": "65", - "safespot": null, - "lifepoints": "50", - "strength_level": "96", - "id": "2264", - "aggressive": "true", - "range_level": "135", - "attack_level": "65" - }, - { - "examine": "Apparently walks the abyss.", - "melee_animation": "2192", - "range_animation": "2192", - "combat_audio": "233,235,234", - "attack_speed": "5", - "defence_animation": "2193", - "weakness": "8", - "slayer_exp": "0", - "magic_animation": "2192", - "death_animation": "2194", - "name": "Abyssal walker", - "defence_level": "55", - "safespot": null, - "lifepoints": "95", - "strength_level": "116", - "id": "2265", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Sparkles the Tinsel Snake", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rogue Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2267", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A snowman armed with a holly bow.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rogue Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2269", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A snowman armed with an ice sword.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Emerald Benedict", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2271", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A snowman armed with a winter staff.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spin Blades", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2272", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2274", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2275", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2276", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2277", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2278", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2279", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2280", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2281", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A warrior blessed by Saradomin.", - "melee_animation": "400", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Sir Leye", - "defence_level": "1", - "safespot": null, - "lifepoints": "21", - "strength_level": "1", - "id": "2285", - "aggressive": "true", - "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": "Miss Cheevers", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2288", - "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": "Ms. Hynn Terprett", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2289", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A carpet merchant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rug Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2291", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A carpet merchant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rug Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2292", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A man who deals in rugs.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rug Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2293", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A man who deals in rugs.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rug Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2294", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Perhaps our oldest relatives.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2301", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Young, but still beefy.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "366,368,367", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow calf", - "defence_level": "2", - "safespot": null, - "lifepoints": "6", - "strength_level": "2", - "id": "2310", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "Farmer/sheep liaison officer. Go on - give the dog a bone!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheepdog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2311", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He rules the", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rooster", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2312", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "2313", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "2314", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yep. Definitely a chicken.", - "melee_animation": "5387", - "range_animation": "5387", - "combat_audio": "355,357,356", - "attack_speed": "6", - "respawn_delay": "18", - "defence_animation": "5388", - "magic_animation": "5387", - "death_animation": "5389", - "name": "Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "2315", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Swine.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pig", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2316", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Porker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pig", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2317", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hog.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Piglet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2318", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Porcine.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Piglet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2319", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He has a certain bovine aroma.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Piglet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2320", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Only the Grim Reaper would have a pet like this.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Muncher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2329", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Perhaps this farmer might look after your crops for you.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Vasquen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2333", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Taria", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2336", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fayeth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2342", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2354", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She can look after my money.", - "combat_style": "2", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "476", - "strength_level": "1", - "id": "2355", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "2359", - "clue_level": "2", - "range_level": "1", - "attack_level": "60" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "2360", - "clue_level": "2", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", - "start_gfx": "250", - "combat_style": "1", - "melee_animation": "428", - "range_animation": "426", - "combat_audio": "425,427,426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "1", - "id": "2361", - "clue_level": "2", - "range_level": "64", - "projectile": "249", - "attack_level": "1" - }, - { - "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", - "start_gfx": "250", - "combat_style": "1", - "melee_animation": "428", - "range_animation": "426", - "combat_audio": "425,427,426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "836", - "name": "Elf warrior", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "1", - "id": "2362", - "clue_level": "2", - "range_level": "64", - "projectile": "249", - "attack_level": "1" - }, - { - "examine": "A Mourner showing his true identity.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head mourner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2372", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Mourner", - "slayer_task": "31", - "melee_animation": "428", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mourner", - "defence_level": "61", - "safespot": null, - "lifepoints": "87", - "strength_level": "61", - "id": "2373", - "aggressive": "true", - "range_level": "1", - "attack_level": "61" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2397", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2398", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2399", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2400", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2401", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Mysterious ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2402", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_task": "29", - "examine": "A dwarf gone bad.", - "respawn_delay": "150", - "slayer_exp": "61", - "weakness": "6", - "name": "Chaos dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2423", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "9230", - "name": "Jarvald", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "melee_animation": "9232", - "strength_level": "10", - "id": "2436", - "range_level": "1", - "attack_level": "10", - "defence_animation": "9231" - }, - { - "examine": "Looks like a wanna be Fremennik.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Askeladden", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2439", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This support is propping the door closed.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Door-support", - "defence_level": "1", - "safespot": null, - "lifepoints": "0", - "strength_level": "1", - "id": "2443", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No one likes crabs... especially really big ones!", - "melee_animation": "2368", - "range_animation": "0", - "combat_audio": "717,723,722", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "1313", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "200", - "safespot": null, - "lifepoints": "180", - "strength_level": "80", - "id": "2452", - "aggressive": "true", - "bonuses": "0,0,0,0,0,225,200,175,-10,250,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "Heavy rock!", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2453", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A teeny-tiny horror from the ocean depths...", - "slayer_task": "24", - "melee_animation": "1341", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1342", - "name": "Dagannoth spawn", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "2454", - "aggressive": "true", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "A horror from the ocean depths...", - "melee_animation": "1343", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "120", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "71", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2455", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "294", - "attack_level": "68" - }, - { - "examine": "A horror from the ocean depths...", - "combat_style": "1", - "melee_animation": "1343", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "0", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "50", - "safespot": null, - "lifepoints": "70", - "strength_level": "70", - "id": "2456", - "aggressive": "true", - "clue_level": "1", - "range_level": "70", - "projectile": "294", - "attack_level": "68" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "combat_audio": "1077,1079,1078", - "attack_speed": "6", - "spell_id": "48", - "magic_level": "100", - "respawn_delay": "60", - "defence_animation": "2366", - "magic_animation": "2365", - "death_animation": "2367", - "name": "Wallasalki", - "defence_level": "80", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "2457", - "aggressive": "true", - "bonuses": "0,0,0,0,0,100,150,175,250,-10,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "5", - "respawn_delay": "25", - "defence_animation": "2300", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "20", - "safespot": null, - "lifepoints": "19", - "strength_level": "5", - "id": "2463", - "aggressive": "true", - "bonuses": "25,85,105,75,103,85,65,0,0,0,0,0,0,0,0", - "range_level": "5", - "projectile": "337", - "attack_level": "5" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "10", - "respawn_delay": "25", - "defence_animation": "2300", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "40", - "safespot": null, - "lifepoints": "40", - "strength_level": "10", - "id": "2464", - "aggressive": "true", - "bonuses": "35,105,125,95,128,105,85,0,0,0,0,0,0,0,0", - "range_level": "10", - "projectile": "337", - "attack_level": "10" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "15", - "respawn_delay": "25", - "defence_animation": "2300", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "50", - "safespot": null, - "lifepoints": "60", - "strength_level": "15", - "id": "2465", - "aggressive": "true", - "bonuses": "35,185,185,155,188,125,165,0,0,0,0,0,0,0,0", - "range_level": "15", - "projectile": "337", - "attack_level": "15" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "2300", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "60", - "safespot": null, - "lifepoints": "80", - "strength_level": "45", - "id": "2466", - "aggressive": "true", - "bonuses": "45,235,235,205,238,145,165,0,0,0,0,0,0,0,0", - "range_level": "45", - "projectile": "337", - "attack_level": "45" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "35", - "respawn_delay": "25", - "defence_animation": "2300", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "45", - "id": "2467", - "aggressive": "true", - "bonuses": "45,285,285,255,288,145,165,0,0,0,0,0,0,0,0", - "range_level": "45", - "projectile": "337", - "attack_level": "45" - }, - { - "examine": "A fowl beast.", - "combat_style": "2", - "melee_animation": "2302", - "range_animation": "2302", - "combat_audio": "355,357,356", - "magic_level": "45", - "respawn_delay": "25", - "defence_animation": "2300", - "weakness": "9", - "magic_animation": "2302", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "90", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", - "id": "2468", - "aggressive": "true", - "bonuses": "45,285,285,255,288,145,165,0,0,0,0,0,0,0,0", - "range_level": "45", - "projectile": "337", - "attack_level": "45" - }, - { - "examine": "A Retired Highwayman", - "combat_style": "1", - "melee_animation": "4230", - "range_animation": "4230", - "attack_speed": "5", - "magic_level": "30", - "defence_animation": "404", - "magic_animation": "4230", - "death_animation": "9055", - "name": "Rick Turpentine", - "defence_level": "8", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "2476", - "bonuses": "30,20,10,20,20,30,30,10,20,10,30,30,20,30,20", - "range_level": "20", - "attack_level": "8" - }, - { - "examine": "Apparently a master of quizzes!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Quiz Master", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2477", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hey", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evil Bob", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2479", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Servant of Evil Bob.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Servant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2481", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Annoying flappy thing.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "292,294,293", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "0", - "slayer_exp": "8", - "magic_animation": "0", - "death_animation": "0", - "name": "Giant bat", - "defence_level": "22", - "safespot": null, - "lifepoints": "32", - "strength_level": "22", - "id": "2482", - "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "A slithering serpent that likes to hide in the bush.", - "melee_animation": "275", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "poison_amount": "11", - "magic_animation": "0", - "death_animation": "278", - "name": "Bush snake", - "defence_level": "61", - "safespot": null, - "lifepoints": "87", - "strength_level": "1", - "id": "2489", - "aggressive": "true", - "range_level": "61", - "attack_level": "1" - }, - { - "melee_animation": "275", - "combat_audio": "3609,3608,3610", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "276", - "poison_amount": "11", - "death_animation": "278", - "name": "Bush snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "2490", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5327", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "5328", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "2491", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5327", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "5328", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "2492", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A flying bloodsucker.", - "melee_animation": "2397", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "2398", - "name": "Large mosquito", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "1", - "id": "2493", - "range_level": "63", - "attack_level": "1" - }, - { - "examine": "A swarm of three highly agile mosquitoes.", - "melee_animation": "2397", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "2398", - "name": "Mosquito swarm", - "defence_level": "66", - "safespot": null, - "lifepoints": "94", - "strength_level": "1", - "id": "2494", - "range_level": "66", - "attack_level": "1" - }, - { - "examine": "A swarm of five highly agile mosquitoes.", - "melee_animation": "2397", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "2398", - "name": "Mosquito swarm", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "1", - "id": "2495", - "range_level": "68", - "attack_level": "1" - }, - { - "name": "Tribesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "2496", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A vicious warrior.", - "melee_animation": "428", - "range_animation": "0", - "attack_speed": "4", - "poisonous": "true", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", - "poison_amount": "11", - "magic_animation": "0", - "death_animation": "836", - "name": "Tribesman", - "defence_level": "26", - "safespot": null, - "lifepoints": "39", - "strength_level": "27", - "id": "2497", - "aggressive": "true", - "clue_level": "1", - "bonuses": "8,8,8,0,0,4,6,6,0,0,0,5,0,0,0", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "An undead victim of some ancient murderous ritual; his skin appears deep green.", - "combat_style": "2", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Broodoo victim", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "2499", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "An undead victim of some ancient murderous ritual; his skin appears pale yellow.", - "combat_style": "2", - "melee_animation": "428", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Broodoo victim", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "2501", - "aggressive": "true", - "range_level": "1", - "attack_level": "43" - }, - { - "combat_style": "2", - "melee_animation": "810", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Broodoo victim", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "2503", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He used to swashbuckle his away across the seven seas.", - "combat_style": "1", - "melee_animation": "4230", - "range_animation": "4230", - "attack_speed": "5", - "magic_level": "20", - "defence_animation": "404", - "magic_animation": "4230", - "death_animation": "9055", - "name": "Cap'n Hand", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "2539", - "bonuses": "10,30,20,30,10,20,15,30,10,30,15,20,20,0,0", - "range_level": "30", - "attack_level": "30" - }, - { - "examine": "A crazy, evil druid.", - "start_gfx": "105", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "10", - "end_gfx": "107", - "respawn_delay": "25", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "422", - "death_animation": "9055", - "name": "Chaos druid", - "defence_level": "12", - "safespot": null, - "lifepoints": "20", - "strength_level": "8", - "id": "2547", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "106", - "attack_level": "8" - }, - { - "examine": "A colourful character.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the dyer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2549", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after the blast furnace.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Blast Furnace Foreman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2553", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Market Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "melee_animation": "400", - "strength_level": "1", - "id": "2571", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "He's guarding the bank.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bank guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Althric", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2588", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2591", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2592", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2593", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2594", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2595", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2596", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like some kind of mystic.", - "start_gfx": "1618", - "combat_style": "2", - "melee_animation": "9260", - "range_animation": "9260", - "attack_speed": "5", - "magic_level": "70", - "end_gfx": "1621", - "defence_animation": "9287", - "magic_animation": "9260", - "death_animation": "9291", - "name": "TzHaar-Mej", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "2597", - "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", - "range_level": "70", - "projectile": "1617", - "attack_level": "70" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2598", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2599", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2600", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2601", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2602", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "2603", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "50" - }, - { - "examine": "Doesn't look very social.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2604", - "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Doesn't look very social.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2605", - "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Doesn't look very social.", - "combat_style": "1", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2606", - "bonuses": "50,30,80,56,80,80,100,60,50,45,45,0,0,0,0", - "range_level": "70", - "projectile": "442", - "attack_level": "70" - }, - { - "examine": "Doesn't look very social.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2607", - "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Doesn't look very social.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2608", - "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Doesn't look very social.", - "combat_style": "1", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "slayer_exp": "0", - "magic_animation": "9345", - "death_animation": "9291", - "name": "TzHaar-Xil", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "70", - "id": "2609", - "bonuses": "50,30,80,56,80,80,100,60,50,45,45,0,0,0,0", - "range_level": "70", - "projectile": "442", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2610", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2611", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2612", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2613", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2614", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2615", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Must be a guard or something.", - "melee_animation": "9345", - "range_animation": "9345", - "magic_level": "70", - "defence_animation": "9287", - "magic_animation": "9345", - "death_animation": "9288", - "name": "TzHaar-Ket", - "defence_level": "70", - "safespot": null, - "lifepoints": "140", - "strength_level": "70", - "id": "2616", - "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", - "range_level": "70", - "attack_level": "70" - }, - { - "name": "Tz-Kih", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "attack_speed": "7", - "id": "2627", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Tz-Kih", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "attack_speed": "7", - "id": "2628", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Tz-Kek", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "attack_speed": "5", - "id": "2629", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Tz-Kek", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "attack_speed": "5", - "id": "2630", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "The Tok-Xil fires deadly spines out of its arm", - "range_animation": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Tok-Xil", - "defence_level": "80", - "safespot": null, - "lifepoints": "114", - "strength_level": "1", - "id": "2631", - "aggressive": "true", - "range_level": "80", - "attack_level": "1" - }, - { - "examine": "A busy-body who loves a bit of gossip.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Miss Schism", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2634", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Dragonkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "2641", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "2674", - "range_level": "1", - "attack_level": "10" - }, - { - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2675", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He holds up passers by.", - "melee_animation": "799", - "range_animation": "799", - "combat_audio": "511,513,512", - "attack_speed": "5", - "defence_animation": "404", - "weakness": "7", - "magic_animation": "799", - "death_animation": "9055", - "name": "Highwayman", - "defence_level": "8", - "safespot": null, - "lifepoints": "13", - "strength_level": "8", - "id": "2677", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2678", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2679", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2680", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "2", - "id": "2681", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "3102,3104,3103", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "2682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A citizen of Rimmington.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hengel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A citizen of Rimmington.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Anja", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2684", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly smelly creature.", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "2685", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "An ugly smelly creature.", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "2686", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "An ugly smelly creature.", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "2687", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "An ugly smelly creature, with a spear.", - "melee_animation": "163", - "range_animation": "163", - "combat_audio": "3520,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "2688", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "Didn't the mage say this procedure was totally safe?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Frog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2689", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A salty seafarer. Needs a wash.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jack Seagull", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2690", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A strange man with a strange name. Probably a strange past, too.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Longbow Ben", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2691", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She quackers.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "3468", - "name": "Duck", - "defence_level": "1", - "water_npc": "true", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2693", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Mini quackers.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "3468", - "name": "Duckling", - "defence_level": "1", - "water_npc": "true", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2694", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This one's had too much to drink!", - "melee_animation": "422", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "2695", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "2696", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "He doesn't look so happy now he's in jail.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Mugger", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "2697", - "aggressive": "true", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "A dark-hearted knight.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black knight", - "defence_level": "25", - "safespot": null, - "lifepoints": "42", - "strength_level": "24", - "id": "2698", - "aggressive": "true", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He's guarding the prison.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "2699", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A prison guard.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "2700", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A prison guard.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "2701", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A prison guard.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "2702", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A prison guard.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "2703", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He's asleep.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2704", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeping an eye out for suspicious activity.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2705", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No one likes crabs...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2706", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sea bird.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "309,311,310", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Seagull", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2707", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Caution: HOT!", - "start_gfx": "99", - "combat_style": "2", - "start_height": "80", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "18", - "respawn_delay": "60", - "end_gfx": "101", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "2553", - "name": "Fire wizard", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "2709", - "range_level": "1", - "projectile": "100", - "attack_level": "1" - }, - { - "examine": "Hydro-power!", - "combat_style": "2", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "14", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "2553", - "name": "Water wizard", - "defence_level": "14", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "2710", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His hands are covered in mud, at least.", - "start_gfx": "96", - "combat_style": "2", - "start_height": "80", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "16", - "respawn_delay": "60", - "end_gfx": "98", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "2553", - "name": "Earth wizard", - "defence_level": "16", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "2711", - "range_level": "1", - "projectile": "97", - "attack_level": "1" - }, - { - "examine": "At least he looks solid enough to fight.", - "start_gfx": "90", - "combat_style": "2", - "start_height": "80", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "12", - "respawn_delay": "60", - "end_gfx": "92", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "2553", - "name": "Air wizard", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "2712", - "range_level": "1", - "projectile": "91", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "2714", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "25", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2716", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She'll store my items for me.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Betty", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2718", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wise barbarian, apparently. He still looks like a thug.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Otto Godblessed", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2725", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holy looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk of Entrana", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2728", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holy looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk of Entrana", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2729", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holy looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk of Entrana", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2731", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He works in the Crafting Guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master Crafter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2732", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He wanders around the Crafting Guild pretending to be working.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Master Crafter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2733", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hangs out in caves.", - "melee_animation": "9232", - "range_animation": "9232", - "attack_speed": "5", - "respawn_delay": "0", - "defence_animation": "9231", - "magic_animation": "9232", - "death_animation": "9230", - "name": "Tz-Kih", - "defence_level": "11", - "safespot": null, - "lifepoints": "10", - "strength_level": "27", - "id": "2734", - "aggressive": "true", - "bonuses": "150,22,22,22,22,11,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Hangs out in caves.", - "melee_animation": "9232", - "range_animation": "9232", - "attack_speed": "5", - "respawn_delay": "0", - "defence_animation": "9231", - "magic_animation": "9232", - "death_animation": "9230", - "name": "Tz-Kih", - "defence_level": "11", - "safespot": null, - "lifepoints": "10", - "strength_level": "27", - "id": "2735", - "aggressive": "true", - "bonuses": "150,22,22,22,22,11,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Looks like living lava...", - "melee_animation": "9233", - "range_animation": "9233", - "attack_speed": "5", - "magic_level": "20", - "defence_animation": "9235", - "magic_animation": "9233", - "death_animation": "9234", - "name": "Tz-Kek", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "60", - "id": "2736", - "aggressive": "true", - "bonuses": "80,45,45,45,45,22,0,0,0,0,0,0,0,0,0", - "range_level": "20", - "attack_level": "60" - }, - { - "examine": "Looks like living lava...", - "melee_animation": "9233", - "range_animation": "9233", - "attack_speed": "5", - "magic_level": "20", - "defence_animation": "9235", - "magic_animation": "9233", - "death_animation": "9234", - "name": "Tz-Kek", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "60", - "id": "2737", - "aggressive": "true", - "bonuses": "80,45,45,45,45,22,0,0,0,0,0,0,0,0,0", - "range_level": "20", - "attack_level": "60" - }, - { - "examine": "Looks like living lava...", - "melee_animation": "9233", - "range_animation": "9233", - "attack_speed": "5", - "defence_animation": "9235", - "magic_animation": "9233", - "death_animation": "9234", - "name": "Tz-Kek", - "defence_level": "11", - "safespot": null, - "lifepoints": "10", - "strength_level": "30", - "id": "2738", - "bonuses": "60,22,22,22,22,11,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "I don't like the look of those spines...", - "melee_animation": "9245", - "range_animation": "9245", - "attack_speed": "5", - "defence_animation": "9242", - "weakness": "4", - "magic_animation": "9245", - "death_animation": "9239", - "name": "Tok-Xil", - "defence_level": "45", - "safespot": null, - "lifepoints": "40", - "strength_level": "80", - "id": "2739", - "bonuses": "80,90,86,90,90,90,45,55,26,55,0,0,0,0,0", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "I don't like the look of those spines...", - "melee_animation": "9245", - "range_animation": "9245", - "attack_speed": "5", - "defence_animation": "9242", - "weakness": "4", - "magic_animation": "9245", - "death_animation": "9239", - "name": "Tok-Xil", - "defence_level": "45", - "safespot": null, - "lifepoints": "40", - "strength_level": "80", - "id": "2740", - "bonuses": "80,90,86,90,90,90,45,55,26,55,0,0,0,0,0", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Holy reptile...", - "melee_animation": "9252", - "range_animation": "9252", - "attack_speed": "5", - "defence_animation": "9249", - "magic_animation": "9252", - "death_animation": "9257", - "name": "Yt-MejKot", - "defence_level": "90", - "safespot": null, - "lifepoints": "80", - "strength_level": "100", - "id": "2741", - "aggressive": "true", - "bonuses": "100,180,180,180,180,90,97,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "Holy reptile...", - "melee_animation": "9252", - "range_animation": "9252", - "attack_speed": "5", - "defence_animation": "9249", - "magic_animation": "9252", - "death_animation": "9257", - "name": "Yt-MejKot", - "defence_level": "90", - "safespot": null, - "lifepoints": "80", - "strength_level": "100", - "id": "2742", - "aggressive": "true", - "bonuses": "100,180,180,180,180,90,97,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "That's one hot dog!", - "melee_animation": "9265", - "range_animation": "9265", - "attack_speed": "5", - "magic_level": "150", - "defence_animation": "9268", - "magic_animation": "9265", - "death_animation": "9269", - "name": "Ket-Zek", - "defence_level": "180", - "safespot": null, - "lifepoints": "160", - "strength_level": "150", - "id": "2743", - "bonuses": "180,180,280,280,280,250,140,152,131,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "150" - }, - { - "examine": "That's one hot dog!", - "melee_animation": "9265", - "range_animation": "9265", - "attack_speed": "5", - "magic_level": "150", - "defence_animation": "9268", - "magic_animation": "9265", - "death_animation": "9269", - "name": "Ket-Zek", - "defence_level": "180", - "safespot": null, - "lifepoints": "160", - "strength_level": "150", - "id": "2744", - "bonuses": "180,180,280,280,280,250,140,152,131,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "150" - }, - { - "agg_radius": "64", - "examine": "This is going to hurt...", - "melee_animation": "9277", - "range_animation": "9277", - "attack_speed": "8", - "magic_level": "480", - "defence_animation": "9278", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "9277", - "death_animation": "9279", - "name": "TzTok-Jad", - "defence_level": "480", - "safespot": null, - "lifepoints": "250", - "strength_level": "960", - "id": "2745", - "aggressive": "true", - "bonuses": "0,0,0,60,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "960", - "attack_level": "640" - }, - { - "examine": "Mini Menace.", - "melee_animation": "9252", - "range_animation": "9252", - "attack_speed": "5", - "defence_animation": "9253", - "magic_animation": "9252", - "death_animation": "9257", - "name": "Yt-HurKot", - "defence_level": "75", - "safespot": null, - "lifepoints": "40", - "strength_level": "75", - "id": "2746", - "aggressive": "true", - "bonuses": "100,110,110,110,110,60,110,30,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "She can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2759", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Woman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,506,505", - "strength_level": "1", - "id": "2776", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Ranger of the Temple Knights.", - "melee_animation": "426", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Ranger", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "2779", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shadow.", - "melee_animation": "2738", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "2739", - "name": "Shadow", - "defence_level": "68", - "safespot": null, - "lifepoints": "100", - "strength_level": "68", - "id": "2782", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "combat_audio": "389,391,390", - "attack_speed": "4", - "magic_level": "160", - "defence_animation": "2732", - "weakness": "4", - "slayer_exp": "225", - "magic_animation": "2731", - "death_animation": "2733", - "name": "Dark beast", - "defence_level": "120", - "safespot": null, - "lifepoints": "220", - "can_tolerate": "false", - "strength_level": "160", - "id": "2783", - "aggressive": "true", - "bonuses": "0,0,0,0,0,30,40,100,90,100,0,0,0,0,0", - "range_level": "1", - "attack_level": "140" - }, - { - "examine": "Digging.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2785", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Digging.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2786", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Confused.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2787", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Drill Sergeant from heck!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sergeant Damien", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2790", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A man down on his luck.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tramp", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2792", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An angry Ogre in a funny hat.", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "defence_animation": "360", - "magic_animation": "359", - "death_animation": "361", - "name": "Ogre", - "defence_level": "30", - "safespot": null, - "lifepoints": "48", - "strength_level": "30", - "id": "2801", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "They just call him 'Coach'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Coach", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2802", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "weakness": "0", - "slayer_exp": "40", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Lizard", - "defence_level": "55", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "2803", - "aggressive": "true", - "range_level": "55", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "weakness": "0", - "slayer_exp": "25", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Desert Lizard", - "defence_level": "45", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "2804", - "aggressive": "true", - "range_level": "45", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "weakness": "0", - "slayer_exp": "25", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Desert Lizard", - "defence_level": "45", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "2805", - "aggressive": "true", - "range_level": "45", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "slayer_exp": "25", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Desert Lizard", - "defence_level": "45", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "2806", - "aggressive": "true", - "range_level": "45", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "weakness": "0", - "slayer_exp": "15", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Small Lizard", - "defence_level": "15", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "2807", - "range_level": "15", - "attack_level": "1" - }, - { - "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", - "melee_animation": "2776", - "range_animation": "2776", - "attack_speed": "5", - "defence_animation": "2777", - "weakness": "0", - "magic_animation": "2776", - "death_animation": "2778", - "name": "Small Lizard", - "defence_level": "15", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "2808", - "aggressive": "true", - "range_level": "15", - "attack_level": "1" - }, - { - "examine": "A camel who has the soul of a poet.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Al the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2809", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A camel whose love is unrequited.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Elly the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2810", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A camel who wants to fly some day.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ollie the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2811", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A camel who likes to rest.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cam the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2812", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A camel who wants to see the world.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Neferti the Camel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2815", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shifty-looking character.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2825", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "2826", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shabby-looking leader.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Braindeath", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2827", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Most of an angry", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "50% Luke", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2828", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if it was all the 'rum' that pickled him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Donnie", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2830", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2831", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2832", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2833", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2834", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2835", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sticking it to 'The Man'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Zombie protester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2836", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2837", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2838", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2839", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2840", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2841", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "An undead sea scoundrel.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "2842", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2843", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2844", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2845", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2846", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2847", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "He talks a good fight.", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie swab", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "2848", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "The pun was intended.", - "melee_animation": "2804", - "range_animation": "0", - "magic_level": "40", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "2805", - "name": "Evil spirit", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "2849", - "aggressive": "true", - "range_level": "40", - "attack_level": "40" - }, - { - "examine": "A bunch of legs, eyes and teeth.", - "slayer_task": "76", - "melee_animation": "5319", - "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "40", - "magic_animation": "0", - "death_animation": "5321", - "name": "Fever spider", - "defence_level": "40", - "safespot": null, - "lifepoints": "40", - "strength_level": "30", - "id": "2850", - "bonuses": "0,0,0,0,0,20,15,10,15,15,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2851", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2852", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2853", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2854", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2855", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2856", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2857", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker in the brewery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brewer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2858", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "2863", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "2866", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "2869", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "2878", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A knee-high horror from the ocean depths...", - "slayer_task": "24", - "melee_animation": "1579", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1581", - "name": "Dagannoth fledgeling", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "52", - "id": "2880", - "aggressive": "true", - "range_level": "1", - "attack_level": "52" - }, - { - "agg_radius": "8", - "examine": "The Dagannoth King responsible for the death of Bukalla.", - "combat_style": "1", - "melee_animation": "2855", - "attack_speed": "4", - "magic_level": "255", - "respawn_delay": "60", - "defence_animation": "2852", - "death_animation": "2856", - "name": "Dagannoth Supreme", - "defence_level": "128", - "movement_radius": "8", - "safespot": null, - "lifepoints": "255", - "strength_level": "255", - "id": "2881", - "aggressive": "true", - "bonuses": "0,0,0,0,0,10,10,10,255,550,0,0,0,0,0", - "clue_level": "2", - "range_level": "255", - "projectile": "475", - "attack_level": "255" - }, - { - "agg_radius": "8", - "examine": "A legendary Dagannoth King, rumoured to fly on the North winds.", - "combat_style": "2", - "melee_animation": "2854", - "attack_speed": "4", - "magic_level": "255", - "spell_id": "48", - "respawn_delay": "60", - "defence_animation": "2852", - "death_animation": "2856", - "name": "Dagannoth Prime", - "defence_level": "255", - "movement_radius": "8", - "safespot": null, - "lifepoints": "255", - "strength_level": "255", - "id": "2882", - "aggressive": "true", - "bonuses": "0,0,0,0,0,255,255,255,255,10,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "255" - }, - { - "agg_radius": "8", - "examine": "Firstborn of the legendary Dagannoth Kings.", - "melee_animation": "2853", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "2852", - "death_animation": "2856", - "name": "Dagannoth Rex", - "defence_level": "255", - "movement_radius": "8", - "safespot": null, - "lifepoints": "255", - "strength_level": "255", - "id": "2883", - "aggressive": "true", - "bonuses": "0,0,0,0,0,255,255,255,10,255,0,0,0,0,0", - "clue_level": "2", - "range_level": "255", - "attack_level": "255" - }, - { - "melee_animation": "1312", - "combat_audio": "717,723,722", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "200", - "safespot": null, - "lifepoints": "180", - "strength_level": "80", - "id": "2885", - "aggressive": "true", - "bonuses": "0,0,0,0,0,225,200,175,-10,250,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "attack_speed": "5", - "id": "2886", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "strength_level": "1", - "id": "2887", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "288", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "2888", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "288", - "attack_level": "1" - }, - { - "examine": "It wasn't a rock... It was a rock lobster!", - "melee_animation": "2860", - "range_animation": "2860", - "combat_audio": "726,728,727", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "movement_radius": "30", - "lifepoints": "150", - "strength_level": "100", - "id": "2889", - "bonuses": "0,0,0,0,0,100,100,50,50,150,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A Rock.", - "melee_animation": "2860", - "range_animation": "2860", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2861", - "name": "Large rock", - "defence_level": "100", - "safespot": null, - "lifepoints": "150", - "strength_level": "100", - "id": "2890", - "aggressive": "true", - "range_level": "1", - "attack_level": "100" - }, - { - "agg_radius": "12", - "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", - "combat_style": "2", - "melee_animation": "2868", - "range_animation": "2866", - "attack_speed": "6", - "magic_level": "65", - "spell_id": "14", - "respawn_delay": "55", - "defence_animation": "2869", - "slayer_exp": "0", - "magic_animation": "2866", - "death_animation": "2866", - "name": "Spinolyp", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "10", - "id": "2892", - "aggressive": "true", - "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", - "range_level": "20", - "projectile": "294", - "attack_level": "1" - }, - { - "agg_radius": "12", - "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", - "combat_style": "2", - "melee_animation": "2868", - "range_animation": "2866", - "attack_speed": "6", - "magic_level": "65", - "spell_id": "14", - "respawn_delay": "55", - "defence_animation": "2869", - "magic_animation": "2866", - "death_animation": "2866", - "name": "Spinolyp", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "10", - "id": "2894", - "aggressive": "true", - "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", - "range_level": "20", - "projectile": "294", - "attack_level": "1" - }, - { - "agg_radius": "12", - "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", - "combat_style": "2", - "melee_animation": "2868", - "range_animation": "2866", - "attack_speed": "6", - "magic_level": "65", - "spell_id": "14", - "respawn_delay": "55", - "defence_animation": "2869", - "slayer_exp": "0", - "magic_animation": "2866", - "death_animation": "2866", - "name": "Spinolyp", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "60", - "id": "2896", - "aggressive": "true", - "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", - "range_level": "70", - "projectile": "294", - "attack_level": "1" - }, - { - "melee_animation": "64", - "respawn_delay": "60", - "defence_animation": "65", - "death_animation": "67", - "name": "Agrith Naar", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "id": "2919", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "25", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "2931", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Who ate all the rats?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Beast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2941", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Lovely, cute, and possibly what dreams are made of.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bones", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2945", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Obviously punches above his weight.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hooknosed Jack", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2948", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks rich like an actor of sorts.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jimmy Dazzler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2949", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Once beautiful, now repugnant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Face", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2950", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What is he?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Smokin' Joe", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2952", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks fairly well fed.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silver merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2958", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2962", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2963", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2964", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2965", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2966", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2967", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2968", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2969", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2970", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2971", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2972", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2973", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "2980", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "2981", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The master of all rats.", - "name": "King rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "2982", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not a soft touch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pusskins", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2984", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A not-so friendly, not-so little cat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Tom", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2986", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fully grown feline.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mittens", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2988", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Cute and fluffy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Topsy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2990", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A friendly feline?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gertrude's cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2997", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very well to do. I wonder what he's doing here.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2998", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Rich.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3001", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Poor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3002", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Poor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3003", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Poor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3004", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Poor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3005", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Poor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gambler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3006", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3007", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3008", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3009", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3010", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3011", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3012", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3013", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3014", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3015", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3016", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3017", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "3018", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tool leprechaun", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3021", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Phenomenal cosmic powers", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Genie", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3022", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An animated clay statue.", - "melee_animation": "2917", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "2919", - "name": "Black golem", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "3026", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "An animated clay statue.", - "melee_animation": "2917", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "2919", - "name": "White golem", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "3027", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "An animated clay statue.", - "melee_animation": "2917", - "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "2919", - "name": "Grey golem", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "3028", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "The oldest man in Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghaslor the Elder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3029", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A water salesman from Pollnivneach.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Carter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3030", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Mayor of Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Awusah the Mayor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3040", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Poltenip", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "melee_animation": "395", - "strength_level": "1", - "id": "3042", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Radat", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "melee_animation": "395", - "strength_level": "1", - "id": "3043", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "Custodian of the shrine to Elidinis.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shiratti the Custodian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3044", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A banker of Nardah.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Nardah Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3046", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3051", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3052", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3053", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3054", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3055", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mysterious watcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mystery figure", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3056", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Champion of the earth warriors.", - "melee_animation": "2951", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "2946", - "name": "Earth Warrior Champion", - "defence_level": "51", - "safespot": null, - "lifepoints": "72", - "strength_level": "51", - "id": "3057", - "aggressive": "true", - "range_level": "1", - "attack_level": "51" - }, - { - "examine": "Champion of the giants.", - "melee_animation": "6368", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "slayer_exp": "35", - "magic_animation": "0", - "death_animation": "6369", - "name": "Giant Champion", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "3058", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Champion of the ghouls.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "441,444,443", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "50", - "magic_animation": "0", - "death_animation": "836", - "name": "Ghoul Champion", - "defence_level": "60", - "safespot": null, - "lifepoints": "100", - "strength_level": "80", - "id": "3059", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Champion of the goblins.", - "melee_animation": "6188", - "range_animation": "0", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6190", - "name": "Goblin Champion", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "6", - "id": "3060", - "range_level": "1", - "attack_level": "6" - }, - { - "examine": "Champion of the hobgoblins.", - "combat_style": "1", - "range_animation": "0", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "2958", - "name": "Hobgoblin Champion", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "14", - "id": "3061", - "aggressive": "true", - "range_level": "28", - "attack_level": "14" - }, - { - "examine": "Champion of the imps.", - "melee_animation": "5285", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "172", - "name": "Imp Champion", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "7", - "id": "3062", - "aggressive": "true", - "range_level": "7", - "attack_level": "7" - }, - { - "examine": "Champion of the jogres.", - "melee_animation": "2100", - "range_animation": "0", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "8576", - "name": "Jogre Champion", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "54", - "id": "3063", - "aggressive": "true", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Champion of the lesser demons.", - "melee_animation": "64", - "range_animation": "0", - "combat_audio": "396,402,401", - "magic_level": "81", - "respawn_delay": "60", - "defence_animation": "65", - "weakness": "5", - "slayer_exp": "79", - "magic_animation": "0", - "death_animation": "67", - "name": "Lesser Demon Champion", - "defence_level": "81", - "safespot": null, - "lifepoints": "115", - "strength_level": "1", - "id": "3064", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Champion of the zombies.", - "melee_animation": "5581", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5575", - "name": "Zombies Champion", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "3066", - "aggressive": "true", - "range_level": "1", - "attack_level": "26" - }, - { - "melee_animation": "7049", - "respawn_delay": "60", - "defence_animation": "7050", - "death_animation": "836", - "name": "Leon d'Cour", - "defence_level": "1", - "safespot": null, - "lifepoints": "123", - "strength_level": "1", - "id": "3067", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", - "start_gfx": "499", - "melee_animation": "2985", - "attack_speed": "6", - "weakness": "9", - "slayer_exp": "210", - "magic_animation": "2985", - "death_animation": "2987", - "lifepoints": "200", - "id": "3068", - "aggressive": "true", - "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", - "agg_radius": "6", - "range_animation": "2989", - "combat_audio": "0,771,770", - "magic_level": "125", - "end_gfx": "501", - "defence_animation": "2983", - "name": "Skeletal Wyvern", - "defence_level": "120", - "safespot": null, - "strength_level": "116", - "clue_level": "2", - "range_level": "120", - "projectile": "500", - "attack_level": "125" - }, - { - "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", - "start_gfx": "499", - "melee_animation": "2985", - "attack_speed": "6", - "weakness": "9", - "slayer_exp": "210", - "magic_animation": "2985", - "death_animation": "2987", - "lifepoints": "200", - "id": "3069", - "aggressive": "true", - "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", - "agg_radius": "6", - "range_animation": "2989", - "combat_audio": "0,771,770", - "magic_level": "125", - "end_gfx": "501", - "defence_animation": "2983", - "name": "Skeletal Wyvern", - "defence_level": "120", - "safespot": null, - "strength_level": "116", - "clue_level": "2", - "range_level": "120", - "projectile": "500", - "attack_level": "125" - }, - { - "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", - "start_gfx": "499", - "melee_animation": "2985", - "attack_speed": "6", - "weakness": "9", - "slayer_exp": "210", - "magic_animation": "2985", - "death_animation": "2987", - "lifepoints": "200", - "id": "3070", - "aggressive": "true", - "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", - "agg_radius": "6", - "range_animation": "2989", - "combat_audio": "0,771,770", - "magic_level": "125", - "end_gfx": "501", - "defence_animation": "2983", - "name": "Skeletal Wyvern", - "defence_level": "120", - "safespot": null, - "strength_level": "116", - "clue_level": "2", - "range_level": "120", - "projectile": "500", - "attack_level": "125" - }, - { - "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", - "start_gfx": "499", - "melee_animation": "2985", - "attack_speed": "6", - "weakness": "9", - "slayer_exp": "210", - "magic_animation": "2985", - "death_animation": "2987", - "lifepoints": "200", - "id": "3071", - "aggressive": "true", - "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", - "agg_radius": "6", - "range_animation": "2989", - "combat_audio": "0,771,770", - "magic_level": "125", - "end_gfx": "501", - "defence_animation": "2983", - "name": "Skeletal Wyvern", - "defence_level": "120", - "safespot": null, - "strength_level": "116", - "clue_level": "2", - "range_level": "120", - "projectile": "500", - "attack_level": "125" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "448,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "70", - "magic_animation": "0", - "death_animation": "4673", - "name": "Ice giant", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "40", - "id": "3072", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "combat_audio": "2500,530,529", - "attack_speed": "4", - "respawn_delay": "30", - "defence_animation": "404", - "weakness": "9", - "slayer_exp": "59", - "magic_animation": "451", - "death_animation": "843", - "name": "Ice warrior", - "defence_level": "47", - "safespot": null, - "lifepoints": "59", - "strength_level": "47", - "id": "3073", - "aggressive": "true", - "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "47" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3074", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3075", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3079", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3080", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3089", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mighty warrior", - "melee_animation": "7048", - "range_animation": "0", - "combat_audio": "511,513,512", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "3090", - "aggressive": "true", - "range_level": "1", - "attack_level": "17" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3091", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The book moves by itself!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Flying Book", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3094", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Entrance Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3097", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Telekinetic Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3098", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Alchemy Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3099", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Enchantment Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3100", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Graveyard Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3101", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Maze Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3102", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guardian of the arena.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rewards Guardian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3103", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Equipment that moves by itself!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Charmed Warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3104", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Equipment that moves by itself!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Charmed Warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3105", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Equipment that moves by itself!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Charmed Warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3106", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This guard looks rather drunk and has beer stains down his armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard Captain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3109", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Balloon Animal", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3121", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I saw the witchdoctor", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Balloon Animal", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3122", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like he's seen the inside of a few tombs.", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Simon Templeton", - "defence_level": "1", - "safespot": "0", - "lifepoints": "12", - "strength_level": "1", - "id": "3123", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It seems to be blocked.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Furnace grate", - "defence_level": "1", - "safespot": null, - "lifepoints": "0", - "strength_level": "1", - "id": "3135", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3150", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A swarm of bugs.", - "slayer_task": "42", - "melee_animation": "1584", - "range_animation": "0", - "combat_audio": "2743,2745,2744", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1585", - "name": "Harpie Bug Swarm", - "defence_level": "32", - "safespot": null, - "lifepoints": "25", - "strength_level": "46", - "id": "3153", - "aggressive": "true", - "bonuses": "0,0,0,0,0,10,5,10,5,10,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Bill Teach the pirate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bill Teach", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3155", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3167", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3168", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3169", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3170", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3171", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3172", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3173", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3174", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3175", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3176", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3177", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3178", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3179", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3180", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3181", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3182", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3183", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3184", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3185", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "3186", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "3187", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3188", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3189", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3190", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3191", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3192", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3193", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3194", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Arghh matey!", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "death_animation": "9055", - "name": "Pirate", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "40", - "id": "3195", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "range_level": "1", - "attack_level": "40" - }, - { - "melee_animation": "400", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "3196", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3198", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3199", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "pUre A cHaOs of crEatuRe!", - "start_gfx": "556", - "melee_animation": "3146", - "attack_speed": "5", - "respawn_delay": "100", - "weakness": "4", - "magic_animation": "5443", - "death_animation": "3147", - "lifepoints": "250", - "id": "3200", - "aggressive": "true", - "bonuses": "0,0,0,0,0,70,70,70,70,70,0,0,0,0,0", - "agg_radius": "16", - "range_animation": "5443", - "combat_audio": "345,351,347", - "magic_level": "270", - "end_gfx": "558", - "defence_animation": "3149", - "name": "Chaos Elemental", - "defence_level": "270", - "movement_radius": "30", - "safespot": "true", - "strength_level": "270", - "range_level": "270", - "projectile": "557", - "attack_level": "270" - }, - { - "slayer_exp": "51", - "name": "Killerwatt", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "attack_speed": "3", - "id": "3201", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "slayer_exp": "51", - "name": "Killerwatt", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "attack_speed": "3", - "id": "3202", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A very small storm!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Storm Cloud", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3203", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very small storm!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Storm Cloud", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3204", - "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", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "3219", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Loves mining.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "3220", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Loves mining.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "3221", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "3223", - "range_level": "1", - "attack_level": "3" - }, - { - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3224", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Gielinor's many citizens. He looks worried about something.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "3225", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "melee_animation": "422", - "combat_audio": "511,506,505", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Woman", - "defence_level": "1", - "safespot": null, - "lifepoints": "9", - "strength_level": "1", - "id": "3226", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Woman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,506,505", - "strength_level": "1", - "id": "3227", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "386", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "3228", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "0", - "range_animation": "2075", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3229", - "clue_level": "1", - "range_level": "13", - "attack_level": "1" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "395", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "3230", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "422", - "range_animation": "426", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3231", - "clue_level": "1", - "range_level": "13", - "attack_level": "1" - }, - { - "examine": "Keeping an eye out for threats to the city.", - "melee_animation": "422", - "range_animation": "426", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3232", - "clue_level": "1", - "range_level": "13", - "attack_level": "1" - }, - { - "examine": "Keeping an eye out for threats to the city.", - "melee_animation": "422", - "range_animation": "426", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3233", - "clue_level": "1", - "range_level": "13", - "attack_level": "1" - }, - { - "examine": "An old gardener.", - "melee_animation": "433", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gardener", - "defence_level": "4", - "safespot": null, - "lifepoints": "5", - "strength_level": "4", - "id": "3234", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "He's learning a trade.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apprentice workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3235", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A busy workman", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3236", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "3241", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A practicer of dark arts.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "25", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "25", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "3242", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A practicer of dark arts.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "27", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "27", - "safespot": null, - "lifepoints": "38", - "strength_level": "1", - "id": "3243", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A practicer of dark arts.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "16", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "16", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "3244", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A practicer of dark arts.", - "combat_style": "2", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "16", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "16", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "3245", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Alberich, a fierce barbarian warrior.", - "melee_animation": "2067", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3246", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Fafner, a tough barbarian warrior.", - "melee_animation": "7048", - "range_animation": "7048", - "attack_speed": "6", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "7048", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "18", - "strength_level": "22", - "id": "3247", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Fasolt, a strong barbarian warrior.", - "melee_animation": "2067", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3248", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Siegmund, a guard in the Barbarian Village.", - "melee_animation": "401", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3249", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Siegfried, a young guard in the Barbarian Village.", - "melee_animation": "2067", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3250", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Lydspor, a well-traveld barbarian warrior.", - "melee_animation": "7048", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3251", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Hagen, a guard in the Barbarian Village.", - "melee_animation": "2067", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3252", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Minarch, a barbarian who likes his beer.", - "melee_animation": "401", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3253", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Wotan, a sturdy barbarian warrior.", - "melee_animation": "401", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3255", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Acelin, a guard in the Barbarian Village.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3256", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Adelino, a barbarian warrior with a big axe.", - "melee_animation": "2067", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3257", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Adolpho, a barbarian warrior with a warhammer.", - "melee_animation": "401", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3258", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Aitan, a barbarian archer.", - "melee_animation": "426", - "range_animation": "426", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3259", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Brunnhilde, a fierce barbarian warrior.", - "melee_animation": "428", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "3260", - "clue_level": "0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Gutrune, a mighty barbarian warrior.", - "melee_animation": "428", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "3261", - "clue_level": "0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Edelschwarz, a barbarian warrior with a spear.", - "melee_animation": "428", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "3262", - "clue_level": "0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Sieglinde, a muscular barbarian warrior.", - "melee_animation": "428", - "range_animation": "428", - "attack_speed": "6", - "defence_animation": "404", - "weakness": "8", - "magic_animation": "428", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "22", - "safespot": null, - "lifepoints": "20", - "strength_level": "20", - "id": "3263", - "clue_level": "0", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "3264", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "3265", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "3266", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "470,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "3267", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3268", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3269", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3270", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3271", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3272", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3273", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3274", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A dwarven worker.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "102", - "name": "Dwarf", - "defence_level": "10", - "safespot": null, - "lifepoints": "14", - "strength_level": "10", - "id": "3275", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "3276", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "3277", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A member of the Black Guard", - "slayer_task": "29", - "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": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "3278", - "range_level": "1", - "attack_level": "30" - }, - { - "slayer_exp": "16", - "name": "Black Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3279", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks busy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Engineering assistant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3280", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's building a cannon.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Engineer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3282", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bushy tail!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squirrel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3283", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A raccoon.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Raccoon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3286", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A raccoon.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Raccoon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3288", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She'll store my items for me.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3293", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf who looks after the mining guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3294", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf who looks after the mining guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3295", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A graceful bird.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swan", - "defence_level": "1", - "safespot": null, - "water_npc": true, - "lifepoints": "10", - "strength_level": "1", - "id": "3296", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps this magic area tidy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sweeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3298", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master at gardening.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Martin the Master Gardener", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3299", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Manages the fairies.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Co-ordinator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3302", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I'm gonna make him an offer he can't refuse.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy Godfather", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3304", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Guardian of the market gate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gatekeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3307", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Converts grass to beef.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "369,371,370", - "attack_speed": "5", - "defence_animation": "5850", - "weakness": "8", - "slayer_exp": "8", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "3309", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Tanglefoot", - "defence_level": "1", - "safespot": null, - "lifepoints": "102", - "strength_level": "1", - "id": "3313", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "An animated shrub.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "name": "Baby tanglefoot", - "defence_level": "41", - "safespot": null, - "lifepoints": "58", - "strength_level": "1", - "id": "3319", - "range_level": "41", - "attack_level": "1" - }, - { - "examine": "An aggressive bush.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "name": "Baby tanglefoot", - "defence_level": "41", - "safespot": null, - "lifepoints": "58", - "strength_level": "1", - "id": "3320", - "range_level": "41", - "attack_level": "1" - }, - { - "examine": "Rather more tired than most.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jeremy Clerksin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3327", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks a little green around the gills.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barfy Bill", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3331", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's made of pure fire.", - "attack_speed": "6", - "magic_level": "300", - "respawn_delay": "25", - "name": "Cavemouth", - "defence_level": "500", - "safespot": null, - "lifepoints": "1000", - "strength_level": "1500", - "id": "3334", - "aggressive": "true", - "bonuses": "126,98,86,297,311,304,319,13,284,30,0,0,0,0,0", - "range_level": "300", - "attack_level": "200" - }, - { - "examine": "Holy Mole-y!", - "melee_animation": "3312", - "range_animation": "3312", - "combat_audio": "1642,1646,1645", - "attack_speed": "4", - "magic_level": "200", - "respawn_delay": "56", - "defence_animation": "3311", - "weakness": "7", - "magic_animation": "3312", - "death_animation": "3310", - "name": "Giant Mole", - "defence_level": "200", - "safespot": null, - "lifepoints": "200", - "strength_level": "200", - "id": "3340", - "bonuses": "0,0,0,0,0,60,80,100,80,60,0,0,0,0,0", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "I will call him, Mini Mole.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby Mole", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3341", - "aggressive": "false", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fungi", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "3344", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense.", - "melee_animation": "2776", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "2775", - "death_animation": "2777", - "name": "Fungi", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "3345", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bouncy fungus.", - "slayer_task": "94", - "melee_animation": "2776", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "2777", - "name": "Zygomite", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "3346", - "range_level": "42", - "attack_level": "1" - }, - { - "examine": "Have a fungus.", - "melee_animation": "2776", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "2775", - "death_animation": "2777", - "name": "Zygomite", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "3347", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "7049", - "combat_audio": "511,513,512", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "7050", - "death_animation": "836", - "name": "White Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "53", - "strength_level": "1", - "id": "3348", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "7049", - "combat_audio": "511,513,512", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "7050", - "death_animation": "836", - "name": "White Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "53", - "strength_level": "1", - "id": "3349", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "7049", - "combat_audio": "511,513,512", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "7050", - "death_animation": "836", - "name": "White Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "53", - "strength_level": "1", - "id": "3350", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3366", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3367", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3368", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3369", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3370", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Evil Chicken", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "355,357,356", - "strength_level": "1", - "id": "3371", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fowl beast.", - "slayer_task": "7", - "melee_animation": "2299", - "range_animation": "0", - "combat_audio": "355,357,356", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "2301", - "name": "Evil Chicken", - "defence_level": "55", - "safespot": null, - "lifepoints": "117", - "strength_level": "55", - "id": "3375", - "aggressive": "true", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "Young but still dangerous.", - "slayer_task": "68", - "melee_animation": "25", - "range_animation": "25", - "combat_audio": "405,407,406", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "32", - "defence_animation": "26", - "weakness": "3", - "slayer_exp": "0", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby black dragon", - "defence_level": "42", - "safespot": null, - "lifepoints": "50", - "strength_level": "80", - "id": "3376", - "aggressive": "true", - "bonuses": "50,40,64,60,30,45,40,40,20,40,40,20,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "He seems to like wearing black.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evil Dave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3378", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Vermin from the underworld.", - "slayer_task": "67", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hell-Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3382", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shifty-looking character.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3389", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks short and grumpy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3390", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "General Wartface", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3391", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "General Bentnoze", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3392", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He provides new players with useful information.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lumbridge Guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3393", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to like wearing black.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Evil Dave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3394", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Rutmir's assistant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3404", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A small ice demon.", - "slayer_task": "46", - "range_animation": "0", - "combat_audio": "531,533,532", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Icefiend", - "defence_level": "60", - "safespot": null, - "lifepoints": "171", - "strength_level": "60", - "id": "3406", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "7218", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7219", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3407", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He tries to keep order around here.", - "melee_animation": "583", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7213", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3408", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "A sentient plant - ready", - "melee_animation": "7246", - "range_animation": "0", - "poisonous": "true", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7249", - "name": "Wild jade vine", - "defence_level": "65", - "safespot": null, - "lifepoints": "185", - "strength_level": "65", - "id": "3409", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "A sentient plant - ready", - "melee_animation": "7246", - "range_animation": "0", - "poisonous": "true", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7249", - "name": "Wild jade vine", - "defence_level": "65", - "safespot": null, - "lifepoints": "185", - "strength_level": "65", - "id": "3410", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "A sentient plant - ready", - "melee_animation": "7246", - "range_animation": "0", - "poisonous": "true", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7249", - "name": "Wild jade vine", - "defence_level": "65", - "safespot": null, - "lifepoints": "185", - "strength_level": "65", - "id": "3411", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "A sentient plant - ready", - "melee_animation": "7246", - "range_animation": "0", - "poisonous": "true", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7249", - "name": "Wild jade vine", - "defence_level": "65", - "safespot": null, - "lifepoints": "185", - "strength_level": "65", - "id": "3412", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "He's got funky socks, but he's still an ugly green creature.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3413", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "More like a goblin cooked.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3414", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Half way up the wall is maybe not a naturally tenable position for a goblin.", - "name": "Goblin Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "3415", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He'll store my items for me.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3416", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He'll store my items for me.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3418", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "attack_speed": "7", - "id": "3419", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "An angry Ogre in a highly amusing hat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mogre Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3420", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Nice claw!", - "melee_animation": "3428", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3430", - "name": "Crab", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3421", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Not the most beautiful fish in the sea.", - "melee_animation": "3433", - "range_animation": "0", - "combat_audio": "241,243,242", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "10", - "defence_animation": "3434", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "26", - "safespot": null, - "lifepoints": "20", - "strength_level": "29", - "id": "3422", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "29" - }, - { - "examine": "Not the most beautiful fish in the sea.", - "melee_animation": "3433", - "combat_audio": "241,243,242", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "5", - "defence_animation": "3434", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "29", - "safespot": null, - "lifepoints": "20", - "strength_level": "30", - "id": "3423", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "death_animation": "3430", - "name": "Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "18", - "melee_animation": "3428", - "strength_level": "1", - "id": "3424", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3429" - }, - { - "examine": "A Red Fantail.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3425", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Red Fantail.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3426", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Red Fantail.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3427", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An Angel Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3431", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An Angel Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3432", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An Angel Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3433", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Harlequin Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3434", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Harlequin Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3435", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Harlequin Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3436", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Discus Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3437", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Discus Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3438", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Discus Fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3439", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shoal of Neon Tetra.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3443", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shoal of Neon Tetra.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3444", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shoal of Neon Tetra.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3445", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Lumbridge Guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "attack_speed": "7", - "id": "3449", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "attack_speed": "7", - "id": "3450", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Lumbridge Guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "attack_speed": "7", - "id": "3451", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "What restful music!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skrach Uglogwee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3463", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A large boisterous bird", - "slayer_task": "7", - "melee_animation": "6800", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "6801", - "name": "Jubbly bird", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "6", - "id": "3476", - "aggressive": "true", - "range_level": "8", - "attack_level": "6" - }, - { - "name": "King Awowogei", - "defence_level": "1", - "safespot": null, - "lifepoints": "328", - "strength_level": "1", - "id": "3478", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A big snake.", - "melee_animation": "3538", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "3540", - "name": "Big Snake", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "46", - "id": "3484", - "aggressive": "true", - "range_level": "1", - "attack_level": "46" - }, - { - "name": "Culinaromancer", - "defence_level": "1", - "safespot": null, - "lifepoints": "140", - "strength_level": "1", - "id": "3491", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "melee_animation": "3501", - "respawn_delay": "60", - "defence_animation": "3500", - "death_animation": "3503", - "name": "Agrith-Na-Na", - "defence_level": "1", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "3493", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1750", - "respawn_delay": "60", - "defence_animation": "1751", - "death_animation": "1752", - "name": "Flambeed", - "defence_level": "1", - "safespot": null, - "lifepoints": "210", - "strength_level": "1", - "id": "3494", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Karamel", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3495", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "3507", - "respawn_delay": "60", - "defence_animation": "3505", - "death_animation": "3510", - "name": "Dessourt", - "defence_level": "1", - "safespot": null, - "lifepoints": "140", - "strength_level": "1", - "id": "3496", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Deadly AND fruity!", - "melee_animation": "1341", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "65", - "safespot": null, - "lifepoints": "139", - "strength_level": "48", - "id": "3497", - "aggressive": "true", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3498", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3499", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3500", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3501", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", - "name": "Gelatinnoth Mother", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "3502", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Chronicler and regaler of your piratical exploits.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sorin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3509", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Vampyre Juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3514", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre.", - "melee_animation": "5783", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5798", - "name": "Vampyre Juvinate", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3521", - "aggressive": "true", - "range_level": "45", - "attack_level": "1" - }, - { - "death_animation": "5798", - "name": "Vampyre Juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "melee_animation": "5783", - "strength_level": "1", - "id": "3522", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "death_animation": "5798", - "name": "Vampyre Juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "melee_animation": "5783", - "strength_level": "1", - "id": "3523", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "death_animation": "5798", - "name": "Vampyre Juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "melee_animation": "5783", - "strength_level": "1", - "id": "3524", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "death_animation": "5798", - "name": "Vampyre Juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "melee_animation": "5783", - "strength_level": "1", - "id": "3525", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre", - "slayer_task": "86", - "melee_animation": "6016", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "6031", - "name": "Vampyre Juvinate", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "3526", - "range_level": "46", - "attack_level": "1" - }, - { - "examine": "A Juvinate vampyre", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5798", - "name": "Held Vampyre Juvinate", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3527", - "range_level": "45", - "attack_level": "1" - }, - { - "examine": "He looks really hungry!", - "slayer_task": "86", - "melee_animation": "6276", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Vampyre Juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "3531", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "She looks really hungry!", - "slayer_task": "86", - "melee_animation": "6276", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Vampyre Juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "3532", - "range_level": "1", - "attack_level": "40" - }, - { - "death_animation": "6781", - "name": "Vampyre Juvenile", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "6276", - "strength_level": "1", - "id": "3533", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He looks really hungry!", - "slayer_task": "86", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Held Vampyre Juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "3534", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "An initiate juvenile vampyre", - "slayer_task": "86", - "melee_animation": "6016", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "6031", - "name": "Juvinate", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "3577", - "range_level": "46", - "attack_level": "1" - }, - { - "examine": "Is it a sheep?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3579", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "49", - "name": "Guard dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3582", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "164", - "combat_audio": "469,472,471", - "respawn_delay": "60", - "defence_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "1", - "id": "3583", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No spider could grow that big! It's unrealistic!", - "melee_animation": "5319", - "range_animation": "0", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "5321", - "name": "Huge spider", - "defence_level": "72", - "safespot": null, - "lifepoints": "122", - "strength_level": "1", - "id": "3585", - "aggressive": "true", - "range_level": "72", - "attack_level": "1" - }, - { - "examine": "Good doggy...", - "slayer_task": "43", - "melee_animation": "6562", - "range_animation": "0", - "combat_audio": "3717,3719,3718", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "116", - "magic_animation": "0", - "death_animation": "6576", - "name": "Hellhound", - "defence_level": "102", - "safespot": null, - "lifepoints": "116", - "strength_level": "104", - "id": "3586", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "105" - }, - { - "examine": "Big, ugly, and smelly.", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", - "respawn_delay": "25", - "defence_animation": "360", - "magic_animation": "359", - "death_animation": "361", - "name": "Rantz", - "defence_level": "27", - "safespot": null, - "lifepoints": "60", - "strength_level": "27", - "id": "3587", - "aggressive": "true", - "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", - "range_level": "27", - "attack_level": "27" - }, - { - "examine": "Young but still dangerous.", - "melee_animation": "25", - "range_animation": "25", - "combat_audio": "405,407,406", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "32", - "defence_animation": "26", - "weakness": "8", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby red dragon", - "defence_level": "42", - "safespot": null, - "lifepoints": "50", - "strength_level": "40", - "id": "3588", - "aggressive": "true", - "bonuses": "20,10,10,20,40,40,20,40,40,20,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "I don't think insect repellent will work...", - "melee_animation": "6223", - "range_animation": "0", - "combat_audio": "567,569,568", - "attack_speed": "4", - "poisonous": "true", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "6227", - "weakness": "6", - "poison_amount": "4", - "magic_animation": "0", - "death_animation": "6228", - "name": "Kalphite Soldier", - "defence_level": "70", - "safespot": null, - "lifepoints": "90", - "strength_level": "70", - "id": "3589", - "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": "Its scales seem to be made of steel.", - "combat_audio": "408,410,409", - "magic_level": "72", - "respawn_delay": "35", - "weakness": "8", - "slayer_exp": "221", - "name": "Steel dragon", - "defence_level": "238", - "safespot": null, - "lifepoints": "210", - "strength_level": "229", - "id": "3590", - "aggressive": "true", - "bonuses": "56,42,104,51,212,115,232,302,331,21,264,100,100,40,0", - "clue_level": "2", - "range_level": "185", - "attack_level": "234" - }, - { - "examine": "A darkened horror from the ocean depths.", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3591", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Arrgh! Look at its pointy teeth!", - "range_animation": "0", - "combat_audio": "400,404,403", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Demon", - "defence_level": "75", - "safespot": null, - "lifepoints": "107", - "strength_level": "75", - "id": "3593", - "aggressive": "true", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "A large snake that thrives in swamps.", - "melee_animation": "3538", - "range_animation": "3538", - "combat_audio": "3609,3608,3610", - "attack_speed": "5", - "defence_animation": "3539", - "weakness": "0", - "magic_animation": "3538", - "death_animation": "3540", - "name": "Swamp snake", - "defence_level": "40", - "safespot": null, - "lifepoints": "121", - "strength_level": "33", - "id": "3599", - "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", - "range_level": "45", - "attack_level": "35" - }, - { - "examine": "A large snake that thrives in swamps.", - "melee_animation": "3538", - "range_animation": "3538", - "combat_audio": "3609,3608,3610", - "attack_speed": "5", - "defence_animation": "3539", - "magic_animation": "3538", - "death_animation": "3540", - "name": "Swamp snake", - "defence_level": "60", - "safespot": null, - "lifepoints": "121", - "strength_level": "50", - "id": "3600", - "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A large snake that thrives in swamps.", - "slayer_task": "86", - "melee_animation": "3538", - "range_animation": "3538", - "combat_audio": "3609,3608,3610", - "attack_speed": "5", - "defence_animation": "3539", - "weakness": "7", - "magic_animation": "3538", - "death_animation": "3540", - "name": "Swamp snake", - "defence_level": "40", - "safespot": null, - "lifepoints": "121", - "strength_level": "33", - "id": "3601", - "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "A large snake that thrives in swamps.", - "melee_animation": "3538", - "range_animation": "3538", - "combat_audio": "3609,3608,3610", - "attack_speed": "5", - "defence_animation": "3539", - "magic_animation": "3538", - "death_animation": "3540", - "name": "Swamp snake", - "defence_level": "40", - "safespot": null, - "lifepoints": "121", - "strength_level": "33", - "id": "3602", - "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "name": "Dead swamp snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3609,3608,3610", - "strength_level": "1", - "id": "3603", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Dead swamp snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3609,3608,3610", - "strength_level": "1", - "id": "3604", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Dead swamp snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3609,3608,3610", - "strength_level": "1", - "id": "3605", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I'm glad I can't see the rest of it!", - "melee_animation": "3618", - "range_animation": "3618", - "magic_level": "80", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3620", - "name": "Tentacle", - "defence_level": "60", - "safespot": null, - "lifepoints": "171", - "strength_level": "80", - "id": "3618", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "I'm glad I can't see the rest of it!", - "melee_animation": "3731", - "range_animation": "0", - "attack_speed": "10", - "magic_level": "80", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3733", - "name": "Head", - "defence_level": "60", - "safespot": null, - "lifepoints": "171", - "strength_level": "85", - "id": "3619", - "aggressive": "true", - "range_level": "80", - "attack_level": "85" - }, - { - "melee_animation": "3731", - "attack_speed": "10", - "respawn_delay": "60", - "defence_animation": "3732", - "death_animation": "3733", - "name": "Head", - "defence_level": "1", - "safespot": null, - "lifepoints": "150", - "strength_level": "1", - "id": "3620", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "3622", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A high-ranking Vyrewatch", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fyiona Fray", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3634", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a little on the cross side!", - "range_animation": "0", - "combat_audio": "297,299,298", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Angry bear", - "defence_level": "38", - "safespot": null, - "lifepoints": "35", - "strength_level": "41", - "id": "3645", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "38" - }, - { - "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", - "strength_level": "41", - "id": "3646", - "aggressive": "false", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4935", - "name": "Angry giant rat", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3647", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "6185", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6182", - "name": "Angry goblin", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3648", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "AHHHHH!", - "melee_animation": "3812", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3813", - "name": "Fear reaper", - "defence_level": "27", - "safespot": null, - "lifepoints": "38", - "strength_level": "27", - "id": "3649", - "range_level": "1", - "attack_level": "27" - }, - { - "examine": "What on Gielinor is that?!?", - "melee_animation": "3818", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3821", - "name": "Confusion beast", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "3650", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A hopeless poor creature.", - "melee_animation": "3823", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3827", - "name": "Hopeless creature", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3655", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "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", - "attack_level": "38" - }, - { - "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", - "id": "3662", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6182", - "name": "Angry goblin", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3663", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "combat_audio": "297,299,298", - "attack_speed": "4", - "magic_level": "1", - "name": "Angry bear", - "defence_level": "38", - "safespot": null, - "lifepoints": "10", - "strength_level": "41", - "id": "3664", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "Freshly sheared.", - "melee_animation": "5341", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5343", - "name": "Ram", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "3672", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "White and shaggy.", - "melee_animation": "5338", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5336", - "name": "Ram", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "3673", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "If you see them circling, run.", - "melee_animation": "2019", - "range_animation": "0", - "combat_audio": "890,892,891", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "2024", - "weakness": "9", - "magic_animation": "0", - "death_animation": "2021", - "name": "Vulture", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "40", - "id": "3675", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "If you see them circling, run.", - "melee_animation": "2025", - "combat_audio": "890,892,891", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "2024", - "death_animation": "2026", - "name": "Vulture", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "40", - "id": "3676", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A young boy handing out flyers.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Leaflet Dropper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3680", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Ulfric", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "6118", - "strength_level": "1", - "id": "3706", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Eww", - "slayer_task": "67", - "melee_animation": "6117", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "50", - "magic_animation": "0", - "death_animation": "6115", - "name": "Brine rat", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "3707", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "A Wilderness fighter of massive repute.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3709", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An annoying flappy thing.", - "slayer_task": "5", - "melee_animation": "4915", - "range_animation": "4915", - "combat_audio": "292,294,293", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "4916", - "weakness": "4", - "slayer_exp": "8", - "magic_animation": "4915", - "death_animation": "4917", - "name": "Giant bat", - "defence_level": "22", - "safespot": null, - "lifepoints": "32", - "strength_level": "22", - "id": "3711", - "aggressive": "true", - "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", - "range_level": "1", - "attack_level": "12" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "3715", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "3726", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "combat_audio": "809,811,810", - "magic_level": "5", - "respawn_delay": "120", - "defence_animation": "3890", - "weakness": "8", - "magic_animation": "3891", - "death_animation": "3888", - "name": "Splatter", - "defence_level": "20", - "safespot": null, - "lifepoints": "13", - "strength_level": "20", - "id": "3727", - "bonuses": "5,10,10,10,10,2,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "combat_audio": "809,811,810", - "magic_level": "10", - "respawn_delay": "120", - "defence_animation": "3890", - "magic_animation": "3891", - "death_animation": "3888", - "name": "Splatter", - "defence_level": "25", - "safespot": null, - "lifepoints": "23", - "strength_level": "25", - "id": "3728", - "bonuses": "10,15,15,15,15,5,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "combat_audio": "809,811,810", - "magic_level": "10", - "respawn_delay": "120", - "defence_animation": "3890", - "magic_animation": "3891", - "death_animation": "3888", - "name": "Splatter", - "defence_level": "30", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "3729", - "bonuses": "15,20,20,20,20,8,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "combat_audio": "809,811,810", - "magic_level": "15", - "respawn_delay": "120", - "defence_animation": "3890", - "magic_animation": "3891", - "death_animation": "3888", - "name": "Splatter", - "defence_level": "35", - "safespot": null, - "lifepoints": "23", - "strength_level": "35", - "id": "3730", - "bonuses": "20,25,25,25,25,10,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "combat_audio": "809,811,810", - "magic_level": "20", - "respawn_delay": "120", - "defence_animation": "3890", - "magic_animation": "3891", - "death_animation": "3888", - "name": "Splatter", - "defence_level": "40", - "safespot": null, - "lifepoints": "23", - "strength_level": "40", - "id": "3731", - "bonuses": "25,30,30,30,30,15,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "14", - "respawn_delay": "127", - "defence_animation": "3902", - "weakness": "9", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "28", - "poison_immune": "true", - "safespot": null, - "lifepoints": "23", - "strength_level": "28", - "id": "3732", - "aggressive": "true", - "bonuses": "20,25,20,40,40,40,40,5,18,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "14", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "28", - "poison_immune": "true", - "safespot": null, - "lifepoints": "23", - "strength_level": "28", - "id": "3733", - "aggressive": "true", - "bonuses": "20,25,20,40,40,40,40,5,18,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "20", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "42", - "poison_immune": "true", - "safespot": null, - "lifepoints": "38", - "strength_level": "42", - "id": "3734", - "aggressive": "true", - "bonuses": "30,35,30,50,50,50,50,10,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "20", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "42", - "poison_immune": "true", - "safespot": null, - "lifepoints": "38", - "strength_level": "42", - "id": "3735", - "aggressive": "true", - "bonuses": "30,35,30,50,50,50,50,10,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "25", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "54", - "poison_immune": "true", - "safespot": null, - "lifepoints": "53", - "strength_level": "54", - "id": "3736", - "aggressive": "true", - "bonuses": "35,40,35,60,60,60,60,20,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "25", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "54", - "poison_immune": "true", - "safespot": null, - "lifepoints": "53", - "strength_level": "54", - "id": "3737", - "aggressive": "true", - "bonuses": "35,40,35,60,60,60,60,20,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "30", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "60", - "poison_immune": "true", - "safespot": null, - "lifepoints": "68", - "strength_level": "60", - "id": "3738", - "aggressive": "true", - "bonuses": "40,50,40,70,70,70,70,30,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "30", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "60", - "poison_immune": "true", - "safespot": null, - "lifepoints": "68", - "strength_level": "60", - "id": "3739", - "aggressive": "true", - "bonuses": "40,50,40,70,70,70,70,30,15,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "35", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "83", - "strength_level": "70", - "id": "3740", - "aggressive": "true", - "bonuses": "50,60,50,80,70,90,80,35,14,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "Nippy little thing!", - "melee_animation": "3901", - "range_animation": "3901", - "combat_audio": "766,768,767", - "magic_level": "35", - "respawn_delay": "127", - "defence_animation": "3902", - "magic_animation": "3901", - "death_animation": "3903", - "name": "Shifter", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "83", - "strength_level": "70", - "id": "3741", - "aggressive": "true", - "bonuses": "50,60,50,80,70,90,80,35,14,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "combat_audio": "714,716,715", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "120", - "defence_animation": "3916", - "weakness": "7", - "magic_animation": "3915", - "death_animation": "3917", - "name": "Ravager", - "defence_level": "15", - "safespot": null, - "lifepoints": "23", - "strength_level": "30", - "id": "3742", - "bonuses": "45,45,45,45,50,5,45,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "combat_audio": "714,716,715", - "attack_speed": "5", - "magic_level": "15", - "respawn_delay": "120", - "defence_animation": "3916", - "magic_animation": "3915", - "death_animation": "3917", - "name": "Ravager", - "defence_level": "20", - "safespot": null, - "lifepoints": "38", - "strength_level": "40", - "id": "3743", - "bonuses": "45,45,45,45,50,5,55,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "combat_audio": "714,716,715", - "attack_speed": "5", - "magic_level": "20", - "respawn_delay": "120", - "defence_animation": "3916", - "magic_animation": "3915", - "death_animation": "3917", - "name": "Ravager", - "defence_level": "30", - "safespot": null, - "lifepoints": "53", - "strength_level": "60", - "id": "3744", - "bonuses": "45,55,55,55,50,10,45,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "combat_audio": "714,716,715", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "120", - "defence_animation": "3916", - "magic_animation": "3915", - "death_animation": "3917", - "name": "Ravager", - "defence_level": "45", - "safespot": null, - "lifepoints": "53", - "strength_level": "75", - "id": "3745", - "bonuses": "60,60,60,60,80,10,45,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "combat_audio": "714,716,715", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "120", - "defence_animation": "3916", - "magic_animation": "3915", - "death_animation": "3917", - "name": "Ravager", - "defence_level": "60", - "safespot": null, - "lifepoints": "53", - "strength_level": "80", - "id": "3746", - "bonuses": "65,70,70,70,90,20,56,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "combat_audio": "805,807,806", - "magic_level": "10", - "respawn_delay": "125", - "defence_animation": "3909", - "weakness": "6", - "magic_animation": "3908", - "death_animation": "3910", - "name": "Spinner", - "defence_level": "20", - "safespot": null, - "lifepoints": "33", - "strength_level": "20", - "id": "3747", - "bonuses": "20,40,40,40,40,10,38,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "combat_audio": "805,807,806", - "magic_level": "15", - "respawn_delay": "125", - "defence_animation": "3909", - "magic_animation": "3908", - "death_animation": "3910", - "name": "Spinner", - "defence_level": "30", - "safespot": null, - "lifepoints": "50", - "strength_level": "30", - "id": "3748", - "bonuses": "30,60,60,60,60,15,38,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "combat_audio": "805,807,806", - "magic_level": "20", - "respawn_delay": "125", - "defence_animation": "3909", - "magic_animation": "3908", - "death_animation": "3910", - "name": "Spinner", - "defence_level": "40", - "safespot": null, - "lifepoints": "67", - "strength_level": "40", - "id": "3749", - "bonuses": "50,70,70,70,70,20,38,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "combat_audio": "805,807,806", - "magic_level": "30", - "respawn_delay": "125", - "defence_animation": "3909", - "magic_animation": "3908", - "death_animation": "3910", - "name": "Spinner", - "defence_level": "60", - "safespot": null, - "lifepoints": "101", - "strength_level": "60", - "id": "3750", - "bonuses": "60,100,100,100,100,30,25,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "combat_audio": "805,807,806", - "magic_level": "25", - "respawn_delay": "125", - "defence_animation": "3909", - "magic_animation": "3908", - "death_animation": "3910", - "name": "Spinner", - "defence_level": "50", - "safespot": null, - "lifepoints": "84", - "strength_level": "50", - "id": "3751", - "bonuses": "55,80,80,80,85,25,30,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "25", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "weakness": "5", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "25", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3752", - "aggressive": "true", - "bonuses": "30,30,30,30,15,30,40,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "25", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "25", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "3753", - "aggressive": "true", - "bonuses": "30,30,30,30,15,30,40,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "40", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "40", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "3754", - "aggressive": "true", - "bonuses": "45,40,40,40,25,35,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "40", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "40", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "3755", - "aggressive": "true", - "bonuses": "45,40,40,40,25,35,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "50", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "50", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "3756", - "aggressive": "true", - "bonuses": "35,40,40,40,30,35,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "50", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "50", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "3757", - "aggressive": "true", - "bonuses": "35,40,40,40,30,35,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "60", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "60", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "3758", - "aggressive": "true", - "bonuses": "60,80,80,80,50,50,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "60", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "60", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "3759", - "aggressive": "true", - "bonuses": "60,80,80,80,50,50,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "70", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "70", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "3760", - "aggressive": "true", - "bonuses": "40,100,100,100,50,50,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Burn, baby, burn!", - "start_gfx": "646", - "combat_style": "2", - "melee_animation": "3882", - "range_animation": "3882", - "combat_audio": "845,848,846", - "magic_level": "70", - "respawn_delay": "120", - "end_gfx": "648", - "defence_animation": "3880", - "magic_animation": "3882", - "death_animation": "3881", - "name": "Torcher", - "defence_level": "70", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "3761", - "aggressive": "true", - "bonuses": "40,100,100,100,50,50,35,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "647", - "attack_level": "1" - }, - { - "examine": "Duck!", - "start_gfx": "657", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "30", - "respawn_delay": "120", - "defence_animation": "3921", - "weakness": "0", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "25", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "3762", - "aggressive": "true", - "bonuses": "20,40,40,40,40,40,40,40,0,0,0,0,0,0,0", - "range_level": "25", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "30", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "25", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "3763", - "aggressive": "true", - "bonuses": "20,40,40,40,40,40,40,40,0,0,0,0,0,0,0", - "range_level": "25", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "start_gfx": "657", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "40", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "3764", - "aggressive": "true", - "bonuses": "30,50,60,60,60,60,40,40,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "40", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "40", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "3765", - "aggressive": "true", - "bonuses": "30,40,60,60,60,60,40,40,0,0,0,0,0,0,0", - "range_level": "40", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "start_gfx": "657", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "50", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "50", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "3766", - "aggressive": "true", - "bonuses": "35,65,70,70,70,70,40,40,0,0,0,0,0,0,0", - "range_level": "50", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "50", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "50", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "3767", - "aggressive": "true", - "bonuses": "35,65,70,70,70,70,40,40,0,0,0,0,0,0,0", - "range_level": "50", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "start_gfx": "657", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "60", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "60", - "safespot": null, - "lifepoints": "78", - "strength_level": "1", - "id": "3768", - "aggressive": "true", - "bonuses": "40,90,80,80,80,80,40,40,0,0,0,0,0,0,0", - "range_level": "60", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "60", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "60", - "safespot": null, - "lifepoints": "78", - "strength_level": "1", - "id": "3769", - "aggressive": "true", - "bonuses": "40,90,80,80,80,80,40,40,0,0,0,0,0,0,0", - "range_level": "60", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "start_gfx": "657", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "70", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "70", - "safespot": null, - "lifepoints": "97", - "strength_level": "1", - "id": "3770", - "aggressive": "true", - "bonuses": "50,100,100,100,120,100,40,40,80,0,0,0,0,0,0", - "range_level": "70", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "combat_audio": "392,394,393", - "magic_level": "70", - "respawn_delay": "120", - "defence_animation": "3921", - "magic_animation": "3920", - "death_animation": "3922", - "name": "Defiler", - "defence_level": "70", - "safespot": null, - "lifepoints": "97", - "strength_level": "1", - "id": "3771", - "aggressive": "true", - "bonuses": "50,100,100,100,120,100,40,40,80,0,0,0,0,0,0", - "range_level": "70", - "projectile": "657", - "attack_level": "1" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "combat_audio": "323,326,325", - "magic_level": "14", - "respawn_delay": "120", - "defence_animation": "3895", - "weakness": "9", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "28", - "safespot": null, - "lifepoints": "53", - "strength_level": "28", - "id": "3772", - "aggressive": "true", - "bonuses": "45,45,50,50,50,50,50,15,50,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "combat_audio": "323,326,325", - "magic_level": "21", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "42", - "safespot": null, - "lifepoints": "83", - "strength_level": "42", - "id": "3773", - "aggressive": "true", - "bonuses": "45,45,60,50,50,50,50,15,50,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "combat_audio": "323,326,325", - "magic_level": "32", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "56", - "safespot": null, - "lifepoints": "113", - "strength_level": "56", - "id": "3774", - "aggressive": "true", - "bonuses": "45,45,60,80,80,80,80,25,50,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "56" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "combat_audio": "323,326,325", - "magic_level": "38", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "74", - "safespot": null, - "lifepoints": "143", - "strength_level": "74", - "id": "3775", - "aggressive": "true", - "bonuses": "45,45,60,60,60,60,60,20,50,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "74" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "combat_audio": "323,326,325", - "magic_level": "45", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "88", - "safespot": null, - "lifepoints": "173", - "strength_level": "88", - "id": "3776", - "aggressive": "true", - "bonuses": "45,45,80,100,100,100,100,30,46,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "88" - }, - { - "examine": "Cheerful, helpful and optimistic, I'll bet.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Doomsayer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3777", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "attack_speed": "5", - "magic_level": "80", - "respawn_delay": "120", - "name": "Void Knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "3782", - "bonuses": "180,180,180,180,80,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Long legged licker.", - "melee_animation": "7260", - "range_animation": "0", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7256", - "name": "Frog", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "3783", - "range_level": "1", - "attack_level": "55" - }, - { - "name": "Void Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "attack_speed": "0", - "id": "3784", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "attack_speed": "5", - "magic_level": "80", - "respawn_delay": "120", - "name": "Void Knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "3785", - "bonuses": "180,180,180,180,80,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A powerful knight of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Void Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3786", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A powerful knight of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Void Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3788", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3790", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3791", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3792", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3793", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3794", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3795", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3796", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3797", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3798", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3799", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3800", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3801", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire (Novice)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3802", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Posts things.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Postie Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3805", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Miss Millicent Miller the Miller of Mill Lane Mill.", - "name": "Millie Miller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3806", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Gillie the Milkmaid milks cows. She's udderly fantastic at it.", - "name": "Gillie Groats", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3807", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Massive beast of War with extra Gnome.", - "melee_animation": "3960", - "range_animation": "3954", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "3962", - "name": "Tortoise", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "3808", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "Tally Ho!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Dalbur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3809", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Huzzah!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Bleemadge", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3810", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Up up and away!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Errdo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3811", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Up up and away!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Klemfoodle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3812", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Gnome Arrow-chucker", - "combat_style": "1", - "melee_animation": "190", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome Archer", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "3814", - "aggressive": "true", - "range_level": "30", - "attack_level": "1" - }, - { - "examine": "Yee haa!", - "melee_animation": "3969", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome Driver", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "3815", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A battle mage of the gnomish variety.", - "combat_style": "2", - "melee_animation": "3968", - "range_animation": "0", - "magic_level": "34", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome Mage", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "1", - "id": "3816", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The cruel tortoise trainer. Boo!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trainer Nacklepen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3818", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A large tortoise.", - "melee_animation": "3960", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "3962", - "name": "Tortoise", - "defence_level": "36", - "safespot": null, - "lifepoints": "51", - "strength_level": "36", - "id": "3819", - "range_level": "1", - "attack_level": "36" - }, - { - "agg_radius": "64", - "melee_animation": "6241", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6240", - "death_animation": "6242", - "name": "Kalphite Queen", - "defence_level": "1", - "safespot": null, - "lifepoints": "255", - "strength_level": "1", - "id": "3835", - "aggressive": "true", - "range_level": "1000", - "attack_level": "1" - }, - { - "agg_radius": "64", - "melee_animation": "6234", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6237", - "death_animation": "6233", - "name": "Kalphite Queen", - "defence_level": "1", - "safespot": null, - "lifepoints": "255", - "strength_level": "1", - "id": "3836", - "aggressive": "true", - "range_level": "1000", - "attack_level": "1" - }, - { - "examine": "An aquatic troll.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Sea troll", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "3840", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "An aquatic troll.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Sea troll", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "3843", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "The mother of all sea trolls!", - "melee_animation": "3991", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "3993", - "name": "Sea Troll Queen", - "defence_level": "65", - "safespot": null, - "lifepoints": "428", - "strength_level": "65", - "id": "3847", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "What have they done to him?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishing spot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3849", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "7", - "strength_level": "1", - "id": "3915", - "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His bark's worse than his blight.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Carpenter Kjallak", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3916", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a bit seedy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Fromund", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3917", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Snake", - "slayer_task": "72", - "melee_animation": "3538", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "3540", - "name": "Sea Snake Young", - "defence_level": "45", - "safespot": null, - "lifepoints": "128", - "strength_level": "33", - "id": "3939", - "aggressive": "true", - "range_level": "45", - "attack_level": "33" - }, - { - "examine": "A baby sea snake. Snaaaaaaake!", - "slayer_task": "72", - "melee_animation": "3538", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "3540", - "name": "Sea Snake Hatchling", - "defence_level": "45", - "safespot": null, - "lifepoints": "100", - "strength_level": "33", - "id": "3940", - "aggressive": "true", - "range_level": "45", - "attack_level": "33" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "3941", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the entrance to the dungeons.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "3942", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A big snake that lives in the sea. How did it get in here?", - "melee_animation": "4040", - "range_animation": "0", - "combat_audio": "3609,3608,3610", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "4039", - "name": "Giant Sea Snake", - "defence_level": "45", - "safespot": null, - "lifepoints": "385", - "strength_level": "33", - "id": "3943", - "aggressive": "true", - "range_level": "45", - "attack_level": "33" - }, - { - "slayer_exp": "37", - "name": "Cockatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4227", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "75", - "name": "Basilisk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "288,290,289", - "strength_level": "1", - "id": "4228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Large, heavy, with sharp things attached to its head.", - "melee_animation": "9439", - "range_animation": "9439", - "combat_audio": "588,590,589", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "9441", - "slayer_exp": "97", - "magic_animation": "9439", - "death_animation": "9440", - "name": "Kurask", - "defence_level": "105", - "safespot": null, - "lifepoints": "97", - "strength_level": "105", - "id": "4229", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "67" - }, - { - "examine": "A denizen of the Abyss!", - "slayer_task": "1", - "melee_animation": "1537", - "range_animation": "1537", - "combat_audio": "276,278,277", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "150", - "magic_animation": "1537", - "death_animation": "1538", - "name": "Abyssal demon", - "defence_level": "135", - "safespot": null, - "lifepoints": "150", - "strength_level": "67", - "id": "4230", - "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "97" - }, - { - "name": "Demon butler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "400,404,403", - "strength_level": "1", - "id": "4243", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Head of the servants' guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chief servant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4245", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She smells unpleasantly of chemicals.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Taxidermist", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4246", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fancy businessman with a mighty fine hat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Estate agent", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4247", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Someone has to get rid of all the stone they dug Keldagrim out of.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Stonemason", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4248", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He changes the shape of wood.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "facing_booth": "true", - "magic_animation": "0", - "death_animation": "0", - "name": "Sawmill operator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4250", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She has green fingers. (Not literally.)", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Garden supplier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4251", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Beak areful with this one", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Macaroni Penguin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4252", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4257", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarven guard.", - "slayer_task": "29", - "melee_animation": "99", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "102", - "name": "Guard", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "4258", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4259", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4260", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4261", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4262", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4263", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4264", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4265", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4266", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4267", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4268", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4269", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4270", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4271", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4272", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4273", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4274", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4275", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4276", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Animated bronze armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Bronze Armour", - "defence_level": "10", - "safespot": null, - "poison_immune": "true", - "lifepoints": "10", - "strength_level": "10", - "id": "4278", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Animated iron armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Iron Armour", - "defence_level": "20", - "safespot": null, - "poison_immune": "true", - "lifepoints": "20", - "strength_level": "20", - "id": "4279", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Steel Armour", - "defence_level": "40", - "safespot": null, - "poison_immune": "true", - "lifepoints": "40", - "strength_level": "40", - "id": "4280", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Animated black armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Black Armour", - "defence_level": "60", - "safespot": null, - "poison_immune": "true", - "lifepoints": "60", - "strength_level": "60", - "id": "4281", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Animated mithril armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Mithril Armour", - "defence_level": "80", - "safespot": null, - "poison_immune": "true", - "lifepoints": "80", - "strength_level": "80", - "id": "4282", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "Animated adamant armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Adamant Armour", - "defence_level": "99", - "safespot": null, - "poison_immune": "true", - "lifepoints": "99", - "strength_level": "99", - "id": "4283", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "99" - }, - { - "examine": "Animated rune armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "4", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Rune Armour", - "defence_level": "120", - "safespot": null, - "poison_immune": "true", - "lifepoints": "120", - "strength_level": "120", - "id": "4284", - "aggressive": "true", - "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", - "range_level": "1", - "attack_level": "120" - }, - { - "examine": "A one-eyed man eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "8", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "26", - "safespot": null, - "lifepoints": "75", - "strength_level": "50", - "id": "4291", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "A one-eyed woman eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "1", - "slayer_exp": "100", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "35", - "safespot": null, - "lifepoints": "100", - "strength_level": "65", - "id": "4292", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "He looks fair and reliable.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ref", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4300", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4301", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4302", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4303", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4304", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4305", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4306", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard for the humans against monster group.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "4307", - "clue_level": "1", - "range_level": "1", - "attack_level": "26" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4308", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4309", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4310", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4311", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4316", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4318", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "H.A.M. Member", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4320", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "H.A.M. Deacon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4329", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "395", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4336", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Snake", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3609,3608,3610", - "strength_level": "1", - "id": "4343", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "220", - "combat_audio": "629,631,630", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "221", - "death_animation": "223", - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "12", - "strength_level": "1", - "id": "4344", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's all white by me.", - "slayer_task": "5", - "melee_animation": "4915", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4917", - "name": "Albino bat", - "defence_level": "31", - "safespot": null, - "lifepoints": "44", - "strength_level": "31", - "id": "4345", - "range_level": "1", - "attack_level": "31" - }, - { - "examine": "A flying blood sucker.", - "melee_animation": "2397", - "range_animation": "0", - "attack_speed": "10", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "2398", - "name": "Giant mosquito", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "4347", - "range_level": "1", - "attack_level": "63" - }, - { - "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", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "55", - "safespot": null, - "lifepoints": "45", - "strength_level": "70", - "id": "4348", - "aggressive": "true", - "clue_level": "1", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "A horrible, emaciated ape like creature with beady yellow eyes.", - "melee_animation": "4234", - "combat_audio": "496,500,499", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "55", - "safespot": null, - "lifepoints": "45", - "strength_level": "70", - "id": "4349", - "aggressive": "true", - "clue_level": "1", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "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", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "55", - "safespot": null, - "lifepoints": "45", - "strength_level": "70", - "id": "4350", - "aggressive": "true", - "clue_level": "1", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "A horrible, emaciated ape like creature with beady blue eyes.", - "melee_animation": "4234", - "combat_audio": "496,500,499", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "55", - "safespot": null, - "lifepoints": "45", - "strength_level": "70", - "id": "4351", - "aggressive": "true", - "clue_level": "1", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "A horrible, emaciated ape like creature with beady pink eyes.", - "melee_animation": "4234", - "combat_audio": "496,500,499", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "55", - "safespot": null, - "lifepoints": "45", - "strength_level": "70", - "id": "4352", - "aggressive": "true", - "clue_level": "1", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "55", - "strength_level": "77", - "id": "4353", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "80" - }, - { - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "55", - "strength_level": "77", - "id": "4354", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "80" - }, - { - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "55", - "strength_level": "77", - "id": "4355", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "80" - }, - { - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "55", - "strength_level": "77", - "id": "4356", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "80" - }, - { - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "50", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "55", - "strength_level": "77", - "id": "4357", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "Patchy the pirate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Patchy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4359", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very flamboyant pirate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy Dan", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4361", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I can't wait to buy from a guy with Honest in his name...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Honest Jimmy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4362", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "4363", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Blue Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "4371", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Red Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "4372", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A colourful bird.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Parrot", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4373", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A retired Gielinor security guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Security Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4375", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bony ghost.", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "6", - "defence_animation": "404", - "slayer_exp": "60", - "magic_animation": "422", - "death_animation": "9055", - "name": "Ankou", - "defence_level": "50", - "safespot": null, - "lifepoints": "60", - "strength_level": "69", - "id": "4381", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A bony ghost.", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "6", - "defence_animation": "404", - "slayer_exp": "60", - "magic_animation": "422", - "death_animation": "9055", - "name": "Ankou", - "defence_level": "50", - "safespot": null, - "lifepoints": "66", - "strength_level": "69", - "id": "4382", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A bony ghost.", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "6", - "defence_animation": "404", - "slayer_exp": "60", - "magic_animation": "422", - "death_animation": "9055", - "name": "Ankou", - "defence_level": "55", - "safespot": null, - "lifepoints": "66", - "strength_level": "74", - "id": "4383", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "60" - }, - { - "melee_animation": "5540", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5541", - "slayer_exp": "25", - "death_animation": "5542", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "36", - "strength_level": "1", - "id": "4387", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "4388", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I don't think insect repellent will work.", - "slayer_task": "34", - "melee_animation": "1184", - "range_animation": "1184", - "attack_speed": "3", - "respawn_delay": "30", - "defence_animation": "1186", - "weakness": "0", - "slayer_exp": "25", - "magic_animation": "1184", - "death_animation": "1190", - "name": "Flesh Crawler", - "defence_level": "10", - "safespot": null, - "lifepoints": "25", - "strength_level": "2", - "id": "4389", - "aggressive": "true", - "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "I don't think insect repellent will work.", - "melee_animation": "1184", - "range_animation": "1184", - "attack_speed": "3", - "respawn_delay": "30", - "defence_animation": "1186", - "slayer_exp": "25", - "magic_animation": "1184", - "death_animation": "1190", - "name": "Flesh Crawler", - "defence_level": "10", - "safespot": null, - "lifepoints": "25", - "strength_level": "2", - "id": "4390", - "aggressive": "true", - "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "I don't think insect repellent will work.", - "melee_animation": "1184", - "range_animation": "1184", - "attack_speed": "3", - "respawn_delay": "30", - "defence_animation": "1186", - "slayer_exp": "25", - "magic_animation": "1184", - "death_animation": "1190", - "name": "Flesh Crawler", - "defence_level": "10", - "safespot": null, - "lifepoints": "25", - "strength_level": "2", - "id": "4391", - "aggressive": "true", - "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "The walking dead.", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5567", - "slayer_exp": "30", - "magic_animation": "5568", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "25", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "4392", - "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.", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "28", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "4393", - "aggressive": "true", - "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Could his name be \"Lurch\"?", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "4394", - "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", - "attack_speed": "5", - "weakness": "9", - "name": "Giant rat", - "defence_level": "23", - "safespot": null, - "lifepoints": "26", - "strength_level": "20", - "id": "4395", - "aggressive": "true", - "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", - "range_level": "1", - "attack_level": "21" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "3102,3104,3103", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "4396", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big Cow-like... But cows don't have serpent tails!", - "melee_animation": "4271", - "attack_speed": "5", - "magic_level": "38", - "spell_id": "7", - "respawn_delay": "74", - "defence_animation": "4273", - "slayer_exp": "50", - "magic_animation": "4272", - "death_animation": "4270", - "name": "Catablepon", - "defence_level": "38", - "safespot": null, - "lifepoints": "50", - "strength_level": "38", - "id": "4397", - "aggressive": "true", - "bonuses": "5,20,15,3,10,20,10,10,20,15,23,25,15,25,10", - "clue_level": "1", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "Big Cow-like... But cows don't have serpent tails!", - "melee_animation": "4271", - "attack_speed": "5", - "magic_level": "38", - "spell_id": "7", - "respawn_delay": "74", - "defence_animation": "4273", - "slayer_exp": "50", - "magic_animation": "4272", - "death_animation": "4270", - "name": "Catablepon", - "defence_level": "38", - "safespot": null, - "lifepoints": "70", - "strength_level": "38", - "id": "4398", - "aggressive": "true", - "bonuses": "20,25,10,20,25,10,10,11,0,0,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "Big Cow-like... But cows don't have serpent tails!", - "melee_animation": "4271", - "attack_speed": "5", - "magic_level": "45", - "spell_id": "7", - "respawn_delay": "74", - "defence_animation": "4273", - "slayer_exp": "50", - "magic_animation": "4272", - "death_animation": "4270", - "name": "Catablepon", - "defence_level": "38", - "safespot": null, - "lifepoints": "50", - "strength_level": "58", - "id": "4399", - "aggressive": "true", - "bonuses": "50,45,50,40,45,20,30,25,33,25,25,20,20,40,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "I think this spider has been genetically modified.", - "melee_animation": "5327", - "range_animation": "5327", - "combat_audio": "537,539,538", - "defence_animation": "5328", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Giant spider", - "defence_level": "51", - "safespot": null, - "lifepoints": "50", - "strength_level": "45", - "id": "4400", - "aggressive": "true", - "bonuses": "19,21,19,62,45,51,54,63,60,18,0,0,0,0,0", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "6249", - "respawn_delay": "60", - "defence_animation": "6250", - "death_animation": "6251", - "name": "Spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "4401", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An extremely vicious scorpion.", - "melee_animation": "6254", - "range_animation": "6254", - "combat_audio": "3611,3612,3610", - "attack_speed": "5", - "defence_animation": "6255", - "slayer_exp": "17", - "magic_animation": "6254", - "death_animation": "6256", - "name": "Scorpion", - "defence_level": "49", - "safespot": null, - "lifepoints": "55", - "strength_level": "51", - "id": "4402", - "aggressive": "true", - "bonuses": "12,23,10,51,32,62,29,54,48,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "An extremely vicious scorpion.", - "melee_animation": "6254", - "range_animation": "6254", - "combat_audio": "3611,3612,3610", - "attack_speed": "5", - "defence_animation": "6255", - "slayer_exp": "17", - "magic_animation": "6254", - "death_animation": "6256", - "name": "Scorpion", - "defence_level": "35", - "safespot": null, - "lifepoints": "37", - "strength_level": "31", - "id": "4403", - "aggressive": "true", - "bonuses": "15,16,12,32,19,38,18,34,32,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "He doesn't look very pleased to see you.", - "slayer_task": "58", - "melee_animation": "4266", - "range_animation": "4266", - "combat_audio": "626,628,627", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "4267", - "weakness": "7", - "slayer_exp": "10", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "4404", - "aggressive": "false", - "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "He doesn't look very pleased to see you.", - "melee_animation": "4266", - "range_animation": "4266", - "combat_audio": "626,628,627", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "4267", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "10", - "id": "4405", - "aggressive": "false", - "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", - "clue_level": "0", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "He doesn't look very pleased to see you.", - "melee_animation": "4266", - "range_animation": "4266", - "combat_audio": "626,628,627", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "15", - "defence_animation": "4267", - "slayer_exp": "22", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "25", - "safespot": null, - "lifepoints": "22", - "strength_level": "25", - "id": "4406", - "aggressive": "false", - "bonuses": "0,0,0,0,0,-10,-10,-10,21,-10,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4407", - "bonuses": "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "11", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4408", - "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "7", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "9", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4409", - "bonuses": "6,6,6,6,6,6,6,6,6,6,6,6,6,6,6", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4410", - "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "17", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4411", - "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "10", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4412", - "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "They say inside you there are two wolves...", - "melee_animation": "6559", - "range_animation": "6559", - "combat_audio": "481,491,490", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "6557", - "slayer_exp": "15", - "magic_animation": "6559", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "9", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "4413", - "bonuses": "7,7,7,7,7,7,7,7,7,7,7,7,7,7,7", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "They say inside you there are two wolves...", - "melee_animation": "6559", - "range_animation": "6559", - "combat_audio": "481,491,490", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "6557", - "slayer_exp": "10", - "magic_animation": "6559", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4414", - "bonuses": "5,5,5,5,5,5,5,5,5,5,5,5,5,5,5", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "slayer_task": "67", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "3102,3104,3103", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "3", - "defence_animation": "2706", - "slayer_exp": "2", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "4415", - "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,-42,-53,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4300", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4301", - "slayer_exp": "112", - "death_animation": "4302", - "name": "Gorak", - "defence_level": "1", - "safespot": null, - "lifepoints": "112", - "strength_level": "1", - "id": "4418", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A highly enlightened being.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cosmic Being", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4419", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Horseplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Centaur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4438", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Horseplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Centaur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4439", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A noble creature!", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "812,814,813", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "90", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Stag", - "defence_level": "13", - "safespot": null, - "lifepoints": "19", - "strength_level": "13", - "id": "4440", - "aggressive": "false", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Twiggy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wood Dryad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4441", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Fairy ring maintenance division.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy Fixit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4455", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He works in the bank.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ork", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4457", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She works in the bank.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ork", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4458", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She works in the bank.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ork", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4459", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very angry nymph.", - "melee_animation": "94", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "97", - "name": "Tree spirit", - "defence_level": "9", - "safespot": null, - "lifepoints": "12", - "strength_level": "9", - "id": "4470", - "aggressive": "true", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "A magic training dummy", - "melee_animation": "94", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "97", - "name": "Magic dummy", - "defence_level": "1", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "4474", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Who's your mummy?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guardian mummy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4476", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4479", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4480", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4481", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4482", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4483", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4484", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4485", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4486", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4487", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4488", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4489", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4490", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4491", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4492", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "General Wartface", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4494", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ugly green creature.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "4499", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is he real or is it just my imagination?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4501", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Does she really exist?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Lady", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4502", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He reminds me of my old mathematics teacher.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Numerator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4503", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The master of accomplishment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4504", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He knows what is possible.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Perceptive", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4505", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He knows the past, present and future. But what of my shoe size?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4506", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Luck is probably on his side.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ethereal Fluke", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4507", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's like looking in the mirror.", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Me", - "defence_level": "60", - "safespot": null, - "lifepoints": "171", - "strength_level": "60", - "id": "4509", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "It's like looking in the mirror.", - "melee_animation": "414", - "range_animation": "0", - "magic_level": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Me", - "defence_level": "60", - "safespot": null, - "lifepoints": "171", - "strength_level": "60", - "id": "4510", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Spiritual leader of the Moonclan.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Oneiromancer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4511", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The craziest house I ever did see!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "House", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4512", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "How does it see where to sweep?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Enchanted Broom", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I'm not sure if it's actually doing anything of use.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Enchanted Bucket", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4524", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4384", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4529", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4530", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4531", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,70,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4384", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4533", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "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", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "85", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "85", - "strength_level": "30", - "id": "4534", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Either a very fremennikey pirate, or a very piratey fremnnik.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lokar Searunner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4537", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I always wondered what that job description actually meant...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cabin boy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4539", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The High Priest has been transformed into an avatar of Bandos.", - "range_animation": "0", - "magic_level": "55", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Bentley", - "defence_level": "55", - "safespot": null, - "lifepoints": "214", - "strength_level": "55", - "id": "4540", - "aggressive": "true", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "A stickler for hygiene in the kitchen.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Beefy' Burns", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4541", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A keen-eyed lookout with a telescope.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Eagle-eye' Shultz", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4542", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "First mate to Captain Bentley.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "First mate 'Davey-boy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4543", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pirate through and through.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Picarron' Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4545", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Jake", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "melee_animation": "422", - "strength_level": "1", - "id": "4546", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "He got his name from his favourite hobby. Reading about beds.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bedread the bold", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4547", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Wilson", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "melee_animation": "395", - "strength_level": "1", - "id": "4548", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "Nobody really knows why he's called Tommy 2-times...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tommy 2-times", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4549", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Don't look him straight in the eyes. He'll eat you alive!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Murky Pat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4550", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Nice beard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jack Sails", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4551", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is he looking at me?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Beedy-eye' Jones", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yes", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jenny Blade", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4555", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A low-down", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Lecherous' Lee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4556", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Apparently his nickname comes from his fondness for jam.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Sticky' Sanders", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4557", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This person is working on the site.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Digsite workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4564", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This person is working on the site.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Digsite workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4565", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Upon examining the examiner you examine it is indeed an examiner!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Examiner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4566", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Upon examining the examiner you examine it is indeed an examiner!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Examiner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4567", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Must be hard at work.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Researcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4568", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He makes pots.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Generic Diplomat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's very diplomatic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ambassador Gimblewap", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4580", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Peaceful man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ambassador Spanfipple", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4581", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Peaceful man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ambassador Ferrnook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your common man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sorrn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4589", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your common woman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mimm", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4590", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your common man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Portobello", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4593", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome pilot off duty", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Ninto", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4594", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome pilot off duty", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Daerkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks alert and ready for action.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard Vemmeldo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4600", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A generic evil henchman.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Fortress Guard", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "4603", - "range_level": "1", - "attack_level": "26" - }, - { - "examine": "A generic evil henchman.", - "melee_animation": "428", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Fortress Guard", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "4604", - "range_level": "1", - "attack_level": "26" - }, - { - "examine": "A generic evil henchman.", - "melee_animation": "428", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Fortress Guard", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "4605", - "range_level": "1", - "attack_level": "26" - }, - { - "examine": "A generic evil henchman.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Fortress Guard", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "26", - "id": "4606", - "range_level": "1", - "attack_level": "26" - }, - { - "examine": "A witch's black cat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Black Cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4607", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4608", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Swine.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Saboteur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4611", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What is that thing!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "0", - "name": "Evil creature", - "defence_level": "1", - "safespot": null, - "lifepoints": "0", - "strength_level": "1", - "id": "4615", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "4633", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "4634", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "4635", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "4636", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "4637", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little warrior.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little warrior.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4639", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little warrior.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little warrior.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4641", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little warrior.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome soldier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4642", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "King Healthorg riding his War Tortoise.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Healthorg and tortoise", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4643", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A weasly, shifty-looking Gnome Mage.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Glouphrie the Untrusted", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4645", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "With the prices he charges, no wonder he can afford to look so sharp.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Stan", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4650", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks very stylish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4651", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "That suit looks a little briny around the edges.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4652", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "First storm he is in that hat will blow away.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4653", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She'll never be able to climb rigging in that.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4654", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "High heels on a ship? What is she thinking?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4655", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The effect is sort of spoiled by all those tattoos.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Crewmember", - "defence_level": "1", - "movement_radius": "710", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4656", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He works evil magic.", - "start_gfx": "93", - "combat_style": "2", - "melee_animation": "810", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "6", - "end_gfx": "95", - "respawn_delay": "60", - "defence_animation": "425", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "5", - "safespot": null, - "lifepoints": "12", - "strength_level": "2", - "id": "4659", - "aggressive": "true", - "range_level": "1", - "projectile": "94", - "attack_level": "5" - }, - { - "examine": "He works evil magic.", - "start_gfx": "96", - "combat_style": "2", - "melee_animation": "810", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "22", - "end_gfx": "98", - "respawn_delay": "60", - "defence_animation": "425", - "magic_animation": "711", - "death_animation": "836", - "name": "Dark wizard", - "defence_level": "14", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "4660", - "aggressive": "true", - "range_level": "1", - "projectile": "97", - "attack_level": "17" - }, - { - "examine": "He works evil magic.", - "combat_style": "2", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "15", - "respawn_delay": "20", - "defence_animation": "404", - "magic_animation": "711", - "death_animation": "9055", - "name": "Dark wizard", - "defence_level": "15", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "4661", - "aggressive": "true", - "range_level": "1", - "projectile": "98", - "attack_level": "1" - }, - { - "examine": "Young but still dangerous.", - "slayer_task": "11", - "melee_animation": "25", - "range_animation": "25", - "combat_audio": "405,407,406", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "26", - "weakness": "3", - "slayer_exp": "50", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby blue dragon", - "defence_level": "40", - "safespot": null, - "lifepoints": "50", - "strength_level": "40", - "id": "4665", - "aggressive": "true", - "bonuses": "0,0,0,0,0,30,50,50,40,30,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Young but still dangerous.", - "slayer_task": "11", - "combat_audio": "405,407,406", - "melee_animation": "25", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "26", - "slayer_exp": "50", - "weakness": "3", - "magic_animation": "25", - "death_animation": "28", - "name": "Baby blue dragon", - "defence_level": "40", - "safespot": null, - "lifepoints": "50", - "strength_level": "40", - "id": "4666", - "aggressive": "true", - "bonuses": "0,0,0,0,0,30,50,50,40,30,0,0,0,0,0", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "Young but still dangerous.", - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "405,407,406", - "strength_level": "1", - "id": "4667", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Young but still dangerous.", - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "405,407,406", - "strength_level": "1", - "id": "4668", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A big powerful dragon.", - "slayer_task": "68", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "143", - "magic_animation": "80", - "death_animation": "92", - "name": "Red dragon", - "defence_level": "130", - "safespot": null, - "lifepoints": "140", - "strength_level": "130", - "id": "4669", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "A big powerful dragon.", - "slayer_task": "68", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "143", - "magic_animation": "80", - "death_animation": "92", - "name": "Red dragon", - "defence_level": "130", - "safespot": null, - "lifepoints": "140", - "strength_level": "130", - "id": "4670", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "A big powerful dragon.", - "slayer_task": "68", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "143", - "magic_animation": "80", - "death_animation": "92", - "name": "Red dragon", - "defence_level": "130", - "safespot": null, - "lifepoints": "140", - "strength_level": "130", - "id": "4671", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "A big powerful dragon.", - "slayer_task": "68", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "143", - "magic_animation": "80", - "death_animation": "92", - "name": "Red dragon", - "defence_level": "130", - "safespot": null, - "lifepoints": "140", - "strength_level": "130", - "id": "4672", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "A fierce dragon with black scales!", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "200", - "magic_animation": "80", - "death_animation": "92", - "name": "Black dragon", - "defence_level": "200", - "safespot": null, - "lifepoints": "190", - "strength_level": "200", - "id": "4673", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "A fierce dragon with black scales!", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "200", - "magic_animation": "80", - "death_animation": "92", - "name": "Black dragon", - "defence_level": "200", - "safespot": null, - "lifepoints": "190", - "strength_level": "200", - "id": "4674", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "A fierce dragon with black scales!", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "200", - "magic_animation": "80", - "death_animation": "92", - "name": "Black dragon", - "defence_level": "200", - "safespot": null, - "lifepoints": "190", - "strength_level": "200", - "id": "4675", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "A fierce dragon with black scales!", - "slayer_task": "9", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "200", - "magic_animation": "80", - "death_animation": "92", - "name": "Black dragon", - "defence_level": "200", - "safespot": null, - "lifepoints": "190", - "strength_level": "200", - "id": "4676", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "200" - }, - { - "examine": "Must be related to Elvarg.", - "slayer_task": "41", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "68", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "75", - "magic_animation": "80", - "death_animation": "92", - "name": "Green dragon", - "defence_level": "68", - "safespot": null, - "lifepoints": "75", - "strength_level": "68", - "id": "4677", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Must be related to Elvarg.", - "slayer_task": "41", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "68", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "75", - "magic_animation": "80", - "death_animation": "92", - "name": "Green dragon", - "defence_level": "68", - "safespot": null, - "lifepoints": "75", - "strength_level": "68", - "id": "4678", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Must be related to Elvarg.", - "slayer_task": "41", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "68", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "75", - "magic_animation": "80", - "death_animation": "92", - "name": "Green dragon", - "defence_level": "68", - "safespot": null, - "lifepoints": "75", - "strength_level": "68", - "id": "4679", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Must be related to Elvarg.", - "slayer_task": "41", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "68", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "75", - "magic_animation": "80", - "death_animation": "92", - "name": "Green dragon", - "defence_level": "68", - "safespot": null, - "lifepoints": "75", - "strength_level": "68", - "id": "4680", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4681", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4682", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4683", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4684", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "448,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "70", - "magic_animation": "0", - "death_animation": "4673", - "name": "Ice giant", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "40", - "id": "4685", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "448,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "70", - "magic_animation": "0", - "death_animation": "4673", - "name": "Ice giant", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "40", - "id": "4686", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "448,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "70", - "magic_animation": "0", - "death_animation": "4673", - "name": "Ice giant", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "40", - "id": "4687", - "aggressive": "true", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "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", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "60", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "60", - "strength_level": "30", - "id": "4688", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A very large foe.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", - "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", - "strength_level": "22", - "id": "4689", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "A very large foe.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", - "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", - "strength_level": "22", - "id": "4690", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "A very large foe.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", - "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", - "strength_level": "22", - "id": "4691", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "A very large foe.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", - "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", - "strength_level": "22", - "id": "4692", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "A very large foe.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "4652", - "combat_audio": "448,451,450", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4651", - "weakness": "6", - "slayer_exp": "35", - "magic_animation": "4652", - "death_animation": "4653", - "name": "Hill Giant", - "defence_level": "26", - "safespot": null, - "lifepoints": "35", - "strength_level": "22", - "id": "4693", - "aggressive": "true", - "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "Lesser, but still pretty big.", - "slayer_task": "56", - "melee_animation": "4630", - "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "4694", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Lesser, but still pretty big.", - "slayer_task": "56", - "melee_animation": "4630", - "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "4695", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Lesser, but still pretty big.", - "slayer_task": "56", - "melee_animation": "4630", - "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "4696", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Lesser, but still pretty big.", - "slayer_task": "56", - "melee_animation": "4630", - "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "4697", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "attack_speed": "5", - "magic_level": "59", - "respawn_delay": "32", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "87", - "magic_animation": "64", - "death_animation": "68", - "name": "Greater demon", - "defence_level": "59", - "safespot": null, - "lifepoints": "87", - "strength_level": "59", - "id": "4698", - "aggressive": "true", - "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", - "clue_level": "2", - "range_level": "59", - "attack_level": "59" - }, - { - "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "attack_speed": "5", - "magic_level": "59", - "respawn_delay": "32", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "87", - "magic_animation": "64", - "death_animation": "68", - "name": "Greater demon", - "defence_level": "59", - "safespot": null, - "lifepoints": "87", - "strength_level": "59", - "id": "4699", - "aggressive": "true", - "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", - "clue_level": "2", - "range_level": "59", - "attack_level": "59" - }, - { - "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "attack_speed": "5", - "magic_level": "59", - "respawn_delay": "32", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "87", - "magic_animation": "64", - "death_animation": "68", - "name": "Greater demon", - "defence_level": "59", - "safespot": null, - "lifepoints": "87", - "strength_level": "59", - "id": "4700", - "aggressive": "true", - "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", - "clue_level": "2", - "range_level": "59", - "attack_level": "59" - }, - { - "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "attack_speed": "5", - "magic_level": "59", - "respawn_delay": "32", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "87", - "magic_animation": "64", - "death_animation": "68", - "name": "Greater demon", - "defence_level": "59", - "safespot": null, - "lifepoints": "87", - "strength_level": "59", - "id": "4701", - "aggressive": "true", - "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", - "clue_level": "2", - "range_level": "59", - "attack_level": "59" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "157", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", - "strength_level": "148", - "id": "4702", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "145" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "157", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", - "strength_level": "148", - "id": "4703", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "145" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "157", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", - "strength_level": "148", - "id": "4704", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "145" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "397,399,398", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "157", - "magic_animation": "64", - "death_animation": "67", - "name": "Black demon", - "defence_level": "152", - "safespot": null, - "lifepoints": "157", - "strength_level": "148", - "id": "4705", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "145" - }, - { - "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", - "attack_speed": "6", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4665", - "weakness": "1", - "slayer_exp": "85", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Moss giant", - "defence_level": "30", - "safespot": null, - "lifepoints": "85", - "strength_level": "30", - "id": "4706", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Mikasi looks ready to teach you about magic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Magic Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4707", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A flea-infested", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Old Man Ral", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4708", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A flea-infested black market trader.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trader Sven", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4716", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A flea-infested", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4717", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A flea-infested", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4718", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4719", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4720", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4721", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4722", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4723", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4724", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4725", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4726", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Cowardly mage.", - "magic_level": "90", - "defence_animation": "0", - "magic_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "60", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "4733", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4734", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4735", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4736", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4737", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4738", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4740", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks pale", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch citizen", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4741", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4746", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4747", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4748", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4749", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4750", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4751", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4752", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4753", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4754", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A poor street urchin!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "A Meiyerditch child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4755", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks dirty and tired!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4756", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks dirty and tired!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4757", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This citizen looks dirty and tired!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Meiyerditch miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4759", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Some rubbish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4765", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like it's got fleas!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Stray dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4766", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A smelly cat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4768", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre; a guard for the mining area.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Juvinate guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4772", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre; a guard for the mining area.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Juvinate guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4773", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A juvenile vampyre.", - "melee_animation": "6276", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Vampyre juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "4774", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A juvenile vampyre.", - "melee_animation": "6276", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Vampyre juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "4775", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "An initiate juvenile vampyre; seems to be a servant.", - "melee_animation": "5783", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5798", - "name": "Vampyre juvinate", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "4776", - "range_level": "44", - "attack_level": "1" - }, - { - "examine": "An initiate juvenile vampyre.", - "melee_animation": "5783", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5798", - "name": "Vampyre juvinate", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "4777", - "range_level": "44", - "attack_level": "1" - }, - { - "examine": "A juvenile vampyre.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Held vampyre juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "4778", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A juvenile vampyre.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6781", - "name": "Held vampyre juvenile", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "4779", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "An initiate juvenile vampyre; seems to be a servant.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5798", - "name": "Held vampyre juvinate", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "4780", - "range_level": "44", - "attack_level": "1" - }, - { - "examine": "An enraged vampyre!", - "slayer_task": "86", - "melee_animation": "6016", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6031", - "name": "Angry vampyre", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "4789", - "range_level": "1", - "attack_level": "42" - }, - { - "name": "Vanstrom Klause", - "defence_level": "1", - "safespot": null, - "lifepoints": "155", - "strength_level": "1", - "id": "4793", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Not as strong as Thok.", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "name": "Vanstrom Klause", - "defence_level": "60", - "safespot": null, - "lifepoints": "28", - "strength_level": "90", - "id": "4796", - "aggressive": "true", - "range_level": "1", - "attack_level": "90" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "50", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "4805", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "4806", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "54", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "1", - "id": "4807", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "56", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "56", - "safespot": null, - "lifepoints": "80", - "strength_level": "1", - "id": "4808", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Flying female vampire", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4810", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Flying female vampire", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4811", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Flying female vampire", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4812", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "50", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "4813", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "4814", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "54", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "1", - "id": "4815", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "56", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "56", - "safespot": null, - "lifepoints": "80", - "strength_level": "1", - "id": "4816", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "50", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "4817", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "4818", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "54", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "1", - "id": "4819", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An evil", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "56", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "0", - "name": "Vyrewatch", - "defence_level": "56", - "safespot": null, - "lifepoints": "80", - "strength_level": "1", - "id": "4820", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Vyrewatch", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4821", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Vyrewatch", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4822", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Vyrewatch", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4823", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Vyrewatch", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4824", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "50", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "1", - "id": "4825", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "4826", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "54", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "1", - "id": "4827", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A thirsty-looking", - "melee_animation": "6783", - "range_animation": "0", - "attack_speed": "6", - "magic_level": "56", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "6861", - "name": "Vyrewatch", - "defence_level": "56", - "safespot": null, - "lifepoints": "80", - "strength_level": "1", - "id": "4828", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4829", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4830", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "4831", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "4832", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4833", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4834", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "4835", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "4836", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4837", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4838", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "4839", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "4840", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4841", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4842", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "4843", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "4844", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A very high-ranking vampyre.", - "melee_animation": "7435", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "7524", - "name": "Flying female vampire", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "4845", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Flying female vampire", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4847", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Flying female vampire", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4848", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4849", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4850", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "4851", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "4852", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Her knives tickle Thok.", - "magic_animation": "0", - "name": "Holgart", - "defence_level": "60", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "4868", - "aggressive": "true", - "range_level": "90", - "attack_level": "1", - "defence_animation": "0" - }, - { - "examine": "Not Thok's pretty lass.", - "magic_level": "90", - "defence_animation": "0", - "magic_animation": "0", - "name": "Fisherman", - "defence_level": "60", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "4870", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of aquatic evil.", - "melee_animation": "4829", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4830", - "name": "Slug Prince", - "defence_level": "37", - "safespot": null, - "lifepoints": "105", - "strength_level": "37", - "id": "4890", - "aggressive": "true", - "range_level": "1", - "attack_level": "37" - }, - { - "examine": "An extremely vicious lobster.", - "slayer_task": "71", - "melee_animation": "6265", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6267", - "name": "Giant Lobster", - "defence_level": "29", - "safespot": null, - "lifepoints": "82", - "strength_level": "29", - "id": "4893", - "aggressive": "true", - "range_level": "1", - "attack_level": "29" - }, - { - "examine": "A rather nasty looking crustacean.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sea slug", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4894", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "164", - "combat_audio": "3520,472,471", - "respawn_delay": "60", - "defence_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "29", - "strength_level": "1", - "id": "4898", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Cordero looks ready to teach you how to cook.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cooking Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4899", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Cadmus, looking a little bit crafty.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Crafting Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4900", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Finlay, mending a crayfish cage.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fishing Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4901", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Monlum, surveying the rocks.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mining Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4902", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Yauchomi, follower of Saradomin.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Prayer Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4903", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Feoras looks a bit fiery.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Smelting Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4904", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Wilfred, a chip off the old block.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Woodcutting Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4906", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Barb, ready to teach you about banking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bank Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4907", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His motives are see-through.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fritz the Glassblower", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4909", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An earth elemental.", - "melee_animation": "4868", - "combat_audio": "1531,1533,1532", - "attack_speed": "6", - "magic_level": "10", - "respawn_delay": "5", - "defence_animation": "4869", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "35", - "safespot": null, - "lifepoints": "35", - "strength_level": "35", - "id": "4910", - "aggressive": "false", - "range_level": "30", - "attack_level": "20" - }, - { - "examine": "An elemental rock.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "0", - "name": "Elemental rock", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4911", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A nasty overgrown rodent.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4934", - "weakness": "9", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Giant crypt rat", - "defence_level": "65", - "safespot": null, - "lifepoints": "70", - "strength_level": "50", - "id": "4920", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A nasty overgrown rodent.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "4934", - "weakness": "9", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Giant crypt rat", - "defence_level": "65", - "safespot": null, - "lifepoints": "70", - "strength_level": "50", - "id": "4921", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "4922", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "4923", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "4924", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "melee_animation": "4933", - "combat_audio": "703,705,704", - "defence_animation": "4934", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4925", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "7", - "id": "4926", - "aggressive": "true", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "7", - "id": "4927", - "aggressive": "true", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "4928", - "aggressive": "true", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "4929", - "aggressive": "true", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "They'll eat anything!", - "melee_animation": "5341", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5343", - "name": "Goat", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "4930", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "They'll eat anything!", - "melee_animation": "5341", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5343", - "name": "Goat", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "4931", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "They'll eat anything!", - "melee_animation": "5341", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5343", - "name": "Billy Goat", - "defence_level": "11", - "safespot": null, - "lifepoints": "15", - "strength_level": "11", - "id": "4932", - "range_level": "1", - "attack_level": "11" - }, - { - "death_animation": "5343", - "name": "Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "21", - "melee_animation": "5341", - "strength_level": "1", - "id": "4933", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5342" - }, - { - "death_animation": "5343", - "name": "Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "21", - "melee_animation": "5341", - "strength_level": "1", - "id": "4934", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5342" - }, - { - "death_animation": "5343", - "name": "Billy Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "28", - "melee_animation": "5341", - "strength_level": "1", - "id": "4935", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5342" - }, - { - "examine": "A dirty rat.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "8", - "id": "4936", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "A dirty rat.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4935", - "name": "Dungeon rat", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "8", - "id": "4937", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "name": "Angry giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "4938", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Angry giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "4939", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a little on the cross side!", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "4935", - "name": "Angry giant rat", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4940", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Blessed giant rat", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "4941", - "range_level": "1", - "attack_level": "12" - }, - { - "melee_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "4942", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4934", - "death_animation": "4935", - "name": "Giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "4943", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Overgrown vermin.", - "combat_audio": "703,705,704", - "attack_speed": "5", - "name": "Giant rat", - "defence_level": "23", - "safespot": null, - "lifepoints": "26", - "strength_level": "20", - "id": "4944", - "aggressive": "true", - "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", - "range_level": "1", - "attack_level": "21" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "combat_audio": "703,705,704", - "attack_speed": "5", - "weakness": "9", - "name": "Giant rat", - "defence_level": "23", - "safespot": null, - "lifepoints": "26", - "strength_level": "20", - "id": "4945", - "aggressive": "true", - "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", - "range_level": "1", - "attack_level": "21" - }, - { - "examine": "Trollish.", - "slayer_task": "83", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "My Arm", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4947", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old sailor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Barnaby", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4962", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a leprechaun sunbathing on a mountain.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tool Leprechaun", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4965", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mountain-dwelling bird. Cute", - "slayer_task": "7", - "melee_animation": "5031", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5033", - "name": "Baby Roc", - "defence_level": "40", - "safespot": null, - "lifepoints": "100", - "strength_level": "40", - "id": "4971", - "aggressive": "true", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A very", - "slayer_task": "7", - "melee_animation": "5024", - "range_animation": "5025", - "magic_level": "55", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5027", - "name": "Giant Roc", - "defence_level": "55", - "safespot": null, - "lifepoints": "285", - "strength_level": "55", - "id": "4972", - "aggressive": "true", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "It looks like he's been here a long time.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Male slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4975", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks like she's been down here a long time.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Female slave", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4977", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mercenary", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4989", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mercenary", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4990", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mercenary", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4991", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Mercenary", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4992", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4993", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4994", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4995", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4996", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4997", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "4998", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "4999", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5000", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5001", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5002", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An apprentice.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Egg launcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5026", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A tired old wizard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Egg launcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5027", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An egg launcher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Egg launcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5028", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A slightly more approachable barbarian.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Commander Connad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5029", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks pretty mean.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Cain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5030", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A stressed out barbarian private.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Private Paldo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5031", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian private.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Private Pendron", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5032", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian private.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Private Pierreb", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5033", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian private.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Private Paldon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5034", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian army teacher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Major Attack", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5035", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian army teacher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Major Collect", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5036", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian army teacher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Major Defend", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5037", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A barbarian army teacher.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Major Heal", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5038", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's keeping a close eye on that nearby door.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sergeant Sambur", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5039", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What on Gielinor is that?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "670,672,671", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penance Fighter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5040", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Shooty-shooty.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penance Ranger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5041", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's making a run for it!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penance Runner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5042", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A nasty piece of work.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penance Healer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5043", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not as strong as Thok.", - "melee_animation": "401", - "range_animation": "0", - "magic_level": "80", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Jeff", - "defence_level": "40", - "safespot": null, - "lifepoints": "22", - "strength_level": "80", - "id": "5048", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Time to run away...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shark", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5067", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It blends in very well with its surroundings.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tropical wagtail", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5072", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This bird obviously doesn't believe in subtlety.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Crimson swift", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5073", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Best served ice cold.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cerulean twitch", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5074", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Actually", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Golden warbler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5075", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Nothing much to get in a flap about.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Copper longtail", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5076", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Chinchompa", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "5079", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It looks fluffy and cute; it's probably deadly.", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "2", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5183", - "name": "Carnivorous chinchompa", - "defence_level": "2", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "5080", - "range_level": "2", - "attack_level": "2" - }, - { - "examine": "Wild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ferret", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5081", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A black warlock. The air seems to distort wherever it passes.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Black warlock", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5082", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a snowy knight butterfly.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snowy knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5083", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sapphire glacialis. It doesn't look as pretentious as its name sounds.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sapphire glacialis", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5084", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a ruby harvest butterfly.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ruby harvest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5085", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Curls up into a ball to protect itself from attack.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Prickly kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5086", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Now that's a big overbite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sabre-toothed kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5087", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It uses its tail to hunt and skewer fish.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barb-tailed kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5088", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "That's a mean looking set of claws.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wild kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5089", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Ha! Thok much stronger.", - "melee_animation": "395", - "range_animation": "0", - "magic_level": "80", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Gyr Falcon", - "defence_level": "40", - "safespot": null, - "lifepoints": "22", - "strength_level": "80", - "id": "5097", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "It seems to be on a permanent sugar rush.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spotted kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5098", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Quieter than a ninja mouse with slippers on.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dark kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5099", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Now you see it; now you don't.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dashing kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5100", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's just like a big, white, furry, deadly can opener.", - "melee_animation": "5228", - "range_animation": "5228", - "attack_speed": "5", - "respawn_delay": "10", - "defence_animation": "404", - "magic_animation": "5228", - "death_animation": "5234", - "name": "Sabre-toothed kyatt", - "defence_level": "99", - "safespot": null, - "lifepoints": "10", - "strength_level": "25", - "id": "5103", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "If you tried to ride that, you'd just impale yourself!", - "melee_animation": "5228", - "range_animation": "5228", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "5228", - "death_animation": "5234", - "name": "Spined larupia", - "defence_level": "99", - "safespot": null, - "lifepoints": "10", - "strength_level": "15", - "id": "5104", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Get in a graahk's way and you're going to know about it... however briefly.", - "melee_animation": "5228", - "range_animation": "5228", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "5228", - "death_animation": "5234", - "name": "Horned graahk", - "defence_level": "99", - "safespot": null, - "lifepoints": "10", - "strength_level": "25", - "id": "5105", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "It's just like a big, white, furry, deadly can opener.", - "melee_animation": "5228", - "range_animation": "5228", - "attack_speed": "5", - "defence_animation": "404", - "magic_animation": "5228", - "death_animation": "9055", - "name": "null", - "defence_level": "40", - "safespot": null, - "lifepoints": "10", - "strength_level": "20", - "id": "5106", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "He must be good at hunting; even his hair blends in with the surroundings.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hunting expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5112", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "With all the furs", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hunting expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5113", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Slightly slimy but kind of cute.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Orange salamander", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5114", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Slightly slimy but certainly striking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Red salamander", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5115", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Slightly slimy and somewhat menacing.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Black salamander", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5116", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Very slimy and generally disgusting.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swamp lizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5117", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5120", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Desert eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5130", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5131", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Polar eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5132", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It seems to be protecting the nest.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5133", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "melee_animation": "5304", - "strength_level": "1", - "id": "5137", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5305" - }, - { - "examine": "Now", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5140", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's playing both sides!", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "75", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Double agent", - "defence_level": "75", - "safespot": null, - "lifepoints": "85", - "strength_level": "75", - "id": "5144", - "aggressive": "true", - "range_level": "1", - "attack_level": "75" - }, - { - "melee_animation": "422", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Double agent", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "5145", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "How cute!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Li'l lamb", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5146", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The black sheep of the family.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lamb", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5147", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and fluffy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5162", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and fluffy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5163", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This goat belongs to the mountain camp people.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5166", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This goat belongs to the mountain camp people.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5167", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and shaggy.", - "melee_animation": "5338", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5337", - "death_animation": "5336", - "name": "Ram", - "defence_level": "1", - "safespot": null, - "lifepoints": "4", - "strength_level": "1", - "id": "5168", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and shaggy.", - "melee_animation": "5338", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5337", - "death_animation": "5336", - "name": "Ram", - "defence_level": "1", - "safespot": null, - "lifepoints": "4", - "strength_level": "1", - "id": "5169", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and shaggy.", - "melee_animation": "5338", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5337", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5336", - "name": "Ram", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "5170", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "This beast doesn't need climbing boots.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain goat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5171", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Freshly shorn.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Golden sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5172", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Lovely thick wool.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Golden sheep", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5173", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", - "combat_style": "2", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre shaman", - "defence_level": "65", - "safespot": null, - "lifepoints": "371", - "strength_level": "65", - "id": "5176", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A mother dragon.", - "slayer_task": "11", - "melee_animation": "80", - "range_animation": "80", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "35", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "108", - "magic_animation": "80", - "death_animation": "92", - "name": "Blue dragon", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "5178", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre shaman", - "defence_level": "65", - "safespot": null, - "lifepoints": "371", - "strength_level": "65", - "id": "5181", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", - "combat_style": "2", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre shaman", - "defence_level": "65", - "safespot": null, - "lifepoints": "371", - "strength_level": "65", - "id": "5184", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", - "combat_style": "2", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre shaman", - "defence_level": "65", - "safespot": null, - "lifepoints": "371", - "strength_level": "65", - "id": "5187", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "It's Nial", - "melee_animation": "8946", - "range_animation": "0", - "magic_level": "58", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8948", - "name": "Saff Waldon", - "defence_level": "58", - "safespot": null, - "lifepoints": "165", - "strength_level": "58", - "id": "5188", - "aggressive": "true", - "range_level": "58", - "attack_level": "58" - }, - { - "examine": "Seems intelligent. For an ogre.", - "combat_style": "2", - "name": "Ogre shaman", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "5190", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Seems intelligent. For an ogre.", - "combat_style": "2", - "name": "Ogre shaman", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "5193", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A wizard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5195", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wizard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5196", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A wizard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5197", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She needs to work out before facing Thok.", - "melee_animation": "7041", - "range_animation": "0", - "magic_level": "80", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Alice's husband", - "defence_level": "40", - "safespot": null, - "lifepoints": "22", - "strength_level": "80", - "id": "5204", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "It's an undead cow.", - "melee_animation": "5849", - "range_animation": "5849", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "5", - "defence_animation": "5850", - "magic_animation": "5849", - "death_animation": "5851", - "name": "Undead cow", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "5211", - "aggressive": "false", - "bonuses": "-15,-15,-15,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Shooty-shooty.", - "combat_style": "1", - "melee_animation": "5395", - "respawn_delay": "60", - "defence_animation": "5396", - "death_animation": "5937", - "name": "Penance Ranger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5229", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Shooty-shooty.", - "combat_style": "1", - "melee_animation": "5395", - "respawn_delay": "60", - "defence_animation": "5396", - "death_animation": "5937", - "name": "Penance Ranger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5237", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Run away! Run away!", - "melee_animation": "5411", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5412", - "name": "Penance Queen", - "defence_level": "132", - "safespot": null, - "lifepoints": "71", - "strength_level": "260", - "id": "5247", - "aggressive": "true", - "range_level": "116", - "attack_level": "260" - }, - { - "examine": "What's it looking at?", - "melee_animation": "5092", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5093", - "name": "Queen spawn", - "defence_level": "50", - "safespot": null, - "lifepoints": "450", - "strength_level": "60", - "id": "5248", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "He looks embarrassed", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain Errdo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5249", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Part scarab, part man.", - "slayer_task": "70", - "combat_style": "2", - "melee_animation": "7615", - "range_animation": "0", - "magic_level": "54", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "7616", - "name": "Scarab mage", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "40", - "id": "5250", - "aggressive": "true", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "A mounted lancer.", - "slayer_task": "70", - "melee_animation": "7584", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "7581", - "name": "Locust rider", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "54", - "id": "5251", - "aggressive": "true", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "A mounted archer.", - "slayer_task": "70", - "melee_animation": "5451", - "range_animation": "5451", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "7581", - "name": "Locust rider", - "defence_level": "54", - "safespot": null, - "lifepoints": "77", - "strength_level": "40", - "id": "5252", - "aggressive": "true", - "range_level": "54", - "attack_level": "40" - }, - { - "examine": "A huge scarab beast.", - "slayer_task": "70", - "melee_animation": "5457", - "range_animation": "0", - "magic_level": "62", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5456", - "name": "Giant scarab", - "defence_level": "62", - "safespot": null, - "lifepoints": "571", - "strength_level": "62", - "id": "5253", - "aggressive": "true", - "range_level": "62", - "attack_level": "62" - }, - { - "examine": "Part scarab, part man.", - "combat_style": "2", - "melee_animation": "7615", - "respawn_delay": "60", - "defence_animation": "7617", - "death_animation": "7616", - "name": "Scarab mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "5254", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5258", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She can look after my money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5260", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard of Sophanem.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sophanem guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5270", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard of Sophanem.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sophanem guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5274", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard of Menaphos.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Menaphite guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5277", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Flings annoying splinters at Thok.", - "melee_animation": "426", - "range_animation": "426", - "magic_level": "80", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Osman", - "defence_level": "40", - "safespot": null, - "lifepoints": "22", - "strength_level": "80", - "id": "5285", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5293", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5294", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5295", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5296", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5297", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5298", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5299", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5300", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5301", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5302", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5303", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5304", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5567", - "slayer_exp": "30", - "magic_animation": "5568", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "10", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "5305", - "bonuses": "6,15,15,15,15,15,15,6,6,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5306", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5567", - "slayer_exp": "30", - "magic_animation": "5568", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "10", - "safespot": null, - "lifepoints": "18", - "strength_level": "1", - "id": "5307", - "bonuses": "6,15,15,15,15,15,15,6,6,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Brains!", - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "5308", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Brains!", - "melee_animation": "5571", - "range_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5574", - "slayer_exp": "30", - "magic_animation": "5571", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "16", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5309", - "bonuses": "10,15,16,16,15,16,16,10,10,16,15,15,15,15,15", - "range_level": "1", - "attack_level": "15" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "5310", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "5311", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "5312", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "5313", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5314", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5315", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5316", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5317", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5318", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Examine not added", - "melee_animation": "5571", - "range_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5574", - "slayer_exp": "30", - "magic_animation": "5571", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "20", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5319", - "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Examine not added", - "melee_animation": "5571", - "range_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5574", - "slayer_exp": "30", - "magic_animation": "5571", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "20", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5320", - "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "Examine not added", - "melee_animation": "5571", - "range_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5574", - "slayer_exp": "30", - "magic_animation": "5571", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "20", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5321", - "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "Dead man walking.", - "melee_animation": "5571", - "range_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "5", - "defence_animation": "5574", - "slayer_exp": "30", - "magic_animation": "5571", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "23", - "safespot": null, - "lifepoints": "30", - "strength_level": "19", - "id": "5322", - "aggressive": "true", - "bonuses": "21,19,20,42,44,34,41,38,40,21,0,0,0,0,0", - "range_level": "1", - "attack_level": "20" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5323", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5324", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5325", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5326", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5327", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5328", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5329", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5330", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5331", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5342", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5343", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5344", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5345", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5346", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5347", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "slayer_task": "36", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5348", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5349", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5350", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5351", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "5352", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Undead one", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "5353", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Undead one", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "5354", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", - "melee_animation": "5568", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5569", - "name": "Undead one", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "5355", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", - "melee_animation": "5568", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5569", - "name": "Undead one", - "defence_level": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "5356", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", - "melee_animation": "5571", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5575", - "name": "Undead one", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "5357", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", - "melee_animation": "5571", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5575", - "name": "Undead one", - "defence_level": "33", - "safespot": null, - "lifepoints": "47", - "strength_level": "33", - "id": "5358", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A giant skeleton.", - "slayer_task": "75", - "melee_animation": "5499", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5503", - "name": "Giant skeleton", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "5359", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A fiendish embodiment of water.", - "slayer_task": "90", - "combat_style": "1", - "melee_animation": "1582", - "range_animation": "1582", - "combat_audio": "3774,3773,3772", - "attack_speed": "4", - "magic_level": "105", - "defence_animation": "1581", - "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Waterfiend", - "defence_level": "128", - "poison_immune": "true", - "safespot": null, - "lifepoints": "128", - "strength_level": "0", - "id": "5361", - "aggressive": "true", - "bonuses": "0,0,0,0,0,100,100,10,100,100,0,0,0,0,0", - "range_level": "105", - "projectile": "16", - "attack_level": "0" - }, - { - "examine": "It appears to be intelligent and savage.", - "slayer_task": "41", - "melee_animation": "91", - "range_animation": "91", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "168", - "defence_animation": "89", - "weakness": "3", - "slayer_exp": "183", - "magic_animation": "91", - "death_animation": "92", - "name": "Brutal green dragon", - "defence_level": "168", - "poison_immune": "true", - "safespot": null, - "lifepoints": "175", - "strength_level": "168", - "id": "5362", - "aggressive": "true", - "clue_level": "2", - "range_level": "0", - "attack_level": "268" - }, - { - "examine": "Experimenting with mithril gone bad!", - "slayer_task": "57", - "melee_animation": "91", - "range_animation": "91", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "168", - "respawn_delay": "30", - "defence_animation": "89", - "weakness": "8", - "slayer_exp": "273", - "magic_animation": "91", - "death_animation": "92", - "name": "Mithril dragon", - "defence_level": "268", - "safespot": null, - "lifepoints": "254", - "strength_level": "268", - "id": "5363", - "aggressive": "true", - "bonuses": "0,0,0,0,0,50,100,70,30,90,0,0,0,0,0", - "clue_level": "2", - "range_level": "168", - "attack_level": "268" - }, - { - "weakness": "7", - "examine": "It appears to be intelligent and savage.", - "name": "Confused barbarian", - "defence_level": "50", - "safespot": null, - "lifepoints": "175", - "strength_level": "167", - "attack_speed": "6", - "id": "5364", - "range_level": "167", - "attack_level": "70" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "36", - "strength_level": "1", - "id": "5369", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "36", - "strength_level": "1", - "id": "5370", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eeek! A ghost!", - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "36", - "strength_level": "1", - "id": "5371", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Why does it attack with twigs? Swords and axes much better.", - "melee_animation": "5532", - "range_animation": "426", - "combat_audio": "436,439,438", - "magic_level": "80", - "defence_animation": "5533", - "weakness": "10", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "40", - "safespot": null, - "lifepoints": "36", - "strength_level": "80", - "id": "5372", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Are these twigs meant to hurt Thok?", - "melee_animation": "5532", - "range_animation": "426", - "combat_audio": "436,439,438", - "magic_level": "80", - "defence_animation": "5533", - "weakness": "10", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "40", - "safespot": null, - "lifepoints": "36", - "strength_level": "80", - "id": "5373", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "It thinks it scary. Thok show it scary.", - "melee_animation": "5532", - "range_animation": "0", - "combat_audio": "436,439,438", - "magic_level": "80", - "defence_animation": "5533", - "weakness": "10", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "70", - "safespot": null, - "lifepoints": "36", - "strength_level": "80", - "id": "5374", - "aggressive": "true", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Examine not added", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5567", - "slayer_exp": "30", - "magic_animation": "5568", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "25", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "5375", - "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", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5567", - "slayer_exp": "30", - "magic_animation": "5568", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "25", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "5376", - "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", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "28", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5377", - "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", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "28", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "5378", - "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", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5379", - "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", - "melee_animation": "5578", - "range_animation": "5578", - "combat_audio": "931,923,922", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "5579", - "slayer_exp": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "40", - "strength_level": "1", - "id": "5380", - "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4167", - "name": "Animated steel armour", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "5382", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "Big and bony, just how I like them.", - "start_gfx": "2713", - "melee_animation": "5499", - "range_animation": "5499", - "attack_speed": "2", - "defence_animation": "5489", - "magic_animation": "5499", - "death_animation": "5503", - "name": "Giant skeleton", - "defence_level": "85", - "poison_immune": "true", - "safespot": null, - "lifepoints": "100", - "strength_level": "95", - "id": "5384", - "aggressive": "true", - "bonuses": "25,25,25,25,25,25,25,25,25,25,25,25,25,25,25", - "range_level": "1", - "projectile": "2718", - "attack_level": "95" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "56", - "strength_level": "1", - "id": "5393", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5394", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5395", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5396", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5397", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5398", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5399", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5400", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5401", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5402", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5403", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "92", - "strength_level": "1", - "id": "5404", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5405", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5406", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5407", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5408", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5409", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "5410", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "How does it move of its own accord?", - "melee_animation": "5591", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5592", - "name": "Possessed pickaxe", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5413", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "How does it move of its own accord?", - "melee_animation": "5597", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5596", - "name": "Animated spade", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "52", - "id": "5414", - "range_level": "1", - "attack_level": "52" - }, - { - "examine": "A terrifying dog beast.", - "slayer_task": "82", - "melee_animation": "5625", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "5628", - "name": "Terror dog", - "defence_level": "47", - "safespot": null, - "lifepoints": "134", - "strength_level": "47", - "id": "5417", - "aggressive": "true", - "range_level": "1", - "attack_level": "47" - }, - { - "melee_animation": "5625", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5626", - "death_animation": "5627", - "name": "Terror dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "82", - "strength_level": "1", - "id": "5418", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5617", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "5618", - "death_animation": "5619", - "name": "Tarn", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "5420", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I preferred him when he was human.", - "melee_animation": "5617", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5619", - "name": "Mutant tarn", - "defence_level": "57", - "safespot": null, - "lifepoints": "325", - "strength_level": "57", - "id": "5421", - "aggressive": "true", - "range_level": "1", - "attack_level": "57" - }, - { - "examine": "What restful music!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5439", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What restful music!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "KGP Agent", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5442", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Instructs agility.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Agility Instructor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5447", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An army commander.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Army Commander", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5448", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sturdy cold being.", - "slayer_task": "48", - "melee_animation": "5724", - "range_animation": "5725", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "5726", - "name": "Icelord", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "22", - "id": "5452", - "aggressive": "true", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "Sturdy cold being.", - "slayer_task": "48", - "melee_animation": "5724", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5726", - "name": "Icelord", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "5453", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Sturdy cold being.", - "slayer_task": "48", - "melee_animation": "5724", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5726", - "name": "Icelord", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "5454", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Sturdy cold being.", - "slayer_task": "48", - "melee_animation": "5724", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "5726", - "name": "Icelord", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "5455", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "An impressive-looking troll.", - "slayer_task": "83", - "melee_animation": "5374", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5209", - "name": "Ice Troll King", - "defence_level": "65", - "safespot": null, - "lifepoints": "185", - "strength_level": "65", - "id": "5472", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "An ice troll youngling.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll runt", - "defence_level": "70", - "safespot": null, - "lifepoints": "60", - "strength_level": "70", - "id": "5473", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A male troll wielding a large club.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll male", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5474", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "An ice troll with a bag of rocks.", - "combat_style": "1", - "range_animation": "1142", - "melee_animation": "1142", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll female", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5475", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "projectile": "276", - "attack_level": "80" - }, - { - "examine": "A large ice troll.", - "melee_animation": "4332", - "range_animation": "0", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll grunt", - "defence_level": "60", - "safespot": null, - "lifepoints": "80", - "strength_level": "100", - "id": "5476", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "An ill-tempered king.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Gjuki Sorvott IV", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5478", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Thorkel Silkbeard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "5480", - "range_level": "1", - "attack_level": "1" - }, - { - "facing_booth": "true", - "name": "Magnus Gram", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "5488", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard on insult duty.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5489", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5490", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5491", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of King Sorvott's militia.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5492", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A miner at work.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Miner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5497", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Burgher's protectors.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Honour guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5514", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Burgher's protectors.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Honour guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5516", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of your militia.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "0", - "name": "Honour guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5517", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ice troll youngling.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll runt", - "defence_level": "70", - "safespot": null, - "lifepoints": "60", - "strength_level": "70", - "id": "5521", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A male troll wielding a large club.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll male", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5522", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "An ice troll with a bag of rocks.", - "combat_style": "1", - "range_animation": "1142", - "melee_animation": "1142", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll female", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5523", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "projectile": "276", - "attack_level": "80" - }, - { - "examine": "An ice troll youngling.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll runt", - "defence_level": "70", - "safespot": null, - "lifepoints": "60", - "strength_level": "70", - "id": "5525", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A male troll wielding a large club.", - "range_animation": "0", - "melee_animation": "284", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll male", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5526", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "An ice troll with a bag of rocks.", - "combat_style": "1", - "range_animation": "1142", - "melee_animation": "1142", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "0", - "magic_animation": "0", - "death_animation": "287", - "name": "Ice troll female", - "defence_level": "40", - "safespot": null, - "lifepoints": "80", - "strength_level": "80", - "id": "5527", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "projectile": "276", - "attack_level": "80" - }, - { - "examine": "A hairy, smelly, grazing animal.", - "melee_animation": "5782", - "range_animation": "5782", - "combat_audio": "3378,3380,3379", - "attack_speed": "4", - "defence_animation": "5783", - "weakness": "7", - "magic_animation": "5782", - "death_animation": "5784", - "name": "Yak", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "10", - "id": "5529", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Antisocial.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sorceress", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5531", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Come", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apprentice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5532", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An autumn elemental.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Autumn Elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5533", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A spring elemental.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spring Elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5539", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A summer elemental.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Summer Elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5547", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A winter elemental.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Winter Elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5553", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It dare attack Pretty Lass? Thok crush its puny skull!", - "range_animation": "0", - "magic_level": "95", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Osman", - "defence_level": "50", - "safespot": null, - "lifepoints": "157", - "strength_level": "95", - "id": "5561", - "aggressive": "true", - "range_level": "95", - "attack_level": "95" - }, - { - "examine": "She's honest about the things you aren't.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sin Seer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5571", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Shadow Realm guardian.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "436,439,438", - "defence_animation": "0", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "0", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5572", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is it male or female?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Homunculus", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5581", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Strong", - "range_animation": "0", - "magic_level": "95", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Cage", - "defence_level": "70", - "safespot": null, - "lifepoints": "85", - "strength_level": "95", - "id": "5584", - "aggressive": "true", - "range_level": "95", - "attack_level": "95" - }, - { - "examine": "Famous for his fights.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Black-eye", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5589", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Count them pinkies!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'No fingers", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5590", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He wishes he had teeth.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "'Gummy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5591", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is it a frog", - "melee_animation": "5842", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "5841", - "name": "Frogeel", - "defence_level": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "36", - "id": "5593", - "aggressive": "true", - "range_level": "49", - "attack_level": "36" - }, - { - "death_animation": "146", - "name": "Spidine", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "melee_animation": "143", - "strength_level": "1", - "id": "5594", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "144" - }, - { - "examine": "Definitely not a chicken or a swordfish.", - "melee_animation": "5387", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5389", - "name": "Swordchick", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "5595", - "range_level": "1", - "attack_level": "45" - }, - { - "melee_animation": "6800", - "respawn_delay": "60", - "defence_animation": "6802", - "death_animation": "6801", - "name": "Jubster", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "5596", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Since when did newts have beaks?", - "melee_animation": "2299", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "2301", - "name": "Newtroost", - "defence_level": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "5597", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Half unicorn", - "melee_animation": "5849", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "41", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "5851", - "name": "Unicow", - "defence_level": "41", - "safespot": null, - "lifepoints": "58", - "strength_level": "30", - "id": "5603", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Holier than thou.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5608", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holier than thou.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5609", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holier than thou.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5610", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Holier than thou.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5611", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I'd prefer it if it were a muffin...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Puffin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5614", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5619", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5620", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5621", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "5622", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bedside manner is a little lacking", - "slayer_task": "93", - "melee_animation": "5643", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5658", - "name": "Sorebones", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5627", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "I hope his hands don't shake.", - "slayer_task": "93", - "melee_animation": "5643", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5658", - "name": "Sorebones", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5628", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5629", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5630", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5631", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5632", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5633", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5634", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5647", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5635", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5636", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5637", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5638", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5639", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5640", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5651", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5641", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5880", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5642", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5880", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5643", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5880", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5644", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5880", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5645", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5880", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5654", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5646", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5884", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5886", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5647", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5884", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5886", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5648", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5649", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5884", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5886", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5650", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5651", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5884", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5886", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5652", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5653", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5884", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5886", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5654", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5655", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5656", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5657", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5658", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5659", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5660", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5661", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5662", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5663", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "melee_animation": "5647", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "5648", - "death_animation": "5649", - "name": "Zombie pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5664", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aaaarg", - "slayer_task": "93", - "melee_animation": "5889", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5891", - "name": "Zombie pirate", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "5665", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "It's trying to mash you flat! Less examine", - "melee_animation": "5895", - "range_animation": "0", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5898", - "name": "Barrelchest", - "defence_level": "52", - "safespot": null, - "lifepoints": "285", - "strength_level": "52", - "id": "5666", - "aggressive": "true", - "range_level": "52", - "attack_level": "52" - }, - { - "examine": "He is one", - "melee_animation": "5970", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5972", - "name": "Undead Lumberjack", - "defence_level": "10", - "safespot": null, - "lifepoints": "100", - "strength_level": "73", - "id": "5680", - "range_level": "1", - "attack_level": "73" - }, - { - "examine": "A big", - "slayer_task": "15", - "melee_animation": "6079", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "93", - "magic_animation": "0", - "death_animation": "6081", - "name": "Cave bug", - "defence_level": "9", - "safespot": null, - "lifepoints": "93", - "strength_level": "1", - "id": "5750", - "range_level": "9", - "attack_level": "1" - }, - { - "examine": "A strange mole-like being.", - "melee_animation": "6012", - "combat_audio": "3492,3494,3493", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "6013", - "slayer_exp": "52", - "death_animation": "6014", - "name": "Molanisk", - "defence_level": "50", - "safespot": null, - "lifepoints": "52", - "strength_level": "40", - "id": "5751", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5752", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5753", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5754", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5755", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5756", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5757", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5758", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5759", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5760", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5761", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5762", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5763", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5764", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5765", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5766", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5767", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5768", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5769", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after cave goblin money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5776", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after cave goblin money.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5777", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5783", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Taking a terribly important box from one place to another.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Crate goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5784", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the many citizens of Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5785", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Good at shorthand.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin scribe", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5786", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He keeps order in the city.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Guard", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "5800", - "clue_level": "1", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "He keeps order in the city.", - "slayer_task": "38", - "melee_animation": "6007", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6003", - "name": "Guard", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "5801", - "clue_level": "1", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "A goblin baby.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Young 'un", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5803", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin baby.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Nipper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5805", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5807", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5808", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5809", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5810", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5811", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5812", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5813", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5814", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5815", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5816", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5817", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5818", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5819", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5820", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5821", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5822", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Don't spit", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spit goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5823", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bug-eyed little goblin fish. How cute.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin fish", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5824", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Annoying little flappy things.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Moths", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5827", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "6092", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "melee_animation": "6090", - "strength_level": "1", - "id": "5829", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6091" - }, - { - "name": "Rat Burgiss", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "5833", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5842", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5843", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5844", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5845", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5846", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5847", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5848", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5849", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5850", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "A mean looking outlaw. Don't get too close!", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Outlaw", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "5851", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Weird eyeball thing. Reminds Thok of breakfast.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "magic_level": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Monkey", - "defence_level": "40", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "5852", - "aggressive": "true", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "For sitting on.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bench", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5854", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5855", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5856", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks pretty skilled with that bow.", - "melee_animation": "426", - "range_animation": "0", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "H.A.M. Archer", - "defence_level": "20", - "safespot": null, - "lifepoints": "57", - "strength_level": "15", - "id": "5859", - "aggressive": "true", - "clue_level": "0", - "range_level": "20", - "attack_level": "15" - }, - { - "examine": "He bristles with arcane power.", - "combat_style": "2", - "melee_animation": "429", - "range_animation": "0", - "magic_level": "20", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "836", - "name": "H.A.M. Mage", - "defence_level": "20", - "safespot": null, - "lifepoints": "57", - "strength_level": "15", - "id": "5860", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "No match for Thok. Silly demon.", - "magic_level": "85", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Zanik", - "defence_level": "85", - "safespot": null, - "lifepoints": "71", - "strength_level": "85", - "id": "5861", - "aggressive": "true", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "A worker on the train link.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Builder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5864", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker on the train link.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Builder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5865", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker on the train link.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Builder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5866", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A worker on the train link.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Builder", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5867", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5873", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big", - "slayer_task": "38", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5874", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin with big", - "slayer_task": "38", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5875", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5876", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5877", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Cave goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "5878", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sells tickets to Keldagrim.", - "slayer_task": "38", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5879", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf from Keldagrim.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5880", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf from Keldagrim.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5881", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarf from Keldagrim.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "16", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5882", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5883", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5884", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5885", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sells tickets to Dorgesh-Kaan.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5886", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Are you good enough to fight?", - "melee_animation": "6318", - "range_animation": "0", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6322", - "name": "The Inadequacy", - "defence_level": "70", - "safespot": null, - "lifepoints": "100", - "strength_level": "70", - "id": "5902", - "aggressive": "true", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Can you endure long enough?", - "melee_animation": "6345", - "range_animation": "0", - "magic_level": "67", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6347", - "name": "The Everlasting", - "defence_level": "67", - "safespot": null, - "lifepoints": "191", - "strength_level": "67", - "id": "5903", - "aggressive": "true", - "range_level": "67", - "attack_level": "67" - }, - { - "examine": "Can you bring yourself to hurt another?", - "melee_animation": "6329", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6331", - "name": "The Untouchable", - "defence_level": "75", - "safespot": null, - "lifepoints": "100", - "strength_level": "65", - "id": "5904", - "aggressive": "true", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "What if you don't know how to win?", - "melee_animation": "6342", - "range_animation": "0", - "magic_level": "63", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "0", - "name": "The Illusive", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "5905", - "aggressive": "true", - "range_level": "63", - "attack_level": "63" - }, - { - "examine": "You can't escape your inadequacy!", - "melee_animation": "6310", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "6315", - "name": "A Doubt", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5906", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Dead stone defending a dead throne room.", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Dying tree", - "defence_level": "50", - "safespot": null, - "lifepoints": "142", - "strength_level": "50", - "id": "5908", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "Don't let her arm-wrestle you for money.", - "melee_animation": "422", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Barbarian", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "5909", - "clue_level": "0", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "A greasy, grimy gourmet.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5910", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's suspiciously talented with a meat-cleaver.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cook", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5911", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This grizzled soldier is here to distribute Rated Clan Wars badges.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Iffie", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5914", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Does what too many people aren't interested in doing.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cleaner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5916", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mangy mutt.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Stray dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5917", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6490", - "name": "Guard", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "5919", - "clue_level": "1", - "range_level": "1", - "attack_level": "18" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6490", - "name": "Guard", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "5920", - "clue_level": "1", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "He's studying to be a guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trainee Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5921", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Trains the guards of the future.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Captain", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5922", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Man", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "5923", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "5924", - "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "9", - "safespot": null, - "lifepoints": "25", - "strength_level": "9", - "id": "5926", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "9", - "safespot": null, - "lifepoints": "25", - "strength_level": "9", - "id": "5927", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "Known for her light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "8", - "safespot": null, - "lifepoints": "24", - "strength_level": "9", - "id": "5928", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "Known for her light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Thief", - "defence_level": "8", - "safespot": null, - "lifepoints": "24", - "strength_level": "9", - "id": "5929", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "Jacques Netis - a slightly pompous art expert.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Art Critic Jacques", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5930", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seth Minas - an aged expert in Gielinor history.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Historian Minas", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5931", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A lady with lots of information about the Museum.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Information clerk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5938", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A teacher and one of his pupils.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Teacher and pupil", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5944", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks kind of familiar.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Schoolboy", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5945", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A teacher and one of her pupils.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Teacher and pupil", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5947", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks hard at work.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5952", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could there be something exciting in his wheelbarrow?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5954", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A powerful knight of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Void Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5956", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Wheelbarrow loader.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Digsite workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5958", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Working hard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barge workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5959", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Working hard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barge workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5960", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Working hard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barge workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5961", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Working hard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barge workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5962", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Working hard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barge foreman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5963", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An aged expert in natural history.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Natural historian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5966", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bloodsuckers!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leech display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5971", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Slugs of the sea variety.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sea slugs display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5972", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A house on its back.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snail display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5973", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A cheeky little monkey.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5974", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A scaly little fellow.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lizard display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5975", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Nice suit.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penguin display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5976", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's got the hump.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Camel display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5977", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Terrifying!", - "melee_animation": "1010", - "range_animation": "0", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorbird display", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "strength_level": "23", - "id": "5978", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like a fire-breather.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "408,410,409", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dragon display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5979", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Needs a good square meal.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wyvern display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5980", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge beast.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Battle tortoise display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5981", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Loves making molehills.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mole display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5982", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It hides in stone", - "range_animation": "0", - "magic_level": "95", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Workman", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "strength_level": "95", - "id": "5986", - "aggressive": "true", - "range_level": "95", - "attack_level": "95" - }, - { - "name": "Rupert the Beard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "5990", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Rupert the Beard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "5992", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very odd looking creature.", - "melee_animation": "6513", - "range_animation": "0", - "magic_level": "46", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6512", - "name": "Experiment No.2", - "defence_level": "46", - "safespot": null, - "lifepoints": "131", - "strength_level": "46", - "id": "5993", - "range_level": "46", - "attack_level": "46" - }, - { - "examine": "A huge mouse. It looks hungry...", - "melee_animation": "6519", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6521", - "name": "Mouse", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "44", - "id": "5994", - "aggressive": "true", - "range_level": "1", - "attack_level": "44" - }, - { - "melee_animation": "6501", - "respawn_delay": "60", - "defence_animation": "6503", - "death_animation": "6502", - "name": "Glod", - "defence_level": "1", - "safespot": null, - "lifepoints": "160", - "strength_level": "1", - "id": "5996", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Rupert the Beard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "5999", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Rupert the Beard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "6001", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6006", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6007", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6008", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6009", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6010", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6011", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6012", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6013", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "examine": "I wish the moon wasn't out!", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "76", - "safespot": null, - "lifepoints": "87", - "strength_level": "76", - "id": "6014", - "aggressive": "true", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,20,20,20", - "clue_level": "1", - "range_level": "1", - "attack_level": "76" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6015", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6016", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6017", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6018", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6538", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "6019", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6020", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "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", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6021", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "45" - }, - { - "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6538", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "6022", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6538", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "6023", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6538", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "6024", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "6536", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "6538", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "6025", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Boris", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6026", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Imre", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6027", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Yuri", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6028", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Joseph", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6029", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Nikolai", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6030", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Eduard", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6031", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Lev", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6032", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "melee_animation": "422", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Georgy", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "6033", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Svetlana", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6034", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Irina", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6035", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Alexis", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6036", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Milla", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6037", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Galina", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6038", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Sofiya", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6039", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Ksenia", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6040", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Yadviga", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6041", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Nikita", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6042", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Vera", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6043", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Zoja", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6044", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "death_animation": "836", - "name": "Liliya", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "melee_animation": "422", - "strength_level": "1", - "id": "6045", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "melee_animation": "6559", - "respawn_delay": "60", - "defence_animation": "6557", - "slayer_exp": "74", - "death_animation": "6558", - "name": "Big Wolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "6046", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A vicious mountain wolf.", - "melee_animation": "6579", - "range_animation": "6579", - "combat_audio": "481,491,490", - "attack_speed": "6", - "defence_animation": "6578", - "slayer_exp": "34", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Wolf", - "defence_level": "22", - "safespot": null, - "lifepoints": "69", - "strength_level": "1", - "id": "6047", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Now it has no armour", - "range_animation": "0", - "combat_audio": "481,491,490", - "magic_level": "95", - "defence_animation": "0", - "weakness": "10", - "slayer_exp": "34", - "magic_animation": "0", - "name": "Wolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "157", - "strength_level": "95", - "id": "6048", - "aggressive": "true", - "range_level": "95", - "attack_level": "95" - }, - { - "examine": "A big coward. Thok mocks scaredy man.", - "combat_audio": "481,491,490", - "magic_level": "75", - "defence_animation": "0", - "weakness": "10", - "slayer_exp": "34", - "magic_animation": "0", - "name": "Wolf", - "defence_level": "75", - "safespot": null, - "lifepoints": "100", - "strength_level": "75", - "id": "6049", - "aggressive": "true", - "range_level": "75", - "attack_level": "75" - }, - { - "examine": "A vicious desert wolf.", - "slayer_task": "92", - "melee_animation": "6579", - "range_animation": "6579", - "attack_speed": "5", - "defence_animation": "6578", - "weakness": "9", - "magic_animation": "6579", - "death_animation": "6558", - "name": "Desert Wolf", - "defence_level": "5", - "safespot": null, - "lifepoints": "55", - "strength_level": "20", - "id": "6050", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "A vicious desert wolf.", - "melee_animation": "6579", - "range_animation": "6579", - "attack_speed": "5", - "defence_animation": "6578", - "magic_animation": "6579", - "death_animation": "6558", - "name": "Desert Wolf", - "defence_level": "5", - "safespot": null, - "lifepoints": "55", - "strength_level": "20", - "id": "6051", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Not man's best friend.", - "melee_animation": "6559", - "respawn_delay": "60", - "defence_animation": "6557", - "death_animation": "6558", - "name": "Ice wolf", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "6052", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An impling who likes the word \"dragon\".", - "name": "Dragon impling", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6054", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An impling who likes the word \"dragon.\"", - "name": "Dragon impling", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6064", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She tends the wheat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy Aeryka", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6072", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a little lost.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wandering impling", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6073", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An angry little imp. Grrr.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Imp defender", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6074", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A one-eyed man eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "4651", - "weakness": "8", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "35", - "safespot": null, - "lifepoints": "75", - "strength_level": "65", - "id": "6078", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A one-eyed woman eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "4651", - "weakness": "1", - "slayer_exp": "100", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "65", - "safespot": null, - "lifepoints": "100", - "strength_level": "65", - "id": "6079", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A one-eyed man eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "4651", - "weakness": "8", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "35", - "safespot": null, - "lifepoints": "75", - "strength_level": "65", - "id": "6080", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A one-eyed woman eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "4651", - "weakness": "1", - "slayer_exp": "100", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "65", - "safespot": null, - "lifepoints": "100", - "strength_level": "65", - "id": "6081", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Overgrown undead vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4935", - "name": "Zombie rat", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "6088", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "Overgrown undead vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4935", - "name": "Zombie rat", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "6089", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "Overgrown undead vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4935", - "name": "Zombie rat", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "6090", - "range_level": "1", - "attack_level": "2" - }, - { - "melee_animation": "5540", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5541", - "slayer_exp": "25", - "death_animation": "5542", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6094", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6095", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6096", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6097", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5532", - "combat_audio": "436,439,438", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5533", - "slayer_exp": "25", - "death_animation": "5534", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6098", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5571", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "6099", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5571", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "6100", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Lesser, but still pretty big.", - "slayer_task": "56", - "melee_animation": "4630", - "range_animation": "4630", - "combat_audio": "400,404,403", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "30", - "defence_animation": "65", - "weakness": "4", - "slayer_exp": "79", - "magic_animation": "4630", - "death_animation": "67", - "name": "Lesser demon", - "defence_level": "71", - "safespot": null, - "lifepoints": "79", - "strength_level": "70", - "id": "6101", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "His face is expressionless.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Lost barbarian", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "6102", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Probably not a chicken.", - "slayer_task": "7", - "melee_animation": "6811", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "6812", - "name": "Entrana firebird", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "6108", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These gnomes know how to get around!", - "slayer_task": "7", - "melee_animation": "6790", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "6109", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "These gnomes know how to get around!", - "melee_animation": "6790", - "respawn_delay": "60", - "defence_animation": "6792", - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "strength_level": "1", - "id": "6110", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These gnomes know how to get around!", - "melee_animation": "6790", - "respawn_delay": "60", - "defence_animation": "6792", - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "strength_level": "1", - "id": "6111", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aww, cute.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ducklings", - "defence_level": "1", - "water_npc": "true", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6112", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Quackers.", - "melee_animation": "747", - "respawn_delay": "60", - "defence_animation": "3268", - "death_animation": "750", - "name": "Duck", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "6113", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A messy bird.", - "slayer_task": "7", - "melee_animation": "3467", - "range_animation": "3467", - "combat_audio": "309,311,310", - "attack_speed": "5", - "defence_animation": "1014", - "weakness": "6", - "magic_animation": "3467", - "death_animation": "3468", - "name": "Seagull", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "6115", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A messy bird.", - "melee_animation": "3467", - "range_animation": "3467", - "combat_audio": "309,311,310", - "attack_speed": "5", - "defence_animation": "1014", - "magic_animation": "3467", - "death_animation": "3468", - "name": "Seagull", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "6116", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Do not upset this teacher. You have been warned!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mr. Mordaut", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6117", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He helps the professor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Observatory assistant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6118", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sleeping like an ugly baby.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sleeping guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6122", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old goblin hag.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Naghead", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6123", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A young goblin 'beauty'.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wagchin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6124", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6125", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6126", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big appetite for a small creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Greasycheeks", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6127", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes drink a little too much.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Smellytoes", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6128", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Lean and green.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Creakyknees", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6129", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "30", - "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "combat_audio": "931,923,922", - "strength_level": "1", - "id": "6131", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6132", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6133", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This imp is clearly the class clown.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dunce", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6134", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Get useful information from this guy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Town crier", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6135", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire (Intermediate)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6140", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A squire of balance.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire (Veteran)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6141", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "50", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6142", - "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "25", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "50", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6143", - "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "50", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6144", - "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "50", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6145", - "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6146", - "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "15", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6147", - "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6148", - "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "200", - "strength_level": "1", - "id": "6149", - "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "70", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6150", - "bonuses": "150,150,150,75,75,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "70", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6151", - "bonuses": "150,150,150,150,37,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "70", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6152", - "bonuses": "150,150,75,150,75,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "70", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "70", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6153", - "bonuses": "75,75,150,150,75,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6154", - "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "15", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6155", - "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6156", - "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "6157", - "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It is cold and solid. Thok will strap it to Marmaros's leg.", - "range_animation": "0", - "magic_level": "50", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Workman", - "defence_level": "65", - "safespot": null, - "lifepoints": "114", - "strength_level": "50", - "id": "6159", - "aggressive": "true", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "Squire to the Knights of the Round Table.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Squire", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6169", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "400", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "422", - "death_animation": "836", - "name": "Sir Lancelot", - "defence_level": "1", - "safespot": null, - "lifepoints": "118", - "strength_level": "1", - "id": "6170", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An officer of the law.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6183", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An officer of the law.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6184", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He isn't very friendly.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "0", - "name": "Renegade knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6188", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dark-hearted knight.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "2503,15,512", - "attack_speed": "5", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "25", - "safespot": null, - "lifepoints": "42", - "strength_level": "25", - "id": "6189", - "aggressive": "true", - "bonuses": "18,18,18,0,0,73,76,70,-11,72,0,16,0,0,0", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "Primping with combs and hair clips.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6190", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The name isn't just for show.", - "range_animation": "0", - "magic_level": "55", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Sinclair", - "defence_level": "55", - "safespot": null, - "lifepoints": "85", - "strength_level": "55", - "id": "6198", - "aggressive": "true", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "He can look after my money.", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6200", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6201", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A servant of the god Zamorak. ", - "slayer_task": "40", - "melee_animation": "6945", - "attack_speed": "6", - "poisonous": "true", - "respawn_delay": "150", - "weakness": "9", - "slayer_exp": "350", - "poison_amount": "16", - "magic_animation": "6945", - "death_animation": "6946", - "lifepoints": "255", - "id": "6203", - "aggressive": "true", - "bonuses": "160,160,160,0,0,80,80,80,130,80,0,31,0,0,0", - "agg_radius": "64", - "range_animation": "6945", - "magic_level": "200", - "defence_animation": "6944", - "name": "K'ril Tsutsaroth", - "defence_level": "270", - "poison_immune": "true", - "safespot": null, - "strength_level": "300", - "clue_level": "2", - "range_level": "1", - "attack_level": "340" - }, - { - "agg_radius": "64", - "examine": "Destroyer of 1000 planes!", - "slayer_task": "40", - "melee_animation": "6945", - "range_animation": "6945", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "50", - "defence_animation": "6944", - "weakness": "9", - "slayer_exp": "142", - "magic_animation": "6945", - "death_animation": "6946", - "name": "Tstanon Karlak", - "defence_level": "125", - "safespot": null, - "lifepoints": "142", - "strength_level": "118", - "id": "6204", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-5,0,0,14,0,0,0", - "range_level": "50", - "attack_level": "124" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6205", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Scourge of light.", - "slayer_task": "56", - "start_gfx": "1208", - "combat_style": "1", - "melee_animation": "7033", - "attack_speed": "5", - "respawn_delay": "50", - "weakness": "0", - "slayer_exp": "150", - "magic_animation": "7033", - "death_animation": "6946", - "lifepoints": "150", - "id": "6206", - "aggressive": "true", - "bonuses": "0,0,0,0,20,0,0,0,-5,0,0,0,0,0,20", - "agg_radius": "64", - "range_animation": "7033", - "magic_level": "50", - "defence_animation": "6945", - "name": "Zakl'n Gritch", - "defence_level": "127", - "safespot": null, - "strength_level": "76", - "range_level": "150", - "projectile": "1209", - "attack_level": "83" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6207", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "Despoiler of Ullek.", - "start_gfx": "1212", - "combat_style": "2", - "melee_animation": "7033", - "range_animation": "7033", - "attack_speed": "5", - "magic_level": "150", - "respawn_delay": "50", - "defence_animation": "6944", - "slayer_exp": "161", - "magic_animation": "7033", - "death_animation": "6946", - "name": "Balfrug Kreeyath", - "defence_level": "153", - "safespot": null, - "lifepoints": "161", - "strength_level": "60", - "id": "6208", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,10,0,0,0,0,0,0", - "range_level": "1", - "projectile": "1213", - "attack_level": "115" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6209", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "From the maws of hell.", - "slayer_task": "43", - "melee_animation": "6579", - "range_animation": "6579", - "combat_audio": "3717,3719,3718", - "attack_speed": "4", - "respawn_delay": "50", - "defence_animation": "6578", - "weakness": "7", - "slayer_exp": "116", - "magic_animation": "6579", - "death_animation": "6576", - "name": "Hellhound", - "defence_level": "102", - "safespot": null, - "lifepoints": "116", - "strength_level": "104", - "id": "6210", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "105" - }, - { - "examine": "Have you checked your pockets lately?", - "melee_animation": "169", - "range_animation": "169", - "combat_audio": "534,536,535", - "defence_animation": "170", - "weakness": "7", - "magic_animation": "169", - "death_animation": "172", - "name": "Imp", - "defence_level": "4", - "safespot": null, - "movement_radius": "25", - "lifepoints": "10", - "strength_level": "4", - "id": "6211", - "aggressive": "true", - "bonuses": "10,5,5,10,10,10,10,10,10,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "respawn_delay": "25", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "64", - "safespot": null, - "lifepoints": "100", - "strength_level": "64", - "id": "6212", - "aggressive": "true", - "bonuses": "50,40,45,60,70,70,70,40,40,50,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", - "melee_animation": "6536", - "range_animation": "6536", - "respawn_delay": "25", - "defence_animation": "6538", - "weakness": "7", - "magic_animation": "6536", - "death_animation": "6537", - "name": "Werewolf", - "defence_level": "64", - "safespot": null, - "lifepoints": "100", - "strength_level": "64", - "id": "6213", - "aggressive": "true", - "bonuses": "50,40,45,60,70,70,70,40,40,50,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "It looks really hungry!", - "melee_animation": "810", - "range_animation": "810", - "respawn_delay": "25", - "defence_animation": "404", - "magic_animation": "810", - "death_animation": "9055", - "name": "Vampire", - "defence_level": "44", - "safespot": null, - "lifepoints": "60", - "strength_level": "44", - "id": "6214", - "aggressive": "true", - "bonuses": "30,35,30,40,50,50,50,25,30,30,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "44" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "combat_audio": "312,314,313", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "9132", - "slayer_exp": "120", - "magic_animation": "1552", - "death_animation": "9131", - "name": "Bloodveld", - "defence_level": "30", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", - "id": "6215", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "1582", - "range_animation": "1582", - "combat_audio": "696,698,697", - "respawn_delay": "25", - "defence_animation": "1581", - "weakness": "7", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Pyrefiend", - "defence_level": "25", - "safespot": null, - "lifepoints": "48", - "strength_level": "25", - "id": "6216", - "aggressive": "true", - "bonuses": "25,25,20,40,40,40,15,30,20,60,20,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "A small ice demon.", - "melee_animation": "8080", - "range_animation": "8080", - "combat_audio": "531,533,532", - "respawn_delay": "25", - "defence_animation": "8079", - "magic_animation": "8080", - "death_animation": "8078", - "name": "Icefiend", - "defence_level": "5", - "safespot": null, - "lifepoints": "15", - "strength_level": "5", - "id": "6217", - "bonuses": "5,5,5,5,10,10,10,10,10,10,5,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Those horns look pretty sharp...", - "slayer_task": "39", - "melee_animation": "4300", - "range_animation": "4300", - "respawn_delay": "25", - "defence_animation": "4301", - "weakness": "9", - "slayer_exp": "112", - "magic_animation": "4300", - "death_animation": "4302", - "name": "Gorak", - "defence_level": "70", - "safespot": null, - "lifepoints": "112", - "strength_level": "70", - "id": "6218", - "aggressive": "true", - "bonuses": "30,30,50,70,40,70,70,50,60,55,0,0,0,0,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "Warrior of Zamorak.", - "slayer_task": "79", - "melee_animation": "390", - "range_animation": "390", - "attack_speed": "5", - "respawn_delay": "25", - "defence_animation": "1156", - "weakness": "6", - "magic_animation": "390", - "death_animation": "9055", - "name": "Spiritual warrior", - "defence_level": "66", - "safespot": null, - "lifepoints": "102", - "strength_level": "66", - "id": "6219", - "aggressive": "true", - "bonuses": "40,60,60,60,60,20,50,55,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "66" - }, - { - "examine": "A ranger spirit dedicated to Zamorak.", - "slayer_task": "78", - "start_gfx": "18", - "combat_style": "1", - "start_height": "96", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "25", - "defence_animation": "404", - "weakness": "1", - "magic_animation": "426", - "death_animation": "9055", - "name": "Spiritual ranger", - "defence_level": "70", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "6220", - "aggressive": "true", - "bonuses": "50,70,50,50,50,70,50,85,85,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "70", - "projectile": "9", - "attack_level": "1" - }, - { - "examine": "A deadly servant of Zamorak.", - "slayer_task": "77", - "combat_style": "2", - "melee_animation": "811", - "attack_speed": "5", - "respawn_delay": "25", - "weakness": "3", - "magic_animation": "811", - "death_animation": "9055", - "lifepoints": "75", - "id": "6221", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_animation": "811", - "magic_level": "180", - "end_gfx": "78", - "defence_animation": "404", - "end_height": "0", - "name": "Spiritual mage", - "defence_level": "61", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "78", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "Graceful avatar of Armadyl.", - "melee_animation": "6977", - "range_animation": "6977", - "attack_speed": "3", - "magic_level": "200", - "respawn_delay": "150", - "defence_animation": "6974", - "weakness": "10", - "slayer_exp": "357", - "magic_animation": "6977", - "death_animation": "6975", - "name": "Kree'arra", - "defence_level": "260", - "poison_immune": "true", - "safespot": null, - "lifepoints": "255", - "strength_level": "200", - "id": "6222", - "aggressive": "true", - "bonuses": "136,136,136,0,120,180,180,180,200,200,0,12,0,0,0", - "clue_level": "2", - "range_level": "380", - "attack_level": "300" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "2", - "melee_animation": "6952", - "attack_speed": "5", - "respawn_delay": "50", - "weakness": "3", - "slayer_exp": "124", - "magic_animation": "6952", - "death_animation": "6956", - "lifepoints": "121", - "id": "6223", - "aggressive": "true", - "bonuses": "45,45,45,0,0,0,0,0,0,0,0,25,0,0,0", - "prj_height": "92", - "agg_radius": "64", - "range_animation": "6952", - "magic_level": "150", - "defence_animation": "6955", - "name": "Wingman Skree", - "defence_level": "160", - "poison_immune": "false", - "safespot": null, - "strength_level": "50", - "range_level": "100", - "projectile": "1199", - "attack_level": "80" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6224", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "attack_speed": "5", - "respawn_delay": "50", - "weakness": "4", - "slayer_exp": "133", - "magic_animation": "6953", - "death_animation": "6956", - "lifepoints": "132", - "id": "6225", - "aggressive": "true", - "bonuses": "0,0,0,0,60,0,0,0,0,0,0,0,0,0,0", - "prj_height": "92", - "agg_radius": "64", - "range_animation": "6953", - "magic_level": "50", - "defence_animation": "6955", - "name": "Flockleader Geerin", - "defence_level": "175", - "poison_immune": "false", - "safespot": null, - "strength_level": "80", - "range_level": "150", - "projectile": "1190", - "attack_level": "80" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6226", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "Graceful, bird-like creature.", - "melee_animation": "6954", - "range_animation": "6954", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "50", - "defence_animation": "6955", - "weakness": "6", - "slayer_exp": "133", - "magic_animation": "6954", - "death_animation": "6956", - "name": "Flight Kilisa", - "defence_level": "175", - "safespot": null, - "lifepoints": "133", - "strength_level": "118", - "id": "6227", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,14,0,0,0", - "range_level": "169", - "attack_level": "124" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A servant of Armadyl.", - "slayer_task": "79", - "combat_style": "1", - "melee_animation": "6954", - "range_animation": "6954", - "respawn_delay": "25", - "defence_animation": "6955", - "weakness": "6", - "magic_animation": "6954", - "death_animation": "6956", - "name": "Spiritual warrior", - "defence_level": "70", - "safespot": null, - "lifepoints": "98", - "strength_level": "70", - "id": "6229", - "aggressive": "true", - "bonuses": "40,40,50,50,20,50,50,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "68", - "attack_level": "70" - }, - { - "examine": "Armadyl's favourite servant.", - "slayer_task": "78", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "weakness": "4", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Spiritual ranger", - "defence_level": "70", - "safespot": null, - "lifepoints": "89", - "strength_level": "1", - "id": "6230", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "70", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "A mage who serves Armadyl above all else - even death.", - "slayer_task": "77", - "combat_style": "2", - "melee_animation": "6952", - "attack_speed": "5", - "respawn_delay": "25", - "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, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "1199", - "attack_level": "1" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "6232", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "40", - "projectile": "1191", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "45", - "safespot": null, - "lifepoints": "83", - "strength_level": "1", - "id": "6233", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "45", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "50", - "safespot": null, - "lifepoints": "86", - "strength_level": "1", - "id": "6234", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "50", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "50", - "safespot": null, - "lifepoints": "86", - "strength_level": "1", - "id": "6235", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "50", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "55", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "id": "6236", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "55", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "55", - "safespot": null, - "lifepoints": "98", - "strength_level": "1", - "id": "6237", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "55", - "projectile": "1191", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "65", - "safespot": null, - "lifepoints": "124", - "strength_level": "1", - "id": "6238", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "65", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "65", - "safespot": null, - "lifepoints": "139", - "strength_level": "1", - "id": "6239", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "65", - "projectile": "1191", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "40", - "safespot": null, - "lifepoints": "63", - "strength_level": "1", - "id": "6240", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "40", - "projectile": "1191", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "40", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "6241", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "40", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "40", - "safespot": null, - "lifepoints": "83", - "strength_level": "1", - "id": "6242", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "40", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "50", - "safespot": null, - "lifepoints": "69", - "strength_level": "1", - "id": "6243", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "50", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "55", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "6244", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "55", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "55", - "safespot": null, - "lifepoints": "98", - "strength_level": "1", - "id": "6245", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "55", - "projectile": "1190", - "attack_level": "1", - "prj_height": "92" - }, - { - "examine": "Graceful, bird-like creature.", - "combat_style": "1", - "melee_animation": "6953", - "range_animation": "6953", - "respawn_delay": "25", - "defence_animation": "6955", - "magic_animation": "6953", - "death_animation": "6956", - "name": "Aviansie", - "defence_level": "65", - "safespot": null, - "lifepoints": "73", - "strength_level": "1", - "id": "6246", - "aggressive": "true", - "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "65", - "projectile": "1191", - "attack_level": "1", - "prj_height": "92" - }, - { - "agg_radius": "64", - "examine": "Commander of Saradomin's forces. ", - "melee_animation": "6964", - "range_animation": "6964", - "attack_speed": "2", - "magic_level": "300", - "respawn_delay": "150", - "defence_animation": "6966", - "weakness": "7", - "slayer_exp": "360", - "magic_animation": "6964", - "death_animation": "6965", - "name": "Commander Zilyana", - "defence_level": "300", - "poison_immune": "true", - "safespot": null, - "lifepoints": "255", - "strength_level": "196", - "id": "6247", - "aggressive": "true", - "bonuses": "195,195,195,200,0,100,100,100,100,100,0,20,0,0,0", - "clue_level": "2", - "range_level": "250", - "attack_level": "280" - }, - { - "agg_radius": "64", - "examine": "The bane of darkness.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "3829,3866,3849", - "attack_speed": "5", - "magic_level": "125", - "respawn_delay": "50", - "defence_animation": "6375", - "weakness": "7", - "slayer_exp": "0", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Starlight", - "defence_level": "120", - "poison_immune": "false", - "safespot": null, - "lifepoints": "160", - "strength_level": "125", - "id": "6248", - "aggressive": "true", - "bonuses": "60,60,60,0,0,12,14,13,5,13,0,10,0,0,0", - "range_level": "1", - "attack_level": "120" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6249", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Defender of the faithful.", - "start_gfx": "1184", - "combat_style": "2", - "melee_animation": "7019", - "attack_speed": "5", - "respawn_delay": "50", - "slayer_exp": "0", - "magic_animation": "7019", - "death_animation": "7016", - "lifepoints": "146", - "id": "6250", - "aggressive": "true", - "bonuses": "10,10,10,0,0,12,14,14,18,5,0,7,0,0,0", - "prj_height": "0", - "agg_radius": "64", - "range_animation": "7019", - "combat_audio": "3830,3869,3867", - "magic_level": "150", - "end_gfx": "1186", - "defence_animation": "7017", - "name": "Growler", - "defence_level": "120", - "safespot": null, - "strength_level": "101", - "range_level": "1", - "projectile": "1185", - "attack_level": "100" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6251", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Champion of Truth.", - "combat_style": "1", - "melee_animation": "7009", - "attack_speed": "5", - "respawn_delay": "50", - "weakness": "0", - "slayer_exp": "0", - "magic_animation": "7009", - "death_animation": "7011", - "lifepoints": "162", - "id": "6252", - "aggressive": "true", - "bonuses": "10,10,10,0,0,12,14,14,18,5,0,7,0,0,0", - "prj_height": "50", - "agg_radius": "64", - "range_animation": "7009", - "magic_level": "80", - "end_gfx": "24", - "defence_animation": "7010", - "name": "Bree", - "defence_level": "130", - "safespot": null, - "strength_level": "80", - "range_level": "150", - "projectile": "1188", - "attack_level": "110" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6253", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man wearing ancient clothing.", - "combat_style": "2", - "melee_animation": "811", - "range_animation": "811", - "poisonous": "true", - "magic_level": "60", - "respawn_delay": "25", - "end_gfx": "76", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "811", - "death_animation": "9055", - "name": "Saradomin priest", - "defence_level": "60", - "safespot": null, - "lifepoints": "89", - "strength_level": "1", - "id": "6254", - "aggressive": "true", - "bonuses": "40,40,40,40,20,50,40,55,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Warrior of Saradomin.", - "slayer_task": "79", - "melee_animation": "390", - "range_animation": "390", - "attack_speed": "5", - "respawn_delay": "25", - "defence_animation": "1156", - "weakness": "8", - "magic_animation": "390", - "death_animation": "9055", - "name": "Spiritual warrior", - "defence_level": "66", - "safespot": null, - "lifepoints": "100", - "strength_level": "66", - "id": "6255", - "aggressive": "true", - "bonuses": "40,60,60,60,60,20,50,45,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "66" - }, - { - "examine": "A ranger spirit dedicated to Saradomin.", - "slayer_task": "78", - "start_gfx": "18", - "combat_style": "1", - "start_height": "96", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "25", - "defence_animation": "404", - "weakness": "0", - "magic_animation": "426", - "death_animation": "9055", - "name": "Spiritual ranger", - "defence_level": "70", - "safespot": null, - "lifepoints": "106", - "strength_level": "1", - "id": "6256", - "aggressive": "true", - "bonuses": "50,70,50,50,50,70,50,65,65,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "70", - "projectile": "9", - "attack_level": "1" - }, - { - "examine": "Saradomin's holy mage.", - "slayer_task": "77", - "combat_style": "2", - "melee_animation": "811", - "attack_speed": "5", - "respawn_delay": "25", - "weakness": "4", - "magic_animation": "811", - "death_animation": "9055", - "lifepoints": "85", - "id": "6257", - "aggressive": "true", - "bonuses": "0,0,0,0,0,8,7,3,16,2,0,0,0,0,0", - "range_animation": "811", - "magic_level": "160", - "end_gfx": "76", - "defence_animation": "404", - "end_height": "0", - "name": "Spiritual mage", - "defence_level": "86", - "safespot": null, - "strength_level": "1", - "clue_level": "2", - "range_level": "1", - "projectile": "76", - "attack_level": "1" - }, - { - "examine": "A valiant knight.", - "melee_animation": "407", - "range_animation": "407", - "respawn_delay": "25", - "defence_animation": "410", - "weakness": "7", - "magic_animation": "407", - "death_animation": "9055", - "name": "Knight of Saradomin", - "defence_level": "50", - "safespot": null, - "lifepoints": "135", - "strength_level": "50", - "id": "6258", - "aggressive": "true", - "bonuses": "40,40,40,40,50,20,40,50,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A valiant knight.", - "melee_animation": "401", - "range_animation": "401", - "respawn_delay": "25", - "defence_animation": "424", - "weakness": "7", - "magic_animation": "401", - "death_animation": "9055", - "name": "Knight of Saradomin", - "defence_level": "50", - "safespot": null, - "lifepoints": "108", - "strength_level": "50", - "id": "6259", - "aggressive": "true", - "bonuses": "40,40,40,40,50,20,40,65,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "agg_radius": "64", - "examine": "A huge war chief.", - "melee_animation": "7060", - "range_animation": "7060", - "attack_speed": "6", - "magic_level": "280", - "respawn_delay": "150", - "defence_animation": "7061", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7060", - "death_animation": "7062", - "name": "General Graardor", - "defence_level": "250", - "poison_immune": "true", - "safespot": null, - "lifepoints": "255", - "strength_level": "350", - "id": "6260", - "aggressive": "true", - "bonuses": "120,120,120,0,100,90,90,90,65,90,0,43,0,0,0", - "clue_level": "2", - "range_level": "350", - "attack_level": "280" - }, - { - "agg_radius": "64", - "examine": "A battle-honed goblin. ", - "slayer_task": "38", - "melee_animation": "6154", - "range_animation": "6154", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "50", - "defence_animation": "6155", - "weakness": "8", - "magic_animation": "6154", - "death_animation": "6156", - "name": "Sergeant Strongstack", - "defence_level": "126", - "safespot": null, - "lifepoints": "128", - "strength_level": "118", - "id": "6261", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,14,0,0,0", - "range_level": "50", - "attack_level": "124" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6262", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A battle-honed goblin. ", - "slayer_task": "38", - "start_gfx": "1202", - "combat_style": "2", - "melee_animation": "6154", - "attack_speed": "5", - "respawn_delay": "50", - "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, - "strength_level": "50", - "range_level": "1", - "projectile": "1203", - "attack_level": "80" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6264", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "A battle-honed goblin. ", - "slayer_task": "38", - "combat_style": "1", - "melee_animation": "6154", - "range_animation": "6154", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "50", - "defence_animation": "6155", - "weakness": "2", - "magic_animation": "6154", - "death_animation": "6156", - "name": "Sergeant Grimspike", - "defence_level": "132", - "safespot": null, - "lifepoints": "146", - "strength_level": "80", - "id": "6265", - "aggressive": "true", - "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,20", - "range_level": "150", - "projectile": "1206", - "attack_level": "80" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6266", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Big, ugly, and smelly.", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", - "respawn_delay": "25", - "defence_animation": "360", - "magic_animation": "359", - "death_animation": "361", - "name": "Fishing spot", - "defence_level": "27", - "safespot": null, - "lifepoints": "60", - "strength_level": "27", - "id": "6267", - "aggressive": "true", - "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", - "range_level": "27", - "attack_level": "27" - }, - { - "examine": "Green and aggressive.", - "melee_animation": "2936", - "range_animation": "2936", - "attack_speed": "6", - "respawn_delay": "25", - "defence_animation": "2937", - "weakness": "8", - "magic_animation": "2936", - "death_animation": "2938", - "name": "Jogre", - "defence_level": "62", - "safespot": null, - "lifepoints": "70", - "strength_level": "62", - "id": "6268", - "aggressive": "true", - "bonuses": "10,20,20,20,20,20,10,20,0,0,0,0,0,0,0", - "clue_level": "1", - "range_level": "1", - "attack_level": "62" - }, - { - "examine": "A one-eyed man-eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "4651", - "weakness": "8", - "slayer_exp": "110", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "48", - "safespot": null, - "lifepoints": "110", - "strength_level": "70", - "id": "6269", - "aggressive": "true", - "bonuses": "22,22,22,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "A one-eyed man-eater.", - "slayer_task": "44", - "melee_animation": "4652", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "25", - "defence_animation": "4651", - "weakness": "8", - "slayer_exp": "110", - "magic_animation": "0", - "death_animation": "4653", - "name": "Cyclops", - "defence_level": "48", - "safespot": null, - "lifepoints": "110", - "strength_level": "70", - "id": "6270", - "aggressive": "true", - "bonuses": "22,22,22,0,0,0,0,0,0,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Ugly, fierce and with a bad attitude.", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "weakness": "8", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Ork", - "defence_level": "68", - "safespot": null, - "lifepoints": "110", - "strength_level": "68", - "id": "6271", - "aggressive": "true", - "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Ugly, fierce and with a bad attitude.", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Ork", - "defence_level": "68", - "safespot": null, - "lifepoints": "110", - "strength_level": "68", - "id": "6272", - "aggressive": "true", - "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Ugly, fierce and with a bad attitude.", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Ork", - "defence_level": "68", - "safespot": null, - "lifepoints": "110", - "strength_level": "68", - "id": "6273", - "aggressive": "true", - "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "Ugly, fierce and with a bad attitude.", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Ork", - "defence_level": "68", - "safespot": null, - "lifepoints": "110", - "strength_level": "68", - "id": "6274", - "aggressive": "true", - "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", - "range_level": "1", - "attack_level": "68" - }, - { - "examine": "An ugly, smelly creature.", - "slayer_task": "45", - "melee_animation": "164", - "range_animation": "164", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "165", - "weakness": "8", - "magic_animation": "164", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "40", - "safespot": null, - "lifepoints": "52", - "strength_level": "40", - "id": "6275", - "aggressive": "true", - "bonuses": "20,30,30,30,30,30,10,30,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "The spirit of a ranger, serving bandos beyond death.", - "slayer_task": "78", - "start_gfx": "95", - "combat_style": "1", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "weakness": "0", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Spiritual ranger", - "defence_level": "70", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "6276", - "aggressive": "true", - "bonuses": "30,50,30,30,50,60,50,40,40,0,0,0,0,0,0", - "clue_level": "2", - "range_level": "70", - "projectile": "1195", - "attack_level": "1" - }, - { - "examine": "A true servant of Bandos.", - "slayer_task": "79", - "melee_animation": "4320", - "range_animation": "4320", - "respawn_delay": "25", - "defence_animation": "4322", - "weakness": "8", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Spiritual warrior", - "defence_level": "70", - "safespot": null, - "lifepoints": "131", - "strength_level": "70", - "id": "6277", - "aggressive": "true", - "bonuses": "50,40,45,60,60,60,60,20,50,60,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "One of Bandos' chosen.", - "slayer_task": "77", - "combat_style": "2", - "melee_animation": "4320", - "range_animation": "4320", - "attack_speed": "5", - "magic_level": "142", - "respawn_delay": "25", - "defence_animation": "4322", - "weakness": "4", - "magic_animation": "4320", - "death_animation": "4321", - "name": "Spiritual mage", - "defence_level": "103", - "safespot": null, - "lifepoints": "106", - "strength_level": "1", - "id": "6278", - "aggressive": "true", - "bonuses": "0,0,0,0,0,12,14,13,35,13,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ancient goblin.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "20", - "strength_level": "5", - "id": "6279", - "aggressive": "true", - "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "A warrior for Bandos.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "6183", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "15", - "strength_level": "5", - "id": "6280", - "aggressive": "true", - "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "A soldier to the death.", - "melee_animation": "6188", - "range_animation": "6188", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "6189", - "magic_animation": "6188", - "death_animation": "6190", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "15", - "strength_level": "5", - "id": "6281", - "aggressive": "true", - "bonuses": "5,10,10,10,10,10,5,10,5,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "A soldier to the death.", - "melee_animation": "6188", - "range_animation": "6188", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "6189", - "magic_animation": "6188", - "death_animation": "6190", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "6282", - "aggressive": "true", - "bonuses": "5,10,10,10,10,10,5,10,5,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "A warrior for Bandos.", - "melee_animation": "6185", - "range_animation": "6185", - "combat_audio": "469,472,471", - "respawn_delay": "25", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "5", - "safespot": null, - "lifepoints": "16", - "strength_level": "5", - "id": "6283", - "aggressive": "true", - "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6285", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6286", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6287", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6288", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6289", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6290", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6291", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "168", - "strength_level": "59", - "id": "6292", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6293", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "308", - "strength_level": "59", - "id": "6294", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", - "combat_style": "1", - "melee_animation": "7108", - "range_animation": "0", - "magic_level": "59", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "59", - "safespot": null, - "lifepoints": "308", - "strength_level": "59", - "id": "6295", - "aggressive": "true", - "range_level": "59", - "projectile": "1468", - "attack_level": "59" - }, - { - "examine": "You don't think you can harm this creature.", - "slayer_task": "89", - "melee_animation": "7093", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "52", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "7091", - "name": "Warped tortoise", - "defence_level": "52", - "safespot": null, - "lifepoints": "148", - "strength_level": "39", - "id": "6296", - "range_level": "1", - "attack_level": "39" - }, - { - "melee_animation": "7093", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "7092", - "death_animation": "7091", - "name": "Warped tortoise", - "defence_level": "1", - "safespot": null, - "lifepoints": "87", - "strength_level": "1", - "id": "6297", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Annoyingly easy to squish.", - "slayer_task": "73", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "2", - "defence_animation": "0", - "magic_animation": "0", - "name": "Bolrie", - "defence_level": "2", - "safespot": null, - "lifepoints": "0", - "strength_level": "1", - "id": "6306", - "aggressive": "true", - "range_level": "2", - "attack_level": "1" - }, - { - "examine": "She looks bored.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard no. 21", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6314", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks even more bored.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard no. 72", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6315", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6322", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6323", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6324", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6325", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6326", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6327", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6328", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6329", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6330", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6331", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7108", - "respawn_delay": "60", - "defence_animation": "7110", - "death_animation": "7109", - "name": "Warped terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "154", - "strength_level": "1", - "id": "6332", - "aggressive": "true", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6334", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6335", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6336", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6337", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6338", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A child of West Ardougne.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Child", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6339", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duck!", - "combat_style": "1", - "melee_animation": "3920", - "range_animation": "3920", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "3922", - "name": "Child", - "defence_level": "65", - "safespot": null, - "lifepoints": "92", - "strength_level": "48", - "id": "6344", - "aggressive": "true", - "range_level": "65", - "attack_level": "48" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6346", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6347", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6348", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6349", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6350", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6351", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6352", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6353", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6354", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6355", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "390", - "combat_audio": "703,705,704", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Pirate", - "defence_level": "1", - "safespot": null, - "lifepoints": "23", - "strength_level": "1", - "id": "6356", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The mother of all pests!", - "attack_speed": "6", - "magic_level": "70", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "0", - "name": "Street urchin", - "defence_level": "70", - "safespot": null, - "lifepoints": "1428", - "strength_level": "70", - "id": "6358", - "aggressive": "true", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "A warrior of Zamorak.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak warrior", - "defence_level": "44", - "safespot": null, - "lifepoints": "62", - "strength_level": "44", - "id": "6363", - "aggressive": "true", - "range_level": "1", - "attack_level": "44" - }, - { - "examine": "A warrior of Zamorak.", - "melee_animation": "390", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak warrior", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "47", - "id": "6364", - "aggressive": "true", - "range_level": "1", - "attack_level": "47" - }, - { - "examine": "A ranger of Zamorak.", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak ranger", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "6365", - "aggressive": "true", - "range_level": "46", - "attack_level": "1" - }, - { - "examine": "A ranger of Zamorak.", - "melee_animation": "426", - "range_animation": "426", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak ranger", - "defence_level": "46", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "6366", - "aggressive": "true", - "range_level": "46", - "attack_level": "1" - }, - { - "examine": "A mage of Zamorak.", - "combat_style": "2", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "49", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak mage", - "defence_level": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "6367", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mage of Zamorak.", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "49", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak mage", - "defence_level": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "6368", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A cave dweller.", - "melee_animation": "7207", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "7205", - "name": "Cave lizard", - "defence_level": "47", - "safespot": null, - "lifepoints": "67", - "strength_level": "1", - "id": "6369", - "aggressive": "true", - "range_level": "47", - "attack_level": "1" - }, - { - "examine": "A representative of the Z.M.I.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mage of Zamorak", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6370", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Z.M.I. runecrafter.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Zamorak crafter", - "defence_level": "19", - "safespot": null, - "lifepoints": "27", - "strength_level": "19", - "id": "6371", - "range_level": "1", - "attack_level": "19" - }, - { - "death_animation": "836", - "name": "Zamorak crafter", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "melee_animation": "422", - "strength_level": "1", - "id": "6372", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "slayer_exp": "49", - "name": "Guard dog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6374", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5327", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "5328", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6376", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5327", - "combat_audio": "537,539,538", - "respawn_delay": "60", - "defence_animation": "5328", - "death_animation": "5329", - "name": "Jungle spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "6377", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wouldn't want to be footing that bill.", - "slayer_task": "7", - "melee_animation": "6775", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6779", - "name": "Tenacious toucan", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "6378", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "I wonder how much he can lift.", - "melee_animation": "7237", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7238", - "name": "Giant ant worker", - "defence_level": "35", - "safespot": null, - "lifepoints": "50", - "strength_level": "35", - "id": "6379", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "Gi-ant-ant-ant-ant...", - "melee_animation": "7237", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7238", - "name": "Giant ant soldier", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "6380", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "That stinger has to hurt...", - "melee_animation": "7242", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7244", - "name": "Giant wasp", - "defence_level": "60", - "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "6381", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "Polynomial - a parrot that goes without breakfast", - "slayer_task": "7", - "melee_animation": "6775", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6777", - "name": "Pernicious parrot", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "6382", - "range_level": "1", - "attack_level": "45" - }, - { - "melee_animation": "7242", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "7243", - "death_animation": "7244", - "name": "Giant wasp", - "defence_level": "1", - "safespot": null, - "lifepoints": "37", - "strength_level": "1", - "id": "6383", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Karamjan Jungle eagle", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6385", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "combat_audio": "428,430,429", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9455", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Gargoyle", - "defence_level": "107", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "6389", - "aggressive": "true", - "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", - "clue_level": "2", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "I wish I could tell where he was looking. What could be beneath that hood?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grim Reaper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6390", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6402", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6403", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6404", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6405", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6406", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6407", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6408", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6409", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6410", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6411", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6412", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6413", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6414", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6415", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6416", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6417", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6418", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6419", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6420", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6421", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6422", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6423", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6424", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6425", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6426", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6427", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6428", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6429", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6430", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6431", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6432", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6433", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6434", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6435", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6436", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6437", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6438", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6439", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6440", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6441", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6442", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6443", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6444", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6445", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6446", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6447", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6448", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6449", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6450", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6451", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6452", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6453", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6454", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6455", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6456", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6457", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6458", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6459", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6460", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6461", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6462", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6463", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6464", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6465", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6466", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6467", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A goblin high priest", - "slayer_task": "75", - "melee_animation": "7317", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "6182", - "name": "Snothead", - "defence_level": "14", - "safespot": null, - "lifepoints": "20", - "strength_level": "14", - "id": "6469", - "aggressive": "true", - "range_level": "1", - "attack_level": "14" - }, - { - "examine": "A goblin high priest", - "slayer_task": "75", - "melee_animation": "7317", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "6182", - "name": "Snailfeet", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "13", - "id": "6470", - "aggressive": "true", - "range_level": "18", - "attack_level": "13" - }, - { - "examine": "A goblin high priest", - "slayer_task": "75", - "melee_animation": "7317", - "range_animation": "0", - "magic_level": "22", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "6182", - "name": "Mosschin", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "16", - "id": "6471", - "aggressive": "true", - "range_level": "1", - "attack_level": "16" - }, - { - "examine": "A goblin high priest", - "slayer_task": "75", - "melee_animation": "7317", - "range_animation": "0", - "magic_level": "26", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "6182", - "name": "Redeyes", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "19", - "id": "6472", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A goblin high priest", - "slayer_task": "75", - "melee_animation": "7317", - "range_animation": "0", - "magic_level": "26", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "6182", - "name": "Strongbones", - "defence_level": "26", - "safespot": null, - "lifepoints": "37", - "strength_level": "19", - "id": "6473", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "Priest of the Ekeleshuun tribe.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6482", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He is expounding the word of the Big High War God.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Preacher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6488", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6490", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6491", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6492", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6493", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6494", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "469,472,471", - "strength_level": "1", - "id": "6495", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very welcoming.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6496", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very welcoming.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "469,472,471", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Goblin guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6497", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing blue armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6498", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing orange armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6499", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing black armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6500", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing white armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6501", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing purple armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6502", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guard wearing yellow armour.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6503", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "25", - "name": "Ghost", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "436,439,438", - "strength_level": "1", - "id": "6504", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very noble spider and attendant to the Queen.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Registrar 2", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6507", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The highest ranking of the Spider Queen's servants.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Registrar 3", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6508", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The head of the Spider Queen's army.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Registrar 4", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6509", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Trying hard to hide his identity.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jury", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6510", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Sir Amik's loyal squire.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spectator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6512", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A being of ore and minerals.", - "magic_animation": "0", - "name": "Spectator", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "range_animation": "0", - "strength_level": "1", - "id": "6514", - "range_level": "1", - "attack_level": "1", - "defence_animation": "0" - }, - { - "examine": "A monkey on a mission.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spectator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6516", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He works for Doric & Son.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spectator", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6518", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Gives an introduction to the Grand Exchange.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grand Exchange Tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The son of Ali M!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farid Morrisane (ores)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's probably seen better days.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob Barter (herbs)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "You can never miss a pirate in disguise.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Murky Matt (runes)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6525", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A tribal man", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Relobo Blinyo (logs)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6526", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's travelled a long way.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hofuthand (armour and weapons)", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Chief architect of the Spider Realm.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Registrar 5", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6536", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6543", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "6544", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tapping his feet and looking very bored.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "null", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6558", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge war chief. ", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "7061", - "magic_animation": "422", - "death_animation": "7062", - "name": "null", - "defence_level": "250", - "safespot": null, - "lifepoints": "254", - "strength_level": "251", - "id": "6560", - "aggressive": "true", - "bonuses": "300,300,300,300,300,300,150,300,300,250,43,96,43,30,0", - "range_level": "200", - "attack_level": "250" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6604", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6605", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6606", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6607", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6608", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6609", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6610", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6611", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6612", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6613", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6614", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6615", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6616", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6617", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6618", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6619", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6620", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6621", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6622", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6623", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6624", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6625", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6626", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6627", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6628", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6629", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6630", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6631", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6632", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6633", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6634", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6635", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6636", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6637", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6638", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6639", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6640", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6641", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6642", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6643", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6644", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "spawn_animation": "7457", - "examine": "The ghost of a cyclops slain during the god wars.", - "melee_animation": "7453", - "range_animation": "7510", - "magic_level": "43", - "defence_animation": "7455", - "weakness": "0", - "slayer_exp": "110", - "magic_animation": "7497", - "death_animation": "7454", - "name": "Revenant cyclops", - "defence_level": "43", - "safespot": null, - "lifepoints": "110", - "strength_level": "43", - "id": "6645", - "clue_level": "2", - "range_level": "43", - "attack_level": "43" - }, - { - "spawn_animation": "7464", - "examine": "The essence of a hellhound slain during the god wars.", - "melee_animation": "7460", - "range_animation": "7501", - "combat_audio": "3717,3719,3718", - "magic_level": "50", - "defence_animation": "7462", - "slayer_exp": "80", - "magic_animation": "7515", - "death_animation": "7461", - "name": "Revenant hellhound", - "defence_level": "1", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "6646", - "clue_level": "2", - "range_level": "50", - "attack_level": "50" - }, - { - "spawn_animation": "7478", - "examine": "The essence of a demon slain during the god wars.", - "melee_animation": "7474", - "range_animation": "7512", - "combat_audio": "400,404,403", - "magic_level": "60", - "defence_animation": "7476", - "slayer_exp": "80", - "magic_animation": "7498", - "death_animation": "7475", - "name": "Revenant demon", - "defence_level": "60", - "safespot": null, - "lifepoints": "80", - "strength_level": "60", - "id": "6647", - "clue_level": "2", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6648", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7471", - "examine": "The ghost of a dark beast slain during the god wars.", - "melee_animation": "7467", - "range_animation": "7514", - "defence_animation": "7469", - "slayer_exp": "140", - "magic_animation": "7515", - "death_animation": "7468", - "name": "Revenant dark beast", - "defence_level": "80", - "safespot": null, - "lifepoints": "140", - "strength_level": "80", - "id": "6649", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6650", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6651", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6652", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6653", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6654", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6655", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6656", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "22", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "22", - "safespot": null, - "lifepoints": "34", - "strength_level": "22", - "id": "6657", - "clue_level": "1", - "range_level": "22", - "attack_level": "22" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6658", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6659", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6660", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6661", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6662", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6663", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6664", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6665", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6666", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6667", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6668", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6669", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6670", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6671", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6672", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6673", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6674", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6675", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6676", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6677", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6678", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6679", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6680", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6681", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6682", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6683", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6684", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6685", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6686", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "spawn_animation": "7457", - "examine": "The ghost of a cyclops slain during the god wars.", - "melee_animation": "7453", - "range_animation": "7510", - "magic_level": "43", - "defence_animation": "7455", - "weakness": "0", - "slayer_exp": "110", - "magic_animation": "7497", - "death_animation": "7454", - "name": "Revenant cyclops", - "defence_level": "43", - "safespot": null, - "lifepoints": "110", - "strength_level": "43", - "id": "6687", - "clue_level": "2", - "range_level": "43", - "attack_level": "43" - }, - { - "spawn_animation": "7464", - "examine": "The essence of a hellhound slain during the god wars.", - "melee_animation": "7460", - "range_animation": "7501", - "combat_audio": "3717,3719,3718", - "magic_level": "50", - "defence_animation": "7462", - "slayer_exp": "80", - "magic_animation": "7515", - "death_animation": "7461", - "name": "Revenant hellhound", - "defence_level": "1", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "6688", - "clue_level": "2", - "range_level": "50", - "attack_level": "50" - }, - { - "spawn_animation": "7478", - "examine": "The essence of a demon slain during the god wars.", - "melee_animation": "7474", - "range_animation": "7512", - "combat_audio": "400,404,403", - "magic_level": "60", - "defence_animation": "7476", - "slayer_exp": "80", - "magic_animation": "7498", - "death_animation": "7475", - "name": "Revenant demon", - "defence_level": "60", - "safespot": null, - "lifepoints": "80", - "strength_level": "60", - "id": "6689", - "clue_level": "2", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6690", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7471", - "examine": "The ghost of a dark beast slain during the god wars.", - "melee_animation": "7467", - "range_animation": "7514", - "defence_animation": "7469", - "slayer_exp": "140", - "magic_animation": "7515", - "death_animation": "7468", - "name": "Revenant dark beast", - "defence_level": "80", - "safespot": null, - "lifepoints": "140", - "strength_level": "80", - "id": "6691", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6692", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6693", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6694", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6695", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6696", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6697", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6698", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6699", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6700", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6701", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6702", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6703", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6704", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6705", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6706", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6707", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6708", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6709", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6710", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6711", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6712", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6713", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6714", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "spawn_animation": "7410", - "examine": "The essence of an imp slain during the god wars.", - "melee_animation": "7407", - "range_animation": "7501", - "combat_audio": "0,0,0", - "magic_level": "10", - "defence_animation": "7404", - "slayer_exp": "10", - "magic_animation": "7500", - "death_animation": "7408", - "name": "Revenant imp", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "10", - "id": "6715", - "range_level": "10", - "attack_level": "10" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6716", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6717", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6718", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7447", - "examine": "The ghost of a goblin slain during the god wars.", - "melee_animation": "7449", - "range_animation": "7513", - "combat_audio": "469,472,471", - "magic_level": "12", - "defence_animation": "7450", - "magic_animation": "7499", - "death_animation": "7448", - "name": "Revenant goblin", - "defence_level": "12", - "safespot": null, - "lifepoints": "24", - "strength_level": "12", - "id": "6719", - "clue_level": "1", - "range_level": "12", - "attack_level": "12" - }, - { - "spawn_animation": "7485", - "examine": "The ghost of an icefiend slain during the God Wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "27", - "defence_animation": "7483", - "slayer_exp": "40", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant icefiend", - "defence_level": "27", - "safespot": null, - "lifepoints": "40", - "strength_level": "27", - "id": "6720", - "clue_level": "1", - "range_level": "27", - "attack_level": "27" - }, - { - "spawn_animation": "7485", - "examine": "The essence of a pyrefiend slain during the god wars.", - "melee_animation": "7481", - "range_animation": "7519", - "attack_speed": "3", - "magic_level": "29", - "defence_animation": "7483", - "slayer_exp": "48", - "magic_animation": "7506", - "death_animation": "7482", - "name": "Revenant pyrefiend", - "defence_level": "29", - "safespot": null, - "lifepoints": "48", - "strength_level": "29", - "id": "6721", - "clue_level": "1", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6722", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7426", - "examine": "The ghost of a vampire slain in the god wars.", - "melee_animation": "7427", - "range_animation": "7520", - "magic_level": "34", - "defence_animation": "7429", - "slayer_exp": "60", - "magic_animation": "7507", - "death_animation": "7428", - "name": "Revenant vampire", - "defence_level": "34", - "safespot": null, - "lifepoints": "60", - "strength_level": "34", - "id": "6723", - "clue_level": "1", - "range_level": "34", - "attack_level": "34" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6724", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6725", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6726", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "The ghost of a hobgoblin slain during the God Wars.", - "melee_animation": "7418", - "range_animation": "7516", - "combat_audio": "469,472,471", - "magic_level": "32", - "defence_animation": "7420", - "slayer_exp": "72", - "magic_animation": "7503", - "death_animation": "7419", - "name": "Revenant hobgoblin", - "defence_level": "32", - "safespot": null, - "lifepoints": "72", - "strength_level": "32", - "id": "6727", - "clue_level": "1", - "range_level": "32", - "attack_level": "32" - }, - { - "spawn_animation": "7403", - "examine": "The ghost of a werewolf slain during the god wars.", - "melee_animation": "7397", - "range_animation": "7521", - "magic_level": "38", - "defence_animation": "7399", - "magic_animation": "7496", - "death_animation": "7398", - "name": "Revenant werewolf", - "defence_level": "38", - "safespot": null, - "lifepoints": "65", - "strength_level": "38", - "id": "6728", - "clue_level": "2", - "range_level": "38", - "attack_level": "38" - }, - { - "examine": "A ghost of an ork slain during the god wars.", - "melee_animation": "7411", - "range_animation": "7518", - "magic_level": "70", - "defence_animation": "7413", - "magic_animation": "7505", - "death_animation": "7412", - "name": "Revenant ork", - "defence_level": "70", - "safespot": null, - "lifepoints": "105", - "strength_level": "70", - "id": "6729", - "clue_level": "2", - "range_level": "70", - "attack_level": "70" - }, - { - "spawn_animation": "7440", - "examine": "A ghost of a knight slain during the god wars.", - "melee_animation": "7441", - "range_animation": "7522", - "poisonous": "true", - "magic_level": "80", - "defence_animation": "7443", - "slayer_exp": "143", - "poison_amount": "8", - "magic_animation": "7508", - "death_animation": "7442", - "name": "Revenant knight", - "defence_level": "80", - "safespot": null, - "lifepoints": "143", - "strength_level": "80", - "id": "6730", - "clue_level": "2", - "range_level": "80", - "attack_level": "80" - }, - { - "examine": "Her lack of body heat is unsettling.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Queen of Snow", - "defence_level": "1", - "movement_radius": "5", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6731", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It looks right at home here.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snow imp", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Snowflakes swirling in the wind.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snow", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6740", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Dragon snowman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6743", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Pirate snowman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", - "strength_level": "1", - "id": "6745", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A snowman armed with a holly bow.", - "name": "Snow ranger", - "defence_level": "10", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "6747", - "range_level": "30", - "attack_level": "30" - }, - { - "examine": "A snowman armed with an ice sword.", - "name": "Snow ranger", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6748", - "magic_level": "30", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A snowman armed with a winter staff.", - "name": "Snow mage", - "defence_level": "10", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "6749", - "range_level": "30", - "attack_level": "30" - }, - { - "examine": "Cut-down and dried.", - "slayer_task": "93", - "melee_animation": "5568", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5569", - "name": "Dried zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "6761", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "Needs moisturising.", - "slayer_task": "93", - "melee_animation": "5568", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5569", - "name": "Dried zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "6762", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "I bet it's parched.", - "slayer_task": "93", - "melee_animation": "5568", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5569", - "name": "Dried zombie", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "6763", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "A guard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Doorman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6769", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge beast of a beetle.", - "slayer_task": "70", - "melee_animation": "7596", - "range_animation": "0", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "7595", - "name": "Giant scarab", - "defence_level": "66", - "safespot": null, - "lifepoints": "285", - "strength_level": "49", - "id": "6770", - "aggressive": "true", - "range_level": "66", - "attack_level": "49" - }, - { - "examine": "It's not quite small enough to stamp upon.", - "melee_animation": "7657", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7656", - "name": "Scarab larva", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "6772", - "range_level": "1", - "attack_level": "18" - }, - { - "examine": "Bred to shoot you down.", - "slayer_task": "70", - "start_gfx": "18", - "combat_style": "1", - "melee_animation": "7607", - "range_animation": "7607", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "7609", - "name": "Scabaras ranger", - "defence_level": "55", - "safespot": null, - "lifepoints": "157", - "strength_level": "41", - "id": "6773", - "aggressive": "true", - "range_level": "55", - "projectile": "11", - "attack_level": "41" - }, - { - "examine": "It might be angry...it's hard to tell.", - "slayer_task": "70", - "melee_animation": "7612", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7614", - "name": "Scabaras lancer", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "6774", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "A giant, mountable locust.", - "melee_animation": "7586", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "7581", - "name": "Scabaras locust", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "41", - "id": "6776", - "aggressive": "true", - "range_level": "55", - "attack_level": "41" - }, - { - "examine": "Bouncy", - "slayer_task": "70", - "melee_animation": "7584", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "7581", - "name": "Locust lancer", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "6777", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "He'll have someone's eye out with that.", - "slayer_task": "70", - "start_gfx": "18", - "combat_style": "1", - "melee_animation": "5451", - "range_animation": "5451", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "magic_animation": "0", - "death_animation": "7581", - "name": "Locust ranger", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "41", - "id": "6778", - "aggressive": "true", - "range_level": "55", - "projectile": "11", - "attack_level": "41" - }, - { - "melee_animation": "7588", - "combat_audio": "386,388,387", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "7590", - "slayer_exp": "62", - "death_animation": "7591", - "name": "Crocodile", - "defence_level": "1", - "safespot": null, - "lifepoints": "72", - "strength_level": "1", - "id": "6779", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Part scarab, part man.", - "slayer_task": "70", - "combat_style": "2", - "melee_animation": "7615", - "range_animation": "0", - "magic_level": "55", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "4", - "magic_animation": "0", - "death_animation": "7616", - "name": "Scabaras mage", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "41", - "id": "6780", - "aggressive": "true", - "range_level": "1", - "attack_level": "41" - }, - { - "examine": "Part scarab, part man.", - "combat_style": "2", - "melee_animation": "7620", - "respawn_delay": "60", - "defence_animation": "7617", - "death_animation": "7616", - "name": "Scabaras mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "6781", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An animated clay statue.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Clay golem", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6785", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks a bit dirty from digging.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lead archaeologist Abigail", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6786", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks a bit dusty from troweling.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Assistant archaeologist Kerner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6787", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He exudes something like holiness.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "High Priest of Scabaras", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6788", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He exudes something like holiness.", - "slayer_task": "70", - "melee_animation": "7612", - "range_animation": "0", - "magic_level": "66", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7614", - "name": "High Priest of Scabaras", - "defence_level": "66", - "safespot": null, - "lifepoints": "94", - "strength_level": "66", - "id": "6789", - "range_level": "66", - "attack_level": "66" - }, - { - "examine": "He exudes something like holiness.", - "slayer_task": "70", - "melee_animation": "7607", - "range_animation": "7607", - "magic_level": "66", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7609", - "name": "High Priest of Scabaras", - "defence_level": "66", - "safespot": null, - "lifepoints": "94", - "strength_level": "66", - "id": "6790", - "range_level": "66", - "attack_level": "66" - }, - { - "examine": "A bird. Literally terrifying.", - "melee_animation": "1010", - "respawn_delay": "0", - "defence_animation": "1011", - "death_animation": "1012", - "name": "Spirit terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "74", - "strength_level": "1", - "id": "6794", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bird. Literally terrifying.", - "melee_animation": "1010", - "range_animation": "0", - "magic_level": "50", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "1013", - "name": "Spirit terrorbird", - "defence_level": "50", - "safespot": null, - "lifepoints": "74", - "strength_level": "50", - "id": "6795", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "Is it a stone? Is it a crab? No! It's Granite Crab!", - "melee_animation": "8104", - "range_animation": "8104", - "attack_speed": "3", - "magic_level": "13", - "respawn_delay": "50", - "defence_animation": "8105", - "magic_animation": "8104", - "death_animation": "8106", - "name": "Granite crab", - "defence_level": "22", - "safespot": null, - "lifepoints": "39", - "strength_level": "17", - "id": "6796", - "bonuses": "16,9,5,12,4,11,57,53,61,12,45,22,0,0,0", - "range_level": "16", - "attack_level": "18" - }, - { - "examine": "Is it a stone? Is it a crab? No! It's Granite Crab!", - "melee_animation": "8104", - "range_animation": "8104", - "attack_speed": "3", - "magic_level": "13", - "respawn_delay": "50", - "defence_animation": "8105", - "weakness": "10", - "magic_animation": "8104", - "death_animation": "8106", - "name": "Granite crab", - "defence_level": "22", - "safespot": null, - "lifepoints": "39", - "strength_level": "17", - "id": "6797", - "bonuses": "16,9,5,12,4,11,57,53,61,12,45,22,0,0,0", - "range_level": "16", - "attack_level": "18" - }, - { - "death_animation": "8065", - "name": "Praying mantis", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "8064", - "strength_level": "1", - "id": "6798", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8066" - }, - { - "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": "60", - "safespot": null, - "lifepoints": "107", - "strength_level": "60", - "id": "6799", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "7854", - "name": "Giant ent", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7853", - "strength_level": "1", - "id": "6800", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7852" - }, - { - "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": "60", - "safespot": null, - "lifepoints": "111", - "strength_level": "60", - "id": "6801", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "8153", - "name": "Spirit cobra", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "8152", - "strength_level": "1", - "id": "6802", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8154" - }, - { - "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": "55", - "safespot": null, - "lifepoints": "90", - "strength_level": "55", - "id": "6803", - "range_level": "55", - "attack_level": "55" - }, - { - "death_animation": "7780", - "name": "Spirit dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7786", - "strength_level": "1", - "id": "6804", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7785" - }, - { - "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": "65", - "safespot": null, - "lifepoints": "115", - "strength_level": "65", - "id": "6805", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "A hermit slug.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "8143", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "55", - "end_gfx": "1387", - "defence_animation": "8145", - "magic_animation": "8143", - "death_animation": "8143", - "name": "Thorny snail", - "defence_level": "22", - "poison_immune": "true", - "safespot": null, - "lifepoints": "28", - "strength_level": "18", - "id": "6806", - "bonuses": "47,50,52,52,54,49,13,3,13,4,0,0,0,0,0", - "range_level": "21", - "projectile": "1386", - "attack_level": "19" - }, - { - "examine": "A hermit slug.", - "combat_style": "1", - "melee_animation": "8143", - "range_animation": "8143", - "attack_speed": "5", - "magic_level": "18", - "respawn_delay": "55", - "end_gfx": "1387", - "defence_animation": "8145", - "weakness": "10", - "magic_animation": "8143", - "death_animation": "8143", - "name": "Thorny snail", - "defence_level": "22", - "poison_immune": "true", - "safespot": null, - "lifepoints": "28", - "strength_level": "18", - "id": "6807", - "bonuses": "47,50,52,52,54,49,13,3,13,4,0,0,0,0,0", - "range_level": "21", - "projectile": "1386", - "attack_level": "19" - }, - { - "combat_style": "2", - "melee_animation": "7970", - "respawn_delay": "0", - "defence_animation": "7967", - "death_animation": "7979", - "name": "Karamthulhu overlord", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6809", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Kneel before squid!", - "combat_style": "2", - "melee_animation": "7963", - "range_animation": "0", - "magic_level": "50", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7964", - "name": "Karamthulhu overlord", - "defence_level": "50", - "safespot": null, - "lifepoints": "82", - "strength_level": "50", - "id": "6810", - "range_level": "50", - "attack_level": "50" - }, - { - "combat_style": "1", - "melee_animation": "7935", - "respawn_delay": "0", - "defence_animation": "7936", - "death_animation": "7937", - "name": "Hydra", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6811", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "7935", - "respawn_delay": "0", - "defence_animation": "7936", - "death_animation": "7937", - "name": "Hydra", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6812", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "7740", - "name": "Bunyip", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "melee_animation": "7741", - "strength_level": "1", - "id": "6813", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7742" - }, - { - "death_animation": "7740", - "name": "Bunyip", - "defence_level": "1", - "safespot": null, - "lifepoints": "40", - "melee_animation": "7741", - "strength_level": "1", - "id": "6814", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7742" - }, - { - "combat_style": "1", - "melee_animation": "8286", - "respawn_delay": "0", - "defence_animation": "8287", - "death_animation": "8285", - "name": "War tortoise", - "defence_level": "1", - "safespot": null, - "lifepoints": "348", - "strength_level": "1", - "id": "6815", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Definitely not teenaged", - "combat_style": "1", - "melee_animation": "8286", - "range_animation": "0", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8285", - "name": "War tortoise", - "defence_level": "55", - "safespot": null, - "lifepoints": "95", - "strength_level": "55", - "id": "6816", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "Fruity and bat-shaped. A winning combination!.", - "name": "Fruit bat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6817", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "2", - "melee_animation": "7675", - "respawn_delay": "0", - "defence_animation": "7670", - "death_animation": "7671", - "name": "Abyssal parasite", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6818", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's an extra-planar little blood hoover.", - "combat_style": "2", - "melee_animation": "8910", - "range_animation": "0", - "magic_level": "50", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7671", - "name": "Abyssal parasite", - "defence_level": "50", - "safespot": null, - "lifepoints": "77", - "strength_level": "50", - "id": "6819", - "range_level": "50", - "attack_level": "50" - }, - { - "death_animation": "7684", - "name": "Abyssal lurker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7680", - "strength_level": "1", - "id": "6820", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7681" - }, - { - "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": "55", - "safespot": null, - "lifepoints": "88", - "strength_level": "55", - "id": "6821", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "The pointiest horse on the planet.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "4135,4196,4195", - "magic_level": "69", - "defence_animation": "6375", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn stallion", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "100", - "strength_level": "62", - "id": "6822", - "bonuses": "48,59,43,52,104,52,116,123,134,46,127,4,0,0,0", - "range_level": "72", - "attack_level": "64" - }, - { - "examine": "The pointiest horse on the planet.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "4135,4196,4195", - "magic_level": "69", - "defence_animation": "6375", - "weakness": "10", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn stallion", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "100", - "strength_level": "62", - "id": "6823", - "bonuses": "48,59,43,52,104,52,116,123,134,46,127,4,0,0,0", - "range_level": "72", - "attack_level": "64" - }, - { - "combat_style": "2", - "melee_animation": "7810", - "attack_speed": "5", - "respawn_delay": "0", - "defence_animation": "5388", - "death_animation": "8012", - "name": "Magpie", - "defence_level": "1", - "safespot": null, - "lifepoints": "16", - "strength_level": "1", - "id": "6824", - "range_level": "1", - "projectile": "1318", - "attack_level": "1" - }, - { - "examine": "Southern fried please!", - "combat_style": "2", - "melee_animation": "7810", - "range_animation": "7810", - "attack_speed": "5", - "magic_level": "23", - "respawn_delay": "50", - "defence_animation": "5388", - "magic_animation": "7810", - "death_animation": "5389", - "name": "Dreadfowl", - "defence_level": "22", - "poison_immune": "true", - "safespot": null, - "lifepoints": "16", - "strength_level": "15", - "id": "6825", - "bonuses": "16,15,29,12,39,13,41,35,29,39,47,2,2,2,7", - "range_level": "16", - "projectile": "1318", - "attack_level": "17" - }, - { - "examine": "Southern fried please!", - "combat_style": "2", - "melee_animation": "7810", - "range_animation": "7810", - "attack_speed": "5", - "magic_level": "23", - "respawn_delay": "50", - "defence_animation": "5388", - "weakness": "10", - "magic_animation": "7810", - "death_animation": "5389", - "name": "Dreadfowl", - "defence_level": "22", - "poison_immune": "true", - "safespot": null, - "lifepoints": "16", - "strength_level": "15", - "id": "6826", - "bonuses": "16,15,29,12,39,13,41,35,29,39,47,2,2,2,7", - "range_level": "16", - "projectile": "1318", - "attack_level": "17" - }, - { - "examine": "It's mean and green.", - "melee_animation": "8208", - "respawn_delay": "0", - "defence_animation": "8205", - "death_animation": "8209", - "name": "Stranger plant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6827", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's mean and green!", - "melee_animation": "8208", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8209", - "name": "Stranger plant", - "defence_level": "55", - "safespot": null, - "lifepoints": "91", - "strength_level": "55", - "id": "6828", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "Man's significantly less-domesticated friend.", - "melee_animation": "8292", - "range_animation": "8292", - "attack_speed": "5", - "magic_level": "7", - "defence_animation": "8294", - "magic_animation": "8292", - "death_animation": "8295", - "name": "Spirit wolf", - "defence_level": "21", - "safespot": null, - "lifepoints": "15", - "strength_level": "18", - "id": "6829", - "bonuses": "12,7,4,10,23,5,29,27,16,12,13,17,0,0,0", - "range_level": "9", - "attack_level": "16" - }, - { - "examine": "Man's significantly less-domesticated friend.", - "melee_animation": "8292", - "range_animation": "8292", - "attack_speed": "5", - "magic_level": "7", - "defence_animation": "8294", - "weakness": "10", - "magic_animation": "8292", - "death_animation": "8295", - "name": "Spirit wolf", - "defence_level": "21", - "safespot": null, - "lifepoints": "15", - "strength_level": "18", - "id": "6830", - "bonuses": "12,7,4,10,23,5,29,27,16,12,13,17,0,0,0", - "range_level": "9", - "attack_level": "16" - }, - { - "death_animation": "7797", - "name": "Desert wyrm", - "defence_level": "1", - "safespot": null, - "lifepoints": "47", - "melee_animation": "7795", - "strength_level": "1", - "id": "6831", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7796" - }, - { - "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": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "6832", - "range_level": "18", - "attack_level": "18" - }, - { - "examine": "If you think he's evil", - "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": "6834", - "range_level": "42", - "attack_level": "42" - }, - { - "death_animation": "8276", - "name": "Vampire bat", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "melee_animation": "8275", - "strength_level": "1", - "id": "6835", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8274" - }, - { - "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": "31", - "safespot": null, - "lifepoints": "44", - "strength_level": "31", - "id": "6836", - "range_level": "31", - "attack_level": "31" - }, - { - "melee_animation": "6254", - "combat_audio": "3611,3612,3610", - "respawn_delay": "0", - "defence_animation": "6255", - "death_animation": "6256", - "name": "Spirit scorpion", - "defence_level": "1", - "safespot": null, - "lifepoints": "67", - "strength_level": "1", - "id": "6837", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Salt and vinegaroon.", - "melee_animation": "6254", - "range_animation": "0", - "combat_audio": "3611,3612,3610", - "magic_level": "19", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6256", - "name": "Spirit scorpion", - "defence_level": "19", - "safespot": null, - "lifepoints": "27", - "strength_level": "19", - "id": "6838", - "range_level": "19", - "attack_level": "19" - }, - { - "examine": "Crikey! Look at the size of those teeth!", - "melee_animation": "4925", - "combat_audio": "498,500,499", - "magic_level": "60", - "respawn_delay": "0", - "defence_animation": "4928", - "death_animation": "4929", - "name": "Arctic bear", - "defence_level": "60", - "safespot": null, - "lifepoints": "10", - "strength_level": "60", - "id": "6839", - "bonuses": "90,50,50,60,90,80,50,30,40,100,0,0,0,0,0", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Crikey! Look at the size of those teeth!", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "magic_level": "60", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "4929", - "name": "Arctic bear", - "defence_level": "60", - "safespot": null, - "lifepoints": "101", - "strength_level": "60", - "id": "6840", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Would scare anyone off their tuffet.", - "melee_animation": "5327", - "range_animation": "5327", - "combat_audio": "537,539,538", - "magic_level": "15", - "respawn_delay": "50", - "defence_animation": "5328", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Spirit spider", - "defence_level": "17", - "safespot": null, - "lifepoints": "18", - "strength_level": "16", - "id": "6841", - "bonuses": "15,16,11,14,34,12,24,36,46,16,37,0,0,0,0", - "range_level": "12", - "attack_level": "17" - }, - { - "examine": "Would scare anyone off their tuffet.", - "melee_animation": "5327", - "range_animation": "5327", - "combat_audio": "537,539,538", - "magic_level": "15", - "respawn_delay": "50", - "defence_animation": "5328", - "weakness": "10", - "magic_animation": "5327", - "death_animation": "5329", - "name": "Spirit spider", - "defence_level": "17", - "safespot": null, - "lifepoints": "18", - "strength_level": "16", - "id": "6842", - "bonuses": "15,16,11,14,34,12,24,36,46,16,37,0,0,0,0", - "range_level": "12", - "attack_level": "17" - }, - { - "death_animation": "7656", - "name": "Bloated leech", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7657", - "strength_level": "1", - "id": "6843", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7655" - }, - { - "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": "49", - "safespot": null, - "lifepoints": "70", - "strength_level": "49", - "id": "6844", - "range_level": "49", - "attack_level": "49" - }, - { - "death_animation": "7925", - "name": "Honey badger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7928", - "strength_level": "1", - "id": "6845", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7927" - }, - { - "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": "32", - "safespot": null, - "lifepoints": "45", - "strength_level": "32", - "id": "6846", - "range_level": "32", - "attack_level": "32" - }, - { - "examine": "The big cheese.", - "combat_audio": "703,705,704", - "magic_level": "27", - "respawn_delay": "50", - "name": "Albino rat", - "defence_level": "32", - "poison_immune": "true", - "safespot": null, - "lifepoints": "68", - "strength_level": "28", - "id": "6847", - "bonuses": "64,42,40,57,64,50,76,82,72,24,75,5,15,5,5", - "range_level": "31", - "attack_level": "31" - }, - { - "examine": "The big cheese.", - "combat_audio": "703,705,704", - "magic_level": "27", - "respawn_delay": "50", - "weakness": "10", - "name": "Albino rat", - "defence_level": "32", - "poison_immune": "true", - "safespot": null, - "lifepoints": "68", - "strength_level": "28", - "id": "6848", - "bonuses": "64,42,40,57,64,50,76,82,72,24,75,5,15,5,5", - "range_level": "31", - "attack_level": "31" - }, - { - "examine": "Puts the 'crust' in 'crustacean'.", - "melee_animation": "8112", - "respawn_delay": "0", - "defence_animation": "8114", - "death_animation": "8113", - "name": "Granite lobster", - "defence_level": "60", - "safespot": null, - "lifepoints": "10", - "strength_level": "60", - "id": "6849", - "bonuses": "93,90,65,90,30,40,50,50,60,40,0,0,0,0,0", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Puts the 'crust' in 'crustacean'.", - "melee_animation": "8112", - "range_animation": "0", - "magic_level": "60", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8113", - "name": "Granite lobster", - "defence_level": "60", - "safespot": null, - "lifepoints": "105", - "strength_level": "60", - "id": "6850", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "8012", - "name": "Macaw", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6851", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1" - }, - { - "death_animation": "8012", - "name": "Macaw", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6852", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "magic_level": "31", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Bronze minotaur", - "defence_level": "31", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "6853", - "bonuses": "15,20,10,15,10,5,35,0,0,0,0,0,0,0,0", - "range_level": "31", - "attack_level": "31" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "range_animation": "0", - "magic_level": "15", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8025", - "name": "Bronze minotaur", - "defence_level": "15", - "safespot": null, - "lifepoints": "51", - "strength_level": "15", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "range_animation": "0", - "magic_level": "25", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8025", - "name": "Iron minotaur", - "defence_level": "25", - "safespot": null, - "lifepoints": "65", - "strength_level": "25", - "id": "6856", - "range_level": "25", - "attack_level": "25" - }, - { - "examine": "He's just a big bully!", - "combat_style": "1", - "melee_animation": "8024", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Steel minotaur", - "defence_level": "70", - "safespot": null, - "lifepoints": "44", - "strength_level": "30", - "id": "6857", - "bonuses": "40,30,30,30,30,50,30,60,60,60,20,80,40,20,0", - "range_level": "1", - "attack_level": "70" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "range_animation": "0", - "magic_level": "35", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8025", - "name": "Steel minotaur", - "defence_level": "35", - "safespot": null, - "lifepoints": "80", - "strength_level": "35", - "id": "6858", - "range_level": "35", - "attack_level": "35" - }, - { - "examine": "He's just a big bully", - "combat_style": "1", - "melee_animation": "8024", - "magic_level": "40", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Mithril minotaur", - "defence_level": "40", - "safespot": null, - "lifepoints": "37", - "strength_level": "40", - "id": "6859", - "bonuses": "56,35,30,30,30,40,10,12,30,40,30,35,40,0,0", - "range_level": "40", - "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", - "respawn_delay": "0", - "defence_animation": "8023", - "slayer_exp": "0", - "death_animation": "8025", - "name": "Rune minotaur", - "defence_level": "65", - "safespot": null, - "lifepoints": "57", - "strength_level": "65", - "id": "6864", - "bonuses": "50,40,40,56,40,30,50,30,60,30,40,30,50,40,40", - "range_level": "65", - "attack_level": "65" - }, - { - "death_animation": "7818", - "name": "Smoke devil", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7816", - "strength_level": "1", - "id": "6865", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7817" - }, - { - "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": "55", - "safespot": null, - "lifepoints": "87", - "strength_level": "55", - "id": "6866", - "range_level": "55", - "attack_level": "55" - }, - { - "death_animation": "7897", - "name": "Bull ant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7896", - "strength_level": "1", - "id": "6867", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7900" - }, - { - "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": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "6868", - "range_level": "40", - "attack_level": "40" - }, - { - "examine": "It's just a harmless wee rabbit. ", - "combat_style": "2", - "melee_animation": "8304", - "range_animation": "8304", - "attack_speed": "5", - "magic_level": "95", - "defence_animation": "8306", - "magic_animation": "8304", - "death_animation": "8305", - "name": "Wolpertinger", - "defence_level": "95", - "poison_immune": "true", - "safespot": null, - "lifepoints": "619", - "strength_level": "1", - "id": "6869", - "bonuses": "59,50,50,50,50,50,50,50,50,50,50,50,50,68,0", - "range_level": "95", - "attack_level": "1" - }, - { - "examine": "It's just a harmless wee rabbit. ", - "combat_style": "2", - "melee_animation": "8304", - "range_animation": "8304", - "attack_speed": "5", - "magic_level": "95", - "defence_animation": "8306", - "magic_animation": "8304", - "death_animation": "8305", - "name": "Wolpertinger", - "defence_level": "95", - "poison_immune": "true", - "safespot": null, - "lifepoints": "619", - "strength_level": "1", - "id": "6870", - "bonuses": "59,50,50,50,50,50,50,50,50,50,50,50,50,68,0", - "range_level": "95", - "attack_level": "1" - }, - { - "melee_animation": "853", - "attack_speed": "5", - "respawn_delay": "0", - "defence_animation": "851", - "death_animation": "852", - "name": "Compost mound", - "defence_level": "1", - "safespot": null, - "lifepoints": "96", - "strength_level": "1", - "id": "6871", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I suppose it could smell worse", - "melee_animation": "7769", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "28", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7770", - "name": "Compost mound", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "6872", - "range_level": "28", - "attack_level": "28" - }, - { - "examine": "Bulk haulage never smelled so bad.", - "melee_animation": "5782", - "range_animation": "5782", - "attack_speed": "5", - "magic_level": "91", - "defence_animation": "5783", - "magic_animation": "5782", - "death_animation": "5784", - "name": "Pack yak", - "defence_level": "121", - "poison_immune": "true", - "safespot": null, - "lifepoints": "710", - "strength_level": "104", - "id": "6873", - "bonuses": "64,67,5,72,128,49,264,234,164,49,132,40,35,40,40", - "range_level": "86", - "attack_level": "112" - }, - { - "examine": "Bulk haulage never smelled so bad.", - "melee_animation": "5782", - "range_animation": "5782", - "attack_speed": "5", - "magic_level": "91", - "defence_animation": "5783", - "weakness": "10", - "magic_animation": "5782", - "death_animation": "5784", - "name": "Pack yak", - "defence_level": "121", - "poison_immune": "true", - "safespot": null, - "lifepoints": "710", - "strength_level": "104", - "id": "6874", - "bonuses": "64,67,5,72,128,49,264,234,164,49,132,40,35,40,40", - "range_level": "86", - "attack_level": "112" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit cockatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6875", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit cockatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6876", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit guthatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6877", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit guthatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6878", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "combat_audio": "703,705,704", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit saratrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6879", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit saratrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6880", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit zamatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6881", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit zamatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6882", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit pengatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6883", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit pengatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6884", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit coraxatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6885", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit coraxatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6886", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "start_gfx": "1467", - "combat_style": "2", - "melee_animation": "7762", - "respawn_delay": "0", - "defence_animation": "7761", - "death_animation": "7763", - "name": "Spirit vulatrice", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6887", - "range_level": "1", - "projectile": "1468", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7763", - "name": "Spirit vulatrice", - "defence_level": "43", - "safespot": null, - "lifepoints": "61", - "strength_level": "43", - "id": "6888", - "range_level": "43", - "projectile": "1468", - "attack_level": "43" - }, - { - "examine": "The only creature with a mouth big enough to hold a cannonball.", - "melee_animation": "7260", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "7257", - "death_animation": "7256", - "name": "Barker toad", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "55", - "id": "6889", - "range_level": "55", - "attack_level": "1" - }, - { - "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": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7256", - "name": "Barker toad", - "defence_level": "55", - "safespot": null, - "lifepoints": "94", - "strength_level": "55", - "id": "6890", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "He looks like the type of guy who would keep penguins.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Penguin keeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6891", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The lady of the pet shop.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pet shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6892", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The man of the pet shop.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pet shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6893", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dalmatian puppy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dalmatian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6896", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bulldog puppy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bulldog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6897", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Hatchling dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6900", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "407,405,406", - "strength_level": "1", - "id": "6901", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Hatchling dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6902", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "407,405,406", - "strength_level": "1", - "id": "6903", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Hatchling dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6904", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "407,405,406", - "strength_level": "1", - "id": "6905", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Hatchling dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "6906", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "407,405,406", - "strength_level": "1", - "id": "6907", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's so adorably tiny!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby gecko", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6917", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A tiny nut-thief.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby squirrel", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6921", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "6942", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "6943", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6944", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "2", - "melee_animation": "8304", - "attack_speed": "5", - "respawn_delay": "0", - "defence_animation": "8306", - "death_animation": "8305", - "name": "Bulldog puppy", - "defence_level": "1", - "safespot": null, - "lifepoints": "651", - "strength_level": "1", - "id": "6969", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A nature lover.", - "melee_animation": "7181", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "7186", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6972", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "range_animation": "0", - "combat_audio": "511,513,512", - "melee_animation": "7182", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6973", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "range_animation": "0", - "combat_audio": "511,513,512", - "melee_animation": "7182", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6974", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7181", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "7186", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6975", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6976", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6977", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6978", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7181", - "combat_audio": "511,513,512", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "7186", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6979", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druid", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6980", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6981", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7181", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "7186", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6982", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6983", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7181", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "7186", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6984", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6985", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6986", - "range_level": "1", - "attack_level": "28" - }, - { - "examine": "A nature lover.", - "melee_animation": "7182", - "range_animation": "0", - "combat_audio": "511,506,505", - "attack_speed": "4", - "magic_level": "25", - "respawn_delay": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "711", - "death_animation": "7185", - "name": "Druidess", - "defence_level": "32", - "safespot": null, - "lifepoints": "30", - "strength_level": "28", - "id": "6987", - "range_level": "1", - "attack_level": "28" - }, - { - "death_animation": "8570", - "name": "Spirit jelly", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "8569", - "strength_level": "1", - "id": "6992", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "8571" - }, - { - "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": "50", - "safespot": null, - "lifepoints": "78", - "strength_level": "50", - "id": "6993", - "range_level": "50", - "attack_level": "50" - }, - { - "combat_style": "1", - "melee_animation": "8519", - "respawn_delay": "0", - "defence_animation": "8518", - "death_animation": "8517", - "name": "Spirit kalphite", - "defence_level": "1", - "safespot": null, - "lifepoints": "77", - "strength_level": "1", - "id": "6994", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hail to the Queen", - "combat_style": "1", - "melee_animation": "6223", - "range_animation": "0", - "magic_level": "25", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "6228", - "name": "Spirit kalphite", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "6995", - "range_level": "25", - "attack_level": "25" - }, - { - "examine": "A stripy little baby raccoon.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby raccoon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "6997", - "range_level": "1", - "attack_level": "1" - }, - { - "spawn_animation": "8595", - "examine": "A ghost of a dragon slain during the god wars.", - "melee_animation": "8591", - "range_animation": "8594", - "combat_audio": "408,410,409", - "poisonous": "true", - "defence_animation": "8592", - "poison_amount": "8", - "magic_animation": "8594", - "death_animation": "8593", - "name": "Revenant dragon", - "defence_level": "85", - "safespot": null, - "lifepoints": "155", - "strength_level": "85", - "id": "6998", - "clue_level": "2", - "range_level": "85", - "attack_level": "85" - }, - { - "spawn_animation": "8595", - "examine": "A ghost of a dragon slain during the god wars.", - "melee_animation": "8591", - "range_animation": "8594", - "combat_audio": "408,410,409", - "poisonous": "true", - "defence_animation": "8592", - "poison_amount": "8", - "magic_animation": "8594", - "death_animation": "8593", - "name": "Revenant dragon", - "defence_level": "85", - "safespot": null, - "lifepoints": "155", - "strength_level": "85", - "id": "6999", - "clue_level": "2", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "7003", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "447,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "4664", - "weakness": "1", - "slayer_exp": "111", - "magic_animation": "4666", - "death_animation": "4668", - "name": "Fire giant", - "defence_level": "65", - "safespot": null, - "lifepoints": "111", - "strength_level": "65", - "id": "7004", - "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Must be the pack leader.", - "slayer_task": "92", - "melee_animation": "6559", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "slayer_exp": "74", - "magic_animation": "0", - "death_animation": "6558", - "name": "Big wolf", - "defence_level": "62", - "safespot": null, - "lifepoints": "21", - "strength_level": "60", - "id": "7005", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" - }, - { - "examine": "An active spiny creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grenwall", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7010", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hiding spiny creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Grenwall", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7011", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What a strange little creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pawya", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7012", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Dug by a Pawya (Not actual exam).", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Earth Mound", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7013", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "What a strange little creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pawya", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7014", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A most unlikely creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Platypus", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7021", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A most unlikely creature.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby platypus", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7024", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An ogre snack with wings.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wimpy bird", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7031", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A shy creature with a toxic bite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Diseased kebbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7039", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Fit to wear the uniform?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogress banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7049", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She walks as if she owns the place.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chief Tess", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7051", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fearsome adversary with no sense of humour.", - "slayer_task": "64", - "melee_animation": "8636", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8640", - "name": "Ogress champion", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "7078", - "aggressive": "true", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "Irascible, belligerent and stupefyingly impolite.", - "slayer_task": "64", - "melee_animation": "8636", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8640", - "name": "Ogress warrior", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "7079", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "Irascible, beliggerent and stupefyingly impolite.", - "slayer_task": "64", - "melee_animation": "8637", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8641", - "name": "Ogress warrior", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "45", - "id": "7080", - "aggressive": "true", - "range_level": "1", - "attack_level": "45" - }, - { - "examine": "A large and contentious lady ogre.", - "slayer_task": "64", - "melee_animation": "8636", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8640", - "name": "Ogress", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "7081", - "aggressive": "true", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "A large and contentious lady ogre.", - "slayer_task": "64", - "melee_animation": "8637", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8641", - "name": "Ogress", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "7082", - "aggressive": "true", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Dead stone defending a dead throne room.", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Eucalyptus ent", - "defence_level": "51", - "safespot": null, - "lifepoints": "145", - "strength_level": "51", - "id": "7084", - "aggressive": "true", - "range_level": "1", - "attack_level": "51" - }, - { - "examine": "Dead stone defending a dead throne room.", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Eucalyptus ent", - "defence_level": "54", - "safespot": null, - "lifepoints": "142", - "strength_level": "54", - "id": "7085", - "aggressive": "true", - "range_level": "1", - "attack_level": "54" - }, - { - "examine": "Dead stone defending a dead throne room.", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Eucalyptus ent", - "defence_level": "57", - "safespot": null, - "lifepoints": "162", - "strength_level": "57", - "id": "7086", - "aggressive": "true", - "range_level": "1", - "attack_level": "57" - }, - { - "examine": "A crazy, evil druid.", - "start_gfx": "105", - "melee_animation": "422", - "range_animation": "422", - "attack_speed": "4", - "magic_level": "10", - "end_gfx": "107", - "respawn_delay": "25", - "defence_animation": "404", - "weakness": "3", - "magic_animation": "422", - "death_animation": "9055", - "name": "Chaos druid", - "defence_level": "12", - "safespot": null, - "lifepoints": "20", - "strength_level": "8", - "id": "7105", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "projectile": "106", - "attack_level": "8" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7106", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7107", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7108", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7109", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7110", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7111", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7112", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7113", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Low on brains, high on aggression.", - "melee_animation": "386", - "range_animation": "0", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "50", - "defence_animation": "404", - "magic_animation": "0", - "death_animation": "9055", - "name": "Thug", - "defence_level": "9", - "safespot": null, - "lifepoints": "18", - "strength_level": "5", - "id": "7114", - "aggressive": "true", - "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "He seems to be enjoying his time in the bar.", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Thaki the delivery dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7115", - "bonuses": "5,5,0,0,0,0,2,3,3,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of King Tyras's men.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tyras guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7120", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hungry-looking rabbit.", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7125", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome farmer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7128", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome farmer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7130", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Owner of this homestead.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mrs. Winkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7132", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "That's one big Ork... ", - "melee_animation": "8754", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "8755", - "death_animation": "8756", - "name": "Bork", - "defence_level": "80", - "lifepoints": "300", - "strength_level": "90", - "id": "7133", - "bonuses": "200,120,100,300,300,120,400,500,300,200,300,400,200,90,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Sharimika", - "id": "2505" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Sharimika", - "id": "2506" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Mamma Bufetta", - "id": "2508" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Mamma Bufetta", - "id": "2509" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Layleen", - "id": "2511" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Layleen", - "id": "2512" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Karaday", - "id": "2514" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Karaday", - "id": "2515" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Safta Doc", - "id": "2517" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Safta Doc", - "id": "2518" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Gabooty", - "id": "2520" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Gabooty", - "id": "2521" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Fanellaman", - "id": "2523" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Fanellaman", - "id": "2524" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Jagbakoba", - "id": "2526" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Jagbakoba", - "id": "2527" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Murcaily", - "id": "2529" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Murcaily", - "id": "2530" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Rionasta", - "id": "2532" - }, - { - "examine": "A native of Tai Bwo Wannai.", - "name": "Rionasta", - "id": "2533" - }, - { - "agg_radius": "64", - "melee_animation": "8754", - "attack_speed": "8", - "respawn_delay": "60", - "defence_animation": "8755", - "death_animation": "8756", - "name": "Bork", - "defence_level": "1", - "safespot": null, - "lifepoints": "300", - "strength_level": "1", - "id": "7134", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Aww, aren't they the cutest li - Argh!", - "melee_animation": "8760", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "8761", - "name": "Ork legion", - "defence_level": "57", - "poison_immune": "true", - "safespot": null, - "lifepoints": "100", - "strength_level": "68", - "id": "7135", - "aggressive": "true", - "bonuses": "5,10,3,5,3,5,5,5,10,0,0,0,0,0,0", - "range_level": "68", - "attack_level": "68" - }, - { - "examine": "A powerful wizard.", - "combat_style": "2", - "attack_speed": "25", - "magic_level": "90", - "spell_id": "11", - "name": "Dagon'hai Elite", - "defence_level": "40", - "safespot": null, - "lifepoints": "70", - "strength_level": "90", - "id": "7137", - "aggressive": "true", - "bonuses": "220,190,140,120,120,130,90,120,120,200,100,154,150,93,190", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A monk of the Dagon'hai.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "98", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Dagon'hai Monk", - "defence_level": "80", - "safespot": null, - "lifepoints": "88", - "strength_level": "84", - "id": "7138", - "aggressive": "true", - "range_level": "1", - "attack_level": "84" - }, - { - "examine": "A monk of the Dagon'hai.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "98", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Dagon'hai Monk", - "defence_level": "80", - "safespot": null, - "lifepoints": "88", - "strength_level": "84", - "id": "7139", - "aggressive": "true", - "range_level": "1", - "attack_level": "84" - }, - { - "examine": "A monk of the Dagon'hai.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "98", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Dagon'hai Monk", - "defence_level": "80", - "safespot": null, - "lifepoints": "88", - "strength_level": "84", - "id": "7140", - "aggressive": "true", - "range_level": "1", - "attack_level": "84" - }, - { - "examine": "He seems to have gone mad.", - "melee_animation": "426", - "range_animation": "426", - "magic_level": "25", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "null", - "defence_level": "25", - "safespot": null, - "lifepoints": "4", - "strength_level": "25", - "id": "7141", - "aggressive": "true", - "range_level": "25", - "attack_level": "25" - }, - { - "examine": "He thumps people who cheat.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7142", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinch jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7144", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinchette jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7145", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinchette jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7146", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinch jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7147", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinch jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7148", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinch jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7149", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An imprisoned gublinch.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gublinch jailmate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7150", - "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", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7152", - "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", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7156", - "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": "7157", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A household pest.", - "melee_animation": "8785", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "8788", - "name": "Cockroach drone", - "defence_level": "3", - "safespot": null, - "lifepoints": "4", - "strength_level": "3", - "id": "7158", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "Eurgh! A big bug.", - "melee_animation": "8787", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "8790", - "name": "Cockroach worker", - "defence_level": "70", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "7159", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "Euww! A giant bug.", - "melee_animation": "8786", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "8789", - "name": "Cockroach soldier", - "defence_level": "70", - "safespot": null, - "lifepoints": "97", - "strength_level": "26", - "id": "7160", - "bonuses": "10,10,10,5,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "38" - }, - { - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Mugger", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "7161", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "422", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Mugger", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "7162", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "4934", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "7202", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "4934", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "7204", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It seems to have eaten a lot of chocolate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cockroach", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7206", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's searching for crumbs of chocolate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Spider", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7207", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It seems to have eaten a lot of chocolate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Snail", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7209", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7210", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7211", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7212", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7213", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7214", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7215", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7216", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7217", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7218", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7219", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7220", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7221", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7222", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7223", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7224", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7225", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7226", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7227", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7229", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7230", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7231", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7232", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a tiny", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7233", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7234", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7235", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Baby monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", - "id": "7236", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A stripy little baby raccoon.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby Raccoon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7275", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's so adorably tiny!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Baby gecko", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7285", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "1", - "melee_animation": "8222", - "magic_level": "70", - "respawn_delay": "0", - "defence_animation": "8224", - "death_animation": "8226", - "name": "Swamp titan", - "defence_level": "78", - "safespot": null, - "lifepoints": "556", - "strength_level": "1", - "id": "7329", - "bonuses": "60,60,60,60,50,50,100,200,200,28,100,100,70,70,70", - "range_level": "70", - "attack_level": "1" - }, - { - "examine": "Do you hear duelling banjos?", - "combat_style": "1", - "melee_animation": "8222", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8226", - "name": "Swamp titan", - "defence_level": "65", - "safespot": null, - "lifepoints": "117", - "strength_level": "60", - "id": "7330", - "bonuses": "60,60,60,60,50,50,100,200,200,28,100,100,70,70,70", - "range_level": "65", - "attack_level": "60" - }, - { - "examine": "It buzzes and bites. Nasty.", - "melee_animation": "8032", - "range_animation": "8032", - "magic_level": "21", - "respawn_delay": "50", - "defence_animation": "8034", - "magic_animation": "8032", - "death_animation": "8033", - "name": "Spirit mosquito", - "defence_level": "25", - "poison_immune": "true", - "safespot": null, - "lifepoints": "43", - "strength_level": "24", - "id": "7331", - "bonuses": "45,49,42,39,47,46,29,43,51,16,53,10,0,0,0", - "range_level": "25", - "attack_level": "28" - }, - { - "examine": "It buzzes and bites. Nasty.", - "melee_animation": "8032", - "range_animation": "8032", - "magic_level": "21", - "respawn_delay": "50", - "defence_animation": "8034", - "weakness": "10", - "magic_animation": "8032", - "death_animation": "8033", - "name": "Spirit mosquito", - "defence_level": "25", - "poison_immune": "true", - "safespot": null, - "lifepoints": "43", - "strength_level": "24", - "id": "7332", - "bonuses": "45,49,42,39,47,46,29,43,51,16,53,10,0,0,0", - "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": "7334", - "range_level": "34", - "attack_level": "34" - }, - { - "death_animation": "7864", - "name": "Forge regent", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7863", - "strength_level": "1", - "id": "7335", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7865" - }, - { - "examine": "This one will burn right through the net!", - "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": "60", - "safespot": null, - "lifepoints": "108", - "strength_level": "60", - "id": "7336", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "5230", - "name": "Spirit larupia", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "5228", - "strength_level": "1", - "id": "7337", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "5227" - }, - { - "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": "50", - "safespot": null, - "lifepoints": "81", - "strength_level": "50", - "id": "7338", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "It'll kill your enemies, and makes a great cup of tea.", - "melee_animation": "7879", - "range_animation": "422", - "magic_level": "70", - "respawn_delay": "50", - "defence_animation": "7878", - "weakness": "0", - "magic_animation": "422", - "death_animation": "7880", - "name": "Geyser titan", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "620", - "strength_level": "70", - "id": "7339", - "bonuses": "115,115,115,110,110,102,95,110,110,105,78,120,115,120,120", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "It'll kill your enemies, and makes a great cup of tea.", - "melee_animation": "7879", - "range_animation": "422", - "magic_level": "70", - "respawn_delay": "50", - "defence_animation": "7878", - "weakness": "0", - "slayer_exp": "0", - "magic_animation": "422", - "death_animation": "7880", - "name": "Geyser titan", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "620", - "strength_level": "70", - "id": "7340", - "bonuses": "115,115,115,110,110,102,95,110,110,105,78,120,115,120,120", - "range_level": "70", - "attack_level": "70" - }, - { - "combat_style": "1", - "melee_animation": "7980", - "respawn_delay": "0", - "defence_animation": "7981", - "death_animation": "7692", - "name": "Lava titan", - "defence_level": "1", - "safespot": null, - "lifepoints": "528", - "strength_level": "1", - "id": "7341", - "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Made of lava.", - "combat_style": "1", - "melee_animation": "7980", - "range_animation": "0", - "magic_level": "65", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "7979", - "name": "Lava titan", - "defence_level": "65", - "safespot": null, - "lifepoints": "115", - "strength_level": "65", - "id": "7342", - "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "The King of the Titans!", - "melee_animation": "8183", - "range_animation": "8183", - "magic_level": "70", - "respawn_delay": "15", - "defence_animation": "8185", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "8183", - "death_animation": "8184", - "name": "Steel titan", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "750", - "strength_level": "70", - "id": "7343", - "bonuses": "105,105,105,100,110,102,95,110,110,105,78,120,115,120,120", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "The King of the Titans!", - "melee_animation": "8183", - "range_animation": "8183", - "magic_level": "70", - "respawn_delay": "15", - "defence_animation": "8185", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "8183", - "death_animation": "8184", - "name": "Steel titan", - "defence_level": "70", - "poison_immune": "true", - "safespot": null, - "lifepoints": "750", - "strength_level": "70", - "id": "7344", - "bonuses": "105,105,105,100,110,102,95,110,110,105,78,120,115,120,120", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "Four fists can make quite an impression IN someone...", - "melee_animation": "8050", - "magic_level": "60", - "respawn_delay": "0", - "defence_animation": "8051", - "death_animation": "8052", - "name": "Obsidian golem", - "defence_level": "60", - "safespot": null, - "lifepoints": "10", - "strength_level": "60", - "id": "7345", - "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", - "range_level": "60", - "attack_level": "60" - }, - { - "examine": "Four fists can make quite an impression IN someone...", - "melee_animation": "8050", - "range_animation": "0", - "magic_level": "60", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8052", - "name": "Obsidian golem", - "defence_level": "60", - "safespot": null, - "lifepoints": "104", - "strength_level": "60", - "id": "7346", - "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "5990", - "name": "Talon beast", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "5989", - "strength_level": "1", - "id": "7347", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "5988" - }, - { - "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": "60", - "safespot": null, - "lifepoints": "110", - "strength_level": "60", - "id": "7348", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "7979", - "name": "Abyssal titan", - "defence_level": "1", - "safespot": null, - "lifepoints": "667", - "melee_animation": "7693", - "strength_level": "1", - "id": "7349", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7691" - }, - { - "examine": "Big", - "melee_animation": "7693", - "range_animation": "0", - "magic_level": "70", - "respawn_delay": "0", - "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": "7350", - "range_level": "70", - "attack_level": "70" - }, - { - "examine": "It torches.", - "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": "7352", - "range_level": "34", - "attack_level": "34" - }, - { - "death_animation": "7758", - "name": "Giant chinchompa", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "7755", - "strength_level": "1", - "id": "7353", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7753" - }, - { - "examine": "Looks a little...volatile.", - "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": "29", - "safespot": null, - "lifepoints": "41", - "strength_level": "29", - "id": "7354", - "range_level": "29", - "attack_level": "29" - }, - { - "examine": "Scorching!", - "melee_animation": "7834", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7833", - "name": "Fire titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7355", - "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "examine": "Scorching!", - "melee_animation": "7834", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7833", - "name": "Fire titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7356", - "bonuses": "64,45,76,82,208,84,221,231,179,89,113,23,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "examine": "Gathers rolling stones to bash people with.", - "melee_animation": "7844", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7843", - "name": "Moss titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7357", - "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "examine": "Gathers rolling stones to bash people with.", - "melee_animation": "7844", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7843", - "name": "Moss titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7358", - "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "examine": "Frosty the highly violent snowman.", - "melee_animation": "7845", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7846", - "name": "Ice titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7359", - "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "examine": "Frosty the highly violent snowman.", - "melee_animation": "7845", - "range_animation": "7834", - "attack_speed": "5", - "magic_level": "40", - "respawn_delay": "50", - "defence_animation": "7832", - "weakness": "10", - "slayer_exp": "0", - "magic_animation": "7834", - "death_animation": "7846", - "name": "Ice titan", - "defence_level": "40", - "poison_immune": "true", - "safespot": null, - "lifepoints": "476", - "strength_level": "30", - "id": "7360", - "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", - "range_level": "30", - "attack_level": "40" - }, - { - "combat_style": "1", - "melee_animation": "8257", - "attack_speed": "3", - "respawn_delay": "0", - "defence_animation": "8256", - "death_animation": "8258", - "name": "Spirit Tz-Kih", - "defence_level": "1", - "safespot": null, - "lifepoints": "62", - "strength_level": "1", - "id": "7361", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This bat burned down the belfry.", - "combat_style": "1", - "melee_animation": "8257", - "range_animation": "0", - "attack_speed": "3", - "magic_level": "22", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8258", - "name": "Spirit Tz-Kih", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "7362", - "range_level": "22", - "attack_level": "22" - }, - { - "start_gfx": "1367", - "melee_animation": "5228", - "respawn_delay": "0", - "defence_animation": "5227", - "death_animation": "5230", - "name": "Spirit graahk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7363", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Those spikes are pretty big!", - "start_gfx": "1367", - "melee_animation": "5229", - "range_animation": "0", - "magic_level": "50", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5230", - "name": "Spirit graahk", - "defence_level": "50", - "safespot": null, - "lifepoints": "81", - "strength_level": "50", - "id": "7364", - "range_level": "50", - "attack_level": "50" - }, - { - "start_gfx": "1365", - "melee_animation": "5228", - "respawn_delay": "0", - "defence_animation": "5227", - "death_animation": "5230", - "name": "Spirit kyatt", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7365", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Those teeth are pretty big!", - "start_gfx": "1365", - "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 kyatt", - "defence_level": "50", - "safespot": null, - "lifepoints": "81", - "strength_level": "50", - "id": "7366", - "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": "7368", - "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": "7371", - "range_level": "34", - "attack_level": "34" - }, - { - "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": "60", - "safespot": null, - "lifepoints": "100", - "strength_level": "60", - "id": "7373", - "range_level": "60", - "attack_level": "60" - }, - { - "death_animation": "7996", - "name": "Ravenous locust", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "7994", - "strength_level": "1", - "id": "7374", - "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7995" - }, - { - "examine": "He is an iron man!", - "combat_style": "1", - "melee_animation": "7946", - "magic_level": "65", - "respawn_delay": "0", - "defence_animation": "7948", - "weakness": "10", - "death_animation": "7947", - "name": "Iron titan", - "defence_level": "65", - "safespot": null, - "lifepoints": "694", - "strength_level": "65", - "id": "7375", - "bonuses": "120,130,100,109,80,120,102,90,103,80,100,108,105,66,90", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "He is an iron man!", - "combat_style": "1", - "melee_animation": "7946", - "combat_audio": "0,0,0", - "magic_level": "65", - "respawn_delay": "0", - "defence_animation": "7948", - "weakness": "10", - "slayer_exp": "0", - "death_animation": "7947", - "name": "Iron titan", - "defence_level": "65", - "safespot": null, - "lifepoints": "694", - "strength_level": "65", - "id": "7376", - "bonuses": "120,130,100,109,80,120,102,90,103,80,100,108,105,66,90", - "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": "7378", - "range_level": "46", - "attack_level": "46" - }, - { - "melee_animation": "8965", - "respawn_delay": "60", - "defence_animation": "8966", - "death_animation": "8967", - "name": "Elfinlocks", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "7379", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "8965", - "respawn_delay": "60", - "defence_animation": "8966", - "death_animation": "8967", - "name": "Missi Sissi", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "7380", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "2", - "melee_animation": "8955", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8957", - "death_animation": "8956", - "name": "Missi Sissi", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "7381", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_style": "2", - "melee_animation": "8955", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8957", - "death_animation": "8956", - "name": "Uberlass", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "id": "7382", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The vengeful spirit of one who died within Daemonheim.", - "melee_animation": "0", - "range_animation": "0", - "magic_level": "25", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "name": "Elisabeta", - "defence_level": "25", - "safespot": null, - "lifepoints": "7", - "strength_level": "25", - "id": "7398", - "aggressive": "true", - "range_level": "25", - "attack_level": "25" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "2705", - "range_animation": "2705", - "combat_audio": "3102,3104,3103", - "attack_speed": "5", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "2", - "strength_level": "1", - "id": "7417", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An impling manager: what a terrifying thought!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wigglewoo", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7425", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "This worker looks after the incubator.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Orangeowns", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7426", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "425,427,426", - "strength_level": "1", - "id": "7438", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "425,427,426", - "strength_level": "1", - "id": "7439", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "425,427,426", - "strength_level": "1", - "id": "7440", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "425,427,426", - "strength_level": "1", - "id": "7441", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He holds up passers by.", - "melee_animation": "386", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Eudav", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "8", - "id": "7443", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "name": "Fairtrade", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "attack_speed": "5", - "id": "7459", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Teapotspout", - "defence_level": "1", - "safespot": null, - "lifepoints": "75", - "strength_level": "1", - "attack_speed": "5", - "id": "7460", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "A popular dwarven delicacy.", - "melee_animation": "4933", - "range_animation": "4933", - "combat_audio": "703,705,704", - "attack_speed": "5", - "defence_animation": "4934", - "magic_animation": "4933", - "death_animation": "4935", - "name": "Rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "7461", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's bubbling, gross.", - "melee_animation": "9130", - "range_animation": "9130", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "9132", - "magic_animation": "9130", - "death_animation": "9131", - "name": "Hotwater", - "defence_level": "99", - "safespot": null, - "lifepoints": "120", - "strength_level": "99", - "id": "7462", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,45,20,0", - "range_level": "1", - "attack_level": "99" - }, - { - "examine": "It's bubbling, gross.", - "melee_animation": "9130", - "range_animation": "9130", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "9132", - "magic_animation": "9130", - "death_animation": "9131", - "name": "Hotwater", - "defence_level": "99", - "safespot": null, - "lifepoints": "120", - "strength_level": "99", - "id": "7463", - "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,45,20,0", - "range_level": "1", - "attack_level": "99" - }, - { - "examine": "His usual sunny disposition is not in evidence.", - "range_animation": "0", - "magic_level": "30", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "name": "Lady Seenit", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7475", - "range_level": "30", - "attack_level": "30" - }, - { - "name": "Berrybree", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "7476", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Stuffstuffer", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "7477", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Learking", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "7479", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Rachael", - "defence_level": "1", - "safespot": null, - "lifepoints": "70", - "strength_level": "1", - "id": "7480", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "You clearly can't live on treasure alone.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Cool Mom227", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7481", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A testament to the effect of greed.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Purepker895", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7482", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "He wanted loot", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Pkmaster0036", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7483", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Eternally looking for that big payday.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Cow1337killr", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7484", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "No more tea-breaks for this one.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Mathdude", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7485", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Even in death you can smell his feet.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "Mathdude", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7486", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "An anatomist's dream.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "1337sp34kr", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7487", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A hand seems to be gripping his spine through his chest. Ouch.", - "slayer_task": "75", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "5491", - "name": "1337sp34kr", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7488", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A desert dweller taken to banditry.", - "melee_animation": "9705", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Sarah Domin", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7492", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Calls himself an archaeologist.", - "melee_animation": "9715", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Sarah Domin", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7493", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "He hasn't found much of use", - "melee_animation": "9705", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Sue Spammers", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7494", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "An amateur historian with added greed.", - "melee_animation": "9715", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Killerwail", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7495", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "It all started with her love of genealogy.", - "melee_animation": "9705", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Wise Old Man", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7496", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A rocky horror.", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Sabre-toothed kyatt", - "defence_level": "27", - "safespot": null, - "lifepoints": "38", - "strength_level": "27", - "id": "7497", - "range_level": "1", - "attack_level": "27" - }, - { - "examine": "This dung beetle has mistaken you for its staple diet.", - "slayer_task": "70", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Cerulean twitch", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7500", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "It's looking unwell - probably something it ate.", - "slayer_task": "70", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Abone", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7501", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "Once a valuable contributor to the ecosystem.", - "slayer_task": "70", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Mythmaster", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7502", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "This is what happens if you play with your food.", - "slayer_task": "70", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Donkey Wrong", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "7503", - "aggressive": "true", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "He likes a good fight", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Creapantic", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7504", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes a good fight", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Frondlike", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7505", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His favourite must be winning.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Happy Spud", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7506", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Blood-thirsty and enthusiastic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Nobodyhere", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7507", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bluehairlass", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7508", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His favourite must be winning.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ilikekebabs", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7510", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "His favourite must be winning.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Trunka Lex", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7511", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Val Razz", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7512", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abstractclas", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7513", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bloodthirsty and enthusiastic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bigbluebox", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7514", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bloodthirsty and enthusiastic.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Funorbrox", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7515", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's asleep.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Morrisnorris", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7518", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's propping the bar up. Or is it the other way round?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Matt Blitzer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7519", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He demands to have some booze.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ketchuppl0x", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7520", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "395", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Redheadmonky", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "7528", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "0", - "examine": "It's an NPC.", - "name": "3sacrowd", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7532", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "90", - "magic_level": "70", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "90", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7551", - "bonuses": "200,200,200,100,100,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "50", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "90", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7552", - "bonuses": "200,200,200,200,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "attack_speed": "5", - "magic_level": "90", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "90", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7553", - "bonuses": "200,200,100,200,100,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not somewhere I want to go...", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "90", - "respawn_delay": "125", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Portal", - "defence_level": "90", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7554", - "bonuses": "100,100,200,200,100,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7555", - "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "attack_speed": "5", - "magic_level": "15", - "respawn_delay": "125", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7556", - "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7557", - "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Void Knight will soon weaken the shield.", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "30", - "respawn_delay": "125", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Portal", - "defence_level": "30", - "safespot": null, - "lifepoints": "250", - "strength_level": "1", - "id": "7558", - "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "323,326,325", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brawler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7559", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lostme", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7560", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Chiercat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7561", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skydischarge", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Agplus", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7563", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Distantthin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7564", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Allmarshes", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7565", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Explosive67", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7566", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks after your Farming tools.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Alpha1beta", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Enjoys locking up animals in small pens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Solltalk", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7570", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Enjoys locking up animals in small pens.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hm Val", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7571", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks like the type of guy who would mind monkeys.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizzydumped", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7572", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks like the type of guy who would mind monkeys.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Oddskater", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7573", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Poledragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "7580", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes inflicting pain.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Al Truism", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Don of penguins.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ohhhhdude", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7583", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A penguin pushing paper", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "845,848,846", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Torcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7585", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A penguin pushing paper", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "845,848,846", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Torcher", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7586", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's a little rough around the edges.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "766,768,767", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Shifter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7593", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He has unbelievable strength!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7605", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7606", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7607", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7608", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7609", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7614", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7615", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7616", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7617", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7618", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7619", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7620", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7621", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7626", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7627", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7628", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7629", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7631", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7632", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7634", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7635", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Ew it's still alive.", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "9125", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "9127", - "weakness": "9", - "magic_animation": "9125", - "death_animation": "9126", - "name": "Skeletal hand", - "defence_level": "85", - "safespot": null, - "lifepoints": "90", - "strength_level": "85", - "id": "7640", - "aggressive": "true", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "85" - }, - { - "examine": "Ew it's still alive.", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "9125", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "9127", - "weakness": "9", - "magic_animation": "9125", - "death_animation": "9126", - "name": "Zombie hand", - "defence_level": "75", - "safespot": null, - "lifepoints": "90", - "strength_level": "75", - "id": "7641", - "aggressive": "true", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "range_level": "1", - "attack_level": "75" - }, - { - "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", - "melee_animation": "9102", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "206", - "magic_animation": "0", - "death_animation": "9131", - "name": "Mutated bloodveld", - "defence_level": "56", - "safespot": null, - "lifepoints": "112", - "strength_level": "56", - "id": "7642", - "aggressive": "true", - "range_level": "1", - "attack_level": "56" - }, - { - "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", - "melee_animation": "9102", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "slayer_exp": "206", - "magic_animation": "0", - "death_animation": "9131", - "name": "Mutated bloodveld", - "defence_level": "58", - "safespot": null, - "lifepoints": "116", - "strength_level": "58", - "id": "7643", - "aggressive": "true", - "range_level": "1", - "attack_level": "58" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7644", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7645", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7646", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7647", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7648", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7649", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7650", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Zaromark Sliver", - "defence_level": "1", - "safespot": null, - "lifepoints": "88", - "strength_level": "1", - "id": "7651", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7654", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7655", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7656", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7657", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7658", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7659", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7660", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Fistandantilus", - "defence_level": "1", - "safespot": null, - "lifepoints": "65", - "strength_level": "1", - "id": "7661", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7682", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7683", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7691", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7692", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7693", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7694", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7695", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7696", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7697", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7698", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7699", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7700", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7701", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7702", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "95", - "strength_level": "1", - "attack_speed": "6", - "id": "7703", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "attack_speed": "6", - "id": "7704", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "attack_speed": "6", - "id": "7705", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Vyrewatch", - "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "attack_speed": "6", - "id": "7706", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "Looks like a big ugly dog.", - "slayer_task": "27", - "melee_animation": "6565", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6564", - "name": "Temple guardian", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "7711", - "range_level": "1", - "attack_level": "24" - }, - { - "melee_animation": "8080", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8084", - "death_animation": "8078", - "name": "Baby icefiend", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "7713", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A small ice demon.", - "slayer_task": "46", - "melee_animation": "8080", - "range_animation": "0", - "combat_audio": "531,533,532", - "attack_speed": "5", - "magic_level": "25", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "8078", - "name": "Icefiend", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "7714", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The icefiend seems to be melting.", - "slayer_task": "46", - "melee_animation": "8080", - "range_animation": "0", - "combat_audio": "531,533,532", - "attack_speed": "5", - "magic_level": "25", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "8078", - "name": "Icefiend", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "7715", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The icefiend seems to be melting.", - "slayer_task": "46", - "melee_animation": "8080", - "range_animation": "0", - "attack_speed": "5", - "magic_level": "25", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "8078", - "name": "null", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "7716", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A holy man.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Monk", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "7727", - "range_level": "1", - "attack_level": "12" - }, - { - "melee_animation": "8080", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8084", - "death_animation": "8078", - "name": "Baby icefiend", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "strength_level": "1", - "id": "7736", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Is it full of rats?", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jiggling crate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7740", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A warrior who has been long forgotten.", - "range_animation": "0", - "magic_level": "25", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Three little kittens", - "defence_level": "25", - "safespot": null, - "lifepoints": "5", - "strength_level": "30", - "id": "7741", - "aggressive": "true", - "range_level": "25", - "attack_level": "30" - }, - { - "examine": "It looks upset.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "TzHaar-Xil-Tog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7747", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A TzHaar librarian.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "TzHaar-Mej-Lor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7752", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A TzHaar-Hur stamping stone tablets.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Library assistant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7756", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "7767", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "7768", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "Looks like a craftsman of some kind.", - "melee_animation": "9286", - "range_animation": "9286", - "attack_speed": "5", - "magic_level": "50", - "defence_animation": "9287", - "magic_animation": "9286", - "death_animation": "9288", - "name": "TzHaar-Hur", - "defence_level": "50", - "safespot": null, - "lifepoints": "80", - "strength_level": "50", - "id": "7769", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "A monster made of magma.", - "combat_style": "1", - "melee_animation": "9337", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "9340", - "name": "Lava monster", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "28", - "id": "7772", - "aggressive": "true", - "range_level": "38", - "attack_level": "28" - }, - { - "examine": "A monster like many others", - "combat_style": "2", - "melee_animation": "9341", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "9344", - "name": "Fire monster", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "28", - "id": "7773", - "aggressive": "true", - "range_level": "38", - "attack_level": "28" - }, - { - "examine": "A big, scary hand! ", - "melee_animation": "1802", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "1803", - "slayer_exp": "105", - "death_animation": "1804", - "name": "Wall Beast", - "defence_level": "38", - "movement_radius": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "38", - "id": "7823", - "aggressive": "true", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "A melee training dummy", - "melee_animation": "94", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "97", - "name": "Melee dummy", - "defence_level": "1", - "safespot": null, - "lifepoints": "15", - "strength_level": "1", - "id": "7891", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A man of his craft.", - "name": "Smelting Tutor", - "defence_level": "1", - "movement_radius": "5", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7958", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master of his craft.", - "name": "Smelting Tutor", - "defence_level": "1", - "movement_radius": "5", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7959", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks like a professional explorer.", - "name": "Explorer Jack", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7969", - "range_level": "1", - "attack_level": "1" - }, - { - "agg_radius": "64", - "examine": "A vision of supernatural horror.", - "melee_animation": "10058", - "attack_speed": "6", - "magic_level": "350", - "respawn_delay": "80", - "defence_animation": "10386", - "magic_animation": "10053", - "death_animation": "10385", - "name": "Corporeal Beast", - "defence_level": "310", - "poison_immune": "true", - "movement_radius": "64", - "safespot": null, - "lifepoints": "2000", - "strength_level": "320", - "id": "8133", - "aggressive": "true", - "bonuses": "50,50,50,0,0,25,200,100,150,230,0,0,0,0,0", - "range_level": "150", - "attack_level": "320" - }, - { - "examine": "He sure looks grave.", - "melee_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "6", - "respawn_delay": "35", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", - "name": "Armoured zombie", - "defence_level": "50", - "safespot": null, - "lifepoints": "62", - "strength_level": "50", - "id": "8149", - "aggressive": "true", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "This is a rotten one.", - "melee_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "6", - "respawn_delay": "35", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", - "name": "Armoured zombie", - "defence_level": "50", - "safespot": null, - "lifepoints": "62", - "strength_level": "50", - "id": "8150", - "aggressive": "true", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "It hasn't quite gotten around to dying.", - "melee_animation": "5571", - "combat_audio": "931,923,922", - "attack_speed": "6", - "respawn_delay": "35", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", - "name": "Armoured zombie", - "defence_level": "50", - "safespot": null, - "lifepoints": "62", - "strength_level": "50", - "id": "8151", - "aggressive": "true", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "50", - "attack_level": "50" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8349", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8350", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8351", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8352", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8353", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8354", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8355", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8356", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8357", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8358", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8359", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "2", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8360", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8361", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8362", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "1", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8363", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "0", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8364", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "2", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8365", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", - "combat_style": "0", - "melee_animation": "10922", - "range_animation": "10919", - "attack_speed": "6", - "magic_level": "85", - "protect_style": "1", - "respawn_delay": "100", - "defence_animation": "10923", - "slayer_exp": "136", - "magic_animation": "10918", - "death_animation": "10924", - "name": "Tormented demon", - "defence_level": "85", - "poison_immune": "true", - "safespot": "true", - "lifepoints": "326", - "strength_level": "85", - "id": "8366", - "aggressive": "true", - "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", - "range_level": "85", - "attack_level": "85" - }, - { - "examine": "Flappy bird.", - "name": "Gull", - "water_npc": "true", - "id": "1179" - }, - { - "examine": "Flappy bird.", - "name": "Gull", - "water_npc": "true", - "id": "450" - }, - { - "examine": "Flappy bird.", - "name": "Gull", - "water_npc": "true", - "id": "451" - }, - { - "examine": "Tough-looking combat type.", - "name": "Mubariz", - "id": "957" - }, - { - "examine": "Looks kinda bored.", - "name": "Fadli", - "id": "958" - }, - { - "examine": "Trained to deal with all sorts of injuries.", - "name": "A'abla", - "id": "959" - }, - { - "examine": "Wow! She's made a statement with that hair!", - "name": "Sabreen", - "id": "960" - }, - { - "examine": "Has the messy job of putting players back together again.", - "name": "Jaraah", - "id": "962" - }, - { - "examine": "Battle-scarred.", - "name": "Zahwa", - "id": "963" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Ima", - "id": "964" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Sabeil", - "id": "965" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Jadid", - "id": "966" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Dalal", - "id": "967" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Afrah", - "id": "968" - }, - { - "examine": "A citizen of Al Kharid", - "name": "Jeed", - "id": "969" - }, - { - "examine": "He smells funny.", - "name": "Diango", - "id": "970" - }, - { - "examine": "Shopkeeper.", - "name": "Chadwell", - "id": "971" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "972" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "973" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "974" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "975" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "976" - }, - { - "examine": "This dwarf looks intoxicated.", - "name": "Kamen", - "id": "996" - }, - { - "examine": "A dwarven maker of gauntlets.", - "name": "Klank", - "id": "995" - }, - { - "examine": "One of King Tyras's men.", - "name": "Tyras guard", - "id": "1206" - }, - { - "examine": "The cave guide.", - "name": "Koftik", - "id": "1209" - }, - { - "examine": "One of King Lathas' messengers.", - "name": "Kings messenger", - "id": "1210" - }, - { - "examine": "Mysterious swamp lights...", - "name": "Will o' the wisp", - "id": "1212" - }, - { - "examine": "He's washing his clothes in the lake.", - "name": "Tegid", - "id": "1213" - }, - { - "examine": "A plant.", - "name": "Thistle", - "id": "1214" - }, - { - "examine": "What a colourful bunch of parrots!", - "name": "Parrots", - "id": "1215" - }, - { - "examine": "Rather dense and soppy looking.", - "name": "Romeo", - "id": "639" - }, - { - "examine": "Newspaper seller.", - "name": "Benny", - "id": "5925" - }, - { - "examine": "One of Gertrude's Sons.", - "name": "Wilough", - "id": "783" - }, - { - "examine": "An old gypsy lady.", - "name": "Gypsy Aris", - "id": "882" - }, - { - "examine": "Interesting assortment of clothes on offer...", - "name": "Thessalia", - "id": "548" - }, - { - "examine": "Sells superior staffs.", - "name": "Zaff", - "id": "546" - }, - { - "examine": "A retired vampyre hunter.", - "name": "Dr Harlow", - "id": "756" - }, - { - "examine": "I could get a beer from him.", - "name": "Bartender", - "id": "733" - }, - { - "examine": "Director of the Grand Exchange.", - "id": "6522" - }, - { - "examine": "He can look after my money. Good with money.", - "id": "6535" - }, - { - "examine": "She can look after my money. Good with money.", - "id": "6532" - }, - { - "examine": "There to help me make my bids.", - "id": "6530" - }, - { - "examine": "There to help me make my bids.", - "id": "6528" - }, - { - "examine": "He can look after my money. Good with money.", - "id": "6534" - }, - { - "examine": "She can look after my money. Good with money.", - "id": "6533" - }, - { - "examine": "There to help me make my bids.", - "id": "6531" - }, - { - "examine": "There to help me make my bids.", - "id": "6529" - }, - { - "examine": "She looks very worried about something.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Caroline", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "696", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very good sailor.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Holgart", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "698", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Smells a bit fishy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ezekial Lovecraft", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4856", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A (semi) retired member of the Temple Knights.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Col. O'Niall", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4872", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There's something fishy about him.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mayor Hobb", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4874", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fresh-faced and innocent priest.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Brother Maledict", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4878", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A down on his luck fisherman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witchaven villager", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4883", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A down on her luck fisherman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witchaven villager", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4885", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A down on his luck fisherman.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witchaven villager", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4887", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A villager named Jeb.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jeb", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "4895", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A little girl. She looks terrified.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Kimberly", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7168", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Stinky! Poor guy.", - "name": "Yeti", - "id": "130" - }, - { - "examine": "A regal cat with an evil glint in its eye.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "HRH Hrafn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5479", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Harder than the rocks he sells.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Hring Hring", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Jatizso's fishmonger.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Flossi Dalksson", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5484", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A guy that will sell you armour.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Raum Urda-Stein", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Jatizso's armour merchant.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Skuli Myrka", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5486", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rough-looking chef.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Keepa Kettilon", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5487", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Jatizso's many citizens.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Freygerd", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5493", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Jatizso's many citizens.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Lensa", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5494", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A happy, friendly landlady.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Vanligga Gastfrihet", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5495", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Jatizso's many citizens.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Sassilik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5496", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A bedraggled-looking tramp.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Eric", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5499", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Mina guards the mine entrance.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Gruva Patrull", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5500", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dwarven trader from Keldagrim.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Brendt", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5501", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hungry-looking rabbit.", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7126", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A gnome farmer.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7129", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of the Burgher's Protectors", - "death_animation": "836", - "name": "Honour Guard", - "melee_animation": "395", - "id": "5515", - "defence_animation": "404" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "4", - "respawn_delay": "70", - "defence_animation": "5489", - "weakness": "8", - "slayer_exp": "29", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "29", - "strength_level": "18", - "id": "90", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "4", - "defence_animation": "5489", - "weakness": "8", - "slayer_exp": "24", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "91", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "4", - "defence_animation": "5489", - "weakness": "8", - "slayer_exp": "17", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "24", - "safespot": null, - "lifepoints": "17", - "strength_level": "24", - "id": "92", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "4", - "defence_animation": "5489", - "weakness": "8", - "slayer_exp": "59", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "36", - "safespot": null, - "lifepoints": "59", - "strength_level": "35", - "id": "93", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Rogueish.", - "melee_animation": "386", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Rogue", - "defence_level": "7", - "safespot": null, - "lifepoints": "10", - "strength_level": "7", - "aggressive": "false", - "id": "187", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "It rattles when it moves.", - "slayer_task": "75", - "combat_style": "2", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "magic_level": "5", - "respawn_delay": "60", - "defence_animation": "5489", - "weakness": "3", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "5", - "safespot": null, - "lifepoints": "14", - "strength_level": "3", - "id": "459", - "aggressive": "true", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "It looks just a bit... underfed.", - "slayer_task": "75", - "melee_animation": "5519", - "range_animation": "5519", - "combat_audio": "774,775,777", - "attack_speed": "4", - "magic_level": "110", - "respawn_delay": "100", - "defence_animation": "5520", - "weakness": "8", - "slayer_exp": "110", - "magic_animation": "5519", - "death_animation": "5521", - "name": "Skeleton", - "defence_level": "110", - "safespot": null, - "lifepoints": "77", - "strength_level": "90", - "id": "1471", - "aggressive": "true", - "bonuses": "50,50,50,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "180" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "5487", - "combat_audio": "774,775,777", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "5513", - "weakness": "8", - "magic_animation": "5487", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "72", - "safespot": null, - "lifepoints": "51", - "strength_level": "72", - "id": "2036", - "aggressive": "true", - "bonuses": "0,0,0,0,0,5,5,-5,0,5,0,0,0,0,0", - "range_level": "1", - "attack_level": "72" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "5487", - "combat_audio": "774,775,777", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "20", - "defence_animation": "5513", - "weakness": "8", - "magic_animation": "5487", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "72", - "safespot": null, - "lifepoints": "51", - "strength_level": "72", - "id": "2037", - "aggressive": "true", - "bonuses": "0,0,0,0,0,5,5,-5,0,5,0,0,0,0,0", - "range_level": "1", - "attack_level": "72" - }, - { - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "774,775,777", - "strength_level": "1", - "id": "2715", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Skeleton mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "774,775,777", - "strength_level": "1", - "id": "2717", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "774,775,777", - "strength_level": "1", - "id": "3151", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of Gielinor's many citizens, currently incapacitated by alcohol.", - "melee_animation": "422", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Drunken man", - "defence_level": "4", - "safespot": null, - "lifepoints": "5", - "strength_level": "4", - "id": "3222", - "range_level": "1", - "attack_level": "4" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "8", - "safespot": null, - "lifepoints": "11", - "strength_level": "8", - "id": "3291", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "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", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "70", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "29", - "strength_level": "18", - "id": "3581", - "aggressive": "true", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Could use a good meal.", - "slayer_task": "75", - "melee_animation": "6128", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "3697", - "range_level": "1", - "attack_level": "17" - }, - { - "melee_animation": "400", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "3698", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "400", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "strength_level": "1", - "id": "3699", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems a little underweight.", - "slayer_task": "75", - "melee_animation": "6128", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "3700", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "He seems a little underweight.", - "slayer_task": "75", - "melee_animation": "6128", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "20", - "id": "3701", - "range_level": "1", - "attack_level": "20" - }, - { - "melee_animation": "395", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "425", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "3702", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems a little underweight.", - "slayer_task": "75", - "melee_animation": "6128", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3703", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "He seems a little underweight.", - "slayer_task": "75", - "melee_animation": "6128", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton fremennik", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "3704", - "range_level": "1", - "attack_level": "22" - }, - { - "name": "Skeleton fremennik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "774,775,777", - "strength_level": "1", - "id": "3705", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "68", - "strength_level": "1", - "id": "4384", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "4385", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "strength_level": "1", - "id": "4386", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4384", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4532", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "combat_audio": "774,775,777", - "melee_animation": "5485", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "70", - "defence_animation": "5513", - "weakness": "8", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "29", - "strength_level": "18", - "id": "5332", - "aggressive": "true", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "range_animation": "0", - "combat_audio": "774,775,777", - "melee_animation": "5485", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "70", - "defence_animation": "5513", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "29", - "strength_level": "18", - "id": "5333", - "aggressive": "true", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "range_animation": "0", - "combat_audio": "774,775,777", - "melee_animation": "5485", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "70", - "defence_animation": "5513", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "17", - "safespot": null, - "lifepoints": "29", - "strength_level": "18", - "id": "5334", - "aggressive": "true", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Could do with gaining a few pounds.", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5489", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "11", - "id": "5335", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Could do with gaining a few pounds.", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5489", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "11", - "id": "5336", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "5489", - "weakness": "8", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "24", - "safespot": null, - "lifepoints": "17", - "strength_level": "24", - "id": "5337", - "aggressive": "true", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5489", - "weakness": "8", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "23", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "5338", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5489", - "weakness": "8", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "23", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "5339", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "5485", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5489", - "weakness": "8", - "magic_animation": "5485", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "23", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "5340", - "range_level": "1", - "attack_level": "12" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5499", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5503", - "name": "Skeleton", - "defence_level": "42", - "safespot": null, - "lifepoints": "60", - "strength_level": "42", - "id": "5341", - "aggressive": "true", - "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "34", - "safespot": null, - "lifepoints": "48", - "strength_level": "34", - "id": "5365", - "aggressive": "true", - "range_level": "1", - "attack_level": "34" - }, - { - "examine": "Could do with gaining a few pounds.", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "5366", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "strength_level": "1", - "id": "5367", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "85", - "strength_level": "1", - "id": "5368", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "62", - "safespot": null, - "lifepoints": "88", - "strength_level": "62", - "id": "5381", - "aggressive": "true", - "range_level": "1", - "attack_level": "62" - }, - { - "examine": "A skeleton in a dress!", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "magic_level": "57", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "5", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "57", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", - "id": "5385", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Achingly thin.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5386", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Look: another skeleton.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5387", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "That skeleton's grinning at me.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "50", - "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "5388", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" - }, - { - "examine": "He needs a tan.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "51", - "safespot": null, - "lifepoints": "72", - "strength_level": "51", - "id": "5389", - "aggressive": "true", - "range_level": "1", - "attack_level": "51" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "30", - "safespot": null, - "lifepoints": "420", - "strength_level": "36", - "id": "5390", - "aggressive": "true", - "range_level": "1", - "attack_level": "36" - }, - { - "examine": "He obviously hasn't realised he's dead.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5391", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Put some meat on those bones!", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "52", - "safespot": null, - "lifepoints": "74", - "strength_level": "52", - "id": "5392", - "aggressive": "true", - "range_level": "1", - "attack_level": "52" - }, - { - "examine": "An angry skeleton.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", - "id": "5411", - "aggressive": "true", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Cross bones.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "57", - "safespot": null, - "lifepoints": "81", - "strength_level": "57", - "id": "5412", - "aggressive": "true", - "range_level": "1", - "attack_level": "57" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5485", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "5422", - "aggressive": "true", - "range_level": "1", - "attack_level": "63" - }, - { - "examine": "Ey.", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "6091", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "6092", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", - "melee_animation": "5487", - "range_animation": "0", - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "9", - "safespot": null, - "lifepoints": "12", - "strength_level": "9", - "id": "6093", - "aggressive": "true", - "range_level": "1", - "attack_level": "9" - }, - { - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton hero", - "defence_level": "53", - "safespot": null, - "lifepoints": "75", - "strength_level": "53", - "id": "6103", - "aggressive": "true", - "range_level": "1", - "attack_level": "53" - }, - { - "examine": "An ex-barbarian, willing to make you ex too.", - "slayer_task": "75", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton brute", - "defence_level": "63", - "safespot": null, - "lifepoints": "90", - "strength_level": "63", - "id": "6104", - "aggressive": "true", - "range_level": "1", - "attack_level": "63" - }, - { - "examine": "He's heartless.", - "slayer_task": "75", - "melee_animation": "2067", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton warlord", - "defence_level": "65", - "safespot": null, - "lifepoints": "92", - "strength_level": "65", - "id": "6105", - "aggressive": "true", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "He's less heavy now.", - "slayer_task": "75", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton heavy", - "defence_level": "64", - "safespot": null, - "lifepoints": "91", - "strength_level": "64", - "id": "6106", - "aggressive": "true", - "range_level": "1", - "attack_level": "64" - }, - { - "examine": "Floats like an anvil, hits like a hammer.", - "slayer_task": "75", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "774,775,777", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Skeleton thug", - "defence_level": "61", - "safespot": null, - "lifepoints": "87", - "strength_level": "61", - "id": "6107", - "aggressive": "true", - "range_level": "1", - "attack_level": "61" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "97", - "strength_level": "1", - "id": "6764", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "108", - "strength_level": "1", - "id": "6765", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "120", - "strength_level": "1", - "id": "6766", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "115", - "strength_level": "1", - "id": "6767", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "combat_audio": "774,775,777", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "5513", - "death_animation": "5491", - "name": "Skeleton", - "defence_level": "1", - "safespot": null, - "lifepoints": "140", - "strength_level": "1", - "id": "6768", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hungry-looking rabbit.", - "melee_animation": "0", - "range_animation": "0", - "respawn_delay": "50", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rabbit", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7127", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A rich landlord, fat and jolly.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Inn Keeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2177", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes to cook with mushrooms.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fairy chef", - "defence_level": "1", - "safespot": null, - "movement_radius": "2", - "lifepoints": "10", - "strength_level": "1", - "id": "3322", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4384", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4527", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4384", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "95", - "safespot": null, - "lifepoints": "105", - "strength_level": "95", - "id": "4528", - "aggressive": "true", - "clue_level": "2", - "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", - "range_level": "1", - "attack_level": "95" - }, - { - "examine": "A large ice troll.", - "melee_animation": "4332", - "range_animation": "0", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice troll grunt", - "defence_level": "60", - "safespot": null, - "lifepoints": "80", - "strength_level": "100", - "id": "5524", - "aggressive": "true", - "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "7247" - }, - { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "6962" - }, - { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "7245" - }, - { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "7248" - }, - { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "6963" - }, - { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "7246" - }, - { - "examine": "One proud little puppy.", - "name": "Bulldog puppy", - "id": "7260" - }, - { - "examine": "One proud little puppy.", - "name": "Bulldog puppy", - "id": "7259" - }, - { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "6968" - }, - { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "7258" - }, - { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "7257" - }, - { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "6964" - }, - { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "7249" - }, - { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "7251" - }, - { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "7252" - }, - { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "7250" - }, - { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "6965" - }, - { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "6960" - }, - { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "7241" - }, - { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "7243" - }, - { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "6961" - }, - { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "7242" - }, - { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "7244" - }, - { - "examine": "It's lively and energetic.", - "name": "Terrier puppy", - "id": "6958" - }, - { - "examine": "A hyperactive little pup.", - "name": "Terrier puppy", - "id": "7237" - }, - { - "examine": "Such a brave doggy.", - "name": "Terrier puppy", - "id": "7239" - }, - { - "examine": "It's lively and energetic.", - "name": "Terrier", - "id": "6959" - }, - { - "examine": "A hyperactive little pup.", - "name": "Terrier", - "id": "7238" - }, - { - "examine": "Such a brave doggy.", - "name": "Terrier", - "id": "7240" - }, - { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "7253" - }, - { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "6966" - }, - { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "7255" - }, - { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "6967" - }, - { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "7254" - }, - { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "7256" - }, - { - "examine": "The sandwich lady.", - "name": "Sandwich lady", - "id": "3117" - }, - { - "examine": "A holy man.", - "name": "Drezel", - "id": "1049" - }, - { - "examine": "A holy man.", - "name": "Drezel", - "id": "7690" - }, - { - "examine": "A holy man.", - "name": "Drezel", - "id": "7707" - }, - { - "examine": "A slightly nervous guard.", - "name": "Ulizius", - "id": "1054" - }, - { - "examine": "A local holy man who supports the people trying to get though Mort Myre.", - "name": "Hiylik Myna", - "id": "1514" - }, - { - "examine": "The animated spirit of a soul not at rest.", - "name": "Filiman Tarlock", - "id": "1050" - }, - { - "examine": "The animated spirit of a soul not at rest.", - "name": "Nature Spirit", - "id": "1051" - }, - { - "examine": "I can see fish swimming in the water.", - "name": "Fishing spot", - "id": "323" - }, - { - "examine": "I can see fish swimming in the water.", - "name": "Fishing spot", - "id": "1176" - }, - { - "examine": "Nice hat.", - "name": "Thormac", - "id": "389" - }, - { - "examine": "An evil priest.", - "name": "Necrovarus", - "id": "1684" - }, - { - "examine": "He looks dangerous.", - "name": "Vannaka", - "id": "1597" - }, - { - "examine": "A soggy, smelly Fremennik.", - "name": "Olaf Hardson", - "id": "2621" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Elstan", - "id": "2323" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Dantaera", - "id": "2324" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Kragen", - "id": "2325" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Lyra", - "id": "2326" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Francis", - "id": "2327" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Rhonen", - "id": "2334" - }, - { - "examine": "Banks gnomish things.", - "name": "Gnome banker", - "id": "166" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Ellena", - "id": "2331" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Alain", - "id": "2339" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Dreven", - "id": "2335" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Frizzy Skernip", - "id": "4560" - }, - { - "examine": "She sells farming equipment.", - "name": "Sarah", - "id": "2304" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Garth", - "id": "2330" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Selena", - "id": "2332" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Rhazien", - "id": "2337" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Torrell", - "id": "2338" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Heskel", - "id": "2340" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Treznor", - "id": "2341" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Bolongo", - "id": "2343" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Gileth", - "id": "2344" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Amaethwyr", - "id": "2860" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Praistan Ebola", - "id": "4562" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Imiago", - "id": "8041" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Prissy Scilla", - "id": "1037" - }, - { - "examine": "Looks after your Farming tools.", - "name": "Teclyn", - "id": "2861" - }, - { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Yulf Squecks", - "id": "4561" - }, - { - "examine": "The head farmer, apparently.", - "name": "Farmer Blinkin", - "id": "7131" - }, - { - "examine": "The lady of the pet shop.", - "name": "Pet shop owner", - "id": "6750" - }, - { - "examine": "Looks after the Burthorpe vinery.", - "name": "Bernald", - "id": "2580" - }, - { - "examine": "An old druid.", - "name": "Sanfew", - "id": "454" - }, - { - "examine": "He runs the Herblore Shop.", - "name": "Jatix", - "id": "587" - }, - { - "examine": "A wise druid.", - "name": "Kaqemeex", - "id": "455" - }, - { - "examine": "He's a very important druid.", - "name": "Pikkupstix", - "id": "6971" - }, - { - "examine": "Ironically, makes a living from swords.", - "name": "Gaius", - "id": "586" - }, - { - "examine": "A gnarly old man.", - "name": "Sylas", - "id": "5987" - }, - { - "examine": "A terrier puppy.", - "name": "Terrier", - "id": "6894" - }, - { - "examine": "A labrador puppy.", - "name": "Labrador", - "id": "6895" - }, - { - "examine": "Dwarvish.", - "name": "Thurgo", - "id": "604" - }, - { - "examine": "Get useful information from this guy.", - "name": "Town crier", - "id": "6137" - }, - { - "examine": "The mace salesman.", - "name": "Flynn", - "id": "580" - }, - { - "examine": "Works in the Rising Sun.", - "name": "Kaylee", - "id": "3217" - }, - { - "examine": "Works in the Rising Sun.", - "name": "Emily", - "id": "736" - }, - { - "examine": "Nice eyes.", - "name": "Cassie", - "id": "577" - }, - { - "examine": "Chief Herald of Falador.", - "name": "Sir Renitee", - "id": "4249" - }, - { - "examine": "An elderly White Knight.", - "name": "Sir Vyvin", - "id": "605" - }, - { - "examine": "Head of recruitment for the Temple Knights.", - "name": "Sir Tiffy Cashien", - "id": "2290" - }, - { - "examine": "An armourer.", - "name": "Wayne", - "id": "581" - }, - { - "examine": "A master of necromancy!", - "name": "Malignius Mortifer", - "id": "2713" - }, - { - "examine": "She seems like a nice sort of person.", - "name": "Betty", - "id": "583" - }, - { - "examine": "Loves his gold!", - "name": "Grum", - "id": "556" - }, - { - "examine": "Unkempt and a bit smelly.", - "name": "Bellemorde", - "id": "2942" - }, - { - "examine": "A Fishing expert.", - "name": "Gerrant", - "id": "558" - }, - { - "examine": "Likes his food to be kept fresh.", - "name": "Wydin", - "id": "557" - }, - { - "examine": "A sea bird.", - "name": "Gull", - "id": "2726" - }, - { - "examine": "An old sea dog.", - "name": "Captain Tobias", - "id": "376" - }, - { - "examine": "A young sailor.", - "name": "Klarense", - "id": "744" - }, - { - "examine": "An expert on axes.", - "name": "Brian", - "id": "559" - }, - { - "examine": "I could get a beer from him.", - "name": "Bartender", - "id": "734" - }, - { - "examine": "She is a seed merchant.", - "name": "Olivia", - "id": "2572" - }, - { - "examine": "Get useful information from this guy.", - "name": "Town crier", - "id": "6136" - }, - { - "examine": "Looks like he could do with a sip of Amontillado.", - "name": "Fortunato", - "id": "3671" - }, - { - "examine": "A young sailor.", - "name": "Seaman Thresnor", - "id": "378" - }, - { - "examine": "A young sailor.", - "name": "Seaman Lorris", - "id": "377" - }, - { - "examine": "A venerable and rich sage.", - "name": "Wise Old Man", - "id": "2253" - }, - { - "examine": "She comes from Al-Kharid.", + "id": "915", "name": "Leela", - "id": "915" + "examine": "She comes from Al-Kharid." }, { - "examine": "An old sailor.", - "name": "Ned", - "id": "743" - }, - { - "examine": "He looks scared.", - "name": "Morgan", - "id": "755" - }, - { - "examine": "A witch.", - "name": "Aggie", - "id": "922" - }, - { - "examine": "Bacon-in-training.", - "name": "Piglet", - "id": "2243" - }, - { - "examine": "An infamous bandit.", - "name": "Lady Keli", - "id": "919" - }, - { - "examine": "A young prince.", - "name": "Prince Ali", - "id": "920" - }, - { - "examine": "A gnome mage on sabbatical.", - "name": "Professor Onglewip", - "id": "4585" - }, - { - "examine": "Chief of research at Wizards Tower.", - "name": "Sedridor", - "id": "300" - }, - { - "examine": "An old wizard.", - "name": "Traiborn", - "id": "881" - }, - { - "examine": "Master of imps.", - "name": "Wizard Grayzag", - "id": "707" - }, - { - "examine": "A very brave merchant.", - "name": "Edmond", - "id": "1785" - }, - { - "examine": "She's an expert on armoured skirts.", - "name": "Ranael", - "id": "544" - }, - { - "examine": "Spymaster of Al Kharid.", - "name": "Osman", - "id": "924" - }, - { - "examine": "Kebabs are full of meaty goodness!", - "name": "Karim", - "id": "543" - }, - { - "examine": "He can look after my money.", - "name": "Banker", - "id": "496" - }, - { - "examine": "She can look after my money.", - "name": "Banker", - "id": "497" - }, - { - "examine": "Manufacturer of fine leathers.", - "name": "Ellis", - "id": "2824" - }, - { - "examine": "Sells superior scimitars.", - "name": "Zeke", - "id": "541" - }, - { - "examine": "If Crafting's your thing, he's your man.", - "name": "Dommik", - "id": "545" - }, - { - "examine": "He's in control of the Shantay Pass.", - "name": "Shantay", - "id": "836" - }, - { - "examine": "A resident of Al Kharid.", - "name": "Irena", - "id": "835" - }, - { - "examine": "A farmer's enemy.", - "name": "Crow", - "id": "1755" - }, - { - "examine": "The hat's a dead giveaway.", - "name": "Witch", - "id": "5200" - }, - { - "examine": "A mad scientist if I ever saw one!", - "name": "Professor Oddenstein", - "id": "286" - }, - { - "examine": "She's watching you.", - "name": "Ava", - "id": "5198" - }, - { - "examine": "Litara, a barbarian explorer.", - "name": "Litara", - "id": "4376" - }, - { - "examine": "Always up-to-date with the latest helmet fashions.", - "name": "Peksa", - "id": "538" - }, - { - "examine": "A strange little man.", - "name": "Oziach", - "id": "747" - }, - { - "examine": "A very brave merchant.", - "name": "Richard", - "id": "1783" - }, - { - "examine": "Loves you more, the more you spend.", - "name": "Shop assistant", - "id": "529" - }, - { - "examine": "Sells stuff.", - "name": "Shopkeeper", - "id": "528" - }, - { - "examine": "One of the king's knights.", - "name": "Sir Prysin", - "id": "883" - }, - { - "examine": "Reldo the librarian.", - "name": "Reldo", - "id": "2660" - }, - { - "examine": "A powerful wizard.", - "name": "Surok Magis", - "id": "5834" - }, - { - "examine": "Animal skins are a specialty.", - "name": "Baraek", - "id": "547" - }, - { - "examine": "One of Gertrude's Sons.", - "name": "Shilop", - "id": "781" - }, - { - "examine": "The man with the armour.", - "name": "Horvik", - "id": "549" - }, - { - "examine": "Sells arrows.", - "name": "Lowe", - "id": "550" - }, - { - "examine": "This looks like an angry, aggressive man.", - "name": "Draul Leptoc", - "id": "3324" - }, - { - "examine": "A quiet, more reserved sort of lady.", - "name": "Phillipa", - "id": "3325" - }, - { - "examine": "A tearful damsel, maybe I can help her?", - "name": "Juliet", - "id": "637" - }, - { - "agg_radius": "", - "examine": "Like a mini man!", - "combat_style": "1", - "melee_animation": "190", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "193", - "weakness": "", - "end_height": "", - "death_animation": "196", - "name": "Gnome", + "id": "916", + "name": "Joe", + "examine": "A crafter at the pinnacle of his art.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "lifepoints": "2", - "strength_level": "1", - "id": "2250", - "aggressive": "", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Like a mini man!", - "combat_style": "1", - "melee_animation": "190", - "attack_speed": "4", + "id": "917", + "name": "Jail guard", + "examine": "I wonder who he's guarding?", + "aggressive": "true", + "attack_level": "19", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "21", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "193", - "death_animation": "196", - "name": "Gnome", + "safespot": null, + "strength_level": "23" + }, + { + "id": "918", + "name": "Ned" + }, + { + "id": "919", + "name": "Lady Keli", + "examine": "An infamous bandit." + }, + { + "id": "920", + "name": "Prince Ali", + "examine": "A young prince." + }, + { + "id": "921", + "name": "Prince Ali" + }, + { + "id": "922", + "name": "Aggie", + "examine": "A witch." + }, + { + "id": "923", + "name": "Hassan", + "examine": "The emir's chief advisor and physician.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "924", + "name": "Osman", + "examine": "Spymaster of Al Kharid." + }, + { + "id": "925", + "name": "Border Guard", + "examine": "Guards the border between Al Kharid and the Kingdom of Misthalin.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "926", + "name": "Border Guard" + }, + { + "id": "927", + "name": "Fishing spot" + }, + { + "id": "928", + "name": "Gujuo" + }, + { + "id": "929", + "name": "Ungadulu" + }, + { + "id": "930", + "name": "Ungadulu" + }, + { + "id": "931", + "name": "Jungle savage", + "examine": "An aggressive native of the Kharazi Jungle.", + "aggressive": "true", + "attack_level": "44", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "44", + "weakness": "6" + }, + { + "id": "932", + "name": "Fionella", + "examine": "She sells general items at the Legends Guild." + }, + { + "id": "933", + "name": "Siegfried Erkle" + }, + { + "id": "934", + "name": "Nezikchened", + "aggressive": "true", + "attack_level": "1", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "150", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "935", + "name": "Viyeldi" + }, + { + "id": "936", + "name": "San Tojalon" + }, + { + "id": "937", + "name": "Irvig Senay" + }, + { + "id": "938", + "name": "Ranalph Devere", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "939", + "name": "Boulder", + "examine": "I wonder what this is doing here.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "940", + "name": "Echned Zekin" + }, + { + "id": "941", + "name": "Green dragon", + "examine": "Must be related to Elvarg.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "68", + "lifepoints": "75", + "magic_animation": "80", + "magic_level": "68", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" + }, + { + "id": "942", + "name": "Master chef", + "examine": "An expert on all things culinary.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "943", + "name": "Survival expert", + "examine": "Very much an outdoors type.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "944", + "name": "Combat instructor", + "examine": "An expert on all forms of combat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "945", + "name": "RuneScape guide", + "examine": "Your introduction to the world of Gielinor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "946", + "name": "Magic instructor", + "examine": "A master of Magic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "947", + "name": "Financial advisor", + "examine": "An official representative from the First National Bank of Gielinor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "948", + "name": "Mining instructor", + "examine": "An expert on Mining-related skills.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "949", + "name": "Quest guide", + "examine": "Your introduction to the world of Gielinor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "950", + "name": "Giant rat", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "4933", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "951", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", "defence_level": "1", "lifepoints": "3", - "strength_level": "1", - "id": "2249", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", "range_level": "1", - "attack_level": "1" + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" }, { - "name": "null", - "defence_level": "1", - "safespot": "null", - "combat_audio": "511,513,512", - "id": "2309", - "range_level": "1", + "id": "952", + "name": "Fishing spot" + }, + { + "id": "953", + "name": "Banker" + }, + { + "id": "954", + "name": "Brother Brace", + "examine": "A holy man.", "attack_level": "1", - "defence_animation": "" - }, - { - "examine": "Yanni Salika; he buys and sells antiques.", - "name": "Yanni Salika", - "id": "515" - }, - { - "examine": "This is Kaleb Paramaya, a warm and friendly inn owner.", - "name": "Kaleb Paramaya", - "id": "512" - }, - { - "examine": "This is Fernahei; he owns the local fishing tackle shop.", - "name": "Fernahei", - "id": "517" - }, - { - "examine": "A warrior from Shilo Village.", - "name": "Mosol Rei", - "id": "500" - }, - { - "examine": "An unquiet soul.", - "name": "Spirit of Zadimus", - "id": "501" - }, - { - "examine": "The animated spirit of Rashiliyia the Zombie Queen.", - "name": "Rashiliyia ", - "id": "506" - }, - { - "examine": "A dwarf miner. He's shoring up the walls.", - "name": "Ferd", - "id": "3937" - }, - { - "examine": "A crafty dwarf.", - "name": "Nolar", - "id": "2158" - }, - { - "examine": "She sells general items at the Legends Guild.", - "name": "Fionella", - "id": "932" - }, - { - "examine": "A dwarf who's currently living in Miscellania.", - "name": "Alviss", - "id": "3933" - }, - { - "examine": "Smithy for Sogthorpe.", - "name": "Derrik", - "id": "1376" - }, - { - "examine": "A rather scared-looking dwarf. He doesn't seem all that drunk.", - "name": "Donal", - "id": "3938" - }, - { - "examine": "Runs the general store.", - "name": "Finn", - "id": "3922" - }, - { - "examine": "A dwarf who's currently living in Miscellania.", - "name": "Fullangr", - "id": "3934" - }, - { - "examine": "A subject of Miscellania.", - "name": "Halla", - "id": "3921" - }, - { - "examine": "A dwarf who's currently living in Miscellania.", - "name": "Jari", - "id": "3935" - }, - { - "examine": "A subject of Miscellania.", - "name": "Einar", - "id": "1380" - }, - { - "examine": "A subject of Miscellania.", - "name": "Ragnar", - "id": "1379" - }, - { - "examine": "A subject of Miscellania.", - "name": "Thora", - "id": "3927" - }, - { - "examine": "A subject of Miscellania.", - "name": "Thorhild", - "id": "1382" - }, - { - "examine": "A subject of Miscellania.", - "name": "Tjorvi", - "id": "3925" - }, - { - "examine": "Likes fish... hates people.", - "melee_animation": "284", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "", - "defence_animation": "285", - "death_animation": "287", - "name": "River troll", - "defence_level": "17", - "lifepoints": "20", - "strength_level": "17", - "id": "391", - "aggressive": "true", - "clue_level": "", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "17" - }, - { - "examine": "Likes fish... hates people.", - "melee_animation": "284", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "285", - "death_animation": "287", - "name": "River troll", - "defence_level": "48", - "lifepoints": "48", - "strength_level": "48", - "id": "393", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "Likes fish... hates people.", - "melee_animation": "284", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "285", - "death_animation": "287", - "name": "River troll", - "defence_level": "65", - "lifepoints": "85", - "strength_level": "65", - "id": "394", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Likes fish... hates people.", - "melee_animation": "284", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "285", - "death_animation": "287", - "name": "River troll", - "defence_level": "100", - "lifepoints": "120", - "strength_level": "100", - "id": "395", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "examine": "Likes fish... hates people.", - "melee_animation": "284", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "285", - "death_animation": "287", - "name": "River troll", - "defence_level": "130", - "lifepoints": "170", - "strength_level": "130", - "id": "396", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "130" - }, - { - "examine": "Rock with attitude.", - "combat_style": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "8", - "name": "Rock Golem", - "lifepoints": "25", - "strength_level": "8", - "id": "413", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "15", - "attack_level": "8" + "range_level": "1", + "safespot": null, + "strength_level": "1" }, { - "examine": "Rock with attitude.", + "id": "955", + "name": "Cow", + "examine": "Converts grass to beef.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "369,371,370", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1", + "weakness": "8" + }, + { + "id": "956", + "name": "Drunken Dwarf", + "examine": "He's had a fair bit to drink...", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "2707,419,418", "combat_style": "1", + "death_animation": "102", + "defence_animation": "101", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "99", + "magic_level": "20", + "melee_animation": "99", + "prj_height": "45", + "projectile": "33", + "range_animation": "99", + "range_level": "6", + "safespot": null, + "slayer_exp": "0", + "start_gfx": "", + "strength_level": "5" + }, + { + "id": "957", + "name": "Mubariz", + "examine": "Tough-looking combat type." + }, + { + "id": "958", + "name": "Fadli", + "examine": "Looks kinda bored." + }, + { + "id": "959", + "name": "A'abla", + "examine": "Trained to deal with all sorts of injuries." + }, + { + "id": "960", + "name": "Sabreen", + "examine": "Wow! She's made a statement with that hair!" + }, + { + "id": "961", + "name": "Surgeon General Tafani", + "examine": "This doctor really knows her stuff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "962", + "name": "Jaraah", + "examine": "Has the messy job of putting players back together again." + }, + { + "id": "963", + "name": "Zahwa", + "examine": "Battle-scarred." + }, + { + "id": "964", + "name": "Ima", + "examine": "A citizen of Al Kharid" + }, + { + "id": "965", + "name": "Sabeil", + "examine": "A citizen of Al Kharid" + }, + { + "id": "966", + "name": "Jadid", + "examine": "A citizen of Al Kharid" + }, + { + "id": "967", + "name": "Dalal", + "examine": "A citizen of Al Kharid" + }, + { + "id": "968", + "name": "Afrah", + "examine": "A citizen of Al Kharid" + }, + { + "id": "969", + "name": "Jeed", + "examine": "A citizen of Al Kharid" + }, + { + "id": "970", + "name": "Diango", + "examine": "He smells funny." + }, + { + "id": "971", + "name": "Chadwell", + "examine": "Shopkeeper." + }, + { + "id": "972", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "973", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "974", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "975", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "976", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "977", + "name": "Blessed spider", + "examine": "It's one of Iban's pets.", + "attack_level": "22", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "5327", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "17", - "name": "Rock Golem", - "lifepoints": "40", - "strength_level": "17", - "id": "414", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "30", - "attack_level": "17" + "safespot": null, + "strength_level": "22", + "weakness": "2" }, { - "examine": "Rock with attitude.", - "combat_style": "1", + "id": "978", + "name": "Blessed giant rat", + "examine": "It's one of Iban's pet vermin.", + "attack_level": "12", + "combat_audio": "703,705,704", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", + "magic_animation": "0", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "36", - "name": "Rock Golem", - "lifepoints": "60", - "strength_level": "36", - "id": "415", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "40", - "attack_level": "38" + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "9" }, { - "examine": "Rock with attitude.", - "combat_style": "1", + "id": "979", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "64", - "name": "Rock Golem", - "lifepoints": "86", - "strength_level": "64", - "id": "416", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "60", - "attack_level": "64" + "range_level": "1", + "safespot": null, + "strength_level": "1" }, { - "examine": "Rock with attitude.", - "combat_style": "1", + "id": "980", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "100", - "name": "Rock Golem", - "lifepoints": "120", - "strength_level": "100", - "id": "417", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "75", - "attack_level": "100" + "range_level": "1", + "safespot": null, + "strength_level": "1" }, { - "examine": "Rock with attitude.", - "combat_style": "1", + "id": "981", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", "range_animation": "0", - "attack_speed": "4", - "defence_animation": "130", - "name": "Rock Golem", - "lifepoints": "166", - "strength_level": "130", - "id": "418", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "982", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "983", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "984", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "985", + "name": "Slave", + "examine": "A wretched slave of Iban.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "986", + "name": "Boulder", + "examine": "Precariously balanced...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "987", + "name": "Unicorn", + "attack_level": "1", + "combat_audio": "369,371,370", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "988", + "name": "Sir Jerro", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "57", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "989", + "name": "Sir Carl", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "57", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "990", + "name": "Sir Harry", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "57", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "991", + "name": "Half-soulless", + "examine": "A creature empty of emotion.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "992", + "name": "Kardia" + }, + { + "id": "993", + "name": "Witch's cat", + "examine": "Curiosity is yet to kill this one...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "994", + "name": "Niloof" + }, + { + "id": "995", + "name": "Klank", + "examine": "A dwarven maker of gauntlets." + }, + { + "id": "996", + "name": "Kamen", + "examine": "This dwarf looks intoxicated." + }, + { + "id": "997", + "name": "Kalrag", + "examine": "A giant spider.", "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "80", - "attack_level": "130" + "attack_level": "1", + "death_animation": "5321", + "defence_animation": "5320", + "defence_level": "1", + "lifepoints": "91", + "melee_animation": "5319", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "examine": "Mooooooooooooole.", - "name": "Baby Mole", - "id": "3342" + "id": "998", + "name": "Othainian", + "examine": "One of the guardians of Iban.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "90", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "examine": "Moley, moley, moley!", - "name": "Baby Mole", - "id": "3343" + "id": "999", + "name": "Doomion", + "examine": "One of the guardians of Iban.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "90", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { + "id": "1000", + "name": "Holthion", + "examine": "One of the guardians of Iban.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "90", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1001", + "name": "Dark mage", + "examine": "A user of dark magic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1002", + "name": "Disciple of Iban", + "examine": "A dark magic user.", + "attack_level": "24", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "32", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "3" + }, + { + "id": "1003", + "name": "Lord Iban" + }, + { + "id": "1004", + "name": "Spider", + "examine": "Incey wincey.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6251", + "defence_animation": "6250", + "defence_level": "1", + "lifepoints": "2", + "melee_animation": "6249", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1005", + "name": "Giant bat", + "examine": "An annoying flappy thing.", + "attack_level": "22", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", + "combat_audio": "292,294,293", + "defence_level": "22", + "lifepoints": "32", + "magic_level": "1", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "8", + "strength_level": "22" + }, + { + "id": "1006", + "name": "Sea slug", + "examine": "A rather nasty looking crustacean." + }, + { + "id": "1007", + "name": "Zamorak wizard", + "examine": "A servant of Zamorak.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "73", + "magic_animation": "811", + "magic_level": "60", + "melee_animation": "811", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "60", + "safespot": null, + "spell_id": "43", + "strength_level": "50", + "weakness": "3" + }, + { + "id": "1008", + "name": "Hamid" + }, + { + "id": "1009", + "name": "Poison spider", + "examine": "A nasty, poisonous arachnid.", + "aggressive": "true", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "28", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "5327", + "poison_amount": "30", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "28", + "weakness": "2" + }, + { + "id": "1010", + "name": "Rantz", "examine": "He hungry.", "combat_style": "1", - "start_gfx": "243", - "name": "Rantz", - "start_height": "100", - "range_animation": "3460", - "id": "1010", + "prj_height": "50", "projectile": "242", - "prj_height": "50" + "range_animation": "3460", + "start_gfx": "243", + "start_height": "100" }, { - "examine": "One of Fenkenstrain's failed experiments.", - "combat_style": "", - "melee_animation": "1616", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "1617", - "death_animation": "1618", - "name": "Experiment", - "defence_level": "30", - "lifepoints": "100", - "strength_level": "1", - "id": "1677", - "aggressive": "", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "id": "1011", + "name": "Fycie" + }, + { + "id": "1012", + "name": "Bugs" + }, + { + "id": "1013", + "name": "Swamp toad", + "examine": "A green skinned croaker", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "34", + "safespot": null, + "strength_level": "1" }, { - "examine": "It has a key hanging from its collar.", - "melee_animation": "1626", + "id": "1014", + "name": "Bloated Toad" + }, + { + "id": "1015", + "name": "Iron pickaxe", + "examine": "Useful for hitting rocks.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1016", + "name": "Chompy bird" + }, + { + "id": "1017", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1018", + "name": "Rooster", + "examine": "He rules the", + "attack_level": "2", + "death_animation": "5389", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "5387", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2", + "weakness": "9" + }, + { + "id": "1019", + "name": "Fire elemental", + "examine": "A fire elemental.", + "attack_level": "24", + "combat_audio": "1534,1536,1535", + "death_animation": "1031", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "32", + "melee_animation": "1029", + "movement_radius": "4", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "3" + }, + { + "id": "1020", + "name": "Earth elemental", + "examine": "An earth elemental.", + "aggressive": "false", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "1531,1533,1532", + "death_animation": "4870", + "defence_animation": "4869", + "defence_level": "35", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "10", + "melee_animation": "4868", + "movement_radius": "4", + "range_animation": "0", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "strength_level": "32", + "weakness": "7" + }, + { + "id": "1021", + "name": "Air elemental", + "examine": "An air elemental.", + "attack_level": "24", + "combat_audio": "1527,1529,1528", + "death_animation": "1041", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "1040", + "movement_radius": "4", + "range_animation": "0", + "range_level": "32", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "1" + }, + { + "id": "1022", + "name": "Water elemental", + "examine": "A water elemental.", + "attack_level": "24", + "combat_audio": "1546,1543,1545", + "death_animation": "1048", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "1044", + "movement_radius": "4", + "range_animation": "0", + "range_level": "32", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "1" + }, + { + "id": "1023", + "name": "Vampire", + "examine": "It looks very hungry!", + "attack_level": "52", + "clue_level": "1", + "death_animation": "7185", + "defence_level": "52", + "lifepoints": "50", + "melee_animation": "7183", + "range_level": "1", + "safespot": null, + "slayer_exp": "50", + "strength_level": "1" + }, + { + "id": "1024", + "name": "Baby impling" + }, + { + "id": "1028", + "name": "Baby Impling", + "examine": "A baby impling. Ahhh." + }, + { + "id": "1029", + "name": "Young Impling", + "examine": "A young impling. It's not fair!" + }, + { + "id": "1030", + "name": "Gourmet Impling", + "examine": "An impling gourmet. Mmmm, tasty." + }, + { + "id": "1031", + "name": "Earth Impling", + "examine": "An earth impling. Rock on." + }, + { + "id": "1032", + "name": "Essence Impling", + "examine": "An impling who likes magic. Magic!" + }, + { + "id": "1033", + "name": "Eclectic Impling", + "examine": "An impling with varied tastes." + }, + { + "id": "1034", + "name": "Nature Impling", + "examine": "A nature impling. Right on, maaan." + }, + { + "id": "1035", + "name": "Magpie Impling", + "examine": "Ooh, shiny things!" + }, + { + "id": "1036", + "name": "Banker" + }, + { + "id": "1037", + "name": "Prissy Scilla", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "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": "1044", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "18", "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "22", + "end_gfx": "101", + "lifepoints": "20", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "18", + "weakness": "3" + }, + { + "id": "1045", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "17", + "end_gfx": "101", + "lifepoints": "10", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "8", + "weakness": "3" + }, + { + "id": "1046", + "name": "Monk of Zamorak", + "examine": "An evil human cleric.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "25", + "end_gfx": "101", + "lifepoints": "25", + "magic_animation": "711", + "magic_level": "40", + "melee_animation": "422", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "25", + "weakness": "3" + }, + { + "id": "1047", + "name": "Drezel" + }, + { + "id": "1049", + "name": "Drezel", + "examine": "A holy man." + }, + { + "id": "1050", + "name": "Filiman Tarlock", + "examine": "The animated spirit of a soul not at rest." + }, + { + "id": "1051", + "name": "Nature Spirit", + "examine": "The animated spirit of a soul not at rest." + }, + { + "id": "1052", + "name": "Ghast", + "examine": "Arrghhh... a Ghast.", + "aggressive": "false", + "attack_level": "1", + "combat_audio": "433,435,434", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1053", + "name": "Ghast", + "examine": "Arrghhh... A Ghast.", + "aggressive": "true", + "attack_level": "22", + "attack_speed": "4", + "combat_audio": "433,435,434", + "death_animation": "1089", + "death_gfx": "265", + "defence_animation": "1088", + "defence_level": "18", + "lifepoints": "45", + "magic_animation": "0", "magic_level": "1", - "defence_animation": "1627", - "death_animation": "1628", + "melee_animation": "1087", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "9" + }, + { + "id": "1054", + "name": "Ulizius", + "examine": "A slightly nervous guard." + }, + { + "id": "1055", + "name": "Pirate Jackie the Fruit", + "examine": "Ticket trader for the Brimhaven Agility Arena.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1056", + "name": "Mime" + }, + { + "id": "1057", + "name": "Strange watcher", + "examine": "A member of the audience.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1058", + "name": "Strange watcher", + "examine": "A member of the audience.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1059", + "name": "Strange watcher", + "examine": "A member of the audience.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1060", + "name": "Denulth", + "examine": "Commander of the Imperial Guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1061", + "name": "Sergeant", + "examine": "A sergeant of the Imperial Guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1062", + "name": "Sergeant", + "examine": "A sergeant of the Imperial Guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1063", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1064", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1065", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "44", + "combat_audio": "511,513,512", + "defence_level": "44", + "lifepoints": "55", + "magic_level": "1", + "range_level": "1", + "safespot": null, + "strength_level": "44" + }, + { + "id": "1066", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1067", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1068", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1069", + "name": "Soldier", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1070", + "name": "Saba" + }, + { + "id": "1071", + "name": "Tenzing" + }, + { + "id": "1072", + "name": "Eadburg", + "examine": "The Burthorpe Castle cook.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2" + }, + { + "id": "1073", + "name": "Archer", + "examine": "A Burthorpe Castle Archer.", + "attack_level": "1", + "bonuses": "54,68,75,72,67,81,54,35,35,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "44", + "lifepoints": "55", + "magic_animation": "426", + "magic_level": "1", + "melee_animation": "426", + "projectile": "10", + "range_animation": "426", + "range_level": "44", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "19", + "strength_level": "1" + }, + { + "id": "1074", + "name": "Archer", + "examine": "A Burthorpe Castle Archer.", + "attack_level": "1", + "bonuses": "54,68,75,72,67,81,54,35,35,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "44", + "lifepoints": "55", + "magic_animation": "426", + "magic_level": "1", + "melee_animation": "426", + "projectile": "10", + "range_animation": "426", + "range_level": "44", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "19", + "strength_level": "1" + }, + { + "id": "1075", + "name": "Archer" + }, + { + "id": "1076", + "name": "Guard", + "examine": "A Burthorpe Castle guard.", + "attack_level": "33", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "33", + "lifepoints": "44", + "magic_level": "1", + "melee_animation": "401", + "range_level": "1", + "safespot": null, + "strength_level": "33" + }, + { + "id": "1077", + "name": "Guard", + "examine": "A Burthorpe Castle guard.", + "attack_level": "33", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "33", + "lifepoints": "44", + "magic_level": "1", + "melee_animation": "401", + "range_level": "1", + "safespot": null, + "strength_level": "33" + }, + { + "id": "1078", + "name": "Harold", + "examine": "An off-duty Burthorpe Castle guard." + }, + { + "id": "1079", + "name": "Tostig", + "examine": "Barman of the Toad and Chicken." + }, + { + "id": "1080", + "name": "Eohric", + "examine": "Head servant for Prince Anlaf." + }, + { + "id": "1081", + "name": "Servant", + "examine": "A servant for Prince Anlaf.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3" + }, + { + "id": "1082", + "name": "Dunstan", + "examine": "Smithy for Burthorpe." + }, + { + "id": "1083", + "name": "Wistan", + "examine": "Shopkeeper for Burthorpe." + }, + { + "id": "1084", + "name": "Breoca", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3" + }, + { + "id": "1085", + "name": "Ocga", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3" + }, + { + "id": "1086", + "name": "Man", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1087", + "name": "Penda", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3" + }, + { + "id": "1088", + "name": "Hygd", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2" + }, + { + "id": "1089", + "name": "Ceolburg", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "3", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2" + }, + { + "id": "1090", + "name": "Hild", + "examine": "A pretty young woman with an air of mystery around her." + }, + { + "id": "1091", + "name": "Bob" + }, + { + "id": "1092", + "name": "White Knight", + "examine": "The knight seems to be watching something.", + "attack_level": "27", + "attack_speed": "8", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "27", + "lifepoints": "38", + "magic_animation": "0", + "melee_animation": "7042", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "27", + "weakness": "9" + }, + { + "id": "1093", + "name": "Billy" + }, + { + "id": "1094", + "name": "Mountain goat" + }, + { + "id": "1095", + "name": "Rock", + "examine": "The biggest and baddest troll.", + "aggressive": "true", + "attack_level": "70", + "bonuses": "60,60,60,0,0,35,60,35,200,200,0,100,0,0,0", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "0", + "melee_animation": "6523", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "110", + "weakness": "7" + }, + { + "id": "1096", + "name": "Stick", + "examine": "Careful, he is a big.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "7", + "bonuses": "50,50,50,0,0,30,30,50,200,200,0,80,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "135", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "110", + "weakness": "7" + }, + { + "id": "1097", + "name": "Pee Hat", + "examine": "A nasty looking troll.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "6", + "bonuses": "40,40,40,0,0,25,25,40,200,200,0,70,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "50", + "lifepoints": "120", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "7" + }, + { + "id": "1098", + "name": "Kraka", + "examine": "A nasty looking troll.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "6", + "bonuses": "40,40,40,0,0,25,25,40,200,400,0,70,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "50", + "lifepoints": "120", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "7" + }, + { + "id": "1099", + "name": "Dung", + "examine": "Human is his speciality.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "1100", + "name": "Ash", + "examine": "Human is his speciality.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "1101", + "name": "Thrower Troll", + "examine": "Small for a troll, but mean, ugly and throws rocks.", + "agg_radius": "6", + "attack_level": "41", + "attack_speed": "7", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1102", + "name": "Thrower Troll", + "examine": "Small for a troll, but mean, ugly and throws rocks.", + "agg_radius": "6", + "attack_level": "41", + "attack_speed": "7", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1103", + "name": "Thrower Troll", + "examine": "Small for a troll, but mean, ugly and throws rocks.", + "agg_radius": "6", + "attack_level": "41", + "attack_speed": "7", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1104", + "name": "Thrower Troll", + "examine": "Small for a troll, but mean, ugly and throws rocks.", + "agg_radius": "6", + "attack_level": "41", + "attack_speed": "7", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1105", + "name": "Thrower Troll", + "examine": "Small for a troll, but mean, ugly and throws rocks.", + "agg_radius": "6", + "attack_level": "41", + "attack_speed": "7", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1106", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1107", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1108", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1109", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1110", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1111", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1112", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1113", + "name": "Eadgar" + }, + { + "id": "1114", + "name": "Godric" + }, + { + "id": "1115", + "name": "Troll general", + "examine": "One of the troll generals.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "92", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "1116", + "name": "Troll general", + "examine": "One of the troll generals.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "92", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "1117", + "name": "Troll general", + "examine": "One of the troll generals.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "92", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "1118", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1119", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1120", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1121", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1122", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1123", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1124", + "name": "Troll spectator", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "1125", + "name": "Dad", + "examine": "An unusually large troll.", + "attack_level": "60", + "bonuses": "40,40,40,0,0,25,25,40,200,200,0,70,0,0,0", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "120", + "magic_animation": "0", + "melee_animation": "1158", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "120", + "weakness": "7" + }, + { + "id": "1126", + "name": "Twig", + "examine": "He's guarding the cells.", + "aggressive": "true", + "attack_level": "40", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "1158", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "90", + "weakness": "7" + }, + { + "id": "1127", + "name": "Berry", + "examine": "He's guarding the cells.", + "aggressive": "true", + "attack_level": "40", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", + "death_animation": "287", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "1158", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "90", + "weakness": "7" + }, + { + "id": "1128", + "name": "Twig", + "examine": "He's guarding the cells.", + "attack_level": "40", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "90", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90", + "weakness": "7" + }, + { + "id": "1129", + "name": "Berry", + "examine": "He's guarding the cells.", + "attack_level": "40", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "90", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "90", + "weakness": "7" + }, + { + "id": "1130", + "name": "Thrower troll", + "examine": "Small for a troll, but it's mean, ugly and throws rocks.", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "7", + "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "start_gfx": "33", + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1131", + "name": "Thrower troll", + "examine": "Small for a troll, but it's mean, ugly and throws rocks.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "7", + "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "start_gfx": "33", + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1132", + "name": "Thrower troll", + "examine": "Small for a troll, but it's mean, ugly and throws rocks.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "7", + "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "start_gfx": "33", + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1133", + "name": "Thrower troll", + "examine": "Small for a troll, but it's mean, ugly and throws rocks.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "7", + "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "start_gfx": "33", + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1134", + "name": "Thrower troll", + "examine": "Small for a troll, but it's mean, ugly and throws rocks.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "41", + "attack_speed": "7", + "bonuses": "30,60,40,40,50,70,50,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "30", + "lifepoints": "95", + "magic_animation": "284", + "melee_animation": "284", + "projectile": "276", + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "start_gfx": "33", + "strength_level": "41", + "weakness": "0" + }, + { + "id": "1135", + "name": "Cook", + "examine": "Human is his speciality.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1136", + "name": "Cook", + "examine": "Human is his speciality.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1137", + "name": "Cook", + "examine": "Human is his speciality.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1138", + "name": "Mountain troll", + "examine": "Small for a troll, but mean and ugly.", + "attack_level": "53", + "attack_speed": "5", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "53", + "lifepoints": "90", + "magic_animation": "284", + "melee_animation": "284", + "range_animation": "284", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1139", + "name": "Mushroom", + "examine": "He's fast asleep.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1140", + "name": "Mountain goat", + "examine": "This beast doesn't need climbing boots.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1141", + "name": "Mountain goat", + "examine": "This beast doesn't need climbing boots.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1142", + "name": "Guard", + "examine": "He's guarding the storeroom.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1143", + "name": "Guard", + "examine": "He's guarding the storeroom.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1144", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1145", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1146", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1147", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1148", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1149", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1150", + "name": "Guard", + "examine": "He's guarding the goutweed.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1151", + "name": "Burntmeat", + "examine": "Human is his speciality.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1152", + "name": "Weird Old Man", + "examine": "What's he mumbling about?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1153", + "name": "Kalphite Worker", + "examine": "I don't think insect repellent will work...", + "attack_level": "20", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,1,10,10,0,0,0,0,0", + "combat_audio": "571,573,572", + "death_animation": "6230", + "defence_animation": "6232", + "defence_level": "20", + "lifepoints": "40", + "magic_animation": "6223", + "magic_level": "1", + "melee_animation": "6223", + "range_animation": "6223", + "range_level": "1", + "respawn_delay": "38", + "safespot": null, + "slayer_exp": "40", + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1154", + "name": "Kalphite Soldier", + "examine": "I don't think insect repellent will work...", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", + "combat_audio": "567,569,568", + "death_animation": "6228", + "defence_animation": "6227", + "defence_level": "70", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6224", + "poison_amount": "20", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "90", + "strength_level": "70", + "weakness": "7" + }, + { + "id": "1155", + "name": "Kalphite Guardian", + "examine": "I don't think insect repellent will work...", + "aggressive": "true", + "attack_level": "110", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", + "death_animation": "6230", + "defence_animation": "6232", + "defence_level": "110", + "lifepoints": "170", + "magic_animation": "6223", + "magic_level": "1", + "melee_animation": "6223", + "poison_amount": "30", + "poisonous": "true", + "range_animation": "6223", + "range_level": "1", + "respawn_delay": "65", + "safespot": null, + "strength_level": "110", + "weakness": "7" + }, + { + "id": "1156", + "name": "Kalphite Worker", + "examine": "I don't think insect repellent will work...", + "attack_level": "20", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,1,10,10,0,0,0,0,0", + "combat_audio": "571,573,572", + "death_animation": "6230", + "defence_animation": "6232", + "defence_level": "20", + "lifepoints": "40", + "magic_animation": "6223", + "magic_level": "1", + "melee_animation": "6223", + "range_animation": "6223", + "range_level": "1", + "respawn_delay": "38", + "safespot": null, + "slayer_exp": "40", + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1157", + "name": "Kalphite Guardian", + "examine": "I don't think insect repellent will work...", + "aggressive": "true", + "attack_level": "110", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", + "death_animation": "6230", + "defence_animation": "6232", + "defence_level": "110", + "lifepoints": "170", + "magic_animation": "6223", + "magic_level": "1", + "melee_animation": "6223", + "poison_amount": "30", + "poisonous": "true", + "range_animation": "6223", + "range_level": "1", + "respawn_delay": "65", + "safespot": null, + "strength_level": "110", + "weakness": "7" + }, + { + "id": "1158", + "name": "Kalphite Queen", + "examine": "I don't think insect repellent will work...", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "300", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,50,10,100,100,0,0,0,0,0", + "death_animation": "6242", + "defence_animation": "6232", + "defence_level": "300", + "lifepoints": "255", + "magic_animation": "6241", + "magic_level": "150", + "melee_animation": "6241", + "poisonous": "", + "range_animation": "6241", + "range_level": "1000", + "respawn_delay": "0", + "safespot": null, + "strength_level": "300", + "weakness": "10" + }, + { + "id": "1159", + "name": "Kalphite Queen" + }, + { + "id": "1160", + "name": "Kalphite Queen", + "examine": "I don't think insect repellent will work...", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "300", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,100,100,100,10,10,0,0,0,0,0", + "death_animation": "6233", + "defence_animation": "6234", + "defence_level": "300", + "lifepoints": "255", + "magic_animation": "6235", + "magic_level": "150", + "melee_animation": "6235", + "range_animation": "6235", + "range_level": "1000", + "respawn_delay": "45", + "safespot": null, + "strength_level": "300", + "weakness": "10" + }, + { + "id": "1161", + "name": "Kalphite Larva", + "examine": "I don't think insect repellent will work...", + "aggressive": "false", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1162", + "name": "Timfraku", + "examine": "The imposing Chief of Tai Bwo Wannai." + }, + { + "id": "1163", + "name": "Tiadeche", + "examine": "A rather depressed looking fisherman." + }, + { + "id": "1164", + "name": "Tiadeche", + "examine": "A rather depressed looking fisherman." + }, + { + "id": "1165", + "name": "Tinsay" + }, + { + "id": "1166", + "name": "Tinsay" + }, + { + "id": "1167", + "name": "Tamayu", + "examine": "A hunter who long ago succumbed to blood lust." + }, + { + "id": "1168", + "name": "Tamayu" + }, + { + "id": "1169", + "name": "Tamayu" + }, + { + "id": "1170", + "name": "Tamayu" + }, + { + "id": "1171", + "name": "Lubufu", + "examine": "A crotchety old fisherman who doesn't like young whippersnappers." + }, + { + "id": "1172", + "name": "The Shaikahan", + "examine": "A huge beast, resembling in some ways a lion, but mostly a twisted nightmare.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "1201", + "defence_animation": "1200", + "defence_level": "50", + "lifepoints": "100", + "magic_animation": "1197", + "melee_animation": "1199", + "range_animation": "1197", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "1173", + "name": "The Shaikahan" + }, + { + "id": "1174", + "name": "Fishing spot" + }, + { + "id": "1175", + "name": "Fishing spot" + }, + { + "id": "1176", + "name": "Fishing spot", + "examine": "I can see fish swimming in the water." + }, + { + "id": "1177", + "name": "Fishing spot" + }, + { + "id": "1178", + "name": "Fishing spot" + }, + { + "id": "1179", + "name": "Gull", + "examine": "Flappy bird.", + "water_npc": "true" + }, + { + "id": "1180", + "name": "Cormorant", + "examine": "A sea bird." + }, + { + "id": "1181", + "name": "Pelican", + "examine": "A sea bird." + }, + { + "id": "1182", + "name": "Lord Iorwerth" + }, + { + "id": "1183", + "name": "Elf warrior", + "examine": "He looks pretty handy with that bow.", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "428", + "projectile": "249", + "range_animation": "426", + "range_level": "72", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "250", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "1184", + "name": "Elf warrior", + "examine": "I don't wanna be at the wrong end of that pike.", + "attack_level": "68", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "425,427,426", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "68", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "68", + "weakness": "9" + }, + { + "id": "1185", + "name": "Elven city guard", + "examine": "An elven city guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1192", + "name": "Rabbit", + "examine": "A cute bunny rabbit.", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6090", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1193", + "name": "Rabbit", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "6091", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "6090", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1194", + "name": "Rabbit", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "6091", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "6090", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1195", + "name": "Grizzly bear", + "examine": "It's an NPC.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "4", + "combat_audio": "300,302,301", + "death_animation": "4929", + "defence_animation": "4927", + "defence_level": "35", + "lifepoints": "35", + "magic_level": "1", + "melee_animation": "4921", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "36" + }, + { + "id": "1196", + "name": "Grizzly bear cub", + "examine": "Eek! A bear cub!", + "attack_level": "21", + "combat_audio": "498,500,499", + "defence_animation": "0", + "defence_level": "21", + "lifepoints": "30", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "21", + "weakness": "6" + }, + { + "id": "1197", + "name": "Grizzly bear cub", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "35", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1198", + "name": "Dire Wolf", + "examine": "What big teeth you have.", + "attack_level": "33", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "6559", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "33", + "weakness": "9" + }, + { + "id": "1199", + "name": "Elf Tracker", + "examine": "An elf tracker.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1200", + "name": "Tyras guard", + "examine": "One of King Tyras's men.", + "attack_level": "42", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "414", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "42", + "weakness": "8" + }, + { + "id": "1201", + "name": "Elf warrior", + "examine": "I don't want to get on the wrong side of him.", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "105", + "melee_animation": "426", + "projectile": "249", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "250", + "strength_level": "1" + }, + { + "id": "1202", + "name": "Arianwyn" + }, + { + "id": "1203", + "name": "Tyras guard", + "examine": "One of King Tyras's men.", + "attack_level": "95", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "110", + "lifepoints": "110", + "melee_animation": "395", + "range_level": "50", + "respawn_delay": "60", + "safespot": null, + "strength_level": "105" + }, + { + "id": "1204", + "name": "Tyras guard", + "examine": "One of King Tyras's men.", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "112", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1205", + "name": "Tyras guard" + }, + { + "id": "1206", + "name": "Tyras guard", + "examine": "One of King Tyras's men." + }, + { + "id": "1207", + "name": "Quartermaster" + }, + { + "id": "1208", + "name": "Quartermaster", + "examine": "Responsible for the food and equipment of the troops.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1209", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "1210", + "name": "Kings messenger", + "examine": "One of King Lathas' messengers." + }, + { + "id": "1211", + "name": "Will o' the wisp", + "examine": "Mysterious swamp lights...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1212", + "name": "Will o' the wisp", + "examine": "Mysterious swamp lights..." + }, + { + "id": "1213", + "name": "Tegid", + "examine": "He's washing his clothes in the lake." + }, + { + "id": "1214", + "name": "Thistle", + "examine": "A plant." + }, + { + "id": "1215", + "name": "Parrots", + "examine": "What a colourful bunch of parrots!" + }, + { + "id": "1216", + "name": "Parroty Pete", + "examine": "He seems strangely familiar...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1217", + "name": "Gardener", + "examine": "An old gardener.", + "attack_level": "4", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "4", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "433", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "4", + "weakness": "8" + }, + { + "id": "1218", + "name": "Ghoul", + "examine": "A scary, man eating ghoul.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "442,444,443", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "50", + "magic_animation": "422", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "50", + "strength_level": "40", + "weakness": "9" + }, + { + "id": "1219", + "name": "Leech", + "examine": "Yuck! It's all slimy!", + "aggressive": "true", + "attack_level": "42", + "death_animation": "1272", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "1273", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "42", + "weakness": "9" + }, + { + "id": "1220", + "name": "Vampire", + "examine": "It looks very hungry!", + "attack_level": "52", + "clue_level": "1", + "death_animation": "7185", + "defence_level": "52", + "lifepoints": "50", + "melee_animation": "1264", + "range_level": "1", + "safespot": null, + "slayer_exp": "50", + "strength_level": "1" + }, + { + "id": "1221", + "name": "Spider" + }, + { + "id": "1222", + "name": "Mist" + }, + { + "id": "1223", + "name": "Vampire", + "examine": "It looks very hungry!", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "1264", + "range_level": "1", + "safespot": null, + "slayer_exp": "40", + "strength_level": "1" + }, + { + "id": "1224", + "name": "Vampyric hound" + }, + { + "id": "1225", + "name": "Vampire", + "examine": "It looks very hungry!", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "50", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1226", + "name": "Tree" + }, + { + "id": "1227", + "name": "Myre Blamish Snail", + "examine": "A marsh coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "4", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "8", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "5", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1228", + "name": "Blood Blamish Snail", + "examine": "A blood coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "13", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "12", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1229", + "name": "Ochre Blamish Snail", + "examine": "A muddy coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "7", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1230", + "name": "Bruise Blamish Snail", + "examine": "A bruise blue coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "12", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "15", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1231", + "name": "Bark Blamish Snail", + "examine": "A branch bark coloured blamish snail, these types are said to spit acid.", + "agg_radius": "", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "22", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "10", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1232", + "name": "Myre Blamish Snail", + "examine": "A marsh coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "4", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "8", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "5", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1233", + "name": "Blood Blamish Snail", + "examine": "A blood coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "13", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "12", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1234", + "name": "Ochre Blamish Snail", + "examine": "A muddy coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "7", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1235", + "name": "Bruise Blamish Snail", + "examine": "A bruise blue coloured blamish snail, these types are said to spit acid.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "6", + "bonuses": "10,10,10,0,0,5,5,30,5,50,0,10,0,0,0", + "combat_style": "1", + "death_animation": "8144", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "12", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "15", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" + }, + { + "id": "1236", + "name": "Fishing spot" + }, + { + "id": "1237", + "name": "Fishing spot" + }, + { + "id": "1238", + "name": "Fishing spot" + }, + { + "id": "1239", + "name": "Bedabin Nomad Fighter", + "examine": "A Bedabin nomad fighter - a sandy swordsman.", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "9" + }, + { + "id": "1240", + "name": "Loar Shadow", + "examine": "A shadowy sort of entity", + "aggressive": "true", + "attack_level": "45", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "38", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "3" + }, + { + "id": "1241", + "name": "Loar Shade", + "examine": "The shadowy remains of a long departed soul.", + "aggressive": "true", + "attack_level": "45", + "death_animation": "1308", + "defence_animation": "1284", + "defence_level": "26", + "lifepoints": "38", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1283", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "3" + }, + { + "id": "1242", + "name": "Shade Spirit" + }, + { + "id": "1243", + "name": "Phrin Shadow", + "examine": "A shadowy sort of entity", + "aggressive": "true", + "attack_level": "64", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "56", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "3" + }, + { + "id": "1244", + "name": "Phrin Shade", + "examine": "The shadowy remains of a long departed soul.", + "aggressive": "true", + "attack_level": "64", + "death_animation": "1287", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "56", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1283", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "3" + }, + { + "id": "1245", + "name": "Riyl Shadow", + "examine": "A shadowy sort of entity", + "aggressive": "true", + "attack_level": "88", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "76", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "3" + }, + { + "id": "1246", + "name": "Riyl Shade", + "examine": "The shadowy remains of a long departed soul.", + "aggressive": "true", + "attack_level": "88", + "death_animation": "1287", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "76", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1283", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "3" + }, + { + "id": "1247", + "name": "Asyn Shadow", + "examine": "A shadowy sort of entity", + "aggressive": "true", + "attack_level": "102", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84", + "weakness": "3" + }, + { + "id": "1248", + "name": "Asyn Shade", + "examine": "The shadowy remains of a long departed soul.", + "aggressive": "true", + "attack_level": "102", + "death_animation": "1287", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1283", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84", + "weakness": "3" + }, + { + "id": "1249", + "name": "Fiyr Shadow", + "examine": "A shadowy sort of entity", + "aggressive": "true", + "attack_level": "120", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "85", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "3" + }, + { + "id": "1250", + "name": "Fiyr Shade", + "examine": "The shadowy remains of a long departed soul.", + "aggressive": "true", + "attack_level": "120", + "death_animation": "1287", + "defence_animation": "0", + "defence_level": "85", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1283", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "3" + }, + { + "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": "1257", + "name": "Afflicted", + "examine": "A local villager of Mort'ton.", + "attack_level": "43", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "43", + "weakness": "6" + }, + { + "id": "1258", + "name": "Afflicted", + "examine": "A local villager of Mort'ton.", + "attack_level": "44", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "44", + "weakness": "6" + }, + { + "id": "1259", + "name": "Mort'ton local" + }, + { + "id": "1260", + "name": "Mort'ton local" + }, + { + "id": "1261", + "name": "Afflicted", + "examine": "A local villager of Mort'ton.", + "attack_level": "45", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "45", + "weakness": "6" + }, + { + "id": "1262", + "name": "Afflicted", + "examine": "A local villager of Mort'ton.", + "aggressive": "true", + "attack_level": "46", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "46", + "weakness": "6" + }, + { + "id": "1263", + "name": "Wizard", + "examine": "Slightly more magical.", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1264", + "name": "Saradomin wizard", + "examine": "A follower of Saradomin.", + "aggressive": "true", + "attack_level": "90", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "811", + "magic_level": "90", + "melee_animation": "376", + "poison_amount": "30", + "poisonous": "true", + "range_animation": "0", + "range_level": "70", + "respawn_delay": "60", + "safespot": null, + "spell_id": "41", + "strength_level": "90", + "weakness": "3" + }, + { + "id": "1265", + "name": "Rock Crab", + "examine": "No one likes crabs...", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "717,723,722", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "1312", + "magic_level": "1", + "melee_animation": "1312", + "range_animation": "1312", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1266", + "name": "Rocks", + "examine": "A rocky outcrop.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "1312", + "melee_animation": "1312", + "range_animation": "1312", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1267", + "name": "Rock Crab", + "examine": "No one likes crabs...", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "717,723,722", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "1312", + "melee_animation": "1312", + "range_animation": "1312", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1268", + "name": "Rocks", + "examine": "A rocky outcrop.", + "aggressive": "true", + "attack_level": "4", + "attack_speed": "5", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "4", + "lifepoints": "50", + "magic_animation": "1312", + "melee_animation": "1312", + "range_animation": "1312", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1269", + "name": "Olaf the Bard", + "examine": "A Fremennik bard.", + "aggressive": "true", + "attack_level": "10", + "attack_speed": "5", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "8", + "lifepoints": "50", + "magic_animation": "1312", + "melee_animation": "1312", + "range_animation": "1312", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1270", + "name": "Lalli", + "examine": "Distinctly troll-shaped.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1271", + "name": "Golden sheep", + "examine": "Freshly shorn.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1272", + "name": "Golden sheep", + "examine": "Lovely thick wool.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1273", + "name": "Fossegrimen", + "examine": "A powerful spirit that lives in this lake.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1274", + "name": "Ospak", + "examine": "Looks like he's had a few drinks already.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1275", + "name": "Styrmir", + "examine": "Doesn't look like the musical type.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1276", + "name": "Torbrund", + "examine": "Waiting for the show.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1277", + "name": "Fridgeir", + "examine": "He sits impatiently with his friends.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1278", + "name": "Longhall Bouncer", + "examine": "He's fat, he's round, he bounces on the ground. That's how he got the job.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1279", + "name": "The Draugen", + "examine": "A fearful spirit of the drowned.", + "aggressive": "true", + "attack_level": "40", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "9" + }, + { + "id": "1280", + "name": "Butterfly" + }, + { + "id": "1281", + "name": "Sigli the Huntsman", + "examine": "A Fremennik hunter.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1282", + "name": "Sigmund The Merchant", + "examine": "A Fremennik merchant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1283", + "name": "Swensen the Navigator", + "examine": "A Fremennik navigator.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1284", + "name": "Bjorn" + }, + { + "id": "1285", + "name": "Eldgrim" + }, + { + "id": "1286", + "name": "Manni the Reveller", + "examine": "Every innkeeper's best friend!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1287", + "name": "Council workman", + "examine": "Supposedly fixes things around Gielinor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1288", + "name": "Peer the Seer", + "examine": "A Fremennik riddler.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1289", + "name": "Thorvald the Warrior", + "examine": "A Fremennik hero.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1290", + "name": "Koschei the deathless", + "examine": "Your challenge awaits! ", + "attack_level": "13", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "13", + "weakness": "10" + }, + { + "id": "1291", + "name": "Koschei the deathless", + "examine": "He lives again!", + "attack_level": "13", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "13", + "weakness": "10" + }, + { + "id": "1292", + "name": "Koschei the deathless", + "examine": "He keeps on living!", + "attack_level": "13", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "13", + "weakness": "10" + }, + { + "id": "1293", + "name": "Koschei the deathless", + "examine": "He just keeps on going.", + "attack_level": "13", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "13", + "weakness": "10" + }, + { + "id": "1294", + "name": "Brundt the Chieftain", + "examine": "The Fremennik tribe's chieftain.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1295", + "name": "Askeladden" + }, + { + "id": "1296", + "name": "Guard", + "examine": "Stands around and looks at stuff all day.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1297", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1298", + "name": "Town Guard", + "examine": "What a rubbish job he has.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1299", + "name": "Town Guard" + }, + { + "id": "1300", + "name": "A bartender.", + "examine": "The Longhall barkeep", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1301", + "name": "Yrsa", + "examine": "Pretty shabbily dressed for a clothes shop owner.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1302", + "name": "Fisherman", + "examine": "There's something fishy about this guy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1303", + "name": "Skulgrimen", + "examine": "Sells and makes weapons and armour.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1304", + "name": "Sailor", + "examine": "He's strong to the finish", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1305", + "name": "Agnar", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1306", + "name": "Freidir", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1307", + "name": "Borrokar", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1308", + "name": "Lanzig", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "51", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1309", + "name": "Pontak", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "51", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1310", + "name": "Freygerd" + }, + { + "id": "1311", + "name": "Lensa" + }, + { + "id": "1312", + "name": "Jennella" + }, + { + "id": "1313", + "name": "Sassilik", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1314", + "name": "Inga", + "examine": "One of Rellekka's many citizens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1315", + "name": "Fish monger", + "examine": "Fish-tastic!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1316", + "name": "Fur trader", + "examine": "I wonder what he does with all that fur?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1317", + "name": "Market Guard", + "examine": "Keeps the stalls secure.", + "attack_level": "28", + "combat_audio": "", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "1318", + "name": "Warrior", + "examine": "A hardened Fremennik warrior.", + "attack_level": "28", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "1319", + "name": "Fox", + "examine": "A cunning animal.", + "attack_level": "32", + "death_animation": "6570", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "6562", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "7" + }, + { + "id": "1320", + "name": "Bunny", + "examine": "Hoppity", + "attack_level": "1", + "death_animation": "1246", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "1245", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1321", + "name": "Bunny", + "attack_level": "1", + "death_animation": "1246", + "defence_animation": "1244", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "1245", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1322", + "name": "Gull" + }, + { + "id": "1323", + "name": "Gull" + }, + { + "id": "1324", + "name": "Gull" + }, + { + "id": "1325", + "name": "Gull" + }, + { + "id": "1326", + "name": "Bear Cub", + "examine": "Cute. But deadly.", + "aggressive": "true", + "attack_level": "8", + "combat_audio": "498,500,499", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "7" + }, + { + "id": "1327", + "name": "Bear Cub", + "examine": "Cute. But deadly.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "20", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1328", + "name": "Unicorn Foal", + "examine": "Horned horsey.", + "attack_level": "10", + "attack_speed": "4", + "combat_audio": "369,371,370", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "10", + "lifepoints": "15", + "magic_animation": "6376", + "magic_level": "1", + "melee_animation": "6376", + "range_animation": "6376", + "range_level": "1", + "respawn_delay": "90", + "safespot": null, + "strength_level": "6", + "weakness": "7" + }, + { + "id": "1329", + "name": "Black unicorn Foal", + "examine": "Cute but evil.", + "aggressive": "false", + "attack_level": "18", + "attack_speed": "4", + "combat_audio": "369,371,370", + "death_animation": "6377", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "6376", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "18", + "weakness": "7" + }, + { + "id": "1330", + "name": "Wolf", + "examine": "Fearsome predator found only in the Fremennik Province", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "481,491,490", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "69", + "magic_animation": "0", + "melee_animation": "6559", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "69", + "strength_level": "35", + "weakness": "7" + }, + { + "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": "1338", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "attack_level": "80", + "attack_speed": "5", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1341", + "defence_level": "70", + "lifepoints": "70", + "magic_animation": "1340", + "melee_animation": "1340", + "projectile": "288", + "range_animation": "1340", + "range_level": "85", + "safespot": null, + "strength_level": "80" + }, + { + "id": "1339", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1343", + "projectile": "288", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1340", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1343", + "projectile": "288", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1341", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1342", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1343", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1344", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1345", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1346", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1347", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1348", + "name": "Dagannoth mother" + }, + { + "id": "1349", + "name": "Dagannoth mother" + }, + { + "id": "1350", + "name": "Dagannoth mother" + }, + { + "id": "1351", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "60", + "death_animation": "1342", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "128", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "1341", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "1352", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1353", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1354", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1355", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1356", + "name": "Dagannoth mother", + "examine": "A horror from the ocean depths.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1357", + "name": "Sam", + "examine": "Pretty barmaid." + }, + { + "id": "1358", + "name": "Rachael", + "examine": "Pretty barmaid." + }, + { + "id": "1359", + "name": "Queen Sigrid" + }, + { + "id": "1360", + "name": "Banker", + "examine": "He seems happy to see you.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1367", + "name": "Skraeling", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "401", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1368", + "name": "Skraeling", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1369", + "name": "Fishmonger", + "examine": "Hmm", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1370", + "name": "Greengrocer", + "examine": "At least he eats his greens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1371", + "name": "Prince Brand" + }, + { + "id": "1372", + "name": "Princess Astrid" + }, + { + "id": "1373", + "name": "King Vargas" + }, + { + "id": "1374", + "name": "Guard", + "examine": "He's guarding the throne room.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1375", + "name": "Advisor Ghrim" + }, + { + "id": "1376", + "name": "Derrik", + "examine": "Smithy for Sogthorpe." + }, + { + "id": "1377", + "name": "Farmer", + "examine": "He's cutting the wheat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1378", + "name": "Flower Girl", + "examine": "No-one would mistake her for a duchess.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1379", + "name": "Ragnar", + "examine": "A subject of Miscellania." + }, + { + "id": "1380", + "name": "Einar", + "examine": "A subject of Miscellania." + }, + { + "id": "1381", + "name": "Alrik", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1382", + "name": "Thorhild", + "examine": "A subject of Miscellania." + }, + { + "id": "1383", + "name": "Halla" + }, + { + "id": "1384", + "name": "Yrsa" + }, + { + "id": "1385", + "name": "Sailor", + "examine": "He's strong to the finish, because he eats cabbage.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1386", + "name": "Rannveig", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1387", + "name": "Thora" + }, + { + "id": "1388", + "name": "Valgerd", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1389", + "name": "Skraeling", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1390", + "name": "Broddi", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1391", + "name": "Skraeling", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1392", + "name": "Ragnvald", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "1", + "melee_animation": "810", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1393", + "name": "Fishmonger", + "examine": "Hmm", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1394", + "name": "Greengrocer", + "examine": "At least he eats his greens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1395", + "name": "Lumberjack Leif", + "examine": "He's a lumberjack", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1396", + "name": "Miner Magnus", + "examine": "He's just mining his own business.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1397", + "name": "Fisherman Frodi", + "examine": "There's something fishy about him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1398", + "name": "Gardener Gunnhild", + "examine": "She's looking a bit weedy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1399", + "name": "Fishing spot" + }, + { + "id": "1400", + "name": "Gull" + }, + { + "id": "1401", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1402", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1403", + "name": "Rooster", + "attack_level": "1", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "5387", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1404", + "name": "Rabbit", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "6091", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "6090", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1419", + "name": "Lumdo", + "examine": "There to help me make my bids.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1427", + "name": "G.L.O. Caranock", + "examine": "An official looking Gnome with small beady eyes.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "movement_radius": "1" + }, + { + "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": "1442", + "name": "Duke", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1443", + "name": "Oipuis", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1444", + "name": "Uyoro", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1445", + "name": "Ouhai", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1387", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1446", + "name": "Uodai", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1447", + "name": "Padulah", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "133", + "melee_animation": "1383", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1448", + "name": "Awowogei" + }, + { + "id": "1449", + "name": "Uwogo" + }, + { + "id": "1450", + "name": "Muruwoi" + }, + { + "id": "1451", + "name": "Sleeping Monkey", + "examine": "A rather sleepy looking guard", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1452", + "name": "Monkey Child", + "examine": "An adorable little monkey child.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1453", + "name": "The Monkey's Uncle", + "examine": "He looks like the Monkey's Uncle.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1454", + "name": "The Monkey's Aunt", + "examine": "She looks like the Monkey's Aunt.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1455", + "name": "Monkey Guard", + "examine": "A scimitar wielding ninja monkey.", + "attack_level": "66", + "combat_audio": "629,631,630", + "death_animation": "1384", + "defence_animation": "0", + "defence_level": "66", + "lifepoints": "94", + "magic_animation": "0", + "melee_animation": "1392", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "66", + "weakness": "8" + }, + { + "id": "1456", + "name": "Monkey Archer", + "examine": "A bow wielding ninja monkey.", + "aggressive": "true", + "attack_level": "43", + "combat_audio": "629,631,630", + "combat_style": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "58", + "lifepoints": "82", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "1394", + "range_level": "58", + "respawn_delay": "60", + "safespot": null, + "strength_level": "43", + "weakness": "1" + }, + { + "id": "1457", + "name": "Monkey Archer", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "629,631,630", + "combat_style": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "1394", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1458", + "name": "Monkey Archer", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "629,631,630", + "combat_style": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "1394", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1459", + "name": "Monkey Guard", + "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", + "attack_level": "100", + "attack_speed": "6", + "combat_audio": "629,631,630", + "death_animation": "1404", + "defence_animation": "1403", + "defence_level": "90", + "lifepoints": "130", + "magic_animation": "1402", + "melee_animation": "1402", + "range_animation": "1402", + "range_level": "1", + "safespot": null, + "strength_level": "130", + "weakness": "8" + }, + { + "id": "1460", + "name": "Monkey Guard", + "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", + "aggressive": "true", + "attack_level": "63", + "attack_speed": "5", + "combat_audio": "629,631,630", + "death_animation": "1404", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "1402", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "8" + }, + { + "id": "1461", + "name": "Elder Guard", + "examine": "A huge brutish gorilla stands here", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1462", + "name": "Elder Guard" + }, + { + "id": "1463", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1464", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1465", + "name": "Monkey Zombie", + "examine": "A large and lumbering undead monkey.", + "aggressive": "true", + "attack_level": "150", + "attack_speed": "4", + "bonuses": "35,35,35,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "629,631,630", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "60", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "1383", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "60", + "weakness": "6" + }, + { + "id": "1466", + "name": "Monkey Zombie", + "examine": "A large and lumbering undead monkey stands here, blocking the way.", + "aggressive": "true", + "attack_level": "150", + "attack_speed": "4", + "bonuses": "35,35,35,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "629,631,630", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "90", + "lifepoints": "90", + "magic_level": "90", + "melee_animation": "1392", + "range_level": "1", + "respawn_delay": "13", + "safespot": null, + "strength_level": "110" + }, + { + "id": "1467", + "name": "Monkey Zombie", + "examine": "A recently deceased monkey. Its flesh seems to be worse for the wear.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "4", + "bonuses": "15,15,15,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "629,631,630", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "60", + "lifepoints": "60", + "magic_level": "60", + "melee_animation": "1392", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1468", + "name": "Bonzara" + }, + { + "id": "1469", + "name": "Monkey minder", + "examine": "He looks like the type of guy who would mind monkeys.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1470", + "name": "Foreman" + }, + { + "id": "1471", + "name": "Skeleton", + "examine": "It looks just a bit... underfed.", + "aggressive": "true", + "attack_level": "180", + "attack_speed": "4", + "bonuses": "50,50,50,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "774,775,777", + "death_animation": "5521", + "defence_animation": "5520", + "defence_level": "110", + "lifepoints": "77", + "magic_animation": "5519", + "magic_level": "110", + "melee_animation": "5519", + "range_animation": "5519", + "range_level": "1", + "respawn_delay": "100", + "safespot": null, + "slayer_exp": "110", + "strength_level": "90", + "weakness": "8" + }, + { + "id": "1472", + "name": "Jungle demon", + "examine": "A Greater Jungle demon. A magical aura emanates from its hide.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "2", + "combat_audio": "400,404,403", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "428", + "magic_animation": "0", + "magic_level": "50", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "1473", + "name": "Spider", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6251", + "defence_animation": "6250", + "defence_level": "1", + "lifepoints": "2", + "melee_animation": "6249", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1474", + "name": "Spider" + }, + { + "id": "1475", + "name": "Bird", + "examine": "It's a brightly coloured bird of the jungle.", + "attack_level": "5", + "death_animation": "6779", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "6775", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "id": "1476", + "name": "Bird", + "examine": "It's a brightly coloured bird of the jungle. It flies very quickly.", + "attack_level": "5", + "death_animation": "6777", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "6775", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "id": "1477", + "name": "Scorpion", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "17", + "strength_level": "1" + }, + { + "id": "1478", + "name": "Jungle spider", + "examine": "A very dangerous looking spider", + "aggressive": "true", + "attack_level": "47", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5327", + "range_animation": "0", + "range_level": "63", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "2" + }, + { + "id": "1479", + "name": "Snake", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1480", + "name": "Small ninja monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1481", + "name": "Medium ninja monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1482", + "name": "Gorilla" + }, + { + "id": "1483", + "name": "Bearded gorilla", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1484", + "name": "Ancient monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1485", + "name": "Small zombie monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1486", + "name": "Large zombie monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1487", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1514", + "name": "Hiylik Myna", + "examine": "A local holy man who supports the people trying to get though Mort Myre." + }, + { + "id": "1515", + "name": "Nail beast" + }, + { + "id": "1521", + "name": "Nail beast", + "examine": "A huge nail beast. Its nails appear very sharp.", + "attack_level": "150", + "death_animation": "5990", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "550", + "magic_animation": "0", + "melee_animation": "5989", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "150" + }, + { + "id": "1522", + "name": "Nail beast" + }, + { + "id": "1523", + "name": "Nail beast" + }, + { + "id": "1524", + "name": "Undead Lumberjack", + "examine": "He is one", + "attack_level": "73", + "death_animation": "5972", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "5970", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "73" + }, + { + "id": "1525", + "name": "Undead Lumberjack" + }, + { + "id": "1526", + "name": "Lanthus" + }, + { + "id": "1527", + "name": "Mine cart" + }, + { + "id": "1528", + "name": "Zealot", + "examine": "His robes prominently display the star of Saradomin.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1529", + "name": "Sheep" + }, + { + "id": "1530", + "name": "Rabbit" + }, + { + "id": "1531", + "name": "Imp", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "8", + "movement_radius": "25", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1532", + "name": "Barricade", + "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "1", + "death_animation": "9658", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "0", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1533", + "name": "Barricade", + "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "1", + "death_animation": "9658", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "0", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1534", + "name": "Barricade", + "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9658", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "0", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1535", + "name": "Barricade", + "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9658", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "0", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1536", + "name": "Possessed pickaxe", + "aggressive": "true", + "attack_level": "1", + "death_animation": "188", + "defence_animation": "186", + "defence_level": "1", + "lifepoints": "40", + "melee_animation": "185", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1537", + "name": "Haze", + "examine": "A strange disturbance in the air.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1538", + "name": "Corpse", + "examine": "Well", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1539", + "name": "Skeletal miner", + "examine": "I don't think the pickaxe is for hitting rocks.", + "aggressive": "true", + "attack_level": "35", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "0", + "strength_level": "35", + "weakness": "8" + }, + { + "id": "1540", + "name": "Treus Dayth", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "5540", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1541", + "name": "Ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "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": "1549", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5540", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "1550", + "name": "Chompy bird", + "examine": "A large boisterous bird, a delicacy for ogres.", + "aggressive": "false", + "attack_level": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "1449,1451,1450", + "death_animation": "6762", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "6761", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "3" + }, + { + "id": "1551", + "name": "Mischievous ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1552", + "name": "Santa" + }, + { + "id": "1553", + "name": "Ug", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1554", + "name": "Aga" + }, + { + "id": "1555", + "name": "Arrg" + }, + { + "id": "1556", + "name": "Arrg", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "150", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1557", + "name": "Ug", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1558", + "name": "Ice wolf", + "examine": "Not man's best friend.", + "aggressive": "true", + "attack_level": "51", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "51", + "lifepoints": "72", + "magic_animation": "0", + "melee_animation": "6559", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "51", + "weakness": "7" + }, + { + "id": "1559", + "name": "Ice wolf" + }, + { + "id": "1560", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1561", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1562", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1563", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1564", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1565", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1566", + "name": "Ice Troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "1567", + "name": "Cyreg Paddlehorn", + "examine": "A tall and imposing man who's more than familiar with boating.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1582", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "1583", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "1584", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "1585", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "1586", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "1587", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "60", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "60", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "1588", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "60", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "60", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "1589", + "name": "Baby red dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "50,40,64,60,30,45,40,40,20,40,40,20,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "42", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "40", + "melee_animation": "25", + "range_animation": "25", + "range_level": "1", + "respawn_delay": "32", + "safespot": null, + "strength_level": "40", + "weakness": "3" + }, + { + "id": "1590", + "name": "Bronze dragon", + "examine": "Its scales seem to be made of bronze.", + "aggressive": "true", + "attack_level": "112", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "112", + "lifepoints": "122", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "40", + "safespot": null, + "slayer_exp": "125", + "strength_level": "112", + "weakness": "7" + }, + { + "id": "1591", + "name": "Iron dragon", + "examine": "Its scales seem to be made of iron.", + "aggressive": "true", + "attack_level": "165", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "165", + "lifepoints": "165", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "45", + "safespot": null, + "slayer_exp": "173", + "strength_level": "165", + "weakness": "7" + }, + { + "id": "1592", + "name": "Steel dragon", + "examine": "Its scales seem to be made of steel.", + "aggressive": "true", + "attack_level": "215", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,30,90,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "215", + "lifepoints": "210", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "45", + "safespot": null, + "slayer_exp": "221", + "strength_level": "215", + "weakness": "7" + }, + { + "id": "1593", + "name": "Wild dog", + "examine": "An unsuitable pet.", + "aggressive": "true", + "attack_level": "25", + "bonuses": "10,15,5,10,30,5,30,30,30,10,30,5,40,5,5", + "death_animation": "6564", + "defence_animation": "6563", + "defence_level": "25", + "lifepoints": "62", + "magic_animation": "6562", + "magic_level": "20", + "melee_animation": "6562", + "range_animation": "6562", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "1594", + "name": "Wild dog", + "examine": "Looks like it's got Rabies!", + "aggressive": "true", + "attack_level": "25", + "bonuses": "10,15,5,10,30,5,30,30,30,10,30,5,40,5,5", + "death_animation": "6564", + "defence_animation": "6563", + "defence_level": "25", + "lifepoints": "62", + "magic_animation": "6562", + "magic_level": "20", + "melee_animation": "6562", + "range_animation": "6562", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "25" + }, + { + "id": "1595", + "name": "Saniboch", + "examine": "Looks like he wants money." + }, + { + "id": "1596", + "name": "Vannaka" + }, + { + "id": "1597", + "name": "Vannaka", + "examine": "He looks dangerous." + }, + { + "id": "1598", + "name": "Chaeldar", + "examine": "She looks dangerous!" + }, + { + "id": "1599", + "name": "Cave crawler" + }, + { + "id": "1600", + "name": "Cave crawler", + "examine": "A spiky crawling critter. ", + "attack_level": "28", + "attack_speed": "4", + "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", + "combat_audio": "341,343,342", + "death_animation": "265", + "defence_animation": "267", + "defence_level": "30", + "lifepoints": "23", + "magic_animation": "266", + "melee_animation": "266", + "poison_amount": "40", + "poisonous": "true", + "range_animation": "266", + "range_level": "38", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "1601", + "name": "Cave crawler", + "examine": "A spiky crawling critter. ", + "attack_level": "28", + "attack_speed": "4", + "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", + "combat_audio": "341,343,342", + "death_animation": "265", + "defence_animation": "267", + "defence_level": "30", + "lifepoints": "23", + "magic_animation": "266", + "melee_animation": "266", + "poison_amount": "40", + "poisonous": "true", + "range_animation": "266", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30" + }, + { + "id": "1602", + "name": "Cave crawler", + "examine": "A spiky crawling critter. ", + "attack_level": "28", + "attack_speed": "4", + "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", + "combat_audio": "341,343,342", + "death_animation": "265", + "defence_animation": "267", + "defence_level": "30", + "lifepoints": "23", + "magic_animation": "266", + "melee_animation": "266", + "poison_amount": "40", + "poisonous": "true", + "range_animation": "266", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30" + }, + { + "id": "1603", + "name": "Cave crawler", + "examine": "A spiky crawling critter. ", + "attack_level": "28", + "attack_speed": "4", + "bonuses": "30,60,60,60,60,66,0,0,0,0,0,0,0,0,0", + "combat_audio": "341,343,342", + "death_animation": "265", + "defence_animation": "267", + "defence_level": "30", + "lifepoints": "23", + "magic_animation": "266", + "melee_animation": "266", + "poison_amount": "40", + "poisonous": "true", + "range_animation": "266", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30" + }, + { + "id": "1604", + "name": "Aberrant spectre", + "examine": "A very smelly ghost.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "272,275,274", + "combat_style": "2", + "death_animation": "9467", + "defence_animation": "9468", + "defence_level": "90", + "end_gfx": "336", + "lifepoints": "90", + "magic_animation": "9466", + "magic_level": "105", + "melee_animation": "9466", + "projectile": "335", + "range_animation": "1507", + "range_level": "1", + "safespot": null, + "slayer_exp": "90", + "start_gfx": "334", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "1605", + "name": "Aberrant spectre", + "examine": "A very smelly ghost.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "272,275,274", + "combat_style": "2", + "death_animation": "9467", + "defence_animation": "9468", + "defence_level": "90", + "end_gfx": "336", + "lifepoints": "90", + "magic_animation": "9466", + "magic_level": "105", + "melee_animation": "9466", + "projectile": "335", + "range_animation": "1507", + "range_level": "1", + "safespot": null, + "slayer_exp": "90", + "start_gfx": "334", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "1606", + "name": "Aberrant spectre", + "examine": "A very smelly ghost.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "272,275,274", + "combat_style": "2", + "death_animation": "9467", + "defence_animation": "9468", + "defence_level": "90", + "end_gfx": "336", + "lifepoints": "90", + "magic_animation": "9466", + "magic_level": "105", + "melee_animation": "9466", + "projectile": "335", + "range_animation": "1507", + "range_level": "1", + "safespot": null, + "slayer_exp": "90", + "start_gfx": "334", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "1607", + "name": "Aberrant spectre", + "examine": "A very smelly ghost.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,15,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "272,275,274", + "combat_style": "2", + "death_animation": "9467", + "defence_animation": "9468", + "defence_level": "90", + "end_gfx": "336", + "lifepoints": "90", + "magic_animation": "9466", + "magic_level": "105", + "melee_animation": "9466", + "projectile": "335", + "range_animation": "1507", + "range_level": "1", + "safespot": null, + "slayer_exp": "90", + "start_gfx": "334", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "1608", + "name": "Kurask", + "examine": "Large, heavy, with sharp things attached to its head.", + "aggressive": "true", + "attack_level": "67", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "combat_audio": "588,590,589", + "death_animation": "9440", + "defence_animation": "9441", + "defence_level": "105", + "lifepoints": "97", + "magic_animation": "9439", + "magic_level": "1", + "melee_animation": "9439", + "range_animation": "9439", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "97", + "strength_level": "105" + }, + { + "id": "1609", + "name": "Kurask", + "examine": "Large, heavy, with sharp things attached to its head.", + "aggressive": "true", + "attack_level": "67", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "combat_audio": "588,590,589", + "death_animation": "9440", + "defence_animation": "9441", + "defence_level": "105", + "lifepoints": "97", + "magic_animation": "9439", + "magic_level": "1", + "melee_animation": "9439", + "range_animation": "9439", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "97", + "strength_level": "105" + }, + { + "id": "1610", + "name": "Gargoyle", + "examine": "Flies like a rock.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "428,430,429", + "death_animation": "1518", + "defence_animation": "9455", + "defence_level": "107", + "lifepoints": "105", + "magic_animation": "1516", + "magic_level": "1", + "melee_animation": "9454", + "range_animation": "1516", + "range_level": "1", + "safespot": null, + "slayer_exp": "105", + "strength_level": "95" + }, + { + "id": "1611", + "name": "Swarming turoth", + "examine": "Flies like a rock.", + "attack_level": "45", + "attack_speed": "5", + "clue_level": "2", + "death_animation": "1518", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "105", + "magic_animation": "1516", + "melee_animation": "1516", + "range_animation": "1516", + "range_level": "49", + "safespot": null, + "slayer_exp": "105", + "strength_level": "104", + "weakness": "0" + }, + { + "id": "1612", + "name": "Banshee", + "examine": "A tortured screaming soul.", + "aggressive": "true", + "attack_level": "22", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,5,0,5,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "284,286,285", + "death_animation": "9450", + "defence_animation": "9451", + "defence_level": "22", + "lifepoints": "22", + "magic_animation": "1523", + "magic_level": "1", + "melee_animation": "9449", + "range_animation": "1523", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "22", + "strength_level": "15" + }, + { + "id": "1613", + "name": "Nechryael", + "examine": "An evil death demon.", + "attack_level": "97", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "645,647,646", + "death_animation": "9488", + "defence_animation": "9489", + "defence_level": "105", + "lifepoints": "105", + "magic_animation": "1528", + "magic_level": "1", + "melee_animation": "9487", + "range_animation": "1528", + "range_level": "1", + "safespot": null, + "strength_level": "97" + }, + { + "id": "1614", + "name": "Death spawn", + "examine": "An evil death spawn.", + "aggressive": "true", + "attack_level": "67", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", + "death_animation": "9460", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "60", + "magic_animation": "1540", + "magic_level": "1", + "melee_animation": "1540", + "poison_immune": "true", + "range_animation": "1540", + "range_level": "1", + "safespot": null, + "strength_level": "7", + "weakness": "2" + }, + { + "id": "1615", + "name": "Abyssal demon", + "examine": "A denizen of the Abyss!", + "attack_level": "97", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "276,278,277", + "death_animation": "1538", + "defence_animation": "0", + "defence_level": "135", + "lifepoints": "150", + "magic_animation": "1537", + "magic_level": "1", + "melee_animation": "1537", + "range_animation": "1537", + "range_level": "1", + "safespot": null, + "slayer_exp": "150", + "strength_level": "67", + "weakness": "1" + }, + { + "id": "1616", + "name": "Basilisk", + "examine": "The eyes of evil.", + "aggressive": "false", + "attack_level": "30", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,0,20,0,0,0,0,0,0", + "combat_audio": "288,290,289", + "death_animation": "264", + "defence_animation": "261", + "defence_level": "75", + "lifepoints": "75", + "magic_animation": "260", + "magic_level": "1", + "melee_animation": "260", + "range_animation": "260", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "75", + "strength_level": "45" + }, + { + "id": "1617", + "name": "Basilisk", + "examine": "The eyes of evil.", + "aggressive": "false", + "attack_level": "30", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,0,20,0,0,0,0,0,0", + "combat_audio": "288,290,289", + "death_animation": "264", + "defence_animation": "261", + "defence_level": "75", + "lifepoints": "75", + "magic_animation": "260", + "magic_level": "1", + "melee_animation": "260", + "range_animation": "260", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "75", + "strength_level": "45" + }, + { + "id": "1618", + "name": "Bloodveld", + "examine": "The tongue of evil.", + "aggressive": "false", + "attack_level": "75", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "312,314,313", + "death_animation": "9131", + "defence_animation": "9132", + "defence_level": "30", + "lifepoints": "120", + "magic_animation": "1552", + "melee_animation": "9130", + "range_animation": "1552", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "120", + "strength_level": "45" + }, + { + "id": "1619", + "name": "Bloodveld", + "examine": "The tongue of evil.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "312,314,313", + "death_animation": "9131", + "defence_animation": "9132", + "defence_level": "30", + "lifepoints": "120", + "magic_animation": "1552", + "melee_animation": "9130", + "range_animation": "1552", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "120", + "strength_level": "45" + }, + { + "id": "1620", + "name": "Cockatrice", + "examine": "The winged reptile.", + "attack_level": "32", + "attack_speed": "5", + "bonuses": "30,30,30,30,30,35,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "363,365,364", + "death_animation": "7763", + "defence_animation": "7761", + "defence_level": "35", + "lifepoints": "37", + "magic_animation": "7762", + "melee_animation": "7762", + "range_animation": "7762", + "range_level": "32", + "safespot": null, + "slayer_exp": "37", + "strength_level": "32", + "weakness": "2" + }, + { + "id": "1621", + "name": "Cockatrice", + "examine": "The winged reptile.", + "attack_level": "32", + "attack_speed": "5", + "bonuses": "30,30,30,30,30,35,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "363,365,364", + "death_animation": "7763", + "defence_animation": "7761", + "defence_level": "35", + "lifepoints": "37", + "magic_animation": "7762", + "melee_animation": "7762", + "range_animation": "7762", + "range_level": "1", + "safespot": null, + "slayer_exp": "37", + "strength_level": "32" + }, + { + "id": "1622", + "name": "Turoth", + "examine": "He's one big leg short!", + "attack_level": "55", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "85", + "lifepoints": "78", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "85" + }, + { + "id": "1623", + "name": "Turoth", + "examine": "He's one big leg short!", + "attack_level": "58", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "88", + "lifepoints": "81", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "88" + }, + { + "id": "1624", + "name": "Dust devil", + "examine": "The vacuumed face of evil.", + "attack_level": "105", + "attack_speed": "4", + "combat_audio": "414,416,415", + "death_animation": "1558", + "defence_animation": "1555", + "defence_level": "40", + "lifepoints": "105", + "magic_animation": "1557", + "melee_animation": "1557", + "range_animation": "1557", + "range_level": "1", + "safespot": null, + "slayer_exp": "105", + "strength_level": "70", + "weakness": "2" + }, + { + "id": "1625", + "name": "Smokedevil", + "examine": "The cave-dwelling cousin of the dust devils.", + "attack_level": "105", + "death_animation": "1558", + "defence_animation": "0", + "defence_level": "61", + "lifepoints": "87", + "magic_animation": "0", + "melee_animation": "1557", + "range_animation": "0", + "range_level": "61", + "safespot": null, + "strength_level": "105", + "weakness": "2" + }, + { + "id": "1626", + "name": "Turoth", + "examine": "She's one leg short!", + "attack_level": "56", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "86", + "lifepoints": "79", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "86" + }, + { + "id": "1627", + "name": "Turoth", + "examine": "It's one leg short!", + "attack_level": "54", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "84", + "lifepoints": "77", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "84" + }, + { + "id": "1628", + "name": "Turoth", + "examine": "It's one small leg short!", + "attack_level": "53", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "83", + "lifepoints": "76", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "83" + }, + { + "id": "1629", + "name": "Turoth", + "examine": "He's one big leg short!", + "attack_level": "57", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "87", + "lifepoints": "80", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "87" + }, + { + "id": "1630", + "name": "Turoth", + "examine": "He's one big leg short!", + "attack_level": "57", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "873,875,874", + "death_animation": "9472", + "defence_animation": "9473", + "defence_level": "87", + "lifepoints": "80", + "magic_animation": "9471", + "magic_level": "1", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "87" + }, + { + "id": "1631", + "name": "Rockslug", + "examine": "A rocky slug.", + "attack_level": "24", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "729,731,730", + "death_animation": "1597", + "defence_animation": "1596", + "defence_level": "30", + "lifepoints": "30", + "magic_animation": "1595", + "magic_level": "1", + "melee_animation": "1595", + "range_animation": "1595", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "7" + }, + { + "id": "1632", + "name": "Rockslug", + "examine": "A rocky slug.", + "attack_level": "24", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "729,731,730", + "death_animation": "1597", + "defence_animation": "1596", + "defence_level": "30", + "lifepoints": "30", + "magic_animation": "1595", + "magic_level": "1", + "melee_animation": "1595", + "range_animation": "1595", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "7" + }, + { + "id": "1633", + "name": "Pyrefiend", + "examine": "A small fire demon.", + "attack_level": "22", + "attack_speed": "5", + "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", + "clue_level": "1", + "combat_audio": "696,698,697", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "19", + "lifepoints": "45", + "magic_level": "10", + "melee_animation": "8080", + "range_level": "19", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" + }, + { + "id": "1634", + "name": "Pyrefiend", + "examine": "A small fire demon.", + "attack_level": "22", + "attack_speed": "5", + "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", + "clue_level": "1", + "combat_audio": "696,698,697", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "19", + "lifepoints": "45", + "magic_level": "10", + "melee_animation": "8080", + "range_level": "19", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" + }, + { + "id": "1635", + "name": "Pyrefiend", + "examine": "A small fire demon.", + "attack_level": "22", + "attack_speed": "5", + "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", + "clue_level": "1", + "combat_audio": "696,698,697", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "19", + "lifepoints": "45", + "magic_level": "10", + "melee_animation": "8080", + "range_level": "19", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" + }, + { + "id": "1636", + "name": "Pyrefiend", + "examine": "A small fire demon.", + "attack_level": "22", + "attack_speed": "5", + "bonuses": "52,30,22,1,1,0,0,0,0,0,0,10,10,10,0", + "clue_level": "1", + "combat_audio": "696,698,697", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "19", + "lifepoints": "45", + "magic_level": "10", + "melee_animation": "8080", + "range_level": "19", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" + }, + { + "id": "1637", + "name": "Jelly", + "examine": "It's a Jelly.", + "attack_level": "40", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "death_animation": "8570", + "defence_animation": "8571", + "defence_level": "40", + "lifepoints": "75", + "magic_animation": "8569", + "magic_level": "40", + "melee_animation": "8569", + "range_animation": "8569", + "range_level": "40", + "safespot": null, + "slayer_exp": "75", + "strength_level": "45", + "weakness": "2" + }, + { + "id": "1638", + "name": "Jelly", + "examine": "Doesn't look so tough...", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "death_animation": "8570", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "melee_animation": "8569", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "1639", + "name": "Jelly", + "examine": "Wibbly.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "death_animation": "8570", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "melee_animation": "8569", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "1640", + "name": "Jelly", + "examine": "There's always room for jelly.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "death_animation": "8570", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "140", + "magic_animation": "0", + "melee_animation": "8569", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "1641", + "name": "Jelly", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "defence_animation": "8571", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "8569", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1" + }, + { + "id": "1642", + "name": "Jelly", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "547,550,549", + "defence_animation": "8571", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "8569", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1" + }, + { + "id": "1643", + "name": "Infernal Mage", + "examine": "An evil magic user.", + "attack_level": "1", + "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "end_gfx": "101", + "lifepoints": "60", + "magic_animation": "811", + "magic_level": "75", + "melee_animation": "811", + "projectile": "100", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "99", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1644", + "name": "Infernal Mage", + "attack_level": "1", + "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "end_gfx": "101", + "lifepoints": "60", + "magic_animation": "811", + "magic_level": "75", + "melee_animation": "811", + "projectile": "100", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "99", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1645", + "name": "Infernal Mage", + "examine": "An evil magic user.", + "attack_level": "1", + "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "end_gfx": "101", + "lifepoints": "60", + "magic_animation": "811", + "magic_level": "75", + "melee_animation": "811", + "projectile": "100", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "99", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1646", + "name": "Infernal Mage", + "examine": "An evil magic user.", + "attack_level": "1", + "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "end_gfx": "101", + "lifepoints": "60", + "magic_animation": "811", + "magic_level": "75", + "melee_animation": "811", + "projectile": "100", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "99", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1647", + "name": "Infernal Mage", + "examine": "An evil magic user.", + "attack_level": "1", + "bonuses": "0,0,0,0,0,0,0,0,40,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "end_gfx": "101", + "lifepoints": "60", + "magic_animation": "811", + "magic_level": "75", + "melee_animation": "811", + "projectile": "100", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "99", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "1648", + "name": "Crawling Hand", + "examine": "Now THAT'S handy.", + "attack_level": "5", + "attack_speed": "5", + "bonuses": "5,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "377,379,378", + "death_animation": "9126", + "defence_animation": "9127", + "defence_level": "5", + "lifepoints": "16", + "magic_animation": "9125", + "melee_animation": "9125", + "range_animation": "9125", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "19", + "strength_level": "5", + "weakness": "9" + }, + { + "id": "1649", + "name": "Crawling Hand", + "examine": "Gimmie five. Actually", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9126", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "9125", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "19", + "strength_level": "8", + "weakness": "9" + }, + { + "id": "1650", + "name": "Crawling Hand", + "examine": "Gimmie five. Actually", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9126", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "9125", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "9" + }, + { + "id": "1651", + "name": "Crawling Hand", + "examine": "Gimmie five. Actually", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9126", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "9125", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "19", + "strength_level": "8", + "weakness": "9" + }, + { + "id": "1652", + "name": "Crawling Hand", + "examine": "Gimmie five. Actually", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9126", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "9125", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "19", + "strength_level": "8", + "weakness": "9" + }, + { + "id": "1653", + "name": "Crawling Hand", + "examine": "Now THAT's handy.", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9445", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "9444", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "16", + "strength_level": "13", + "weakness": "9" + }, + { + "id": "1654", + "name": "Crawling Hand", + "examine": "I'm glad it's just the hand I can see...", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9445", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "9444", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "16", + "strength_level": "13", + "weakness": "9" + }, + { + "id": "1655", + "name": "Crawling Hand", + "examine": "A big severed hand.", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9445", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "9444", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "9" + }, + { + "id": "1656", + "name": "Crawling Hand", + "examine": "Give the guy a big hand.....", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9445", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "9444", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "16", + "strength_level": "13", + "weakness": "9" + }, + { + "id": "1657", + "name": "Crawling Hand", + "examine": "A big severed hand.", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "377,379,378", + "death_animation": "9445", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "9444", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "16", + "strength_level": "13", + "weakness": "9" + }, + { + "id": "1658", + "name": "Robe Store owner", + "examine": "A Supplier of Magical robes.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1659", + "name": "Skullball" + }, + { + "id": "1660", + "name": "Skullball Boss", + "examine": "He's in charge of the Skullball Course.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1661", + "name": "Agility Boss", + "examine": "He's in charge of the Agility Course.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1662", + "name": "Skullball Trainer", + "examine": "A skullball guide.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1663", + "name": "Agility Trainer", + "examine": "A werewolf agility trainer.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1664", + "name": "Agility Trainer" + }, + { + "id": "1665", + "name": "Werewolf", + "examine": "Looks like he's guarding a trapdoor...", + "attack_level": "1", + "clue_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1666", + "name": "Dr Fenkenstrain" + }, + { + "id": "1671", + "name": "Fenkenstrain's Monster" + }, + { + "id": "1674", + "name": "Lord Rologarth" + }, + { + "id": "1675", + "name": "Gardener Ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1676", "name": "Experiment", + "examine": "It has a key hanging from its collar.", + "attack_level": "40", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "1628", + "defence_animation": "1627", "defence_level": "50", "lifepoints": "40", - "strength_level": "50", - "id": "1676", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "magic_level": "1", + "melee_animation": "1626", "range_level": "1", - "attack_level": "40" + "strength_level": "50" }, { - "examine": "One of Fenkenstrain's failed experiments.", - "melee_animation": "1612", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "1613", - "death_animation": "1611", + "id": "1677", "name": "Experiment", + "examine": "One of Fenkenstrain's failed experiments.", + "aggressive": "", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "", + "death_animation": "1618", + "defence_animation": "1617", "defence_level": "30", "lifepoints": "100", - "strength_level": "1", - "id": "1678", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "magic_level": "1", + "melee_animation": "1616", "range_level": "1", - "attack_level": "1" + "strength_level": "1" + }, + { + "id": "1678", + "name": "Experiment", + "examine": "One of Fenkenstrain's failed experiments.", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "1611", + "defence_animation": "1613", + "defence_level": "30", + "lifepoints": "100", + "magic_level": "1", + "melee_animation": "1612", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "1679", + "name": "Eluned" + }, + { + "id": "1680", + "name": "Islwyn" + }, + { + "id": "1681", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "66,62,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "60", + "lifepoints": "120", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "safespot": null, + "strength_level": "60", + "weakness": "3" + }, + { + "id": "1682", + "name": "Golrie" + }, + { + "id": "1683", + "name": "Velorina" + }, + { + "id": "1684", + "name": "Necrovarus", + "examine": "An evil priest." + }, + { + "id": "1685", + "name": "Gravingas" + }, + { + "id": "1686", + "name": "Ghost disciple", + "examine": "A ghost disciple.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1687", + "name": "Ak-Haranu" + }, + { + "id": "1688", + "name": "Ak-Haranu", + "examine": "A trader from across the eastern sea.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "movement_radius": "2", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "0", + "start_height": "0", + "strength_level": "1" + }, + { + "id": "1689", + "name": "Undead cow" + }, + { + "id": "1691", + "name": "Undead cow", + "examine": "It's an undead cow.", + "aggressive": "false", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "-15,-15,-15,0,0,-21,-21,-21,-21,-21,0,-15,0,0,0", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5849", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "5", + "safespot": null, + "strength_level": "3", + "weakness": "8" + }, + { + "id": "1692", + "name": "Undead chicken", + "examine": "Yup, definitely a chicken... an undead chicken.", + "aggressive": "false", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,0,-42,0,0,0", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5387", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1693", + "name": "Giant lobster", + "examine": "An extremely vicious lobster.", + "attack_level": "30", + "death_animation": "6267", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "128", + "magic_animation": "0", + "melee_animation": "6265", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "1694", + "name": "Robin" + }, + { + "id": "1695", + "name": "Old crone", + "examine": "An old", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1696", + "name": "Old man", + "examine": "A creaky old man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1697", + "name": "Ghost villager", + "examine": "A spooky ghost villager.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1698", + "name": "Tortured soul", + "examine": "This poor soul cannot understand why it has not passed to the next world.", + "aggressive": "true", + "attack_level": "52", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "51", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "51", + "strength_level": "62", + "weakness": "3" + }, + { + "id": "1699", + "name": "Ghost shopkeeper", + "examine": "Beware the ghostly shopkeeper's wares!", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1700", + "name": "Ghost innkeeper", + "examine": "Doesn't look like the bar's open anymore.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1701", + "name": "Ghost farmer", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1702", + "name": "Ghost banker", + "examine": "A ghost banker.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1703", + "name": "Ghost sailor", + "examine": "A ghost sailor.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1704", + "name": "Ghost captain", + "examine": "A ghostship captain.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1705", + "name": "Ghost captain", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1706", + "name": "Ghost guard", + "examine": "This ghost guards the gates of Port Phasmatys.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1707", + "name": "Ghost (?)", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1708", + "name": "Ghost (?)", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1709", + "name": "Johanhus Ulsbrecht" + }, + { + "id": "1710", + "name": "H.A.M. Guard", + "examine": "A guard for the humans against monster group.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "1711", + "name": "H.A.M. Guard", + "examine": "A guard for the humans against monster group.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "1712", + "name": "H.A.M. Guard", + "examine": "A guard for the humans against monster group.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "1713", + "name": "H.A.M. Deacon", + "examine": "A deacon in the Humans Against Monsters group. A rather enthusiastic chap.", + "attack_level": "1", + "clue_level": "0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1714", + "name": "H.A.M. Member", + "examine": "A member of the 'Humans Against Monsters' group.", + "attack_level": "1", + "clue_level": "0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1715", + "name": "H.A.M. Member", + "examine": "A member of the 'Humans Against Monsters' group.", + "attack_level": "1", + "clue_level": "0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1716", + "name": "H.A.M. Member", + "examine": "A member of the 'Humans Against Monsters' group.", + "attack_level": "1", + "clue_level": "0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1717", + "name": "H.A.M. Member", + "examine": "A member of the 'Humans Against Monsters' group.", + "attack_level": "1", + "clue_level": "0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1718", + "name": "Jimmy the Chisel", + "examine": "A young man with a dark and mysterious past.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1739", + "name": "Oak", + "examine": "Manages the mill.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1740", + "name": "Yew", + "examine": "One of Sigmund's elite guards.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1741", + "name": "Evergreen", + "examine": "One of Sigmund's elite guards.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1742", + "name": "Evergreen", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1743", + "name": "Evergreen", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1744", + "name": "Tree", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1745", + "name": "Dead tree", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1746", + "name": "Achey Tree", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1747", + "name": "Tree", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1748", + "name": "Tree", + "examine": "A member of Sigmund's H.A.M. splinter group.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1749", + "name": "Hollow tree" + }, + { + "id": "1750", + "name": "Hollow tree" + }, + { + "id": "1751", + "name": "Terrorbird", + "examine": "A giant raptor.", + "aggressive": "false", + "attack_level": "23", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "832,834,833", + "death_animation": "1013", + "defence_animation": "1012", + "defence_level": "19", + "lifepoints": "34", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1010", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "23" + }, + { + "id": "1752", + "name": "Mounted terrorbird gnome", + "examine": "These gnomes know how to get around!", + "attack_level": "33", + "death_animation": "6791", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "6790", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "6" + }, + { + "id": "1753", + "name": "Mounted terrorbird gnome" + }, + { + "id": "1754", + "name": "Crow", + "examine": "A farmer's enemy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1755", + "name": "Crow", + "examine": "A farmer's enemy." + }, + { + "id": "1756", + "name": "Crow" + }, + { + "id": "1757", + "name": "Farmer", + "examine": "He grows the crops in this area.", + "attack_level": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "9" + }, + { + "id": "1758", + "name": "Farmer", + "examine": "He grows the crops in this area.", + "attack_level": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "9" + }, + { + "id": "1759", + "name": "Farmer", + "examine": "Farming.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1760", + "name": "Farmer", + "examine": "He grows the crops in this area.", + "attack_level": "5", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "9" + }, + { + "id": "1761", + "name": "Farmer", + "examine": "Farming the wheat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1762", + "name": "Sheep" + }, + { + "id": "1763", + "name": "Sheep", + "examine": "White and fluffy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1764", + "name": "Sheep" + }, + { + "id": "1765", + "name": "Sheep" + }, + { + "id": "1766", + "name": "Cow calf", + "examine": "Young, but still beefy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "366,368,367", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "6", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1" + }, + { + "id": "1767", + "name": "Cow", + "examine": "Where beef comes from.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "369,371,370", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1", + "weakness": "8" + }, + { + "id": "1768", + "name": "Cow calf", + "examine": "Young, but still beefy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "366,368,367", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "6", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1" + }, + { + "id": "1769", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1770", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1771", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1772", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1773", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1774", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1775", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1776", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2", + "weakness": "7" + }, + { + "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": "1783", + "name": "Richard", + "examine": "A very brave merchant." + }, + { + "id": "1784", + "name": "Neil" + }, + { + "id": "1785", + "name": "Edmond", + "examine": "A very brave merchant." + }, + { + "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": "1794", + "name": "Hammerspike Stoutbeard", + "examine": "He looks quite independent in an aggressive and business like way.", + "attack_level": "1", + "combat_audio": "498,500,499", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1795", + "name": "Dwarf gang member", + "examine": "A short stout menacing fellow.", + "attack_level": "25", + "attack_speed": "6", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "1796", + "name": "Dwarf gang member", + "examine": "A short stout menacing fellow.", + "attack_level": "25", + "attack_speed": "6", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "1797", + "name": "Dwarf gang member", + "examine": "A short stout menacing fellow.", + "attack_level": "25", + "attack_speed": "6", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "1798", + "name": "Phantuwti Fanstuwi Farsight" + }, + { + "id": "1799", + "name": "Tindel Marchant" + }, + { + "id": "1800", + "name": "Gnormadium Avlafrim" + }, + { + "id": "1801", + "name": "Petra Fiyed" + }, + { + "id": "1802", + "name": "Slagilith", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1752", + "defence_animation": "1751", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "1750", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1803", + "name": "Rock pile", + "examine": "A pile of boulders.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1804", + "name": "Slagilith" + }, + { + "id": "1805", + "name": "Guard", + "examine": "He seems to be guarding a pile of rocks. Interesting job.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1806", + "name": "Guard", + "examine": "He seems to be guarding a single rock. Interesting job.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1807", + "name": "Hamal the Chieftain", + "examine": "The Chieftain of the mountain camp.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1808", + "name": "Ragnar" + }, + { + "id": "1809", + "name": "Svidi" + }, + { + "id": "1810", + "name": "Jokul" + }, + { + "id": "1811", + "name": "The Kendal" + }, + { + "id": "1812", + "name": "The Kendal", + "examine": "Is that a bear... or a man?", + "aggressive": "true", + "attack_level": "23", + "death_animation": "1759", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "85", + "magic_animation": "0", + "magic_level": "23", + "melee_animation": "1760", + "range_animation": "0", + "range_level": "23", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "10" + }, + { + "id": "1813", + "name": "The Kendal", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1759", + "defence_animation": "1758", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "1760", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1814", + "name": "Camp dweller", + "examine": "One of the inhabitants of the camp.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "1815", + "name": "Camp dweller", + "examine": "One of the inhabitants of the camp.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "1816", + "name": "Camp dweller", + "examine": "One of the inhabitants of the camp.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "1817", + "name": "Camp dweller", + "examine": "One of the inhabitants of the camp.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "1818", + "name": "Camp dweller", + "examine": "One of the inhabitants of the camp.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "id": "1819", + "name": "Mountain Goat", + "examine": "Looks a little underfed.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1820", + "name": "Mountain Goat", + "examine": "This goat belongs to the mountain camp people.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1821", + "name": "Bald Headed Eagle" + }, + { + "id": "1822", + "name": "Cave goblin", + "examine": "A goblin with big bulging eyes.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6001", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1823", + "name": "Cave goblin", + "examine": "A goblin with big bulging eyes.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6001", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1824", + "name": "Cave goblin", + "examine": "A goblin with big bulging eyes.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6001", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1825", + "name": "Cave goblin", + "examine": "A goblin with big bulging eyes.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6001", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "1826", + "name": "Zombie", + "examine": "A human zombie.", + "attack_level": "30", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "30", + "lifepoints": "50", + "melee_animation": "5568", + "range_level": "1", + "safespot": null, + "strength_level": "30" + }, + { + "id": "1827", + "name": "Gargoyle", + "examine": "Flies like a rock.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "428,430,429", + "death_animation": "1518", + "defence_animation": "9455", + "defence_level": "107", + "lifepoints": "105", + "magic_animation": "1516", + "magic_level": "1", + "melee_animation": "9454", + "range_animation": "1516", + "range_level": "1", + "safespot": null, + "slayer_exp": "105", + "strength_level": "95" + }, + { + "id": "1828", + "name": "Giant frog", + "examine": "It didn't get that big eating flies!", + "attack_level": "45", + "attack_speed": "6", + "bonuses": "10,10,10,10,10,10,1,10,10,10,10,37,0,0,0", + "death_animation": "1795", + "defence_animation": "1794", + "defence_level": "60", + "lifepoints": "100", + "magic_animation": "1793", + "melee_animation": "1793", + "range_animation": "1793", + "range_level": "32", + "safespot": null, + "strength_level": "45", + "weakness": "1" + }, + { + "id": "1829", + "name": "Big frog", + "examine": "It didn't get that big eating flies.", + "attack_level": "1", + "death_animation": "1795", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "1793", + "range_animation": "0", + "range_level": "26", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "1830", + "name": "Frog" + }, + { + "id": "1831", + "name": "Cave slime", + "examine": "A foul-smelling blob of protoplasm.", + "aggressive": "false", + "attack_level": "13", + "attack_speed": "4", + "clue_level": "0", + "combat_audio": "784,786,785", + "death_animation": "1792", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "13", + "melee_animation": "1789", + "poison_amount": "15", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "25", + "strength_level": "13", + "weakness": "1" + }, + { + "id": "1832", + "name": "Cave bug", + "examine": "A nasty crawling critter.", + "attack_level": "6", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,5,5,10,0,0,0,0,0", + "death_animation": "6082", + "defence_animation": "0", + "defence_level": "6", + "lifepoints": "5", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6079", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "5", + "strength_level": "5", + "weakness": "2" + }, + { + "id": "1833", + "name": "Cave bug larva", + "examine": "A little", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1834", + "name": "Candle seller", + "examine": "Has an odd smell about him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1835", + "name": "Easter Bunny" + }, + { + "id": "1836", + "name": "Dondakan the Dwarf" + }, + { + "id": "1838", + "name": "Dondakan the Dwarf" + }, + { + "id": "1839", + "name": "Dondakan the Dwarf" + }, + { + "id": "1840", + "name": "Dwarven Engineer", + "examine": "A short angry guy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1841", + "name": "Rolad" + }, + { + "id": "1842", + "name": "Khorvak, a dwarven engineer", + "examine": "The little guy is having trouble standing up right.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1843", + "name": "Dwarven Ferryman", + "examine": "It's probably you who will be paying the ferryman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1844", + "name": "Dwarven Ferryman", + "examine": "It's probably you who will be paying the ferryman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1845", + "name": "Dwarven Boatman" + }, + { + "id": "1846", + "name": "Dwarven Boatman", + "examine": "He seems to be in charge of the ship on the river.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1847", + "name": "Miodvetnir" + }, + { + "id": "1848", + "name": "Dernu" + }, + { + "id": "1849", + "name": "Derni" + }, + { + "id": "1850", + "name": "Arzinian Avatar of Strength", + "examine": "It is the avatar of the Arzinian Being of Bordanzan", + "attack_level": "40", + "death_animation": "1841", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "114", + "magic_animation": "0", + "melee_animation": "1840", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "1851", + "name": "Arzinian Avatar of Strength", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1852", + "name": "Arzinian Avatar of Strength", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1853", + "name": "Arzinian Avatar of Ranging", + "examine": "It is the avatar of the Arzinian Being of Bordanzan", + "aggressive": "true", + "attack_level": "30", + "death_animation": "1841", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "114", + "magic_animation": "0", + "melee_animation": "1843", + "range_animation": "1843", + "range_level": "40", + "safespot": null, + "strength_level": "30", + "weakness": "0" + }, + { + "id": "1854", + "name": "Arzinian Avatar of Ranging", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1855", + "name": "Arzinian Avatar of Ranging", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1856", + "name": "Arzinian Avatar of Magic", + "examine": "It is the avatar of the Arzinian Being of Bordanzan", + "aggressive": "true", + "attack_level": "30", + "death_animation": "1841", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "114", + "magic_animation": "0", + "magic_level": "40", + "melee_animation": "1844", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "3" + }, + { + "id": "1857", + "name": "Arzinian Avatar of Magic", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1858", + "name": "Arzinian Avatar of Magic", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1840", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1859", + "name": "Arzinian Being of Bordanzan", + "examine": "It's the Arzinian Being", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1860", + "name": "Brian" + }, + { + "id": "1861", + "name": "Ranged Tutor", + "examine": "Nemarti looks ready to teach you about ranged combat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1862", + "name": "Ali Morrisane" + }, + { + "id": "1863", + "name": "Drunken Ali", + "examine": "Drunk man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1864", + "name": "Ali The barman", + "examine": "A hassled looking barman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1865", + "name": "Ali the Kebab seller", + "examine": "A kebab seller.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1866", + "name": "Brian", + "examine": "He sells ranging equipment.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1867", + "name": "Ali the Camel Man", + "examine": "Ali the discount animal seller.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1868", + "name": "Street urchin", + "examine": "A mischievous looking child.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1869", + "name": "Ali the Mayor" + }, + { + "id": "1871", + "name": "Ali the Hag", + "examine": "An old Hag named Alice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1872", + "name": "Ali the Snake Charmer", + "examine": "A snake charmer.", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1873", + "name": "Ali the Camel", + "examine": "A foul tempered ugly lumpy yellow horse prone to spitting.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1874", + "name": "Desert snake", + "examine": "A slithering serpent.", + "attack_level": "4", + "combat_audio": "3609,3608,3610", + "death_animation": "278", + "defence_animation": "0", + "defence_level": "6", + "lifepoints": "8", + "magic_animation": "0", + "melee_animation": "275", + "range_animation": "0", + "range_level": "6", + "respawn_delay": "60", + "safespot": null, + "strength_level": "4", + "weakness": "1" + }, + { + "id": "1875", + "name": "Snake", + "examine": "A toothless old Snake.", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "examine": "A well dressed thief." + }, + { + "id": "1885", + "name": "Bandit champion", + "examine": "A very tough-looking bandit.", + "aggressive": "true", + "attack_level": "39", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "39", + "lifepoints": "55", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "39", + "weakness": "9" + }, + { + "id": "1886", + "name": "Cowardly Bandit", + "examine": "Probably the weakest bandit in the world ....ever.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "1902", + "name": "Ali the Operator", + "examine": "Smooth operator.", + "attack_level": "6", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,5,5,10,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1903", + "name": "Menaphite Thug" + }, + { + "id": "1904", + "name": "Menaphite Thug", + "examine": "Menaphite thug.", + "aggressive": "false", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,9,8,10,0,0,0,9,0,0,0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "0", + "respawn_delay": "5", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "1905", + "name": "Menaphite Thug", + "examine": "Menaphite thug.", + "aggressive": "false", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,9,8,10,0,0,0,9,0,0,0", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "20", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "0", + "melee_animation": "395", + "range_animation": "0", + "range_level": "0", + "respawn_delay": "5", + "safespot": null, + "strength_level": "50" + }, + { + "id": "1906", + "name": "Tough Guy", + "examine": "Tough looking Menaphite.", + "aggressive": "true", + "attack_level": "39", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "39", + "lifepoints": "114", + "magic_animation": "0", + "melee_animation": "395", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "39", + "weakness": "9" + }, + { + "id": "1907", + "name": "Broken clay golem" + }, + { + "id": "1909", + "name": "Damaged clay golem" + }, + { + "id": "1910", + "name": "Clay golem" + }, + { + "id": "1911", + "name": "Desert Phoenix", + "examine": "Definitely not a chicken.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1912", + "name": "Elissa" + }, + { + "id": "1913", + "name": "Kamil", + "examine": "Ice warrior.", + "attack_level": "190", + "attack_speed": "5", + "bonuses": "0,60,0,0,0,35,60,35,0,0,0,100,0,0,0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "135", + "lifepoints": "130", + "magic_animation": "412", + "melee_animation": "440", + "range_animation": "440", + "range_level": "1", + "safespot": null, + "strength_level": "80" + }, + { + "id": "1914", + "name": "Dessous", + "examine": "A vampyre warrior of Zamorak.", + "aggressive": "true", + "attack_level": "99", + "bonuses": "0,0,50,0,0,10,150,150,0,0,0,50,0,0,0", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "99", + "lifepoints": "200", + "melee_animation": "1264", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "99" + }, + { + "id": "1915", + "name": "Dessous", + "examine": "A vampyre warrior of Zamorak.", + "aggressive": "true", + "attack_level": "99", + "bonuses": "0,0,50,0,0,10,150,150,0,0,0,50,0,0,0", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "99", + "lifepoints": "200", + "melee_animation": "1264", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "99" + }, + { + "id": "1916", + "name": "Ruantun", + "examine": "Luckily, I can't see much of his face.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1917", + "name": "Bandit shopkeeper", + "examine": "I guess he sells what he steals...?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1918", + "name": "Archaeologist", + "examine": "Hardened by the cutthroat world of archaeology.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1919", + "name": "Stranger", + "examine": "Very mysterious looking...", + "aggressive": "true", + "attack_level": "58", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "58", + "lifepoints": "82", + "magic_animation": "0", + "melee_animation": "377", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "58", + "weakness": "7" + }, + { + "id": "1920", + "name": "Malak", + "examine": "One of Morytania's vampyric nobility.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1921", + "name": "Bartender", + "examine": "Looks like a rough-and-ready type.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1922", + "name": "Eblis" + }, + { + "id": "1924", + "name": "Eblis" + }, + { + "id": "1926", + "name": "Bandit", + "examine": "A tough-looking criminal.", + "attack_level": "30", + "attack_speed": "5", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "65", + "magic_animation": "412", + "melee_animation": "412", + "range_animation": "412", + "range_level": "1", + "safespot": null, + "strength_level": "70" + }, + { + "id": "1927", + "name": "Bandit" + }, + { + "id": "1928", + "name": "Bandit" + }, + { + "id": "1929", + "name": "Bandit" + }, + { + "id": "1930", + "name": "Bandit" + }, + { + "id": "1931", + "name": "Bandit", + "examine": "This is an NPC.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "412", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "100", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1932", + "name": "Troll child" + }, + { + "id": "1934", + "name": "Troll child" + }, + { + "id": "1935", + "name": "Ice troll", + "examine": "An ice troll.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1936", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1937", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1938", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1939", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1940", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1941", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1942", + "name": "Ice troll", + "examine": "Brrrrr...he must be cold!", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" + }, + { + "id": "1943", + "name": "Ice block", + "examine": "A troll frozen in a block of ice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1944", + "name": "Ice block", + "examine": "A troll frozen in a block of ice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1945", + "name": "Ice block", + "examine": "A troll frozen in a block of ice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1946", + "name": "Ice block", + "examine": "A troll frozen in a block of ice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "1947", + "name": "Troll father", + "examine": "An ice troll." + }, + { + "id": "1948", + "name": "Troll father", + "examine": "An ice troll." + }, + { + "id": "1949", + "name": "Troll mother", + "examine": "An ice troll." + }, + { + "id": "1950", + "name": "Troll mother", + "examine": "An ice troll." + }, + { + "id": "1951", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1952", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1953", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1954", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1955", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1956", + "name": "Ice wolf", + "examine": "Not a man's best friend.", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "60", + "lifepoints": "72", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1957", + "name": "Mummy" + }, + { + "id": "1958", + "name": "Mummy", + "examine": "Highly flammable!", + "attack_level": "55", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "1959", + "name": "Mummy" + }, + { + "id": "1960", + "name": "Mummy ashes" + }, + { + "id": "1961", + "name": "Mummy", + "examine": "A tightly-wrapped monster.", + "aggressive": "true", + "attack_level": "90", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90", + "weakness": "9" + }, + { + "id": "1962", + "name": "Mummy", + "examine": "Spooky, bandaged dead dude.", + "aggressive": "true", + "attack_level": "90", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5549", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90", + "weakness": "9" + }, + { + "id": "1963", + "name": "Mummy", + "examine": "A victim of poor first aid.", + "aggressive": "true", + "attack_level": "90", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5549", + "poison_immune": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90", + "weakness": "9" + }, + { + "id": "1964", + "name": "Mummy", + "examine": "But who's the daddy?", + "aggressive": "true", + "attack_level": "90", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90", + "weakness": "9" + }, + { + "id": "1965", + "name": "Mummy", + "examine": "A tightly-wrapped monster.", + "attack_level": "55", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "1966", + "name": "Mummy" + }, + { + "id": "1967", + "name": "Mummy", + "examine": "A victim of poor first aid.", + "attack_level": "55", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "1968", + "name": "Mummy" + }, + { + "id": "1969", + "name": "Scarabs", + "examine": "I think they're some kind of beetle...", + "attack_level": "255", + "attack_speed": "3", + "death_animation": "5464", + "defence_animation": "1946", + "defence_level": "10", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "1948", + "poison_amount": "15", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "2", + "weakness": "7" + }, + { + "id": "1970", + "name": "Azzanadra" + }, + { + "id": "1972", + "name": "Rasolo", + "examine": "A wandering merchant.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1973", + "name": "Giant skeleton", + "examine": "A giant skeleton.", + "attack_level": "60", + "death_animation": "5503", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "5499", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "60", + "weakness": "8" + }, + { + "id": "1974", + "name": "Damis", + "examine": "The warrior of darkness.", + "attack_level": "90", + "bonuses": "0,0,0,0,0,60,60,60,60,60,0,80,0,0,0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "440", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "90", + "weakness": "0" + }, + { + "id": "1975", + "name": "Damis", + "examine": "The warrior of darkness.", + "attack_level": "160", + "attack_speed": "3", + "bonuses": "0,0,0,0,0,100,100,100,80,120,0,100,0,0,0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "160", + "lifepoints": "200", + "magic_animation": "0", + "melee_animation": "440", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "0" + }, + { + "id": "1976", + "name": "Shadow Hound", + "examine": "Looks hungry!", + "attack_level": "48", + "death_animation": "6564", + "defence_animation": "0", + "defence_level": "48", + "lifepoints": "68", + "magic_animation": "0", + "melee_animation": "6565", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "7" + }, + { + "id": "1977", + "name": "Fareed", + "examine": "Zamorak's warrior of fire.", + "aggressive": "true", + "attack_level": "190", + "bonuses": "0,0,0,0,0,100,100,100,0,0,0,120,0,0,0", + "death_animation": "836", + "defence_animation": "434", + "defence_level": "135", + "lifepoints": "130", + "magic_level": "100", + "melee_animation": "799", + "range_level": "100", + "respawn_delay": "60", + "safespot": null, + "strength_level": "120" + }, + { + "id": "1978", + "name": "Slave", + "examine": "A malnourished worker.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1979", + "name": "Slave", + "examine": "A malnourished worker.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1980", + "name": "Embalmer", + "examine": "A strange-smelling merchant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1981", + "name": "Carpenter", + "examine": "A block of a man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1982", + "name": "Raetul" + }, + { + "id": "1983", + "name": "Siamun" + }, + { + "id": "1984", + "name": "High Priest" + }, + { + "id": "1987", + "name": "Priest" + }, + { + "id": "1988", + "name": "Priest", + "examine": "Preach my brother!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1989", + "name": "Priest" + }, + { + "id": "1990", + "name": "Sphinx" + }, + { + "id": "1991", + "name": "Possessed Priest", + "examine": "He has a dangerous glint in his eye.", + "aggressive": "true", + "attack_level": "30", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "429", + "range_animation": "0", + "range_level": "40", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "1" + }, + { + "id": "1992", + "name": "Neite" + }, + { + "id": "1993", + "name": "Crocodile", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "386,388,387", + "death_animation": "7591", + "defence_animation": "7590", + "defence_level": "1", + "lifepoints": "63", + "melee_animation": "7588", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "62", + "strength_level": "1" + }, + { + "id": "1994", + "name": "Jackal", + "examine": "He has had his day.", + "aggressive": "true", + "attack_level": "17", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "544,546,545", + "death_animation": "6567", + "defence_animation": "0", + "defence_level": "15", + "lifepoints": "27", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "18", + "weakness": "6" + }, + { + "id": "1995", + "name": "Locust", + "examine": "Obnoxious", + "aggressive": "true", + "attack_level": "12", + "attack_speed": "5", + "death_animation": "2013", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "34", + "magic_animation": "0", + "melee_animation": "2015", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "12", + "weakness": "6" + }, + { + "id": "1996", + "name": "Vulture" + }, + { + "id": "1997", + "name": "Plague frog", + "examine": "A very smelly frog.", + "aggressive": "true", + "attack_level": "15", + "death_animation": "1022", + "defence_animation": "0", + "defence_level": "15", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "1021", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "6" + }, + { + "id": "1998", + "name": "Plague cow", + "examine": "I might give the burgers a miss in this town.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "id": "1999", + "name": "Plague cow", + "examine": "I don't fancy eating any part of this.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "id": "2000", + "name": "Plague cow" + }, + { + "id": "2001", + "name": "Scarab swarm", + "examine": "I think they're some kind of beetle.", + "aggressive": "true", + "attack_level": "9", + "attack_speed": "2", + "death_animation": "1946", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "900", + "magic_animation": "0", + "melee_animation": "1948", + "poison_amount": "15", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "6" + }, + { + "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": "2015", + "name": "Mummy", + "examine": "This mummy looks like it means business!", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5554", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2016", + "name": "Mummy", + "examine": "An irate warrior-mummy.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5554", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2017", + "name": "Mummy", + "examine": "This mummy looks like it means business!", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5554", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2018", + "name": "Mummy", + "examine": "An irate mummy.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5554", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2019", + "name": "Mummy", + "examine": "A wizened old warrior.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "death_animation": "5555", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5554", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2020", + "name": "Light creature" + }, + { + "id": "2021", + "name": "Light creature", + "examine": "A shimmering creature of blue light.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2022", + "name": "Light creature" + }, + { + "id": "2023", + "name": "Juna" + }, + { + "id": "2024", + "name": "Strange Old Man", + "examine": "Wonder how long he's been here...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2025", + "name": "Ahrim the Blighted", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "68,68,68,73,-19,103,85,117,73,0,0,68,0,49,0", + "combat_style": "2", + "death_animation": "7197", + "defence_animation": "420", + "defence_level": "100", + "end_gfx": "157", + "lifepoints": "100", + "magic_animation": "1162", + "magic_level": "100", + "melee_animation": "1162", + "projectile": "156", + "range_animation": "1162", + "range_level": "1", + "safespot": null, + "start_gfx": "155", + "start_height": "96", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "2026", + "name": "Dharok the Wretched", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "7", + "bonuses": "105,105,105,-58,-18,252,250,244,-11,249,0,105,0,0,0", + "death_animation": "7197", + "defence_animation": "2063", + "defence_level": "100", + "lifepoints": "100", + "magic_animation": "2066", + "magic_level": "1", + "melee_animation": "2066", + "range_animation": "2066", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "100", + "weakness": "9" + }, + { + "id": "2027", + "name": "Guthan the Infested", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "75,75,75,-50,-19,259,247,241,-11,-250,0,75,0,0,0", + "death_animation": "7197", + "defence_animation": "2079", + "defence_level": "100", + "lifepoints": "100", + "magic_animation": "2080", + "magic_level": "1", + "melee_animation": "2080", + "range_animation": "2080", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "100", + "weakness": "6" + }, + { + "id": "2028", + "name": "Karil the Tainted", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,-26,134,79,71,90,106,100,0,0,0,0,61", + "combat_style": "1", + "death_animation": "7197", + "defence_animation": "424", + "defence_level": "100", + "lifepoints": "100", + "magic_animation": "2075", + "magic_level": "1", + "melee_animation": "2075", + "projectile": "27", + "range_animation": "2075", + "range_level": "100", + "respawn_delay": "16", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2029", + "name": "Torag the Corrupted", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "72,72,72,-33,-11,221,235,222,0,221,0,72,0,0,0", + "death_animation": "7197", + "defence_animation": "404", + "defence_level": "100", + "lifepoints": "100", + "magic_animation": "2068", + "magic_level": "1", + "melee_animation": "2068", + "range_animation": "2068", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "100", + "weakness": "7" + }, + { + "id": "2030", + "name": "Verac the Defiled", + "examine": "A vengeful spirit corrupted by dark magic.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "72,72,72,-42,-14,227,230,221,0,225,72,78,0,0,0", + "combat_audio": "", + "death_animation": "7197", + "defence_animation": "2063", + "defence_level": "100", + "lifepoints": "100", + "magic_animation": "2062", + "magic_level": "1", + "melee_animation": "2062", + "range_animation": "2062", + "range_level": "1", + "safespot": null, + "strength_level": "100", + "weakness": "8" + }, + { + "id": "2031", + "name": "Bloodworm", + "examine": "I think I should keep my distance...", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "2073", + "defence_animation": "2072", + "defence_level": "35", + "lifepoints": "45", + "magic_animation": "2070", + "magic_level": "1", + "melee_animation": "2070", + "range_animation": "2070", + "range_level": "1", + "respawn_delay": "26", + "safespot": null, + "strength_level": "20", + "weakness": "6" + }, + { + "id": "2032", + "name": "Crypt rat", + "examine": "A nasty little rodent.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "243", + "defence_animation": "241", + "defence_level": "20", + "lifepoints": "35", + "magic_animation": "240", + "magic_level": "1", + "melee_animation": "240", + "range_animation": "240", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "20", + "weakness": "9" + }, + { + "id": "2033", + "name": "Giant crypt rat", + "examine": "A nasty overgrown rodent.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "65", + "lifepoints": "70", + "magic_animation": "4933", + "magic_level": "1", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "2034", + "name": "Crypt spider", + "examine": "Incey wincey.", + "aggressive": "true", + "attack_level": "45", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,10,17,20,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "6251", + "defence_animation": "6250", + "defence_level": "45", + "lifepoints": "45", + "magic_animation": "6249", + "magic_level": "1", + "melee_animation": "6249", + "range_animation": "6249", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "2035", + "name": "Giant crypt spider", + "examine": "Not very incey wincey.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "65", + "lifepoints": "80", + "magic_animation": "5327", + "magic_level": "1", + "melee_animation": "5327", + "range_animation": "5327", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "strength_level": "67", + "weakness": "7" + }, + { + "id": "2036", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "72", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,-5,0,5,0,0,0,0,0", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "72", + "lifepoints": "51", + "magic_animation": "5487", + "magic_level": "1", + "melee_animation": "5487", + "range_animation": "5487", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "72", + "weakness": "8" + }, + { + "id": "2037", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "72", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,5,5,-5,0,5,0,0,0,0,0", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "72", + "lifepoints": "51", + "magic_animation": "5487", + "magic_level": "1", + "melee_animation": "5487", + "range_animation": "5487", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "72", + "weakness": "8" + }, + { + "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": "2044", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2045", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2046", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2047", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2048", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2049", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2050", + "name": "Skogre", + "examine": "It's falling apart!", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2051", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2052", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2053", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2054", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2055", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2056", + "name": "Skogre", + "examine": "It's falling apart!", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2057", + "name": "Skogre", + "examine": "It's falling apart!", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "35", + "lifepoints": "71", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2058", + "name": "Hole in the wall", + "examine": "What could be hiding in that crack in the wall?", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "931,923,922", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1", + "weakness": "7" + }, + { + "id": "2059", + "name": "Zavistic Rarve" + }, + { + "id": "2060", + "name": "Slash Bash", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "6", + "combat_audio": "", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "60", + "lifepoints": "100", + "magic_animation": "359", + "magic_level": "1", + "melee_animation": "359", + "range_animation": "359", + "range_level": "100", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "", + "slayer_task": "64", + "strength_level": "120", + "weakness": "7" + }, + { + "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": "2067", + "name": "Fishing spot", + "examine": "I can see fish swimming in the water.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2068", + "name": "Fishing spot" + }, + { + "id": "2069", + "name": "Cave goblin miner", + "examine": "This one is slacking off.", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "2070", + "name": "Cave goblin miner", + "examine": "This one is slacking off.", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "2071", + "name": "Cave goblin miner", + "examine": "This one is slacking off.", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "2072", + "name": "Cave goblin miner", + "examine": "This one is slacking off.", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "2073", + "name": "Cave goblin guard", + "examine": "He protects the miners.", + "attack_level": "15", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "15", + "lifepoints": "21", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "6" + }, + { + "id": "2074", + "name": "Cave goblin guard", + "examine": "He protects the miners.", + "attack_level": "15", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "15", + "lifepoints": "21", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "6" + }, + { + "id": "2075", + "name": "Cave goblin miner", + "examine": "He's working hard!", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "melee_animation": "6007", + "range_level": "1", + "safespot": null, + "strength_level": "5" + }, + { + "id": "2076", + "name": "Cave goblin miner", + "examine": "He's working hard!", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "melee_animation": "6007", + "range_level": "1", + "safespot": null, + "strength_level": "5" + }, + { + "id": "2077", + "name": "Cave goblin miner", + "examine": "He's working hard!", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "melee_animation": "6007", + "range_level": "1", + "safespot": null, + "strength_level": "5" + }, + { + "id": "2078", + "name": "Cave goblin miner", + "examine": "He's working hard!", + "attack_level": "5", + "combat_audio": "469,472,471", + "death_animation": "6003", + "defence_animation": "6008", + "defence_level": "5", + "lifepoints": "10", + "melee_animation": "6007", + "range_level": "1", + "safespot": null, + "strength_level": "5" + }, + { + "id": "2079", + "name": "Sigmund" + }, + { + "id": "2082", + "name": "Sigmund", + "examine": "Advisor to the Duke of Lumbridge.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2091", + "name": "Secretary", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "5327", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2092", + "name": "Purple Pewter Secretary", + "examine": "The head secretary of the Purple Pewter mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2093", + "name": "Yellow Fortune Secretary", + "examine": "The head secretary of the Yellow Fortune mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2094", + "name": "Blue Opal Secretary", + "examine": "The head secretary of the Blue Opal mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2095", + "name": "Green Gemstone Secretary", + "examine": "The head secretary of the Green Gemstone mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2096", + "name": "White Chisel Secretary", + "examine": "The head secretary of the White Chisel mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2097", + "name": "Silver Cog Secretary", + "examine": "The head secretary of the Silver Cog mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2098", + "name": "Brown Engine Secretary", + "examine": "The head secretary of the Brown Engine mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2099", + "name": "Red Axe Secretary" + }, + { + "id": "2100", + "name": "Purple Pewter Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2101", + "name": "Blue Opal Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2102", + "name": "Yellow Fortune Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2103", + "name": "Green Gemstone Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2104", + "name": "White Chisel Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2105", + "name": "Silver Cog Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2106", + "name": "Brown Engine Director", + "examine": "One of the leaders of the business Consortium", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2107", + "name": "Red Axe Director" + }, + { + "id": "2108", + "name": "Red Axe Cat" + }, + { + "id": "2109", + "name": "Trader", + "examine": "A trader for the Purple Pewter mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2110", + "name": "Trader", + "examine": "A trader for the Purple Pewter mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2111", + "name": "Trader", + "examine": "A trader for the Blue Opal mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2112", + "name": "Trader", + "examine": "A trader for the Blue Opal mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2113", + "name": "Trader", + "examine": "A trader for the Yellow Fortune mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2114", + "name": "Trader", + "examine": "A trader for the Yellow Fortune mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2115", + "name": "Trader", + "examine": "A trader for the Green Gemstone mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2116", + "name": "Trader", + "examine": "A trader for the Green Gemstone mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2117", + "name": "Trader", + "examine": "A trader for the White Chisel mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2118", + "name": "Trader", + "examine": "A trader for the White Chisel mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2119", + "name": "Trader", + "examine": "A trader for the Silver Cog mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2120", + "name": "Trader", + "examine": "A trader for the Silver Cog mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2121", + "name": "Trader", + "examine": "A trader for the Brown Engine mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2122", + "name": "Trader", + "examine": "A trader for the Brown Engine mining company.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2123", + "name": "Trader" + }, + { + "id": "2125", + "name": "Trader" + }, + { + "id": "2127", + "name": "Trade Referee", + "examine": "He is regulating the flow of goods on the trade floor and maintaining order.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2128", + "name": "Supreme Commander" + }, + { + "id": "2129", + "name": "Commander Veldaban" + }, + { + "id": "2130", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "34", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2131", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "34", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2132", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "34", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2133", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "34", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2134", + "name": "Black Guard Berserker", + "examine": "An elite member of the Black Guard.", + "attack_level": "38", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "38", + "weakness": "6" + }, + { + "id": "2135", + "name": "Black Guard Berserker", + "examine": "An elite member of the Black Guard.", + "attack_level": "38", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "38", + "weakness": "6" + }, + { + "id": "2136", + "name": "Black Guard Berserker", + "examine": "An elite member of the Black Guard.", + "attack_level": "38", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "38", + "weakness": "6" + }, + { + "id": "2137", + "name": "Gnome emissary" + }, + { + "id": "2138", + "name": "Gnome traveller", + "examine": "A gnome traveller, visiting Keldagrim.", + "attack_level": "1", + "defence_level": "1", + "force_talk": "", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2139", + "name": "Gnome traveller", + "examine": "A gnome traveller, visiting Keldagrim.", + "attack_level": "1", + "defence_level": "1", + "force_talk": "", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2140", + "name": "Dromund's cat", + "examine": "Very protective of his master... and his property.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2141", + "name": "Blasidar the sculptor", + "examine": "Makes sculptures.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2154", + "name": "Gunslik", + "examine": "Proprietor of Gunslik's Assorted Items.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2155", + "name": "Wemund" + }, + { + "id": "2156", + "name": "Randivor" + }, + { + "id": "2157", + "name": "Hervi" + }, + { + "id": "2158", + "name": "Nolar", + "examine": "A crafty dwarf." + }, + { + "id": "2159", + "name": "Gulldamar" + }, + { + "id": "2160", + "name": "Tati", + "examine": "An old mining dwarf, now he sells pickaxes and generally acts grumpy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2161", + "name": "Agmundi" + }, + { + "id": "2162", + "name": "Vermundi" + }, + { + "id": "2163", + "name": "Banker", + "examine": "A rather personable banker lady.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2164", + "name": "Banker", + "examine": "A rather serious old fella.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2165", + "name": "Librarian", + "examine": "He takes care of the library and its many books.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2166", + "name": "Assistant" + }, + { + "id": "2167", + "name": "Customer", + "examine": "A customer looking for books.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2168", + "name": "Customer", + "examine": "Looks like a human traveler visiting the library.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2169", + "name": "Dromund" + }, + { + "id": "2170", + "name": "Rind the gardener", + "examine": "He tends to the plants in the palace garden.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2171", + "name": "Factory Manager", + "examine": "Looks after the factory.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2172", + "name": "Factory Worker", + "examine": "Works hard at whatever it is he does.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2173", + "name": "Factory Worker", + "examine": "Works hard at whatever it is he does.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2174", + "name": "Factory Worker", + "examine": "Works hard at whatever it is he does.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2175", + "name": "Factory Worker", + "examine": "Works hard at whatever it is he does.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2176", + "name": "Inn Keeper" + }, + { + "id": "2177", + "name": "Inn Keeper", + "examine": "A rich landlord, fat and jolly.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2178", + "name": "Barmaid", + "examine": "These Dwarf ladies are so attractive!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2179", + "name": "Barman" + }, + { + "id": "2180", + "name": "Cart conductor", + "examine": "He makes sure the carts don't run anyone over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2181", + "name": "Cart conductor", + "examine": "He makes sure the carts don't run anyone over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2182", + "name": "Cart conductor", + "examine": "He makes sure the carts don't run anyone over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2183", + "name": "Cart conductor", + "examine": "He makes sure the carts don't run anyone over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2184", + "name": "Cart conductor", + "examine": "He makes sure the carts don't run anyone over.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2185", + "name": "Cart conductor", + "examine": "Remember: don't drink and ride in mine carts.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2186", + "name": "Cart conductor", + "examine": "Keeps the line clear of traffic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2187", + "name": "Rowdy dwarf", + "examine": "A loud, drunk and generally obnoxious dwarf.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2188", + "name": "Hegir" + }, + { + "id": "2189", + "name": "Haera" + }, + { + "id": "2190", + "name": "Runvastr", + "examine": "A sad old dwarf living in a sad old home.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2191", + "name": "Sune", + "examine": "He seems to always be busy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2192", + "name": "Bentamir", + "examine": "This dwarf doesn't seem to be very well off.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2193", + "name": "Ulifed", + "examine": "A dwarf minding his own business.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2194", + "name": "Reinald" + }, + { + "id": "2195", + "name": "Karl", + "examine": "A short, merry guy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2196", + "name": "Gauss" + }, + { + "id": "2197", + "name": "Myndill" + }, + { + "id": "2198", + "name": "Kjut", + "examine": "His kebabs certainly smell delicious.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2199", + "name": "Tombar" + }, + { + "id": "2200", + "name": "Odmar" + }, + { + "id": "2201", + "name": "Audmann", + "examine": "He seems to be very well off.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2202", + "name": "Drunken Dwarf", + "examine": "He's had a fair bit to drink...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2203", + "name": "Drunken Dwarf", + "examine": "He's had a fair bit to drink...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2204", + "name": "Drunken Dwarf" + }, + { + "id": "2205", + "name": "Dwarven Boatman", + "examine": "You cannot see his ship, but presumably he has one.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2206", + "name": "Dwarven Boatman", + "examine": "You cannot see his ship, but presumably he has one.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2207", + "name": "Dwarven Miner", + "examine": "He carries a heavy load.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2232", + "name": "Black Guard Berserker", + "examine": "An elite member of the Black Guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2233", + "name": "Olivia" + }, + { + "id": "2234", + "name": "Master Farmer", + "examine": "A master at farming.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2235", + "name": "Master Farmer", + "examine": "A master at farming.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2236", + "name": "Market Guard", + "examine": "He guards the Draynor Market stalls from thieves.", + "attack_level": "30", + "combat_audio": "2547,1979,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "2237", + "name": "Gee", + "examine": "A local farmer. He may be able to provide some useful information.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2238", + "name": "Donie" + }, + { + "id": "2239", + "name": "Pig" + }, + { + "id": "2240", + "name": "Pig", + "examine": "There to help me make my bids.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2241", + "name": "Piglet", + "examine": "There to help me make my bids.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2242", + "name": "Piglet", + "examine": "Porcine.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2243", + "name": "Piglet", + "examine": "Bacon-in-training." + }, + { + "id": "2244", + "name": "Lumbridge Guide", + "examine": "He provides new players with useful information.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2245", + "name": "Khazard trooper", + "examine": "It's one of General Khazard's warriors.", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "401", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2246", + "name": "Khazard trooper", + "examine": "It's one of General Khazard's warriors.", + "attack_level": "32", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "429", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "8" + }, + { + "id": "2247", + "name": "Gnome troop", + "examine": "It's a tree gnome trooper.", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "3", + "melee_animation": "190", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2248", + "name": "Gnome troop", + "examine": "It's a tree gnome trooper.", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "3", + "melee_animation": "190", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2249", + "name": "Gnome", + "examine": "Like a mini man!", + "attack_level": "1", + "attack_speed": "4", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "3", + "melee_animation": "190", + "range_level": "1", + "respawn_delay": "60", + "strength_level": "1" + }, + { + "id": "2250", + "name": "Gnome", + "examine": "Like a mini man!", + "agg_radius": "", + "aggressive": "", + "attack_level": "1", + "attack_speed": "4", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "end_height": "", + "lifepoints": "2", + "melee_animation": "190", + "range_level": "1", + "respawn_delay": "60", + "strength_level": "1", + "weakness": "" + }, + { + "id": "2251", + "name": "Gnome", + "examine": "Like a mini man!", + "attack_level": "1", + "attack_speed": "4", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "force_talk": "", + "lifepoints": "3", + "melee_animation": "190", + "range_level": "1", + "respawn_delay": "60", + "strength_level": "1" + }, + { + "id": "2252", + "name": "Crow" + }, + { + "id": "2253", + "name": "Wise Old Man", + "examine": "A venerable and rich sage." + }, + { + "id": "2254", + "name": "Bed" + }, + { + "id": "2255", + "name": "Thing under the bed" + }, + { + "id": "2256", + "name": "Paladin", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "66", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2257", + "name": "Mage of Zamorak" + }, + { + "id": "2259", + "name": "Mage of Zamorak" + }, + { + "id": "2260", + "name": "Mage of Zamorak" + }, + { + "id": "2262", + "name": "Dark mage", + "examine": "An initiate of Zamorak.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2263", + "name": "Abyssal leech", + "examine": "A blood-drinking denizen of the abyss.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "3", + "combat_audio": "232,231,230", + "death_animation": "2183", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "2181", + "range_animation": "0", + "range_level": "52", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "0", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "2264", + "name": "Abyssal guardian", + "examine": "It seems to have eyes in the back of its head...", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "6", + "death_animation": "2189", + "defence_animation": "2188", + "defence_level": "65", + "lifepoints": "50", + "magic_animation": "2186", + "magic_level": "57", + "melee_animation": "2186", + "range_animation": "2186", + "range_level": "135", + "safespot": null, + "slayer_exp": "0", + "strength_level": "96", + "weakness": "3" + }, + { + "id": "2265", + "name": "Abyssal walker", + "examine": "Apparently walks the abyss.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "5", + "combat_audio": "233,235,234", + "death_animation": "2194", + "defence_animation": "2193", + "defence_level": "55", + "lifepoints": "95", + "magic_animation": "2192", + "melee_animation": "2192", + "range_animation": "2192", + "range_level": "1", + "safespot": null, + "slayer_exp": "0", + "strength_level": "116", + "weakness": "8" + }, + { + "id": "2266", + "name": "Brian O'Richard" + }, + { + "id": "2267", + "name": "Rogue Guard", + "examine": "Sparkles the Tinsel Snake", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2268", + "name": "Rogue Guard" + }, + { + "id": "2269", + "name": "Rogue Guard", + "examine": "A snowman armed with a holly bow.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2270", + "name": "Martin Thwait" + }, + { + "id": "2271", + "name": "Emerald Benedict", + "examine": "A snowman armed with an ice sword.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2272", + "name": "Spin Blades", + "examine": "A snowman armed with a winter staff.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2273", + "name": "Spin Blades" + }, + { + "id": "2274", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2275", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2276", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2277", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2278", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2279", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2280", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2281", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2282", + "name": "Sir Spishyus", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2283", + "name": "Lady Table", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2284", + "name": "Sir Kuam Ferentse", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2285", + "name": "Sir Leye", + "examine": "A warrior blessed by Saradomin.", + "aggressive": "true", + "attack_level": "18", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "15", + "lifepoints": "21", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "18" + }, + { + "id": "2286", + "name": "Sir Tinley", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2287", + "name": "Sir Ren Itchood", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2288", + "name": "Miss Cheevers", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2289", + "name": "Ms. Hynn Terprett", + "examine": "An observer for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2290", + "name": "Sir Tiffy Cashien", + "examine": "Head of recruitment for the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2291", + "name": "Rug Merchant", + "examine": "A carpet merchant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2292", + "name": "Rug Merchant", + "examine": "A carpet merchant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2293", + "name": "Rug Merchant", + "examine": "A man who deals in rugs.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2294", + "name": "Rug Merchant", + "examine": "A man who deals in rugs.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2295", + "name": "Rug Merchant" + }, + { + "id": "2297", + "name": "Rug Merchant" + }, + { + "id": "2299", + "name": "Rug Station Attendant" + }, + { + "id": "2301", + "name": "Monkey", + "examine": "Perhaps our oldest relatives.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2302", + "name": "Sarah" + }, + { + "id": "2304", + "name": "Sarah", + "examine": "She sells farming equipment." + }, + { + "id": "2305", + "name": "Vanessa" + }, + { + "id": "2306", + "name": "Richard" + }, + { + "id": "2307", + "name": "Alice" + }, + { + "id": "2308", + "name": "Capt' Arnav" + }, + { + "id": "2309", + "name": "null", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_animation": "", + "defence_level": "1", + "range_level": "1", + "safespot": "null" + }, + { + "id": "2310", + "name": "Cow calf", + "examine": "Young, but still beefy.", + "attack_level": "2", + "attack_speed": "5", + "combat_audio": "366,368,367", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "2", + "lifepoints": "6", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "2", + "weakness": "8" + }, + { + "id": "2311", + "name": "Sheepdog", + "examine": "Farmer/sheep liaison officer. Go on - give the dog a bone!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2312", + "name": "Rooster", + "examine": "He rules the", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2313", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2314", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2315", + "name": "Chicken", + "examine": "Yep. Definitely a chicken.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "355,357,356", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2316", + "name": "Pig", + "examine": "Swine.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2317", + "name": "Pig", + "examine": "Porker.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2318", + "name": "Piglet", + "examine": "Hog.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2319", + "name": "Piglet", + "examine": "Porcine.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2320", + "name": "Piglet", + "examine": "He has a certain bovine aroma.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2321", + "name": "Blandebir", + "examine": "An expert on the brewing of ales and cider.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2322", + "name": "Metarialus", + "examine": "In his former life an expert brewer." + }, + { + "id": "2323", + "name": "Elstan", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2324", + "name": "Dantaera", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2325", + "name": "Kragen", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2326", + "name": "Lyra", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2327", + "name": "Francis", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2328", + "name": "Wizard Cromperty", + "examine": "The hat is a dead give away." + }, + { + "id": "2329", + "name": "Muncher", + "examine": "Only the Grim Reaper would have a pet like this.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2330", + "name": "Garth", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2331", + "name": "Ellena", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2332", + "name": "Selena", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2333", + "name": "Vasquen", + "examine": "Perhaps this farmer might look after your crops for you.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2334", + "name": "Rhonen", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2335", + "name": "Dreven", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2336", + "name": "Taria", + "examine": "Perhaps this gardener might look after your crops for you.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2337", + "name": "Rhazien", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2338", + "name": "Torrell", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2339", + "name": "Alain", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2340", + "name": "Heskel", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2341", + "name": "Treznor", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2342", + "name": "Fayeth", + "examine": "Perhaps this gardener might look after your crops for you.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2343", + "name": "Bolongo", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2344", + "name": "Gileth", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "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": "2354", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2355", + "name": "Banker", + "examine": "She can look after my money.", + "attack_level": "1", + "combat_style": "2", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "476", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2356", + "name": "Dalldav" + }, + { + "id": "2357", + "name": "Gethin" + }, + { + "id": "2358", + "name": "Arianwyn" + }, + { + "id": "2359", + "name": "Elf warrior", + "examine": "I don't wanna be at the wrong end of that pike.", + "attack_level": "60", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "425,427,426", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "2360", + "name": "Elf warrior", + "examine": "I don't wanna be at the wrong end of that pike.", + "attack_level": "60", + "attack_speed": "5", + "clue_level": "2", + "combat_audio": "425,427,426", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "2361", + "name": "Elf warrior", + "examine": "He looks pretty handy with that bow.", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "428", + "projectile": "249", + "range_animation": "426", + "range_level": "64", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "250", + "strength_level": "1", + "weakness": "2" + }, + { + "id": "2362", + "name": "Elf warrior", + "examine": "He looks pretty handy with that bow.", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "428", + "projectile": "249", + "range_animation": "426", + "range_level": "64", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "250", + "strength_level": "1", + "weakness": "2" + }, + { + "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": "2372", + "name": "Head mourner", + "examine": "A Mourner showing his true identity.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "2373", + "name": "Mourner", + "examine": "A mourner, or plague healer.", + "aggressive": "true", + "attack_level": "61", + "attack_speed": "4", + "death_animation": "836", + "defence_animation": "", + "defence_level": "80", + "lifepoints": "87", + "magic_animation": "", + "melee_animation": "428", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "61", + "weakness": "" + }, + { + "id": "2374", + "name": "Mourner", + "examine": "A Mourner, or plague healer." + }, + { + "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", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2382", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2383", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2384", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2385", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2386", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2387", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2388", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2389", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2390", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2391", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2392", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2393", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2394", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2395", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2396", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2397", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2398", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2399", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2400", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2401", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2402", + "name": "Mysterious ghost", + "examine": "Seems to flitter in and out of existence...", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2423", + "name": "Chaos dwarf", + "examine": "A dwarf gone bad.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "61", + "strength_level": "1", + "weakness": "6" + }, + { + "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": "2436", + "name": "Jarvald", + "attack_level": "10", + "death_animation": "9230", + "defence_animation": "9231", + "defence_level": "10", + "lifepoints": "10", + "melee_animation": "9232", + "range_level": "1", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2437", + "name": "Jarvald" + }, + { + "id": "2438", + "name": "Jarvald" + }, + { + "id": "2439", + "name": "Askeladden", + "examine": "Looks like a wanna be Fremennik.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2440", + "name": "Door-support" + }, + { + "id": "2441", + "name": "Door" + }, + { + "id": "2442", + "name": "Door" + }, + { + "id": "2443", + "name": "Door-support", + "examine": "This support is propping the door closed.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "0", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2452", + "name": "Giant Rock Crab", + "examine": "No one likes crabs... especially really big ones!", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,225,200,175,-10,250,0,0,0,0,0", + "combat_audio": "717,723,722", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "200", + "lifepoints": "180", + "magic_animation": "0", + "melee_animation": "2368", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "80", + "weakness": "9" + }, + { + "id": "2453", + "name": "Boulder", + "examine": "Heavy rock!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "240", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2454", + "name": "Dagannoth spawn", + "examine": "A teeny-tiny horror from the ocean depths...", + "aggressive": "true", + "attack_level": "47", + "death_animation": "1342", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "1341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "9" + }, + { + "id": "2455", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "clue_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "71", + "lifepoints": "120", + "melee_animation": "1343", + "projectile": "294", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "120", + "strength_level": "70" + }, + { + "id": "2456", + "name": "Dagannoth", + "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "50", + "lifepoints": "70", + "melee_animation": "1343", + "projectile": "294", + "range_level": "70", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "0", + "strength_level": "70" + }, + { + "id": "2457", + "name": "Wallasalki", + "examine": "A fearsome magical creature from the deep.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,100,150,175,250,-10,0,0,0,0,0", + "combat_audio": "1077,1079,1078", + "combat_style": "2", + "death_animation": "2367", + "defence_animation": "2366", + "defence_level": "80", + "lifepoints": "120", + "magic_animation": "2365", + "magic_level": "100", + "melee_animation": "2365", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "spell_id": "48", + "strength_level": "1" + }, + { + "id": "2458", + "name": "Freaky Forester" + }, + { + "id": "2459", + "name": "Pheasant", + "examine": "A brightly coloured game bird.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "690,692,691", + "death_animation": "2373", + "defence_animation": "2371", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2372", + "melee_animation": "2372", + "range_animation": "2372", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2460", + "name": "Pheasant", + "examine": "A brightly coloured game bird.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "690,692,691", + "death_animation": "2373", + "defence_animation": "2371", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2372", + "melee_animation": "2372", + "range_animation": "2372", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2461", + "name": "Pheasant", + "examine": "A brightly coloured game bird.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "690,692,691", + "death_animation": "2373", + "defence_animation": "2371", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2372", + "melee_animation": "2372", + "range_animation": "2372", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2462", + "name": "Pheasant", + "examine": "A brightly coloured game bird.", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "690,692,691", + "death_animation": "2373", + "defence_animation": "2371", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2372", + "melee_animation": "2372", + "range_animation": "2372", + "range_level": "1", + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2463", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "25,85,105,75,103,85,65,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "20", + "lifepoints": "19", + "magic_animation": "2302", + "magic_level": "5", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "5", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "2464", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "10", + "bonuses": "35,105,125,95,128,105,85,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "40", + "lifepoints": "40", + "magic_animation": "2302", + "magic_level": "10", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "10", + "respawn_delay": "25", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2465", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "15", + "bonuses": "35,185,185,155,188,125,165,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "50", + "lifepoints": "60", + "magic_animation": "2302", + "magic_level": "15", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "15", + "respawn_delay": "25", + "safespot": null, + "strength_level": "15" + }, + { + "id": "2466", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "45", + "bonuses": "45,235,235,205,238,145,165,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "2302", + "magic_level": "25", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "45", + "respawn_delay": "25", + "safespot": null, + "strength_level": "45" + }, + { + "id": "2467", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "45", + "bonuses": "45,285,285,255,288,145,165,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "2302", + "magic_level": "35", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "45", + "respawn_delay": "25", + "safespot": null, + "strength_level": "45" + }, + { + "id": "2468", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "45", + "bonuses": "45,285,285,255,288,145,165,0,0,0,0,0,0,0,0", + "combat_audio": "2288,357,356", + "combat_style": "2", + "death_animation": "2301", + "defence_animation": "2300", + "defence_level": "90", + "lifepoints": "120", + "magic_animation": "2302", + "magic_level": "45", + "melee_animation": "2302", + "projectile": "337", + "range_animation": "2302", + "range_level": "45", + "respawn_delay": "25", + "safespot": null, + "strength_level": "45", + "weakness": "9" + }, + { + "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": "2476", + "name": "Rick Turpentine", + "examine": "A Retired Highwayman", + "attack_level": "8", + "attack_speed": "5", + "bonuses": "30,20,10,20,20,30,30,10,20,10,30,30,20,30,20", + "combat_audio": "2695,513,512", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "8", + "lifepoints": "10", + "magic_animation": "4230", + "magic_level": "30", + "melee_animation": "4230", + "prj_height": "", + "projectile": "27", + "range_animation": "4230", + "range_level": "20", + "safespot": null, + "strength_level": "8" + }, + { + "id": "2477", + "name": "Quiz Master", + "examine": "Apparently a master of quizzes!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2478", + "name": "Evil Bob" + }, + { + "id": "2479", + "name": "Evil Bob", + "examine": "Hey, it's Bob the cat! Or... is it?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2480", + "name": "Servant" + }, + { + "id": "2481", + "name": "Servant", + "examine": "Servant of Evil Bob.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2482", + "name": "Giant bat", + "examine": "Annoying flappy thing.", + "attack_level": "22", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", + "combat_audio": "292,294,293", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "32", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "8", + "strength_level": "22" + }, + { + "id": "2483", + "name": "Bush snake" + }, + { + "id": "2489", + "name": "Bush snake", + "examine": "A slithering serpent that likes to hide in the bush.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "7", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "278", + "defence_animation": "276", + "defence_level": "50", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "275", + "poison_amount": "55", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "1" + }, + { + "id": "2490", + "name": "Bush snake", + "examine": "A slithering serpent that likes to hide in the bush.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "7", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "278", + "defence_animation": "276", + "defence_level": "50", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "275", + "poison_amount": "55", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25" + }, + { + "id": "2491", + "name": "Jungle spider", + "examine": "", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "5327", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2492", + "name": "Jungle spider", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "5327", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2493", + "name": "Large mosquito", + "examine": "A flying bloodsucker.", + "attack_level": "1", + "death_animation": "2398", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "2397", + "range_animation": "0", + "range_level": "63", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "2494", + "name": "Mosquito swarm", + "examine": "A swarm of three highly agile mosquitoes.", + "attack_level": "1", + "death_animation": "2398", + "defence_animation": "0", + "defence_level": "66", + "lifepoints": "94", + "magic_animation": "0", + "melee_animation": "2397", + "range_animation": "0", + "range_level": "66", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "2495", + "name": "Mosquito swarm", + "examine": "A swarm of five highly agile mosquitoes.", + "attack_level": "1", + "death_animation": "2398", + "defence_animation": "0", + "defence_level": "68", + "lifepoints": "97", + "magic_animation": "0", + "melee_animation": "2397", + "range_animation": "0", + "range_level": "68", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "2496", + "name": "Tribesman", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2497", + "name": "Tribesman", + "examine": "A vicious warrior.", + "aggressive": "true", + "attack_level": "23", + "attack_speed": "4", + "bonuses": "8,8,8,0,0,4,6,6,0,0,0,5,0,0,0", + "clue_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "39", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "428", + "poison_amount": "55", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "27", + "weakness": "9" + }, + { + "id": "2498", + "name": "Broodoo victim" + }, + { + "id": "2499", + "name": "Broodoo victim", + "examine": "An undead victim of some ancient murderous ritual; his skin appears deep green.", + "aggressive": "true", + "attack_level": "45", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "9" + }, + { + "id": "2500", + "name": "Broodoo victim" + }, + { + "id": "2501", + "name": "Broodoo victim", + "examine": "An undead victim of some ancient murderous ritual; his skin appears pale yellow.", + "aggressive": "true", + "attack_level": "43", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "43", + "weakness": "9" + }, + { + "id": "2502", + "name": "Broodoo victim" + }, + { + "id": "2503", + "name": "Broodoo victim", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "100", + "melee_animation": "810", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2504", + "name": "Sharimika" + }, + { + "id": "2505", + "name": "Sharimika", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2506", + "name": "Sharimika", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2507", + "name": "Mamma Bufetta" + }, + { + "id": "2508", + "name": "Mamma Bufetta", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2509", + "name": "Mamma Bufetta", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2510", + "name": "Layleen" + }, + { + "id": "2511", + "name": "Layleen", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2512", + "name": "Layleen", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2513", + "name": "Karaday" + }, + { + "id": "2514", + "name": "Karaday", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2515", + "name": "Karaday", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2516", + "name": "Safta Doc" + }, + { + "id": "2517", + "name": "Safta Doc", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2518", + "name": "Safta Doc", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2519", + "name": "Gabooty" + }, + { + "id": "2520", + "name": "Gabooty", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2521", + "name": "Gabooty", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2522", + "name": "Fanellaman" + }, + { + "id": "2523", + "name": "Fanellaman", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2524", + "name": "Fanellaman", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2525", + "name": "Jagbakoba" + }, + { + "id": "2526", + "name": "Jagbakoba", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2527", + "name": "Jagbakoba", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2528", + "name": "Murcaily" + }, + { + "id": "2529", + "name": "Murcaily", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2530", + "name": "Murcaily", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2531", + "name": "Rionasta" + }, + { + "id": "2532", + "name": "Rionasta", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2533", + "name": "Rionasta", + "examine": "A native of Tai Bwo Wannai." + }, + { + "id": "2534", + "name": "Mahogany" + }, + { + "id": "2535", + "name": "Teak" + }, + { + "id": "2536", + "name": "Niles" + }, + { + "id": "2537", + "name": "Miles" + }, + { + "id": "2538", + "name": "Giles" + }, + { + "id": "2539", + "name": "Cap'n Hand", + "examine": "He used to swashbuckle his away across the seven seas.", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "10,30,20,30,10,20,15,30,10,30,15,20,20,0,0", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "4230", + "magic_level": "20", + "melee_animation": "4230", + "range_animation": "4230", + "range_level": "30", + "safespot": null, + "strength_level": "10" + }, + { + "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": "2547", + "name": "Chaos druid", + "examine": "A crazy, evil druid.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "12", + "end_gfx": "107", + "lifepoints": "20", + "magic_animation": "422", + "magic_level": "10", + "melee_animation": "422", + "projectile": "106", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "105", + "strength_level": "8", + "weakness": "3" + }, + { + "id": "2548", + "name": "Blackjack seller" + }, + { + "id": "2549", + "name": "Ali the dyer", + "examine": "A colourful character.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2550", + "name": "Dwarven Miner" + }, + { + "id": "2551", + "name": "Dwarven Miner" + }, + { + "id": "2552", + "name": "Dwarven Miner" + }, + { + "id": "2553", + "name": "Blast Furnace Foreman", + "examine": "Looks after the blast furnace.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2564", + "name": "Ordan", + "examine": "He smells of rock dust." + }, + { + "id": "2565", + "name": "Jorzik", + "examine": "He buys stuff." + }, + { + "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": "2571", + "name": "Market Guard", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2572", + "name": "Olivia", + "examine": "She is a seed merchant." + }, + { + "id": "2573", + "name": "Pillory Guard" + }, + { + "id": "2574", + "name": "Bank guard", + "examine": "He's guarding the bank.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2575", + "name": "Purepker895" + }, + { + "id": "2576", + "name": "Qutiedoll" + }, + { + "id": "2577", + "name": "1337sp34kr" + }, + { + "id": "2578", + "name": "Elfinlocks" + }, + { + "id": "2579", + "name": "Cool Mom227" + }, + { + "id": "2580", + "name": "Bernald", + "examine": "Looks after the Burthorpe vinery." + }, + { + "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": "2588", + "name": "Brother Althric", + "examine": "A holy man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2589", + "name": "PKMaster0036" + }, + { + "id": "2590", + "name": "King Roald" + }, + { + "id": "2591", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2592", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2593", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2594", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2595", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2596", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2597", + "name": "TzHaar-Mej", + "examine": "Looks like some kind of mystic.", + "attack_level": "70", + "attack_speed": "5", + "bonuses": "60,120,90,120,60,60,50,45,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "end_gfx": "1621", + "lifepoints": "100", + "magic_animation": "9260", + "magic_level": "70", + "melee_animation": "9260", + "projectile": "1617", + "range_animation": "9260", + "range_level": "70", + "safespot": null, + "start_gfx": "1618", + "strength_level": "70" + }, + { + "id": "2598", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2599", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2600", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2601", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2602", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2603", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "70", + "safespot": null, + "strength_level": "50" + }, + { + "id": "2604", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2605", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2606", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,80,56,80,80,100,60,50,45,45,0,0,0,0", + "combat_style": "1", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "projectile": "442", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2607", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2608", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,120,120,120,130,20,50,45,0,0,0,0,0,0", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2609", + "name": "TzHaar-Xil", + "examine": "Doesn't look very social.", + "attack_level": "70", + "bonuses": "50,30,80,56,80,80,100,60,50,45,45,0,0,0,0", + "combat_style": "1", + "death_animation": "9291", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "projectile": "442", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "slayer_exp": "0", + "strength_level": "70" + }, + { + "id": "2610", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2611", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2612", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2613", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2614", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2615", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2616", + "name": "TzHaar-Ket", + "examine": "Must be a guard or something.", + "attack_level": "70", + "bonuses": "50,120,120,120,130,20,60,45,0,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "70", + "lifepoints": "140", + "magic_animation": "9345", + "magic_level": "70", + "melee_animation": "9345", + "range_animation": "9345", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "2617", + "name": "TzHaar-Mej-Jal", + "examine": "Another one of those mystic-types." + }, + { + "id": "2618", + "name": "TzHaar-Mej-Kah", + "examine": "Another one of those mystic-types." + }, + { + "id": "2619", + "name": "TzHaar-Ket-Zuh", + "examine": "Maybe it'll guard my possessions." + }, + { + "id": "2620", + "name": "TzHaar-Hur-Tel", + "examine": "Wonder what it's making." + }, + { + "id": "2621", + "name": "Olaf Hardson", + "examine": "A soggy, smelly Fremennik." + }, + { + "id": "2622", + "name": "TzHaar-Hur-Lek", + "examine": "Wonder what it's making." + }, + { + "id": "2623", + "name": "TzHaar-Mej-Roh", + "examine": "Another one of those mystic-types." + }, + { + "id": "2624", + "name": "TzHaar-Ket" + }, + { + "id": "2625", + "name": "TzHaar-Ket" + }, + { + "id": "2626", + "name": "Rocks" + }, + { + "id": "2627", + "name": "Tz-Kih", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2628", + "name": "Tz-Kih", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2629", + "name": "Tz-Kek", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "20", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2630", + "name": "Tz-Kek", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "20", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2631", + "name": "Tok-Xil", + "examine": "The Tok-Xil fires deadly spines out of its arm", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "80", + "lifepoints": "114", + "magic_animation": "0", + "range_animation": "0", + "range_level": "80", + "safespot": null, + "strength_level": "1", + "weakness": "10" + }, + { + "id": "2632", + "name": "Tok-Xil" + }, + { + "id": "2633", + "name": "Wise Old Man" + }, + { + "id": "2634", + "name": "Miss Schism", + "examine": "A busy-body who loves a bit of gossip.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2641", + "name": "Dragonkin", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2655", + "name": "Unferth", + "examine": "Nice but dim." + }, + { + "id": "2656", + "name": "Unferth" + }, + { + "id": "2657", + "name": "Unferth" + }, + { + "id": "2658", + "name": "Unferth" + }, + { + "id": "2659", + "name": "Unferth" + }, + { + "id": "2660", + "name": "Reldo", + "examine": "Reldo the librarian." + }, + { + "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": "2674", + "name": "Thief", + "examine": "Known for his light-fingered qualities.", + "attack_level": "10", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "10", + "weakness": "9" + }, + { + "id": "2675", + "name": "Man", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2676", + "name": "Make-over Mage" + }, + { + "id": "2677", + "name": "Highwayman", + "examine": "He holds up passers by.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "8", + "lifepoints": "13", + "magic_animation": "799", + "melee_animation": "799", + "range_animation": "799", + "range_level": "1", + "safespot": null, + "strength_level": "8", + "weakness": "7" + }, + { + "id": "2678", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2679", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2680", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2681", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "2682", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "3102,3104,3103", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2683", + "name": "Hengel", + "examine": "A citizen of Rimmington.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2684", + "name": "Anja", + "examine": "A citizen of Rimmington.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2685", + "name": "Hobgoblin", + "examine": "An ugly smelly creature.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "1", + "lifepoints": "29", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2686", + "name": "Hobgoblin", + "examine": "An ugly smelly creature.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "1", + "lifepoints": "29", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2687", + "name": "Hobgoblin", + "examine": "An ugly smelly creature.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "1", + "lifepoints": "29", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2688", + "name": "Hobgoblin", + "examine": "An ugly smelly creature, with a spear.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "3520,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "1", + "lifepoints": "29", + "magic_animation": "163", + "melee_animation": "163", + "range_animation": "163", + "range_level": "20", + "safespot": null, + "strength_level": "10" + }, + { + "id": "2689", + "name": "Frog", + "examine": "Didn't the mage say this procedure was totally safe?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2690", + "name": "Jack Seagull", + "examine": "A salty seafarer. Needs a wash.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2691", + "name": "Longbow Ben", + "examine": "A strange man with a strange name. Probably a strange past, too.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2692", + "name": "Ahab", + "examine": "So what can one do with a drunken sailor?" + }, + { + "id": "2693", + "name": "Duck", + "examine": "She quackers.", + "attack_level": "1", + "death_animation": "3468", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "water_npc": "true", + "weakness": "8" + }, + { + "id": "2694", + "name": "Duckling", + "examine": "Mini quackers.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "3468", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "water_npc": "true", + "weakness": "8" + }, + { + "id": "2695", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2696", + "name": "Thief", + "examine": "Known for his light-fingered qualities.", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "8" + }, + { + "id": "2697", + "name": "Mugger", + "examine": "He doesn't look so happy now he's in jail.", + "aggressive": "true", + "attack_level": "11", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "8" + }, + { + "id": "2698", + "name": "Black knight", + "examine": "A dark-hearted knight.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "42", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "24", + "weakness": "7" + }, + { + "id": "2699", + "name": "Guard", + "examine": "He's guarding the prison.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "2700", + "name": "Guard", + "examine": "A prison guard.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "2701", + "name": "Guard", + "examine": "A prison guard.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "2702", + "name": "Guard", + "examine": "A prison guard.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "2703", + "name": "Guard", + "examine": "A prison guard.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "2704", + "name": "Guard", + "examine": "He's asleep.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2705", + "name": "Guard", + "examine": "Keeping an eye out for suspicious activity.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2706", + "name": "Crab", + "examine": "No one likes crabs...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2707", + "name": "Seagull", + "examine": "A sea bird.", + "attack_level": "1", + "combat_audio": "309,311,310", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2708", + "name": "Seagull" + }, + { + "id": "2709", + "name": "Fire wizard", + "examine": "Caution: HOT!", + "attack_level": "1", + "combat_style": "2", + "death_animation": "2553", + "defence_animation": "0", + "defence_level": "18", + "end_gfx": "101", + "lifepoints": "25", + "magic_animation": "711", + "magic_level": "18", + "melee_animation": "414", + "projectile": "100", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "start_height": "80", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "2710", + "name": "Water wizard", + "examine": "Hydro-power!", + "attack_level": "1", + "combat_style": "2", + "death_animation": "2553", + "defence_animation": "0", + "defence_level": "14", + "lifepoints": "20", + "magic_animation": "711", + "magic_level": "14", + "melee_animation": "414", + "projectile": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "93", + "start_height": "80", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "2711", + "name": "Earth wizard", + "examine": "His hands are covered in mud, at least.", + "attack_level": "1", + "combat_style": "2", + "death_animation": "2553", + "defence_animation": "0", + "defence_level": "16", + "end_gfx": "98", + "lifepoints": "22", + "magic_animation": "711", + "magic_level": "16", + "melee_animation": "414", + "projectile": "97", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "96", + "start_height": "80", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "2712", + "name": "Air wizard", + "examine": "At least he looks solid enough to fight.", + "attack_level": "1", + "combat_style": "2", + "death_animation": "2553", + "defence_animation": "0", + "defence_level": "12", + "end_gfx": "92", + "lifepoints": "17", + "magic_animation": "711", + "magic_level": "12", + "melee_animation": "414", + "projectile": "91", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "90", + "start_height": "80", + "strength_level": "1", + "weakness": "3" + }, + { + "id": "2713", + "name": "Malignius Mortifer", + "examine": "A master of necromancy!" + }, + { + "id": "2714", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "2715", + "name": "Skeleton", + "attack_level": "1", + "combat_audio": "774,775,777", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2716", + "name": "Ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "2717", + "name": "Skeleton mage", + "attack_level": "1", + "combat_audio": "774,775,777", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2718", + "name": "Betty", + "examine": "She'll store my items for me.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2725", + "name": "Otto Godblessed", + "examine": "A wise barbarian, apparently. He still looks like a thug.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2726", + "name": "Gull", + "examine": "A sea bird.", + "water_npc": "true" + }, + { + "id": "2727", + "name": "Gull" + }, + { + "id": "2728", + "name": "Monk of Entrana", + "examine": "Holy looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2729", + "name": "Monk of Entrana", + "examine": "Holy looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2730", + "name": "Monk of Entrana" + }, + { + "id": "2731", + "name": "Monk of Entrana", + "examine": "Holy looking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2732", + "name": "Master Crafter", + "examine": "He works in the Crafting Guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2733", + "name": "Master Crafter", + "examine": "He wanders around the Crafting Guild pretending to be working.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2734", + "name": "Tz-Kih", + "examine": "Hangs out in caves.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "150,22,22,22,22,11,0,0,0,0,0,0,0,0,0", + "death_animation": "9230", + "defence_animation": "9231", + "defence_level": "11", + "lifepoints": "10", + "magic_animation": "9232", + "melee_animation": "9232", + "range_animation": "9232", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "27" + }, + { + "id": "2735", + "name": "Tz-Kih", + "examine": "Hangs out in caves.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "150,22,22,22,22,11,0,0,0,0,0,0,0,0,0", + "death_animation": "9230", + "defence_animation": "9231", + "defence_level": "11", + "lifepoints": "10", + "magic_animation": "9232", + "melee_animation": "9232", + "range_animation": "9232", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "27" + }, + { + "id": "2736", + "name": "Tz-Kek", + "examine": "Looks like living lava...", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "5", + "bonuses": "80,45,45,45,45,22,0,0,0,0,0,0,0,0,0", + "death_animation": "9234", + "defence_animation": "9235", + "defence_level": "22", + "lifepoints": "20", + "magic_animation": "9233", + "magic_level": "20", + "melee_animation": "9233", + "range_animation": "9233", + "range_level": "20", + "safespot": null, + "strength_level": "60" + }, + { + "id": "2737", + "name": "Tz-Kek", + "examine": "Looks like living lava...", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "5", + "bonuses": "80,45,45,45,45,22,0,0,0,0,0,0,0,0,0", + "death_animation": "9234", + "defence_animation": "9235", + "defence_level": "22", + "lifepoints": "20", + "magic_animation": "9233", + "magic_level": "20", + "melee_animation": "9233", + "range_animation": "9233", + "range_level": "20", + "safespot": null, + "strength_level": "60" + }, + { + "id": "2738", + "name": "Tz-Kek", + "examine": "Looks like living lava...", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "60,22,22,22,22,11,0,0,0,0,0,0,0,0,0", + "death_animation": "9234", + "defence_animation": "9235", + "defence_level": "11", + "lifepoints": "10", + "magic_animation": "9233", + "melee_animation": "9233", + "range_animation": "9233", + "range_level": "1", + "safespot": null, + "strength_level": "30" + }, + { + "id": "2739", + "name": "Tok-Xil", + "examine": "I don't like the look of those spines...", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "80,90,86,90,90,90,45,55,26,55,0,0,0,0,0", + "death_animation": "9239", + "defence_animation": "9242", + "defence_level": "45", + "lifepoints": "40", + "magic_animation": "9245", + "melee_animation": "9245", + "range_animation": "9245", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "4" + }, + { + "id": "2740", + "name": "Tok-Xil", + "examine": "I don't like the look of those spines...", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "80,90,86,90,90,90,45,55,26,55,0,0,0,0,0", + "death_animation": "9239", + "defence_animation": "9242", + "defence_level": "45", + "lifepoints": "40", + "magic_animation": "9245", + "melee_animation": "9245", + "range_animation": "9245", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "4" + }, + { + "id": "2741", + "name": "Yt-MejKot", + "examine": "Holy reptile...", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "100,180,180,180,180,90,97,0,0,0,0,0,0,0,0", + "death_animation": "9257", + "defence_animation": "9249", + "defence_level": "90", + "lifepoints": "80", + "magic_animation": "9252", + "melee_animation": "9252", + "range_animation": "9252", + "range_level": "1", + "safespot": null, + "strength_level": "100" + }, + { + "id": "2742", + "name": "Yt-MejKot", + "examine": "Holy reptile...", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "100,180,180,180,180,90,97,0,0,0,0,0,0,0,0", + "death_animation": "9257", + "defence_animation": "9249", + "defence_level": "90", + "lifepoints": "80", + "magic_animation": "9252", + "melee_animation": "9252", + "range_animation": "9252", + "range_level": "1", + "safespot": null, + "strength_level": "100" + }, + { + "id": "2743", + "name": "Ket-Zek", + "examine": "That's one hot dog!", + "attack_level": "150", + "attack_speed": "5", + "bonuses": "180,180,280,280,280,250,140,152,131,0,0,0,0,0,0", + "death_animation": "9269", + "defence_animation": "9268", + "defence_level": "180", + "lifepoints": "160", + "magic_animation": "9265", + "magic_level": "150", + "melee_animation": "9265", + "range_animation": "9265", + "range_level": "1", + "safespot": null, + "strength_level": "150" + }, + { + "id": "2744", + "name": "Ket-Zek", + "examine": "That's one hot dog!", + "attack_level": "150", + "attack_speed": "5", + "bonuses": "180,180,280,280,280,250,140,152,131,0,0,0,0,0,0", + "death_animation": "9269", + "defence_animation": "9268", + "defence_level": "180", + "lifepoints": "160", + "magic_animation": "9265", + "magic_level": "150", + "melee_animation": "9265", + "range_animation": "9265", + "range_level": "1", + "safespot": null, + "strength_level": "150" + }, + { + "id": "2745", + "name": "TzTok-Jad", + "examine": "This is going to hurt...", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "640", + "attack_speed": "8", + "bonuses": "0,0,0,60,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "9279", + "defence_animation": "9278", + "defence_level": "480", + "lifepoints": "250", + "magic_animation": "9277", + "magic_level": "480", + "melee_animation": "9277", + "range_animation": "9277", + "range_level": "960", + "safespot": null, + "slayer_exp": "0", + "strength_level": "960", + "weakness": "10" + }, + { + "id": "2746", + "name": "Yt-HurKot", + "examine": "Mini Menace.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "5", + "bonuses": "100,110,110,110,110,60,110,30,0,0,0,0,0,0,0", + "death_animation": "9257", + "defence_animation": "9253", + "defence_level": "75", + "lifepoints": "40", + "magic_animation": "9252", + "melee_animation": "9252", + "range_animation": "9252", + "range_level": "1", + "safespot": null, + "strength_level": "75" + }, + { + "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": "2759", + "name": "null", + "examine": "She can look after my money.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2776", + "name": "Woman", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "511,506,505", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2777", + "name": "Black Knight" + }, + { + "id": "2778", + "name": "Black Knight" + }, + { + "id": "2779", + "name": "Ranger", + "examine": "A Ranger of the Temple Knights.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "20", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2780", + "name": "Solus Dellagar" + }, + { + "id": "2781", + "name": "Gnome guard" + }, + { + "id": "2782", + "name": "Shadow", + "examine": "A shadow.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "5", + "death_animation": "2739", + "defence_animation": "0", + "defence_level": "68", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "2738", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "68", + "weakness": "6" + }, + { + "id": "2783", + "name": "Dark beast", + "examine": "From a darker dimension.", + "aggressive": "true", + "attack_level": "140", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,40,100,90,100,0,0,0,0,0", + "can_tolerate": "false", + "combat_audio": "389,391,390", + "death_animation": "2733", + "defence_animation": "2732", + "defence_level": "120", + "lifepoints": "220", + "magic_animation": "2731", + "magic_level": "160", + "melee_animation": "2731", + "range_animation": "2731", + "range_level": "1", + "safespot": null, + "slayer_exp": "225", + "strength_level": "160", + "weakness": "4" + }, + { + "id": "2784", + "name": "Mourner", + "examine": "A mourner, or plague healer." + }, + { + "id": "2785", + "name": "Slave", + "examine": "Digging.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "2786", + "name": "Slave", + "examine": "Digging.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "2787", + "name": "Slave", + "examine": "Confused.", + "attack_level": "", + "death_animation": "", + "defence_animation": "", + "defence_level": "", + "lifepoints": "", + "magic_animation": "", + "melee_animation": "", + "range_animation": "", + "range_level": "", + "safespot": null, + "strength_level": "" + }, + { + "id": "2788", + "name": "Thorgel" + }, + { + "id": "2790", + "name": "Sergeant Damien", + "examine": "Drill Sergeant from heck!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2791", + "name": "Pillory Guard" + }, + { + "id": "2792", + "name": "Tramp", + "examine": "A man down on his luck.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2793", + "name": "Tramp" + }, + { + "id": "2794", + "name": "Tramp" + }, + { + "id": "2795", + "name": "Skippy" + }, + { + "id": "2796", + "name": "Skippy", + "examine": "He looks angry and smells drunk." + }, + { + "id": "2797", + "name": "Skippy" + }, + { + "id": "2798", + "name": "Skippy" + }, + { + "id": "2799", + "name": "Skippy" + }, + { + "id": "2800", + "name": "A pile of broken glass" + }, + { + "id": "2801", + "name": "Ogre", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "54", + "attack_speed": "6", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "54", + "lifepoints": "60", + "magic_animation": "359", + "melee_animation": "359", + "range_animation": "359", + "range_level": "1", + "safespot": null, + "strength_level": "54" + }, + { + "id": "2802", + "name": "Gnome Coach", + "examine": "They just call him 'Coach'.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2803", + "name": "Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "55", + "lifepoints": "40", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "55", + "safespot": null, + "slayer_exp": "40", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2804", + "name": "Desert Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "45", + "lifepoints": "25", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "45", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2805", + "name": "Desert Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "45", + "lifepoints": "25", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "45", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2806", + "name": "Desert Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "45", + "lifepoints": "25", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "45", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "2807", + "name": "Small Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "15", + "lifepoints": "15", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "15", + "safespot": null, + "slayer_exp": "15", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2808", + "name": "Small Lizard", + "examine": "A cold-blooded creature, partial to warmth.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2778", + "defence_animation": "2777", + "defence_level": "15", + "lifepoints": "15", + "magic_animation": "2776", + "melee_animation": "2776", + "range_animation": "2776", + "range_level": "15", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "2809", + "name": "Al the Camel", + "examine": "A camel who has the soul of a poet.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2810", + "name": "Elly the Camel", + "examine": "A camel whose love is unrequited.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2811", + "name": "Ollie the Camel", + "examine": "A camel who wants to fly some day.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2812", + "name": "Cam the Camel", + "examine": "A camel who likes to rest.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2813", + "name": "Alice the Camel" + }, + { + "id": "2815", + "name": "Neferti the Camel", + "examine": "A camel who wants to see the world.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2824", + "name": "Ellis", + "examine": "Manufacturer of fine leathers." + }, + { + "id": "2825", + "name": "Pirate Pete", + "examine": "A shifty-looking character.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2826", + "name": "Pirate Pete", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2827", + "name": "Captain Braindeath", + "examine": "A shabby-looking leader.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2828", + "name": "50% Luke", + "examine": "Most of an angry", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2829", + "name": "Davey" + }, + { + "id": "2830", + "name": "Captain Donnie", + "examine": "I wonder if it was all the 'rum' that pickled him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2831", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2832", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2833", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2834", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2835", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2836", + "name": "Zombie protester", + "examine": "Sticking it to 'The Man'.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2837", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2838", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2839", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2840", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2841", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2842", + "name": "Zombie pirate", + "examine": "An undead sea scoundrel.", + "aggressive": "true", + "attack_level": "36", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "51", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "2843", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2844", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2845", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2846", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2847", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2848", + "name": "Zombie swab", + "examine": "He talks a good fight.", + "aggressive": "true", + "attack_level": "34", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "34", + "weakness": "6" + }, + { + "id": "2849", + "name": "Evil spirit", + "examine": "The pun was intended.", + "aggressive": "true", + "attack_level": "40", + "death_animation": "2805", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "magic_level": "40", + "melee_animation": "2804", + "range_animation": "0", + "range_level": "40", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "10" + }, + { + "id": "2850", + "name": "Fever spider", + "examine": "A bunch of legs, eyes and teeth.", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,15,10,15,15,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5321", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "5319", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "40", + "strength_level": "30", + "weakness": "2" + }, + { + "id": "2851", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2852", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2853", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2854", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2855", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2856", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2857", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2858", + "name": "Brewer", + "examine": "A worker in the brewery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2859", + "name": "Fishing spot" + }, + { + "id": "2860", + "name": "Amaethwyr", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "2861", + "name": "Teclyn", + "examine": "Looks after your Farming tools." + }, + { + "id": "2862", + "name": "Death" + }, + { + "id": "2863", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "2864", + "name": "Most of a Zombie" + }, + { + "id": "2865", + "name": "Most of a Zombie" + }, + { + "id": "2866", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "2867", + "name": "Most of a Zombie" + }, + { + "id": "2868", + "name": "Zombie Head" + }, + { + "id": "2869", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "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": "2878", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "2879", + "name": "Bardur" + }, + { + "id": "2880", + "name": "Dagannoth fledgeling", + "examine": "A knee-high horror from the ocean depths...", + "aggressive": "true", + "attack_level": "52", + "death_animation": "1581", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "melee_animation": "1579", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "52", + "weakness": "6" + }, + { + "id": "2881", + "name": "Dagannoth Supreme", + "examine": "The Dagannoth King responsible for the death of Bukalla.", + "agg_radius": "8", + "aggressive": "true", + "attack_level": "255", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,10,255,550,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "2856", + "defence_animation": "2852", + "defence_level": "128", + "lifepoints": "255", + "magic_level": "255", + "melee_animation": "2855", + "movement_radius": "8", + "projectile": "475", + "range_level": "255", + "respawn_delay": "60", + "safespot": null, + "strength_level": "255" + }, + { + "id": "2882", + "name": "Dagannoth Prime", + "examine": "A legendary Dagannoth King, rumoured to fly on the North winds.", + "agg_radius": "8", + "aggressive": "true", + "attack_level": "255", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,255,255,255,255,10,0,0,0,0,0", + "clue_level": "2", + "combat_style": "2", + "death_animation": "2856", + "defence_animation": "2852", + "defence_level": "255", + "lifepoints": "255", + "magic_level": "255", + "melee_animation": "2854", + "movement_radius": "8", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "spell_id": "48", + "strength_level": "255" + }, + { + "id": "2883", + "name": "Dagannoth Rex", + "examine": "Firstborn of the legendary Dagannoth Kings.", + "agg_radius": "8", + "aggressive": "true", + "attack_level": "255", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,255,255,255,10,255,0,0,0,0,0", + "clue_level": "2", + "death_animation": "2856", + "defence_animation": "2852", + "defence_level": "255", + "lifepoints": "255", + "magic_level": "1", + "melee_animation": "2853", + "movement_radius": "8", + "range_level": "255", + "respawn_delay": "60", + "safespot": null, + "strength_level": "255" + }, + { + "id": "2884", + "name": "Wallasalki" + }, + { + "id": "2885", + "name": "Giant Rock Crab", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,225,200,175,-10,250,0,0,0,0,0", + "combat_audio": "717,723,722", + "death_animation": "1314", + "defence_animation": "1313", + "defence_level": "200", + "lifepoints": "180", + "melee_animation": "1312", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "80" + }, + { + "id": "2886", + "name": "Boulder", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "240", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2887", + "name": "Dagannoth", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "1343", + "projectile": "288", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2888", + "name": "Dagannoth", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "combat_style": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "1343", + "projectile": "288", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2889", + "name": "Rock lobster", + "examine": "It wasn't a rock... It was a rock lobster!", + "attack_level": "100", + "attack_speed": "2", + "bonuses": "0,0,0,0,0,100,100,50,50,150,0,0,0,0,0", + "combat_audio": "726,728,727", + "death_animation": "2862", + "defence_animation": "2861", + "defence_level": "100", + "lifepoints": "150", + "magic_animation": "2860", + "melee_animation": "2860", + "movement_radius": "30", + "range_animation": "2860", + "range_level": "1", + "safespot": null, + "strength_level": "100", + "weakness": "7" + }, + { + "id": "2890", + "name": "Large rock", + "examine": "A Rock.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "2", + "death_animation": "2861", + "defence_animation": "2861", + "defence_level": "100", + "lifepoints": "150", + "magic_animation": "2860", + "melee_animation": "2860", + "range_animation": "2860", + "range_level": "1", + "safespot": null, + "strength_level": "100", + "weakness": "7" + }, + { + "id": "2891", + "name": "Suspicious water" + }, + { + "id": "2892", + "name": "Spinolyp", + "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", + "agg_radius": "12", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", + "combat_style": "2", + "death_animation": "2866", + "defence_animation": "2869", + "defence_level": "1", + "lifepoints": "75", + "magic_animation": "2866", + "magic_level": "65", + "melee_animation": "2868", + "projectile": "294", + "range_animation": "2866", + "range_level": "20", + "respawn_delay": "55", + "safespot": null, + "slayer_exp": "0", + "spell_id": "14", + "strength_level": "10" + }, + { + "id": "2893", + "name": "Suspicious water" + }, + { + "id": "2894", + "name": "Spinolyp", + "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", + "agg_radius": "12", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", + "combat_style": "2", + "death_animation": "2866", + "defence_animation": "2869", + "defence_level": "1", + "lifepoints": "75", + "magic_animation": "2866", + "magic_level": "65", + "melee_animation": "2868", + "projectile": "294", + "range_animation": "2866", + "range_level": "20", + "respawn_delay": "55", + "safespot": null, + "spell_id": "14", + "strength_level": "10" + }, + { + "id": "2895", + "name": "Suspicious water" + }, + { + "id": "2896", + "name": "Spinolyp", + "examine": "A sneaky, spiny, subterranean sea-dwelling scamp.", + "agg_radius": "12", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "bonuses": "70,50,60,50,10,60,30,10,60,60,70,50,0,0,0", + "combat_style": "2", + "death_animation": "2866", + "defence_animation": "2869", + "defence_level": "1", + "lifepoints": "75", + "magic_animation": "2866", + "magic_level": "65", + "melee_animation": "2868", + "projectile": "294", + "range_animation": "2866", + "range_level": "70", + "respawn_delay": "55", + "safespot": null, + "slayer_exp": "0", + "spell_id": "14", + "strength_level": "60" + }, + { + "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": "2919", + "name": "Agrith Naar", + "aggressive": "true", + "attack_level": "1", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "95", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2920", + "name": "Sand storm" + }, + { + "id": "2922", + "name": "Clay golem" + }, + { + "id": "2928", + "name": "Clay golem" + }, + { + "id": "2929", + "name": "Ghost" + }, + { + "id": "2931", + "name": "Ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "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": "2941", + "name": "The Beast", + "examine": "Who ate all the rats?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2942", + "name": "Bellemorde", + "examine": "Unkempt and a bit smelly." + }, + { + "id": "2943", + "name": "Pox" + }, + { + "id": "2944", + "name": "Pox" + }, + { + "id": "2945", + "name": "Bones", + "examine": "Lovely, cute, and possibly what dreams are made of.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2946", + "name": "Grimesquit", + "examine": "Cracking personality." + }, + { + "id": "2947", + "name": "Phingspet", + "examine": "Lovely girl, shame about the smell." + }, + { + "id": "2948", + "name": "Hooknosed Jack", + "examine": "Obviously punches above his weight.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2949", + "name": "Jimmy Dazzler", + "examine": "Looks rich like an actor of sorts.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2950", + "name": "The Face", + "examine": "Once beautiful, now repugnant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2951", + "name": "Felkrash" + }, + { + "id": "2952", + "name": "Smokin' Joe", + "examine": "What is he?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2958", + "name": "Silver merchant", + "examine": "Looks fairly well fed.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2959", + "name": "Silk merchant" + }, + { + "id": "2960", + "name": "Zenesha" + }, + { + "id": "2961", + "name": "Ali Morrisane" + }, + { + "id": "2962", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2963", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2964", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2965", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2966", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2967", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2968", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2969", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2970", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2971", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2972", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2973", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2974", + "name": "Rat" + }, + { + "id": "2980", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2981", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2982", + "name": "King rat", + "examine": "The master of all rats.", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2983", + "name": "Turbogroomer" + }, + { + "id": "2984", + "name": "Pusskins", + "examine": "Not a soft touch.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2985", + "name": "Loki" + }, + { + "id": "2986", + "name": "Captain Tom", + "examine": "A not-so friendly, not-so little cat.", + "aggressive": "true", + "attack_level": "10", + "attack_speed": "4", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2987", + "name": "Treacle" + }, + { + "id": "2988", + "name": "Mittens", + "examine": "A fully grown feline.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2989", + "name": "Claude" + }, + { + "id": "2990", + "name": "Topsy", + "examine": "Cute and fluffy.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "2997", + "name": "Gertrude's cat", + "examine": "A friendly feline?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2998", + "name": "Gambler", + "examine": "Very well to do. I wonder what he's doing here.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "2999", + "name": "Gambler" + }, + { + "id": "3000", + "name": "Barman" + }, + { + "id": "3001", + "name": "Gambler", + "examine": "Rich.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3002", + "name": "Gambler", + "examine": "Poor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3003", + "name": "Gambler", + "examine": "Poor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3004", + "name": "Gambler", + "examine": "Poor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3005", + "name": "Gambler", + "examine": "Poor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3006", + "name": "Gambler", + "examine": "Poor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3007", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3008", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3009", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3010", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3011", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3012", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3013", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3014", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3015", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3016", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3017", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3018", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3019", + "name": "Fishing spot" + }, + { + "id": "3020", + "name": "Rug Merchant" + }, + { + "id": "3021", + "name": "Tool leprechaun", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3022", + "name": "Genie", + "examine": "Phenomenal cosmic powers", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3023", + "name": "Nirrie" + }, + { + "id": "3024", + "name": "Tirrie" + }, + { + "id": "3025", + "name": "Hallak" + }, + { + "id": "3026", + "name": "Black golem", + "examine": "An animated clay statue.", + "aggressive": "true", + "attack_level": "55", + "death_animation": "2919", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "2917", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "2" + }, + { + "id": "3027", + "name": "White golem", + "examine": "An animated clay statue.", + "aggressive": "true", + "attack_level": "55", + "death_animation": "2919", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "2917", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "55", + "weakness": "0" + }, + { + "id": "3028", + "name": "Grey golem", + "examine": "An animated clay statue.", + "aggressive": "true", + "attack_level": "55", + "death_animation": "2919", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "2917", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "55", + "weakness": "1" + }, + { + "id": "3029", + "name": "Ghaslor the Elder", + "examine": "The oldest man in Nardah.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3030", + "name": "Ali the Carter", + "examine": "A water salesman from Pollnivneach.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3040", + "name": "Awusah the Mayor", + "examine": "The Mayor of Nardah.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3041", + "name": "Tarik" + }, + { + "id": "3042", + "name": "Poltenip", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3043", + "name": "Radat", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3044", + "name": "Shiratti the Custodian", + "examine": "Custodian of the shrine to Elidinis.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3045", + "name": "Rokuh" + }, + { + "id": "3046", + "name": "Nardah Banker", + "examine": "A banker of Nardah.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3047", + "name": "Target" + }, + { + "id": "3048", + "name": "Target" + }, + { + "id": "3049", + "name": "Larxus" + }, + { + "id": "3051", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3052", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3053", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3054", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3055", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3056", + "name": "Mystery figure", + "examine": "A mysterious watcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3057", + "name": "Earth Warrior Champion", + "examine": "Champion of the earth warriors.", + "aggressive": "true", + "attack_level": "84", + "death_animation": "2946", + "defence_animation": "0", + "defence_level": "84", + "lifepoints": "108", + "magic_animation": "0", + "melee_animation": "2951", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84", + "weakness": "7" + }, + { + "id": "3058", + "name": "Giant Champion", + "examine": "Champion of the giants.", + "aggressive": "true", + "attack_level": "36", + "attack_speed": "4", + "death_animation": "6369", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "70", + "magic_animation": "0", + "melee_animation": "6368", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "35", + "strength_level": "44", + "weakness": "8" + }, + { + "id": "3059", + "name": "Ghoul Champion", + "examine": "Champion of the ghouls.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "441,444,443", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "50", + "strength_level": "80", + "weakness": "9" + }, + { + "id": "3060", + "name": "Goblin Champion", + "examine": "Champion of the goblins.", + "attack_level": "1", + "attack_speed": "4", + "combat_audio": "469,472,471", + "death_animation": "6190", + "defence_animation": "0", + "defence_level": "14", + "lifepoints": "32", + "magic_animation": "0", + "magic_level": "26", + "melee_animation": "6188", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "10" + }, + { + "id": "3061", + "name": "Hobgoblin Champion", + "examine": "Champion of the hobgoblins.", + "aggressive": "true", + "attack_level": "44", + "attack_speed": "4", + "combat_audio": "469,472,471", + "combat_style": "1", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "48", + "lifepoints": "58", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "44", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "0" + }, + { + "id": "3062", + "name": "Imp Champion", + "examine": "Champion of the imps.", + "aggressive": "true", + "attack_level": "5", + "attack_speed": "4", + "death_animation": "172", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "5285", + "range_animation": "0", + "range_level": "5", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "7" + }, + { + "id": "3063", + "name": "Jogre Champion", + "examine": "Champion of the jogres.", + "aggressive": "true", + "attack_level": "86", + "death_animation": "2938", + "defence_animation": "2937", + "defence_level": "86", + "lifepoints": "120", + "magic_animation": "2936", + "melee_animation": "2936", + "poison_amount": "40", + "poisonous": "true", + "range_animation": "2936", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "86", + "weakness": "9" + }, + { + "id": "3064", + "name": "Lesser Demon Champion", + "examine": "Champion of the lesser demons.", + "aggressive": "true", + "attack_level": "136", + "combat_audio": "396,402,401", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "142", + "lifepoints": "148", + "magic_animation": "0", + "magic_level": "136", + "melee_animation": "64", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "79", + "strength_level": "140", + "weakness": "5" + }, + { + "id": "3065", + "name": "Skeleton Champion", + "examine": "Champion of the jogres.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1444", + "defence_animation": "5493", + "defence_level": "54", + "lifepoints": "58", + "magic_animation": "0", + "melee_animation": "5485", + "poisonous": "", + "range_animation": "5493", + "range_level": "36", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "3066", + "name": "Zombies Champion", + "examine": "Champion of the zombies.", + "aggressive": "true", + "attack_level": "26", + "attack_speed": "5", + "death_animation": "5575", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "5581", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "3067", + "name": "Leon d'Cour", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "7050", + "defence_level": "1", + "lifepoints": "123", + "melee_animation": "7049", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3068", + "name": "Skeletal Wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "125", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "0,771,770", + "death_animation": "2987", + "defence_animation": "2983", + "defence_level": "120", + "end_gfx": "501", + "lifepoints": "200", + "magic_animation": "2985", + "magic_level": "125", + "melee_animation": "2985", + "projectile": "500", + "range_animation": "2989", + "range_level": "120", + "safespot": null, + "slayer_exp": "210", + "start_gfx": "499", + "strength_level": "116", + "weakness": "9" + }, + { + "id": "3069", + "name": "Skeletal Wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "125", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "0,771,770", + "death_animation": "2987", + "defence_animation": "2983", + "defence_level": "120", + "end_gfx": "501", + "lifepoints": "200", + "magic_animation": "2985", + "magic_level": "125", + "melee_animation": "2985", + "projectile": "500", + "range_animation": "2989", + "range_level": "120", + "safespot": null, + "slayer_exp": "210", + "start_gfx": "499", + "strength_level": "116", + "weakness": "9" + }, + { + "id": "3070", + "name": "Skeletal Wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "125", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "0,771,770", + "death_animation": "2987", + "defence_animation": "2983", + "defence_level": "120", + "end_gfx": "501", + "lifepoints": "200", + "magic_animation": "2985", + "magic_level": "125", + "melee_animation": "2985", + "projectile": "500", + "range_animation": "2989", + "range_level": "120", + "safespot": null, + "slayer_exp": "210", + "start_gfx": "499", + "strength_level": "116", + "weakness": "9" + }, + { + "id": "3071", + "name": "Skeletal Wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", + "agg_radius": "6", + "aggressive": "true", + "attack_level": "125", + "attack_speed": "6", + "bonuses": "0,0,0,0,0,140,90,90,80,140,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "0,771,770", + "death_animation": "2987", + "defence_animation": "2983", + "defence_level": "120", + "end_gfx": "501", + "lifepoints": "200", + "magic_animation": "2985", + "magic_level": "125", + "melee_animation": "2985", + "projectile": "500", + "range_animation": "2989", + "range_level": "120", + "safespot": null, + "slayer_exp": "210", + "start_gfx": "499", + "strength_level": "116", + "weakness": "9" + }, + { + "id": "3072", + "name": "Ice giant", + "examine": "He's got icicles in his beard.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4673", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4672", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "70", + "strength_level": "40", + "weakness": "9" + }, + { + "id": "3073", + "name": "Ice warrior", + "examine": "A cold-hearted elemental warrior.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,40,20,10,30,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "2500,530,529", + "death_animation": "843", + "defence_animation": "404", + "defence_level": "47", + "lifepoints": "59", + "magic_animation": "451", + "melee_animation": "451", + "range_animation": "451", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "59", + "strength_level": "47", + "weakness": "9" + }, + { + "id": "3074", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3075", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3076", + "name": "Dead Monk" + }, + { + "id": "3078", + "name": "High Priest" + }, + { + "id": "3079", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3080", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3089", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3090", + "name": "", + "examine": "A mighty warrior", + "aggressive": "true", + "attack_level": "17", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "17", + "lifepoints": "24", + "magic_animation": "0", + "melee_animation": "7048", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "17", + "weakness": "8" + }, + { + "id": "3091", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3093", + "name": "Flying Book" + }, + { + "id": "3094", + "name": "Flying Book", + "examine": "The book moves by itself!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3095", + "name": "Flying Book" + }, + { + "id": "3096", + "name": "Pizzaz Hat" + }, + { + "id": "3097", + "name": "Entrance Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3098", + "name": "Telekinetic Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3099", + "name": "Alchemy Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3100", + "name": "Enchantment Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3101", + "name": "Graveyard Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3102", + "name": "Maze Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3103", + "name": "Rewards Guardian", + "examine": "A guardian of the arena.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3104", + "name": "Charmed Warrior", + "examine": "Equipment that moves by itself!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3105", + "name": "Charmed Warrior", + "examine": "Equipment that moves by itself!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3106", + "name": "Charmed Warrior", + "examine": "Equipment that moves by itself!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3107", + "name": "Charmed Warrior" + }, + { + "id": "3108", + "name": "Bert" + }, + { + "id": "3109", + "name": "Guard Captain", + "examine": "This guard looks rather drunk and has beer stains down his armour.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3110", + "name": "Sandy" + }, + { + "id": "3112", + "name": "Sandy", + "examine": "Angry and stressed, he doesn't seem to have time for anything other than sand." + }, + { + "id": "3113", + "name": "Sandy" + }, + { + "id": "3114", + "name": "Mazion" + }, + { + "id": "3115", + "name": "Blaec", + "examine": "Blaec seems to be covered in sand." + }, + { + "id": "3116", + "name": "Reeso" + }, + { + "id": "3117", + "name": "Sandwich lady", + "examine": "The sandwich lady." + }, + { + "id": "3118", + "name": "Prison Pete" + }, + { + "id": "3119", + "name": "Balloon Animal" + }, + { + "id": "3120", + "name": "Balloon Animal" + }, + { + "id": "3121", + "name": "Balloon Animal", + "examine": "Looks after farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3122", + "name": "Balloon Animal", + "examine": "I saw the witchdoctor", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3123", + "name": "Simon Templeton", + "examine": "Looks like he's seen the inside of a few tombs.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "12", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": "0", + "strength_level": "1" + }, + { + "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": "3135", + "name": "Furnace grate", + "examine": "It seems to be blocked.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "0", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3150", + "name": "Knight", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3151", + "name": "Skeleton", + "attack_level": "1", + "combat_audio": "774,775,777", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3152", + "name": "Harpie Bug Swarm" + }, + { + "id": "3153", + "name": "Harpie Bug Swarm", + "examine": "A swarm of bugs.", + "aggressive": "true", + "attack_level": "54", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,5,10,5,10,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "2743,2745,2744", + "death_animation": "1585", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1584", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "25", + "strength_level": "46", + "weakness": "2" + }, + { + "id": "3154", + "name": "Count Draynor" + }, + { + "id": "3155", + "name": "Bill Teach", + "examine": "Bill Teach the pirate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3166", + "name": "Mike", + "examine": "Dodgy Mike." + }, + { + "id": "3167", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3168", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3169", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3170", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3171", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3172", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3173", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3174", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3175", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3176", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3177", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3178", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3179", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3180", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3181", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3182", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3183", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3184", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3185", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3186", + "name": "Pirate", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3187", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3188", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3189", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3190", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3191", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3192", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3193", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3194", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3195", + "name": "Pirate", + "examine": "Arghh matey!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "combat_audio": "703,705,704", + "death_animation": "9055", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "45", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3196", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3197", + "name": "Gull" + }, + { + "id": "3198", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3199", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3200", + "name": "Chaos Elemental", + "examine": "pUre A cHaOs of crEatuRe!", + "agg_radius": "16", + "aggressive": "true", + "attack_level": "270", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,70,70,70,70,70,0,0,0,0,0", + "combat_audio": "345,351,347", + "death_animation": "3147", + "defence_animation": "3149", + "defence_level": "270", + "end_gfx": "558", + "lifepoints": "250", + "magic_animation": "5443", + "magic_level": "270", + "melee_animation": "3146", + "movement_radius": "30", + "projectile": "557", + "range_animation": "5443", + "range_level": "270", + "respawn_delay": "100", + "safespot": "true", + "start_gfx": "556", + "strength_level": "270", + "weakness": "4" + }, + { + "id": "3201", + "name": "Killerwatt", + "attack_level": "1", + "attack_speed": "3", + "defence_level": "1", + "lifepoints": "51", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "51", + "strength_level": "1" + }, + { + "id": "3202", + "name": "Killerwatt", + "attack_level": "1", + "attack_speed": "3", + "defence_level": "1", + "lifepoints": "51", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "51", + "strength_level": "1" + }, + { + "id": "3203", + "name": "Storm Cloud", + "examine": "A very small storm!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3204", + "name": "Storm Cloud", + "examine": "A very small storm!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3205", + "name": "Romily Weaklax" + }, + { + "id": "3206", + "name": "Priest" + }, + { + "id": "3207", + "name": "Pious Pete" + }, + { + "id": "3208", + "name": "Taper" + }, + { + "id": "3209", + "name": "Elena", + "examine": "She doesn't look too happy." + }, + { + "id": "3210", + "name": "Alrena" + }, + { + "id": "3211", + "name": "Alrena" + }, + { + "id": "3212", + "name": "Bravek" + }, + { + "id": "3213", + "name": "Edmond", + "examine": "A local civilian.", + "attack_level": "", + "combat_audio": "", + "defence_level": "", + "lifepoints": "", + "range_level": "", + "safespot": null, + "slayer_exp": "", + "strength_level": "" + }, + { + "id": "3214", + "name": "Edmond", + "examine": "A local civilian.", + "attack_level": "", + "combat_audio": "", + "defence_level": "", + "lifepoints": "", + "range_level": "", + "safespot": null, + "slayer_exp": "", + "strength_level": "" + }, + { + "id": "3215", + "name": "Elena", + "examine": "She looks concerned." + }, + { + "id": "3216", + "name": "Mourner", + "examine": "A mourner, or plague healer." + }, + { + "id": "3217", + "name": "Kaylee", + "examine": "Works in the Rising Sun." + }, + { + "id": "3218", + "name": "Tina" + }, + { + "id": "3219", + "name": "Dwarf", + "examine": "Loves mining.", + "attack_level": "28", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "28", + "weakness": "6" + }, + { + "id": "3220", + "name": "Dwarf", + "examine": "Loves mining.", + "attack_level": "32", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "32", + "weakness": "6" + }, + { + "id": "3221", + "name": "Dwarf", + "examine": "Loves mining.", + "attack_level": "28", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "28", + "weakness": "6" + }, + { + "id": "3222", + "name": "Drunken man", + "examine": "One of Gielinor's many citizens, currently incapacitated by alcohol.", + "attack_level": "4", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "4", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "4", + "weakness": "9" + }, + { + "id": "3223", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" + }, + { + "id": "3224", + "name": "Man", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3225", + "name": "Man", + "examine": "One of Gielinor's many citizens. He looks worried about something.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" + }, + { + "id": "3226", + "name": "Woman", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "9", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3227", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "511,506,505", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3228", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "3229", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "4230", + "range_animation": "2075", + "range_level": "13", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3230", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "13", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "395", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "3231", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "426", + "range_level": "13", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3232", + "name": "Guard", + "examine": "Keeping an eye out for threats to the city.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "426", + "range_level": "13", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3233", + "name": "Guard", + "examine": "Keeping an eye out for threats to the city.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "426", + "range_level": "13", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3234", + "name": "Gardener", + "examine": "An old gardener.", + "attack_level": "4", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "4", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "433", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "4", + "weakness": "8" + }, + { + "id": "3235", + "name": "Apprentice workman", + "examine": "He's learning a trade.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3236", + "name": "Workman", + "examine": "A busy workman", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3237", + "name": "Cuffs", + "examine": "He looks a bit dodgy.", + "agg_radius": "", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "7", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3238", + "name": "Narf", + "examine": "Looks unpleasant.", + "agg_radius": "", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "7", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3239", + "name": "Rusty", + "examine": "Seems to be loitering.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "7", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3240", + "name": "Jeff", + "examine": "Untrustworthy.", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "7", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3241", + "name": "Guard", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3242", + "name": "Dark wizard", + "examine": "A practicer of dark arts.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "3243", + "name": "Dark wizard", + "examine": "A practicer of dark arts.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "27", + "lifepoints": "38", + "magic_animation": "711", + "magic_level": "27", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "3244", + "name": "Dark wizard", + "examine": "A practicer of dark arts.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "16", + "lifepoints": "22", + "magic_animation": "711", + "magic_level": "16", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "3245", + "name": "Dark wizard", + "examine": "A practicer of dark arts.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "16", + "lifepoints": "22", + "magic_animation": "711", + "magic_level": "16", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "3246", + "name": "Barbarian", + "examine": "Alberich, a fierce barbarian warrior.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3247", + "name": "Barbarian", + "examine": "Fafner, a tough barbarian warrior.", + "attack_level": "22", + "attack_speed": "6", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "18", + "magic_animation": "7048", + "melee_animation": "7048", + "range_animation": "7048", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3248", + "name": "Barbarian", + "examine": "Fasolt, a strong barbarian warrior.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3249", + "name": "Barbarian", + "examine": "Siegmund, a guard in the Barbarian Village.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3250", + "name": "Barbarian", + "examine": "Siegfried, a young guard in the Barbarian Village.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3251", + "name": "Barbarian", + "examine": "Lydspor, a well-traveld barbarian warrior.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "7048", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3252", + "name": "Barbarian", + "examine": "Hagen, a guard in the Barbarian Village.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3253", + "name": "Barbarian", + "examine": "Minarch, a barbarian who likes his beer.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3254", + "name": "Hunding" + }, + { + "id": "3255", + "name": "Barbarian", + "examine": "Wotan, a sturdy barbarian warrior.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3256", + "name": "Barbarian", + "examine": "Acelin, a guard in the Barbarian Village.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3257", + "name": "Barbarian", + "examine": "Adelino, a barbarian warrior with a big axe.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3258", + "name": "Barbarian", + "examine": "Adolpho, a barbarian warrior with a warhammer.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "401", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3259", + "name": "Barbarian", + "examine": "Aitan, a barbarian archer.", + "attack_level": "22", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", + "range_level": "1", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "3260", + "name": "Barbarian", + "examine": "Brunnhilde, a fierce barbarian warrior.", + "attack_level": "20", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "8" + }, + { + "id": "3261", + "name": "Barbarian", + "examine": "Gutrune, a mighty barbarian warrior.", + "attack_level": "20", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "8" + }, + { + "id": "3262", + "name": "Barbarian", + "examine": "Edelschwarz, a barbarian warrior with a spear.", + "attack_level": "20", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "8" + }, + { + "id": "3263", + "name": "Barbarian", + "examine": "Sieglinde, a muscular barbarian warrior.", + "attack_level": "22", + "attack_speed": "6", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "22", + "lifepoints": "20", + "magic_animation": "428", + "melee_animation": "428", + "range_animation": "428", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "8" + }, + { + "id": "3264", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "3265", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "3266", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "3267", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "470,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "3268", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3269", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3270", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3271", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3272", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3273", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3274", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3275", + "name": "Dwarf", + "examine": "A dwarven worker.", + "attack_level": "10", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "14", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "10", + "weakness": "6" + }, + { + "id": "3276", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "30", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "30", + "weakness": "6" + }, + { + "id": "3277", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "30", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "30", + "weakness": "6" + }, + { + "id": "3278", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "30", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "30", + "weakness": "6" + }, + { + "id": "3279", + "name": "Black Guard", + "examine": "A member of the Black Guard", + "attack_level": "30", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "30", + "weakness": "6" + }, + { + "id": "3280", + "name": "Engineering assistant", + "examine": "He looks busy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3281", + "name": "Engineering assistant" + }, + { + "id": "3282", + "name": "Engineer", + "examine": "He's building a cannon.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3283", + "name": "Squirrel", + "examine": "Bushy tail!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3284", + "name": "Squirrel", + "examine": "Bushy tail!" + }, + { + "id": "3285", + "name": "Squirrel", + "examine": "Bushy tail!" + }, + { + "id": "3286", + "name": "Raccoon", + "examine": "A raccoon.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3287", + "name": "Raccoon", + "examine": "A raccoon." + }, + { + "id": "3288", + "name": "Raccoon", + "examine": "A raccoon.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3289", + "name": "Skeleton" + }, + { + "id": "3291", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "8" + }, + { + "id": "3292", + "name": "Witch" + }, + { + "id": "3293", + "name": "null", + "examine": "She'll store my items for me.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3294", + "name": "Dwarf", + "examine": "A dwarf who looks after the mining guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "3295", + "name": "Dwarf", + "examine": "A dwarf who looks after the mining guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "3296", + "name": "Swan", + "examine": "A graceful bird.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "water_npc": true + }, + { + "id": "3297", + "name": "Black Swan", + "examine": "A rare bird.", + "water_npc": "true" + }, + { + "id": "3298", + "name": "Sweeper", + "examine": "Keeps this magic area tidy.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3299", + "name": "Martin the Master Gardener", + "examine": "A master at gardening.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3300", + "name": "Frog" + }, + { + "id": "3301", + "name": "Storm cloud" + }, + { + "id": "3302", + "name": "Co-ordinator", + "examine": "Manages the fairies.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3303", + "name": "Fairy Nuff" + }, + { + "id": "3304", + "name": "Fairy Godfather", + "examine": "I'm gonna make him an offer he can't refuse.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3305", + "name": "Slim Louie" + }, + { + "id": "3306", + "name": "Fat Rocco" + }, + { + "id": "3307", + "name": "Gatekeeper", + "examine": "Guardian of the market gate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3308", + "name": "Zandar Horfyre" + }, + { + "id": "3309", + "name": "Cow", + "examine": "Converts grass to beef.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "369,371,370", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1", + "weakness": "8" + }, + { + "id": "3310", + "name": "Sheep", + "examine": "White and fluffy" + }, + { + "id": "3311", + "name": "Sheep" + }, + { + "id": "3312", + "name": "Zanaris choir", + "examine": "They're going to sing to you!" + }, + { + "id": "3313", + "name": "Tanglefoot", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "102", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3314", + "name": "Baby tanglefoot" + }, + { + "id": "3319", + "name": "Baby tanglefoot", + "examine": "An animated shrub.", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "41", + "lifepoints": "58", + "magic_animation": "0", + "range_animation": "0", + "range_level": "41", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "3320", + "name": "Baby tanglefoot", + "examine": "An aggressive bush.", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "41", + "lifepoints": "58", + "magic_animation": "0", + "range_animation": "0", + "range_level": "41", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "3321", + "name": "Gatekeeper" + }, + { + "id": "3322", + "name": "Fairy chef", + "examine": "Likes to cook with mushrooms.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "movement_radius": "2", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3323", + "name": "Draul Leptoc" + }, + { + "id": "3324", + "name": "Draul Leptoc", + "examine": "This looks like an angry, aggressive man." + }, + { + "id": "3325", + "name": "Phillipa", + "examine": "A quiet, more reserved sort of lady." + }, + { + "id": "3326", + "name": "Martina Scorsby" + }, + { + "id": "3327", + "name": "Jeremy Clerksin", + "examine": "Rather more tired than most.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3328", + "name": "Tarquin" + }, + { + "id": "3329", + "name": "Sigurd" + }, + { + "id": "3330", + "name": "Hari" + }, + { + "id": "3331", + "name": "Barfy Bill", + "examine": "Looks a little green around the gills.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3332", + "name": "Trees" + }, + { + "id": "3333", + "name": "Trees" + }, + { + "id": "3334", + "name": "Cavemouth", + "examine": "It's made of pure fire.", + "aggressive": "true", + "attack_level": "200", + "attack_speed": "6", + "bonuses": "126,98,86,297,311,304,319,13,284,30,0,0,0,0,0", + "defence_level": "500", + "lifepoints": "1000", + "magic_level": "300", + "range_level": "300", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1500" + }, + { + "id": "3335", + "name": "Bullrush" + }, + { + "id": "3336", + "name": "Bullrush" + }, + { + "id": "3337", + "name": "Cave scenery" + }, + { + "id": "3338", + "name": "Cave scenery" + }, + { + "id": "3339", + "name": "Cave scenery" + }, + { + "id": "3340", + "name": "Giant Mole", + "examine": "Holy Mole-y!", + "attack_level": "200", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,60,80,100,80,60,0,0,0,0,0", + "combat_audio": "1642,1646,1645", + "death_animation": "3310", + "defence_animation": "3311", + "defence_level": "200", + "lifepoints": "200", + "magic_animation": "3312", + "magic_level": "200", + "melee_animation": "3312", + "range_animation": "3312", + "range_level": "1", + "respawn_delay": "56", + "safespot": null, + "strength_level": "200", + "weakness": "7" + }, + { + "id": "3341", + "name": "Baby Mole", + "examine": "I will call him, Mini Mole.", + "aggressive": "false", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3342", + "name": "Baby Mole", + "examine": "Mooooooooooooole." + }, + { + "id": "3343", + "name": "Baby Mole", + "examine": "Moley, moley, moley!" + }, + { + "id": "3344", + "name": "Fungi", + "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3345", + "name": "Fungi", + "examine": "A fun guy. No wait, that's awful. Plus it doesn't even make sense.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2777", + "defence_animation": "2775", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "2776", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3346", + "name": "Zygomite", + "examine": "A bouncy fungus.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2777", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "2776", + "range_animation": "0", + "range_level": "42", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "3347", + "name": "Zygomite", + "examine": "Have a fungus.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "2777", + "defence_animation": "2775", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "2776", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3348", + "name": "White Knight", + "attack_level": "1", + "attack_speed": "8", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "7050", + "defence_level": "1", + "lifepoints": "53", + "melee_animation": "7049", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3349", + "name": "White Knight", + "attack_level": "1", + "attack_speed": "8", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "7050", + "defence_level": "1", + "lifepoints": "53", + "melee_animation": "7049", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3350", + "name": "White Knight", + "attack_level": "1", + "attack_speed": "8", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "7050", + "defence_level": "1", + "lifepoints": "53", + "melee_animation": "7049", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3366", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3367", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3368", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3369", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3370", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3371", + "name": "Evil Chicken", + "attack_level": "1", + "combat_audio": "355,357,356", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3372", + "name": "Sir Amik Varze" + }, + { + "id": "3373", + "name": "Sir Amik Varze" + }, + { + "id": "3375", + "name": "Evil Chicken", + "examine": "A fowl beast.", + "aggressive": "true", + "attack_level": "55", + "combat_audio": "355,357,356", + "death_animation": "2301", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "117", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "2299", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "3376", + "name": "Baby black dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "50,40,64,60,30,45,40,40,20,40,40,20,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "42", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "40", + "melee_animation": "25", + "range_animation": "25", + "range_level": "1", + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "0", + "strength_level": "80", + "weakness": "3" + }, + { + "id": "3377", + "name": "K'klik" + }, + { + "id": "3378", + "name": "Evil Dave", + "examine": "He seems to like wearing black.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3379", + "name": "Evil Dave" + }, + { + "id": "3381", + "name": "Doris" + }, + { + "id": "3382", + "name": "Hell-Rat", + "examine": "Vermin from the underworld.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3383", + "name": "Gypsy" + }, + { + "id": "3386", + "name": "Gypsy" + }, + { + "id": "3387", + "name": "Culinaromancer" + }, + { + "id": "3388", + "name": "Osman" + }, + { + "id": "3389", + "name": "Pirate Pete", + "examine": "A shifty-looking character.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3390", + "name": "Mountain Dwarf", + "examine": "He looks short and grumpy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "3391", + "name": "General Wartface", + "examine": "An ugly green creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3392", + "name": "General Bentnoze", + "examine": "An ugly green creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3393", + "name": "Lumbridge Guide", + "examine": "He provides new players with useful information.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3394", + "name": "Evil Dave", + "examine": "He seems to like wearing black.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3404", + "name": "null", + "examine": "Rutmir's assistant.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3406", + "name": "Icefiend", + "examine": "A small ice demon.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "531,533,532", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "171", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "3407", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7219", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "7218", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "3408", + "name": "Guard", + "examine": "He tries to keep order around here.", + "attack_level": "25", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "7213", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "583", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "3409", + "name": "Wild jade vine", + "examine": "A sentient plant - ready", + "aggressive": "true", + "attack_level": "65", + "death_animation": "7249", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "185", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7246", + "poison_amount": "35", + "poisonous": "true", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "3410", + "name": "Wild jade vine", + "examine": "A sentient plant - ready", + "aggressive": "true", + "attack_level": "65", + "death_animation": "7249", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "185", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7246", + "poison_amount": "35", + "poisonous": "true", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "3411", + "name": "Wild jade vine", + "examine": "A sentient plant - ready", + "aggressive": "true", + "attack_level": "65", + "death_animation": "7249", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "185", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7246", + "poison_amount": "35", + "poisonous": "true", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "3412", + "name": "Wild jade vine", + "examine": "A sentient plant - ready", + "aggressive": "true", + "attack_level": "65", + "death_animation": "7249", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "185", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7246", + "poison_amount": "35", + "poisonous": "true", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "3413", + "name": "Goblin Cook", + "examine": "He's got funky socks, but he's still an ugly green creature.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3414", + "name": "Goblin Cook", + "examine": "More like a goblin cooked.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3415", + "name": "Goblin Cook", + "examine": "Half way up the wall is maybe not a naturally tenable position for a goblin.", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3416", + "name": "Pirate Pete", + "examine": "He'll store my items for me.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3417", + "name": "Pirate Pete" + }, + { + "id": "3418", + "name": "Pirate Pete", + "examine": "He'll store my items for me.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3419", + "name": "Ogre", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "27", + "lifepoints": "60", + "melee_animation": "359", + "respawn_delay": "60", + "safespot": null, + "slayer_task": "64", + "strength_level": "27", + "weakness": "8" + }, + { + "id": "3420", + "name": "Mogre Guard", + "examine": "An angry Ogre in a highly amusing hat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3421", + "name": "Crab", + "examine": "Nice claw!", + "attack_level": "22", + "death_animation": "3430", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "3428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "9" + }, + { + "id": "3422", + "name": "Mudskipper", + "examine": "Not the most beautiful fish in the sea.", + "aggressive": "true", + "attack_level": "29", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "241,243,242", + "death_animation": "3435", + "defence_animation": "3434", + "defence_level": "26", + "lifepoints": "20", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "3433", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "10", + "safespot": null, + "strength_level": "29", + "weakness": "9" + }, + { + "id": "3423", + "name": "Mudskipper", + "examine": "Not the most beautiful fish in the sea.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "241,243,242", + "death_animation": "3435", + "defence_animation": "3434", + "defence_level": "29", + "lifepoints": "20", + "magic_level": "1", + "melee_animation": "3433", + "range_level": "1", + "respawn_delay": "5", + "safespot": null, + "strength_level": "30" + }, + { + "id": "3424", + "name": "Crab", + "attack_level": "1", + "death_animation": "3430", + "defence_animation": "3429", + "defence_level": "1", + "lifepoints": "18", + "melee_animation": "3428", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3425", + "name": "Fish", + "examine": "A Red Fantail.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3426", + "name": "Fish", + "examine": "A Red Fantail.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3427", + "name": "Fish", + "examine": "A Red Fantail.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3428", + "name": "Fish" + }, + { + "id": "3429", + "name": "Fish" + }, + { + "id": "3430", + "name": "Fish" + }, + { + "id": "3431", + "name": "Fish", + "examine": "An Angel Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3432", + "name": "Fish", + "examine": "An Angel Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3433", + "name": "Fish", + "examine": "An Angel Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3434", + "name": "Fish", + "examine": "A Harlequin Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3435", + "name": "Fish", + "examine": "A Harlequin Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3436", + "name": "Fish", + "examine": "A Harlequin Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3437", + "name": "Fish", + "examine": "A Discus Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3438", + "name": "Fish", + "examine": "A Discus Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3439", + "name": "Fish", + "examine": "A Discus Fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3440", + "name": "Fish" + }, + { + "id": "3441", + "name": "Fish" + }, + { + "id": "3442", + "name": "Fish" + }, + { + "id": "3443", + "name": "Fish", + "examine": "A shoal of Neon Tetra.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3444", + "name": "Fish", + "examine": "A shoal of Neon Tetra.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3445", + "name": "Fish", + "examine": "A shoal of Neon Tetra.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3446", + "name": "Fish" + }, + { + "id": "3447", + "name": "Fish" + }, + { + "id": "3448", + "name": "Fish" + }, + { + "id": "3449", + "name": "Lumbridge Guide", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "defence_level": "1", + "lifepoints": "60", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3450", + "name": "null", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "defence_level": "1", + "lifepoints": "60", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3451", + "name": "Lumbridge Guide", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "7", + "defence_level": "1", + "lifepoints": "60", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3463", + "name": "Skrach Uglogwee", + "examine": "What restful music!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3476", + "name": "Jubbly bird", + "examine": "A large boisterous bird", + "aggressive": "true", + "attack_level": "6", + "death_animation": "6801", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "melee_animation": "6800", + "range_animation": "0", + "range_level": "8", + "safespot": null, + "strength_level": "6", + "weakness": "0" + }, + { + "id": "3477", + "name": "Jubbly bird" + }, + { + "id": "3478", + "name": "King Awowogei", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "328", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3479", + "name": "King Awowogei" + }, + { + "id": "3481", + "name": "Mizaru" + }, + { + "id": "3482", + "name": "Kikazaru" + }, + { + "id": "3483", + "name": "Iwazaru" + }, + { + "id": "3484", + "name": "Big Snake", + "examine": "A big snake.", + "aggressive": "true", + "attack_level": "46", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "3538", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "46", + "weakness": "8" + }, + { + "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": "3491", + "name": "Culinaromancer", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "140", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3492", + "name": "Culinaromancer" + }, + { + "id": "3493", + "name": "Agrith-Na-Na", + "aggressive": "true", + "attack_level": "1", + "death_animation": "3503", + "defence_animation": "3500", + "defence_level": "1", + "lifepoints": "200", + "melee_animation": "3501", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3494", + "name": "Flambeed", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1752", + "defence_animation": "1751", + "defence_level": "1", + "lifepoints": "210", + "melee_animation": "1750", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3495", + "name": "Karamel", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3496", + "name": "Dessourt", + "aggressive": "true", + "attack_level": "1", + "death_animation": "3510", + "defence_animation": "3505", + "defence_level": "1", + "lifepoints": "140", + "melee_animation": "3507", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3497", + "name": "Gelatinnoth Mother", + "examine": "Deadly AND fruity!", + "aggressive": "true", + "attack_level": "48", + "death_animation": "1342", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "139", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "1341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "5" + }, + { + "id": "3498", + "name": "Gelatinnoth Mother", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3499", + "name": "Gelatinnoth Mother", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3500", + "name": "Gelatinnoth Mother", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3501", + "name": "Gelatinnoth Mother", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3502", + "name": "Gelatinnoth Mother", + "aggressive": "true", + "attack_level": "1", + "death_animation": "1342", + "defence_animation": "1340", + "defence_level": "1", + "lifepoints": "240", + "melee_animation": "1341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3509", + "name": "Sorin", + "examine": "Chronicler and regaler of your piratical exploits.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3510", + "name": "Wiskit" + }, + { + "id": "3512", + "name": "Vampyre Juvinate" + }, + { + "id": "3514", + "name": "Vampyre Juvinate", + "examine": "An initiate juvenile vampyre.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3515", + "name": "Vampyre Juvinate" + }, + { + "id": "3516", + "name": "Gadderanks" + }, + { + "id": "3518", + "name": "Gadderanks" + }, + { + "id": "3519", + "name": "Gadderanks" + }, + { + "id": "3520", + "name": "Vampyre Juvinate" + }, + { + "id": "3521", + "name": "Vampyre Juvinate", + "examine": "An initiate juvenile vampyre.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5798", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "5783", + "range_animation": "0", + "range_level": "45", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3522", + "name": "Vampyre Juvinate", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5798", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5783", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3523", + "name": "Vampyre Juvinate", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5798", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5783", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3524", + "name": "Vampyre Juvinate", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5798", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5783", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3525", + "name": "Vampyre Juvinate", + "aggressive": "true", + "attack_level": "1", + "death_animation": "5798", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5783", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3526", + "name": "Vampyre Juvinate", + "examine": "An initiate juvenile vampyre", + "attack_level": "1", + "death_animation": "6031", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "6016", + "range_animation": "0", + "range_level": "46", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3527", + "name": "Held Vampyre Juvinate", + "examine": "A Juvinate vampyre", + "attack_level": "1", + "death_animation": "5798", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "45", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3528", + "name": "Vampyre Juvinate" + }, + { + "id": "3529", + "name": "Vampyre Juvinate" + }, + { + "id": "3530", + "name": "Mist" + }, + { + "id": "3531", + "name": "Vampyre Juvenile", + "examine": "He looks really hungry!", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "6276", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "3532", + "name": "Vampyre Juvenile", + "examine": "She looks really hungry!", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "6276", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "3533", + "name": "Vampyre Juvenile", + "attack_level": "1", + "death_animation": "6781", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "6276", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3534", + "name": "Held Vampyre Juvenile", + "examine": "He looks really hungry!", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "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": "3577", + "name": "Juvinate", + "examine": "An initiate juvenile vampyre", + "attack_level": "1", + "death_animation": "6031", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "6016", + "range_animation": "0", + "range_level": "46", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3578", + "name": "Juvinate" + }, + { + "id": "3579", + "name": "Sheep", + "examine": "Is it a sheep?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3580", + "name": "Tentacle" + }, + { + "id": "3581", + "name": "Skeleton", + "examine": "He guards the dungeon with the faithfulness of the undead.", + "aggressive": "true", + "attack_level": "15", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "17", + "lifepoints": "29", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "70", + "safespot": null, + "strength_level": "18", + "weakness": "8" + }, + { + "id": "3582", + "name": "Guard dog", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "49", + "strength_level": "1" + }, + { + "id": "3583", + "name": "Hobgoblin", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "163", + "defence_level": "1", + "lifepoints": "29", + "melee_animation": "164", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3584", + "name": "Troll" + }, + { + "id": "3585", + "name": "Huge spider", + "examine": "No spider could grow that big! It's unrealistic!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "537,539,538", + "death_animation": "5321", + "defence_animation": "0", + "defence_level": "72", + "lifepoints": "122", + "magic_animation": "0", + "melee_animation": "5319", + "range_animation": "0", + "range_level": "72", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "3586", + "name": "Hellhound", + "examine": "Good doggy...", + "aggressive": "true", + "attack_level": "105", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "3717,3719,3718", + "death_animation": "6576", + "defence_animation": "0", + "defence_level": "102", + "lifepoints": "116", + "magic_animation": "0", + "melee_animation": "6562", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "116", + "strength_level": "104", + "weakness": "7" + }, + { + "id": "3587", + "name": "Rantz", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "27", + "attack_speed": "6", + "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "27", + "lifepoints": "60", + "magic_animation": "359", + "magic_level": "27", + "melee_animation": "359", + "range_animation": "359", + "range_level": "27", + "respawn_delay": "25", + "safespot": null, + "strength_level": "27" + }, + { + "id": "3588", + "name": "Baby red dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "20,10,10,20,40,40,20,40,40,20,0,0,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "42", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "40", + "melee_animation": "25", + "range_animation": "25", + "range_level": "1", + "respawn_delay": "32", + "safespot": null, + "strength_level": "40", + "weakness": "8" + }, + { + "id": "3589", + "name": "Kalphite Soldier", + "examine": "I don't think insect repellent will work...", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", + "combat_audio": "567,569,568", + "death_animation": "6228", + "defence_animation": "6227", + "defence_level": "70", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6223", + "poison_amount": "20", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "6" + }, + { + "id": "3590", + "name": "Steel dragon", + "examine": "Its scales seem to be made of steel.", + "aggressive": "true", + "attack_level": "234", + "bonuses": "56,42,104,51,212,115,232,302,331,21,264,100,100,40,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "defence_level": "238", + "lifepoints": "210", + "magic_level": "72", + "range_level": "185", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "221", + "strength_level": "229", + "weakness": "8" + }, + { + "id": "3591", + "name": "Dagannoth", + "examine": "A darkened horror from the ocean depths.", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3592", + "name": "Tok-Xil" + }, + { + "id": "3593", + "name": "Demon", + "examine": "Arrgh! Look at its pointy teeth!", + "aggressive": "true", + "attack_level": "75", + "combat_audio": "400,404,403", + "defence_animation": "0", + "defence_level": "75", + "lifepoints": "107", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "75", + "weakness": "7" + }, + { + "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": "3599", + "name": "Swamp snake", + "examine": "A large snake that thrives in swamps.", + "attack_level": "35", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "3539", + "defence_level": "40", + "lifepoints": "121", + "magic_animation": "3538", + "melee_animation": "3538", + "range_animation": "3538", + "range_level": "45", + "safespot": null, + "strength_level": "33", + "weakness": "0" + }, + { + "id": "3600", + "name": "Swamp snake", + "examine": "A large snake that thrives in swamps.", + "attack_level": "60", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "3539", + "defence_level": "60", + "lifepoints": "121", + "magic_animation": "3538", + "melee_animation": "3538", + "range_animation": "3538", + "range_level": "1", + "safespot": null, + "strength_level": "50" + }, + { + "id": "3601", + "name": "Swamp snake", + "examine": "A large snake that thrives in swamps.", + "attack_level": "35", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "3539", + "defence_level": "40", + "lifepoints": "121", + "magic_animation": "3538", + "melee_animation": "3538", + "range_animation": "3538", + "range_level": "1", + "safespot": null, + "strength_level": "33", + "weakness": "7" + }, + { + "id": "3602", + "name": "Swamp snake", + "examine": "A large snake that thrives in swamps.", + "attack_level": "35", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,1,10,6,10,6,4,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "3539", + "defence_level": "40", + "lifepoints": "121", + "magic_animation": "3538", + "melee_animation": "3538", + "range_animation": "3538", + "range_level": "1", + "safespot": null, + "strength_level": "33" + }, + { + "id": "3603", + "name": "Dead swamp snake", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3604", + "name": "Dead swamp snake", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3605", + "name": "Dead swamp snake", + "attack_level": "1", + "combat_audio": "3609,3608,3610", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3618", + "name": "Tentacle", + "examine": "I'm glad I can't see the rest of it!", + "aggressive": "true", + "attack_level": "80", + "death_animation": "3620", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "171", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "3618", + "range_animation": "3618", + "range_level": "80", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "9" + }, + { + "id": "3619", + "name": "Head", + "examine": "I'm glad I can't see the rest of it!", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "10", + "death_animation": "3733", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "171", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "3731", + "range_animation": "0", + "range_level": "80", + "respawn_delay": "60", + "safespot": null, + "strength_level": "85", + "weakness": "9" + }, + { + "id": "3620", + "name": "Head", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "10", + "death_animation": "3733", + "defence_animation": "3732", + "defence_level": "1", + "lifepoints": "150", + "melee_animation": "3731", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3621", + "name": "Tentacle" + }, + { + "id": "3622", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "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": "3634", + "name": "Fyiona Fray", + "examine": "A high-ranking Vyrewatch", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3645", + "name": "Angry bear", + "examine": "He looks a little on the cross side!", + "attack_level": "38", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "297,299,298", + "death_animation": "4929", + "defence_animation": "4927", + "defence_level": "38", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4927", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "41", + "weakness": "9" + }, + { + "id": "3646", + "name": "Angry unicorn", + "examine": "He looks a little on the cross side!", + "aggressive": "false", + "attack_level": "38", + "attack_speed": "4", + "combat_audio": "876,878,877", + "death_animation": "6377", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "6376", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "41", + "weakness": "9" + }, + { + "id": "3647", + "name": "Angry giant rat", + "examine": "He looks a little on the cross side!", + "attack_level": "25", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "3648", + "name": "Angry goblin", + "examine": "He looks a little on the cross side!", + "attack_level": "25", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "6185", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "3649", + "name": "Fear reaper", + "examine": "AHHHHH!", + "attack_level": "41", + "attack_speed": "4", + "combat_audio": "297,299,298", + "death_animation": "3813", + "defence_animation": "3811", + "defence_level": "40", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "3812", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "41", + "weakness": "" + }, + { + "id": "3650", + "name": "Confusion beast", + "examine": "What on Gielinor is that?!?", + "attack_level": "28", + "death_animation": "3821", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "3818", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "3651", + "name": "Confusion beast" + }, + { + "id": "3652", + "name": "Confusion beast" + }, + { + "id": "3653", + "name": "Confusion beast" + }, + { + "id": "3654", + "name": "Confusion beast" + }, + { + "id": "3655", + "name": "Hopeless creature", + "examine": "A hopeless poor creature.", + "attack_level": "25", + "death_animation": "3827", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "3823", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "3656", + "name": "Hopeless creature" + }, + { + "id": "3657", + "name": "Hopeless creature" + }, + { + "id": "3658", + "name": "Tolna" + }, + { + "id": "3659", + "name": "Tolna" + }, + { + "id": "3660", + "name": "Tolna" + }, + { + "id": "3661", + "name": "Angry unicorn", + "examine": "He looks a little on the cross side!", + "aggressive": "false", + "attack_level": "38", + "attack_speed": "4", + "combat_audio": "876,878,877", + "death_animation": "6377", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "6376", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "41", + "weakness": "9" + }, + { + "id": "3662", + "name": "Angry giant rat", + "examine": "He looks a little on the cross side!", + "attack_level": "38", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "38", + "lifepoints": "50", + "melee_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "41" + }, + { + "id": "3663", + "name": "Angry goblin", + "examine": "He looks a little on the cross side!", + "attack_level": "38", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "38", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "6185", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "41", + "weakness": "9" + }, + { + "id": "3664", + "name": "Angry bear", + "examine": "He looks a little on the cross side!", + "attack_level": "38", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "297,299,298", + "death_animation": "4929", + "defence_animation": "4927", + "defence_level": "38", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "4927", + "range_level": "1", + "safespot": null, + "strength_level": "41" + }, + { + "id": "3665", + "name": "Fear reaper", + "examine": "AHHHHH!", + "attack_level": "45", + "attack_speed": "4", + "bonuses": "", + "combat_audio": "297,299,298", + "death_animation": "3813", + "defence_animation": "3811", + "defence_level": "45", + "lifepoints": "57", + "magic_level": "1", + "melee_animation": "3812", + "range_level": "1", + "safespot": null, + "strength_level": "48" + }, + { + "id": "3666", + "name": "Confusion beast", + "examine": "What on Gielinor is that?!?", + "attack_level": "52", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "297,299,298", + "death_animation": "3821", + "defence_animation": "3820", + "defence_level": "52", + "lifepoints": "64", + "magic_level": "1", + "melee_animation": "3818", + "range_level": "1", + "safespot": null, + "strength_level": "55" + }, + { + "id": "3667", + "name": "Hopeless creature", + "examine": "He looks a little on the cross side!", + "attack_level": "59", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "297,299,298", + "death_animation": "3827", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "71", + "magic_level": "1", + "melee_animation": "3823", + "range_level": "1", + "safespot": null, + "strength_level": "62" + }, + { + "id": "3668", + "name": "Hopeless beast" + }, + { + "id": "3669", + "name": "Hopeless beast" + }, + { + "id": "3670", + "name": "Odd Old Man", + "examine": "That's a lot of bones on his back..." + }, + { + "id": "3671", + "name": "Fortunato", + "examine": "Looks like he could do with a sip of Amontillado." + }, + { + "id": "3672", + "name": "Ram", + "examine": "Freshly sheared.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "757,759,758", + "death_animation": "5343", + "defence_animation": "5337", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "0", + "melee_animation": "5341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "3673", + "name": "Ram", + "examine": "White and fluffy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "757,759,758", + "death_animation": "5336", + "defence_animation": "5337", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "0", + "melee_animation": "5338", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "3674", + "name": "Sack" + }, + { + "id": "3675", + "name": "Vulture", + "examine": "If you see them circling, run.", + "attack_level": "40", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "890,892,891", + "death_animation": "2021", + "defence_animation": "2024", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "2019", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "40", + "weakness": "9" + }, + { + "id": "3676", + "name": "Vulture", + "examine": "If you see them circling, run.", + "attack_level": "40", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "890,892,891", + "death_animation": "2026", + "defence_animation": "2024", + "defence_level": "10", + "lifepoints": "10", + "magic_level": "1", + "melee_animation": "2025", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3677", + "name": "Sinister Stranger" + }, + { + "id": "3678", + "name": "Sinister Stranger" + }, + { + "id": "3679", + "name": "Vestri" + }, + { + "id": "3680", + "name": "Ali the Leaflet Dropper", + "examine": "A young boy handing out flyers.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3697", + "name": "Skeleton fremennik", + "examine": "Could use a good meal.", + "attack_level": "17", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "17", + "lifepoints": "24", + "magic_animation": "0", + "melee_animation": "6128", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "17", + "weakness": "6" + }, + { + "id": "3698", + "name": "Skeleton fremennik", + "attack_level": "1", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3699", + "name": "Skeleton fremennik", + "attack_level": "1", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3700", + "name": "Skeleton fremennik", + "examine": "He seems a little underweight.", + "attack_level": "20", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "6128", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "20", + "weakness": "6" + }, + { + "id": "3701", + "name": "Skeleton fremennik", + "examine": "He seems a little underweight.", + "attack_level": "20", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "6128", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "20", + "weakness": "6" + }, + { + "id": "3702", + "name": "Skeleton fremennik", + "attack_level": "1", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "35", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3703", + "name": "Skeleton fremennik", + "examine": "He seems a little underweight.", + "attack_level": "22", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "6128", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "3704", + "name": "Skeleton fremennik", + "examine": "He seems a little underweight.", + "attack_level": "22", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "6128", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "6" + }, + { + "id": "3705", + "name": "Skeleton fremennik", + "attack_level": "1", + "combat_audio": "774,775,777", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3706", + "name": "Ulfric", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "6118", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3707", + "name": "Brine rat", + "examine": "Eww", + "attack_level": "35", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "6115", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "6117", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "50", + "strength_level": "35", + "weakness": "9" + }, + { + "id": "3708", + "name": "Boulder" + }, + { + "id": "3709", + "name": "Boulder", + "examine": "A Wilderness fighter of massive repute.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3710", + "name": "Ulfric" + }, + { + "id": "3711", + "name": "Giant bat", + "examine": "An annoying flappy thing.", + "aggressive": "true", + "attack_level": "12", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,10,10,12,10,8,0,0,0,0,0", + "combat_audio": "292,294,293", + "death_animation": "4917", + "defence_animation": "4916", + "defence_level": "22", + "lifepoints": "32", + "magic_animation": "4915", + "magic_level": "1", + "melee_animation": "4915", + "range_animation": "4915", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "8", + "strength_level": "22", + "weakness": "4" + }, + { + "id": "3712", + "name": "Zanik" + }, + { + "id": "3713", + "name": "Sigmund" + }, + { + "id": "3714", + "name": "Zanik" + }, + { + "id": "3715", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "3726", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "3727", + "name": "Splatter", + "examine": "Don't burst its bubble!", + "attack_level": "20", + "bonuses": "5,10,10,10,10,2,0,0,0,0,0,0,0,0,0", + "combat_audio": "809,811,810", + "death_animation": "3888", + "defence_animation": "3890", + "defence_level": "20", + "lifepoints": "13", + "magic_animation": "3891", + "magic_level": "5", + "melee_animation": "3891", + "range_animation": "3891", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "20", + "weakness": "8" + }, + { + "id": "3728", + "name": "Splatter", + "examine": "Don't burst its bubble!", + "attack_level": "25", + "bonuses": "10,15,15,15,15,5,0,0,0,0,0,0,0,0,0", + "combat_audio": "809,811,810", + "death_animation": "3888", + "defence_animation": "3890", + "defence_level": "25", + "lifepoints": "23", + "magic_animation": "3891", + "magic_level": "10", + "melee_animation": "3891", + "range_animation": "3891", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "25" + }, + { + "id": "3729", + "name": "Splatter", + "examine": "Don't burst its bubble!", + "attack_level": "30", + "bonuses": "15,20,20,20,20,8,0,0,0,0,0,0,0,0,0", + "combat_audio": "809,811,810", + "death_animation": "3888", + "defence_animation": "3890", + "defence_level": "30", + "lifepoints": "23", + "magic_animation": "3891", + "magic_level": "10", + "melee_animation": "3891", + "range_animation": "3891", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "30" + }, + { + "id": "3730", + "name": "Splatter", + "examine": "Don't burst its bubble!", + "attack_level": "35", + "bonuses": "20,25,25,25,25,10,0,0,0,0,0,0,0,0,0", + "combat_audio": "809,811,810", + "death_animation": "3888", + "defence_animation": "3890", + "defence_level": "35", + "lifepoints": "23", + "magic_animation": "3891", + "magic_level": "15", + "melee_animation": "3891", + "range_animation": "3891", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "35" + }, + { + "id": "3731", + "name": "Splatter", + "examine": "Don't burst its bubble!", + "attack_level": "40", + "bonuses": "25,30,30,30,30,15,0,0,0,0,0,0,0,0,0", + "combat_audio": "809,811,810", + "death_animation": "3888", + "defence_animation": "3890", + "defence_level": "40", + "lifepoints": "23", + "magic_animation": "3891", + "magic_level": "20", + "melee_animation": "3891", + "range_animation": "3891", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3732", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "28", + "bonuses": "20,25,20,40,40,40,40,5,18,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "28", + "lifepoints": "23", + "magic_animation": "3901", + "magic_level": "14", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "3733", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "28", + "bonuses": "20,25,20,40,40,40,40,5,18,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "28", + "lifepoints": "23", + "magic_animation": "3901", + "magic_level": "14", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "28" + }, + { + "id": "3734", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "42", + "bonuses": "30,35,30,50,50,50,50,10,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "42", + "lifepoints": "38", + "magic_animation": "3901", + "magic_level": "20", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "42" + }, + { + "id": "3735", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "42", + "bonuses": "30,35,30,50,50,50,50,10,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "42", + "lifepoints": "38", + "magic_animation": "3901", + "magic_level": "20", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "42" + }, + { + "id": "3736", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "54", + "bonuses": "35,40,35,60,60,60,60,20,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "54", + "lifepoints": "53", + "magic_animation": "3901", + "magic_level": "25", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "54" + }, + { + "id": "3737", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "54", + "bonuses": "35,40,35,60,60,60,60,20,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "54", + "lifepoints": "53", + "magic_animation": "3901", + "magic_level": "25", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "54" + }, + { + "id": "3738", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,50,40,70,70,70,70,30,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "60", + "lifepoints": "68", + "magic_animation": "3901", + "magic_level": "30", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "60" + }, + { + "id": "3739", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "60", + "bonuses": "40,50,40,70,70,70,70,30,15,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "60", + "lifepoints": "68", + "magic_animation": "3901", + "magic_level": "30", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "60" + }, + { + "id": "3740", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "70", + "bonuses": "50,60,50,80,70,90,80,35,14,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "70", + "lifepoints": "83", + "magic_animation": "3901", + "magic_level": "35", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "70" + }, + { + "id": "3741", + "name": "Shifter", + "examine": "Nippy little thing!", + "aggressive": "true", + "attack_level": "70", + "bonuses": "50,60,50,80,70,90,80,35,14,0,0,0,0,0,0", + "combat_audio": "766,768,767", + "death_animation": "3903", + "defence_animation": "3902", + "defence_level": "70", + "lifepoints": "83", + "magic_animation": "3901", + "magic_level": "35", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", + "range_level": "1", + "respawn_delay": "127", + "safespot": null, + "strength_level": "70" + }, + { + "id": "3742", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "15", + "attack_speed": "5", + "bonuses": "45,45,45,45,50,5,45,0,0,0,0,0,0,0,0", + "combat_audio": "714,716,715", + "death_animation": "3917", + "defence_animation": "3916", + "defence_level": "15", + "lifepoints": "23", + "magic_animation": "3915", + "magic_level": "10", + "melee_animation": "3915", + "range_animation": "3915", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "30", + "weakness": "7" + }, + { + "id": "3743", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "20", + "attack_speed": "5", + "bonuses": "45,45,45,45,50,5,55,0,0,0,0,0,0,0,0", + "combat_audio": "714,716,715", + "death_animation": "3917", + "defence_animation": "3916", + "defence_level": "20", + "lifepoints": "38", + "magic_animation": "3915", + "magic_level": "15", + "melee_animation": "3915", + "range_animation": "3915", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3744", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "45,55,55,55,50,10,45,0,0,0,0,0,0,0,0", + "combat_audio": "714,716,715", + "death_animation": "3917", + "defence_animation": "3916", + "defence_level": "30", + "lifepoints": "53", + "magic_animation": "3915", + "magic_level": "20", + "melee_animation": "3915", + "range_animation": "3915", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "60" + }, + { + "id": "3745", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "45", + "attack_speed": "5", + "bonuses": "60,60,60,60,80,10,45,0,0,0,0,0,0,0,0", + "combat_audio": "714,716,715", + "death_animation": "3917", + "defence_animation": "3916", + "defence_level": "45", + "lifepoints": "53", + "magic_animation": "3915", + "magic_level": "30", + "melee_animation": "3915", + "range_animation": "3915", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "75" + }, + { + "id": "3746", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "60", + "attack_speed": "5", + "bonuses": "65,70,70,70,90,20,56,0,0,0,0,0,0,0,0", + "combat_audio": "714,716,715", + "death_animation": "3917", + "defence_animation": "3916", + "defence_level": "60", + "lifepoints": "53", + "magic_animation": "3915", + "magic_level": "40", + "melee_animation": "3915", + "range_animation": "3915", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "80" + }, + { + "id": "3747", + "name": "Spinner", + "examine": "Eeewww!", + "attack_level": "20", + "bonuses": "20,40,40,40,40,10,38,0,0,0,0,0,0,0,0", + "combat_audio": "805,807,806", + "death_animation": "3910", + "defence_animation": "3909", + "defence_level": "20", + "lifepoints": "33", + "magic_animation": "3908", + "magic_level": "10", + "melee_animation": "3908", + "range_animation": "3908", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "20", + "weakness": "6" + }, + { + "id": "3748", + "name": "Spinner", + "examine": "Eeewww!", + "attack_level": "30", + "bonuses": "30,60,60,60,60,15,38,0,0,0,0,0,0,0,0", + "combat_audio": "805,807,806", + "death_animation": "3910", + "defence_animation": "3909", + "defence_level": "30", + "lifepoints": "50", + "magic_animation": "3908", + "magic_level": "15", + "melee_animation": "3908", + "range_animation": "3908", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "30" + }, + { + "id": "3749", + "name": "Spinner", + "examine": "Eeewww!", + "attack_level": "40", + "bonuses": "50,70,70,70,70,20,38,0,0,0,0,0,0,0,0", + "combat_audio": "805,807,806", + "death_animation": "3910", + "defence_animation": "3909", + "defence_level": "40", + "lifepoints": "67", + "magic_animation": "3908", + "magic_level": "20", + "melee_animation": "3908", + "range_animation": "3908", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "40" + }, + { + "id": "3750", + "name": "Spinner", + "examine": "Eeewww!", + "attack_level": "60", + "bonuses": "60,100,100,100,100,30,25,0,0,0,0,0,0,0,0", + "combat_audio": "805,807,806", + "death_animation": "3910", + "defence_animation": "3909", + "defence_level": "60", + "lifepoints": "101", + "magic_animation": "3908", + "magic_level": "30", + "melee_animation": "3908", + "range_animation": "3908", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "60" + }, + { + "id": "3751", + "name": "Spinner", + "examine": "Eeewww!", + "attack_level": "50", + "bonuses": "55,80,80,80,85,25,30,0,0,0,0,0,0,0,0", + "combat_audio": "805,807,806", + "death_animation": "3910", + "defence_animation": "3909", + "defence_level": "50", + "lifepoints": "84", + "magic_animation": "3908", + "magic_level": "25", + "melee_animation": "3908", + "range_animation": "3908", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "50" + }, + { + "id": "3752", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "30,30,30,30,15,30,40,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "25", + "end_gfx": "648", + "lifepoints": "18", + "magic_animation": "3882", + "magic_level": "25", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "3753", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "30,30,30,30,15,30,40,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "25", + "end_gfx": "648", + "lifepoints": "18", + "magic_animation": "3882", + "magic_level": "25", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3754", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "45,40,40,40,25,35,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "40", + "end_gfx": "648", + "lifepoints": "30", + "magic_animation": "3882", + "magic_level": "40", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3755", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "45,40,40,40,25,35,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "40", + "end_gfx": "648", + "lifepoints": "30", + "magic_animation": "3882", + "magic_level": "40", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3756", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "35,40,40,40,30,35,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "50", + "end_gfx": "648", + "lifepoints": "42", + "magic_animation": "3882", + "magic_level": "50", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3757", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "35,40,40,40,30,35,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "50", + "end_gfx": "648", + "lifepoints": "42", + "magic_animation": "3882", + "magic_level": "50", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3758", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,80,80,80,50,50,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "60", + "end_gfx": "648", + "lifepoints": "51", + "magic_animation": "3882", + "magic_level": "60", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3759", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,80,80,80,50,50,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "60", + "end_gfx": "648", + "lifepoints": "51", + "magic_animation": "3882", + "magic_level": "60", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3760", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "40,100,100,100,50,50,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "70", + "end_gfx": "648", + "lifepoints": "62", + "magic_animation": "3882", + "magic_level": "70", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3761", + "name": "Torcher", + "examine": "Burn, baby, burn!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "40,100,100,100,50,50,35,0,0,0,0,0,0,0,0", + "combat_audio": "845,848,846", + "combat_style": "2", + "death_animation": "3881", + "defence_animation": "3880", + "defence_level": "70", + "end_gfx": "648", + "lifepoints": "62", + "magic_animation": "3882", + "magic_level": "70", + "melee_animation": "3882", + "projectile": "647", + "range_animation": "3882", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "646", + "strength_level": "1" + }, + { + "id": "3762", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "20,40,40,40,40,40,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "25", + "lifepoints": "27", + "magic_animation": "3920", + "magic_level": "30", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "25", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "657", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "3763", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "20,40,40,40,40,40,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "25", + "lifepoints": "27", + "magic_animation": "3920", + "magic_level": "30", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "25", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3764", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "30,50,60,60,60,60,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "40", + "lifepoints": "45", + "magic_animation": "3920", + "magic_level": "40", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "40", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "657", + "strength_level": "1" + }, + { + "id": "3765", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "30,40,60,60,60,60,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "40", + "lifepoints": "45", + "magic_animation": "3920", + "magic_level": "40", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "40", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3766", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "35,65,70,70,70,70,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "50", + "lifepoints": "62", + "magic_animation": "3920", + "magic_level": "50", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "50", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "657", + "strength_level": "1" + }, + { + "id": "3767", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "35,65,70,70,70,70,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "50", + "lifepoints": "62", + "magic_animation": "3920", + "magic_level": "50", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "50", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3768", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "40,90,80,80,80,80,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "60", + "lifepoints": "78", + "magic_animation": "3920", + "magic_level": "60", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "60", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "657", + "strength_level": "1" + }, + { + "id": "3769", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "40,90,80,80,80,80,40,40,0,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "60", + "lifepoints": "78", + "magic_animation": "3920", + "magic_level": "60", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "60", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3770", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "50,100,100,100,120,100,40,40,80,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "70", + "lifepoints": "97", + "magic_animation": "3920", + "magic_level": "70", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "70", + "respawn_delay": "120", + "safespot": null, + "start_gfx": "657", + "strength_level": "1" + }, + { + "id": "3771", + "name": "Defiler", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "1", + "bonuses": "50,100,100,100,120,100,40,40,80,0,0,0,0,0,0", + "combat_audio": "392,394,393", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "3921", + "defence_level": "70", + "lifepoints": "97", + "magic_animation": "3920", + "magic_level": "70", + "melee_animation": "3920", + "projectile": "657", + "range_animation": "3920", + "range_level": "70", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3772", + "name": "Brawler", + "examine": "Mind your toes!", + "aggressive": "true", + "attack_level": "28", + "bonuses": "45,45,50,50,50,50,50,15,50,0,0,0,0,0,0", + "combat_audio": "323,326,325", + "death_animation": "3894", + "defence_animation": "3895", + "defence_level": "28", + "lifepoints": "53", + "magic_animation": "3896", + "magic_level": "14", + "melee_animation": "3896", + "range_animation": "3896", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "28", + "weakness": "9" + }, + { + "id": "3773", + "name": "Brawler", + "examine": "Mind your toes!", + "aggressive": "true", + "attack_level": "42", + "bonuses": "45,45,60,50,50,50,50,15,50,0,0,0,0,0,0", + "combat_audio": "323,326,325", + "death_animation": "3894", + "defence_animation": "3895", + "defence_level": "42", + "lifepoints": "83", + "magic_animation": "3896", + "magic_level": "21", + "melee_animation": "3896", + "range_animation": "3896", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "42" + }, + { + "id": "3774", + "name": "Brawler", + "examine": "Mind your toes!", + "aggressive": "true", + "attack_level": "56", + "bonuses": "45,45,60,80,80,80,80,25,50,0,0,0,0,0,0", + "combat_audio": "323,326,325", + "death_animation": "3894", + "defence_animation": "3895", + "defence_level": "56", + "lifepoints": "113", + "magic_animation": "3896", + "magic_level": "32", + "melee_animation": "3896", + "range_animation": "3896", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "56" + }, + { + "id": "3775", + "name": "Brawler", + "examine": "Mind your toes!", + "aggressive": "true", + "attack_level": "74", + "bonuses": "45,45,60,60,60,60,60,20,50,0,0,0,0,0,0", + "combat_audio": "323,326,325", + "death_animation": "3894", + "defence_animation": "3895", + "defence_level": "74", + "lifepoints": "143", + "magic_animation": "3896", + "magic_level": "38", + "melee_animation": "3896", + "range_animation": "3896", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "74" + }, + { + "id": "3776", + "name": "Brawler", + "examine": "Mind your toes!", + "aggressive": "true", + "attack_level": "88", + "bonuses": "45,45,80,100,100,100,100,30,46,0,0,0,0,0,0", + "combat_audio": "323,326,325", + "death_animation": "3894", + "defence_animation": "3895", + "defence_level": "88", + "lifepoints": "173", + "magic_animation": "3896", + "magic_level": "45", + "melee_animation": "3896", + "range_animation": "3896", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "88" + }, + { + "id": "3777", + "name": "Doomsayer", + "examine": "Cheerful, helpful and optimistic, I'll bet.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3778", + "name": "Arthur" + }, + { + "id": "3780", + "name": "Squire" + }, + { + "id": "3782", + "name": "Void Knight", + "examine": "A squire of balance.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "180,180,180,180,80,0,0,0,0,0,0,0,0,0,0", + "defence_level": "80", + "lifepoints": "200", + "magic_level": "80", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3783", + "name": "Frog", + "examine": "Long legged licker.", + "attack_level": "55", + "death_animation": "7256", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "7260", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "3784", + "name": "Void Knight", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "0", + "defence_level": "1", + "lifepoints": "200", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3785", + "name": "Void Knight", + "examine": "A squire of balance.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "180,180,180,180,80,0,0,0,0,0,0,0,0,0,0", + "defence_level": "80", + "lifepoints": "200", + "magic_level": "80", + "range_level": "1", + "respawn_delay": "120", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3786", + "name": "Void Knight", + "examine": "A powerful knight of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3787", + "name": "Sir Palomedes" + }, + { + "id": "3788", + "name": "Void Knight", + "examine": "A powerful knight of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3789", + "name": "Void Knight" + }, + { + "id": "3790", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3791", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3792", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3793", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3794", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3795", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3796", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3797", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3798", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3799", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3800", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3801", + "name": "Squire", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3802", + "name": "Squire (Novice)", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3803", + "name": "Fishing spot" + }, + { + "id": "3805", + "name": "Postie Pete", + "examine": "Posts things.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3806", + "name": "Millie Miller", + "examine": "Miss Millicent Miller the Miller of Mill Lane Mill.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3807", + "name": "Gillie Groats", + "examine": "Gillie the Milkmaid milks cows. She's udderly fantastic at it.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3808", + "name": "Tortoise", + "examine": "Massive beast of War with extra Gnome.", + "attack_level": "36", + "death_animation": "3962", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "121", + "magic_animation": "0", + "melee_animation": "3960", + "range_animation": "3954", + "range_level": "1", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "3809", + "name": "Captain Dalbur", + "examine": "Tally Ho!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3810", + "name": "Captain Bleemadge", + "examine": "Huzzah!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3811", + "name": "Captain Errdo", + "examine": "Up up and away!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3812", + "name": "Captain Klemfoodle", + "examine": "Up up and away!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3814", + "name": "Gnome Archer", + "examine": "A Gnome Arrow-chucker", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "190", + "prj_height": "30", + "projectile": "10", + "range_animation": "190", + "range_level": "5", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "2" + }, + { + "id": "3815", + "name": "Gnome Driver", + "examine": "Yee haa!", + "aggressive": "true", + "attack_level": "5", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "3969", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "3816", + "name": "Gnome Mage", + "examine": "A battle mage of the gnomish variety.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "death_animation": "196", + "defence_animation": "193", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "200", + "magic_level": "5", + "melee_animation": "3968", + "prj_height": "30", + "projectile": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "spell_id": "", + "start_gfx": "93", + "start_height": "80", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "3817", + "name": "Lieutenant Schepbur" + }, + { + "id": "3818", + "name": "Trainer Nacklepen", + "examine": "The cruel tortoise trainer. Boo!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3819", + "name": "Tortoise", + "examine": "A large tortoise.", + "attack_level": "36", + "death_animation": "3962", + "defence_animation": "0", + "defence_level": "36", + "lifepoints": "101", + "magic_animation": "0", + "melee_animation": "3960", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "36", + "weakness": "6" + }, + { + "id": "3820", + "name": "Wise Old Man" + }, + { + "id": "3822", + "name": "Herman Caranos", + "examine": "Founder and Director of the Piscatoris Fishing Colony." + }, + { + "id": "3823", + "name": "Franklin Caranos", + "examine": "Engineer for the Piscatoris Fishing Colony." + }, + { + "id": "3824", + "name": "Arnold Lydspor", + "examine": "Secretary and Storemaster for the Piscatoris Fishing Colony." + }, + { + "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": "3835", + "name": "Kalphite Queen", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "6242", + "defence_animation": "6240", + "defence_level": "1", + "lifepoints": "255", + "melee_animation": "6241", + "range_level": "1000", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3836", + "name": "Kalphite Queen", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "6233", + "defence_animation": "6237", + "defence_level": "1", + "lifepoints": "255", + "melee_animation": "6234", + "range_level": "1000", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3837", + "name": "Drunken Dwarf" + }, + { + "id": "3838", + "name": "Wise Old Man" + }, + { + "id": "3839", + "name": "Wise Old Man" + }, + { + "id": "3840", + "name": "Sea troll", + "examine": "An aquatic troll.", + "aggressive": "true", + "attack_level": "45", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "45", + "weakness": "6" + }, + { + "id": "3841", + "name": "Sea troll" + }, + { + "id": "3842", + "name": "Sea troll" + }, + { + "id": "3843", + "name": "Sea troll", + "examine": "An aquatic troll.", + "aggressive": "true", + "attack_level": "60", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "60", + "weakness": "6" + }, + { + "id": "3844", + "name": "Skeleton Mage" + }, + { + "id": "3845", + "name": "Sea troll" + }, + { + "id": "3846", + "name": "Sea Troll General" + }, + { + "id": "3847", + "name": "Sea Troll Queen", + "examine": "The mother of all sea trolls!", + "aggressive": "true", + "attack_level": "65", + "death_animation": "3993", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "428", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "3991", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "3848", + "name": "Fishing spot" + }, + { + "id": "3849", + "name": "Fishing spot", + "examine": "What have they done to him?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3850", + "name": "Skeleton Mage" + }, + { + "id": "3851", + "name": "Skeleton Mage", + "examine": "Doesn't seem to want to go away.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "811", + "magic_level": "100", + "melee_animation": "811", + "range_animation": "811", + "range_level": "1", + "strength_level": "10" + }, + { + "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": "3915", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "7", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "3916", + "name": "Carpenter Kjallak", + "examine": "His bark's worse than his blight.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3917", + "name": "Farmer Fromund", + "examine": "He looks a bit seedy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3918", + "name": "Prince Brand" + }, + { + "id": "3919", + "name": "Princess Astrid" + }, + { + "id": "3920", + "name": "Runa" + }, + { + "id": "3921", + "name": "Halla", + "examine": "A subject of Miscellania." + }, + { + "id": "3922", + "name": "Finn", + "examine": "Runs the general store." + }, + { + "id": "3923", + "name": "Osvald" + }, + { + "id": "3924", + "name": "Runolf" + }, + { + "id": "3925", + "name": "Tjorvi", + "examine": "A subject of Miscellania." + }, + { + "id": "3926", + "name": "Ingrid" + }, + { + "id": "3927", + "name": "Thora", + "examine": "A subject of Miscellania." + }, + { + "id": "3928", + "name": "Signy" + }, + { + "id": "3929", + "name": "Hild" + }, + { + "id": "3930", + "name": "Armod" + }, + { + "id": "3931", + "name": "Beigarth" + }, + { + "id": "3932", + "name": "Reinn" + }, + { + "id": "3933", + "name": "Alviss", + "examine": "A dwarf who's currently living in Miscellania." + }, + { + "id": "3934", + "name": "Fullangr", + "examine": "A dwarf who's currently living in Miscellania." + }, + { + "id": "3935", + "name": "Jari", + "examine": "A dwarf who's currently living in Miscellania." + }, + { + "id": "3936", + "name": "Thorodin" + }, + { + "id": "3937", + "name": "Ferd", + "examine": "A dwarf miner. He's shoring up the walls." + }, + { + "id": "3938", + "name": "Donal", + "examine": "A rather scared-looking dwarf. He doesn't seem all that drunk." + }, + { + "id": "3939", + "name": "Sea Snake Young", + "examine": "Snake", + "aggressive": "true", + "attack_level": "33", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "128", + "magic_animation": "0", + "melee_animation": "3538", + "range_animation": "0", + "range_level": "45", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "1" + }, + { + "id": "3940", + "name": "Sea Snake Hatchling", + "examine": "A baby sea snake. Snaaaaaaake!", + "aggressive": "true", + "attack_level": "33", + "combat_audio": "3609,3608,3610", + "death_animation": "3540", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "3538", + "range_animation": "0", + "range_level": "45", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "1" + }, + { + "id": "3941", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3942", + "name": "Guard", + "examine": "He's guarding the entrance to the dungeons.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "3943", + "name": "Giant Sea Snake", + "examine": "A big snake that lives in the sea. How did it get in here?", + "aggressive": "true", + "attack_level": "33", + "combat_audio": "3609,3608,3610", + "death_animation": "4039", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "385", + "magic_animation": "0", + "melee_animation": "4040", + "range_animation": "0", + "range_level": "45", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "1" + }, + { + "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": "4227", + "name": "Cockatrice", + "attack_level": "1", + "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "37", + "strength_level": "1" + }, + { + "id": "4228", + "name": "Basilisk", + "attack_level": "1", + "combat_audio": "288,290,289", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1" + }, + { + "id": "4229", + "name": "Kurask", + "examine": "Large, heavy, with sharp things attached to its head.", + "aggressive": "true", + "attack_level": "67", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,20,20,0,0,0,0,0,0,0", + "combat_audio": "588,590,589", + "death_animation": "9440", + "defence_animation": "9441", + "defence_level": "105", + "lifepoints": "97", + "magic_animation": "9439", + "magic_level": "1", + "melee_animation": "9439", + "range_animation": "9439", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "97", + "strength_level": "105" + }, + { + "id": "4230", + "name": "Abyssal demon", + "examine": "A denizen of the Abyss!", + "attack_level": "97", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,20,0,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "276,278,277", + "death_animation": "1538", + "defence_animation": "0", + "defence_level": "135", + "lifepoints": "150", + "magic_animation": "1537", + "magic_level": "1", + "melee_animation": "1537", + "range_animation": "1537", + "range_level": "1", + "safespot": null, + "slayer_exp": "150", + "strength_level": "67", + "weakness": "1" + }, + { + "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": "4239", + "name": "Cook", + "examine": "I hope she can find all the ingredients!" + }, + { + "id": "4240", + "name": "Butler" + }, + { + "id": "4242", + "name": "Demon butler" + }, + { + "id": "4243", + "name": "Demon butler", + "attack_level": "1", + "combat_audio": "400,404,403", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4244", + "name": "Chief servant" + }, + { + "id": "4245", + "name": "Chief servant", + "examine": "Head of the servants' guild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4246", + "name": "Taxidermist", + "examine": "She smells unpleasantly of chemicals.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4247", + "name": "Estate agent", + "examine": "A fancy businessman with a mighty fine hat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4248", + "name": "Stonemason", + "examine": "Someone has to get rid of all the stone they dug Keldagrim out of.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4249", + "name": "Sir Renitee", + "examine": "Chief Herald of Falador." + }, + { + "id": "4250", + "name": "Sawmill operator", + "examine": "He changes the shape of wood.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "facing_booth": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4251", + "name": "Garden supplier", + "examine": "She has green fingers. (Not literally.)", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4252", + "name": "Macaroni Penguin", + "examine": "Beak areful with this one", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4253", + "name": "Guard" + }, + { + "id": "4257", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4258", + "name": "Guard", + "examine": "A dwarven guard.", + "aggressive": "true", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "102", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", + "magic_animation": "0", + "melee_animation": "99", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "6" + }, + { + "id": "4259", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4260", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4261", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4262", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4263", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4264", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4265", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4266", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4267", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4268", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4269", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4270", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4271", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4272", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4273", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4274", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4275", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4276", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4277", + "name": "Buinn" + }, + { + "id": "4278", + "name": "Animated Bronze Armour", + "examine": "Animated bronze armour.", + "aggressive": "true", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "10", + "weakness": "7" + }, + { + "id": "4279", + "name": "Animated Iron Armour", + "examine": "Animated iron armour.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "20", + "lifepoints": "20", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "4280", + "name": "Animated Steel Armour", + "examine": "Animated steel armour.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "40", + "lifepoints": "40", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4281", + "name": "Animated Black Armour", + "examine": "Animated black armour.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "60", + "lifepoints": "60", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "60", + "weakness": "7" + }, + { + "id": "4282", + "name": "Animated Mithril Armour", + "examine": "Animated mithril armour.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "80", + "lifepoints": "80", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "80", + "weakness": "7" + }, + { + "id": "4283", + "name": "Animated Adamant Armour", + "examine": "Animated adamant armour.", + "aggressive": "true", + "attack_level": "99", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "99", + "lifepoints": "99", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "99", + "weakness": "7" + }, + { + "id": "4284", + "name": "Animated Rune Armour", + "examine": "Animated rune armour.", + "aggressive": "true", + "attack_level": "120", + "attack_speed": "4", + "bonuses": "4,4,4,0,0,50,25,19,400,400,0,5,0,0,0", + "combat_audio": "2549,1979,512", + "death_animation": "4167", + "defence_animation": "388", + "defence_level": "120", + "lifepoints": "120", + "magic_animation": "386", + "melee_animation": "386", + "poison_immune": "true", + "range_animation": "386", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "120", + "weakness": "7" + }, + { + "id": "4285", + "name": "Ghommal", + "examine": "The big door man." + }, + { + "id": "4286", + "name": "Harrallak Menarous", + "examine": "Guild Master." + }, + { + "id": "4287", + "name": "Gamfred", + "examine": "The guild engineer." + }, + { + "id": "4288", + "name": "Ajjat", + "examine": "Used to be a black knight." + }, + { + "id": "4289", + "name": "Kamfreena", + "examine": "Blademistress, skillful female warrior." + }, + { + "id": "4290", + "name": "Shanomi", + "examine": "A warrior mage from the east." + }, + { + "id": "4291", + "name": "Cyclops", + "examine": "A one-eyed man eater.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "75", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "75", + "strength_level": "50", + "weakness": "8" + }, + { + "id": "4292", + "name": "Cyclops", + "examine": "A one-eyed woman eater.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "35", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "100", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "4293", + "name": "Lidio", + "examine": "Food shopkeeper." + }, + { + "id": "4294", + "name": "Lilly", + "examine": "Potion shopkeeper." + }, + { + "id": "4295", + "name": "Anton", + "examine": "Armour shopkeeper." + }, + { + "id": "4296", + "name": "Jade", + "examine": "Bank staff." + }, + { + "id": "4297", + "name": "Sloane", + "examine": "He looks like a strong warrior." + }, + { + "id": "4298", + "name": "Jimmy", + "examine": "He looks a bit drunk." + }, + { + "id": "4299", + "name": "Ref", + "examine": "He looks fair and reliable." + }, + { + "id": "4300", + "name": "Ref", + "examine": "He looks fair and reliable.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4301", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4302", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4303", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4304", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4305", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4306", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4307", + "name": "Guard", + "examine": "A guard for the humans against monster group.", + "attack_level": "26", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "4308", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4309", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4310", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4311", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4312", + "name": "Nardok" + }, + { + "id": "4313", + "name": "Dartog" + }, + { + "id": "4315", + "name": "Dwarf" + }, + { + "id": "4316", + "name": "Dwarf", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "4317", + "name": "H.A.M. Member" + }, + { + "id": "4318", + "name": "H.A.M. Member", + "attack_level": "1", + "clue_level": "0", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4319", + "name": "H.A.M. Member" + }, + { + "id": "4320", + "name": "H.A.M. Member", + "attack_level": "1", + "clue_level": "0", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4329", + "name": "H.A.M. Deacon", + "attack_level": "1", + "clue_level": "0", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4336", + "name": "Guard", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4337", + "name": "Zanik" + }, + { + "id": "4338", + "name": "Zanik" + }, + { + "id": "4340", + "name": "Zanik" + }, + { + "id": "4341", + "name": "Zanik" + }, + { + "id": "4342", + "name": "Zanik" + }, + { + "id": "4343", + "name": "Snake", + "examine": "Snake! Snaaaaake! ", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "3", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "3609,3608,3610", + "death_animation": "278", + "defence_animation": "276", + "defence_level": "50", + "end_gfx": "", + "end_height": "", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "275", + "poison_amount": "15", + "poisonous": "true", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "25" + }, + { + "id": "4344", + "name": "Monkey", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "629,631,630", + "death_animation": "223", + "defence_animation": "221", + "defence_level": "1", + "lifepoints": "12", + "melee_animation": "220", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4345", + "name": "Albino bat", + "examine": "It's all white by me.", + "attack_level": "31", + "death_animation": "4917", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "44", + "magic_animation": "0", + "melee_animation": "4915", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "31", + "weakness": "8" + }, + { + "id": "4346", + "name": "Crab" + }, + { + "id": "4347", + "name": "Giant mosquito", + "examine": "A flying blood sucker.", + "attack_level": "63", + "attack_speed": "10", + "death_animation": "2398", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "2397", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "8" + }, + { + "id": "4348", + "name": "Jungle horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "55", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4235", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70", + "weakness": "7" + }, + { + "id": "4349", + "name": "Jungle horror", + "examine": "A horrible, emaciated ape like creature with beady yellow eyes.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "55", + "lifepoints": "45", + "magic_level": "1", + "melee_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70" + }, + { + "id": "4350", + "name": "Jungle horror", + "examine": "A horrible, emaciated ape like creature with beady green eyes.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "55", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4235", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70", + "weakness": "7" + }, + { + "id": "4351", + "name": "Jungle horror", + "examine": "A horrible, emaciated ape like creature with beady blue eyes.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "55", + "lifepoints": "45", + "magic_level": "1", + "melee_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70" + }, + { + "id": "4352", + "name": "Jungle horror", + "examine": "A horrible, emaciated ape like creature with beady pink eyes.", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "55", + "lifepoints": "45", + "magic_level": "1", + "melee_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70" + }, + { + "id": "4353", + "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "62", + "lifepoints": "55", + "magic_animation": "4234", + "magic_level": "80", + "melee_animation": "4234", + "range_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "55", + "strength_level": "77", + "weakness": "6" + }, + { + "id": "4354", + "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "62", + "lifepoints": "55", + "magic_animation": "4234", + "magic_level": "80", + "melee_animation": "4234", + "range_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "55", + "strength_level": "77", + "weakness": "6" + }, + { + "id": "4355", + "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "62", + "lifepoints": "55", + "magic_animation": "4234", + "magic_level": "80", + "melee_animation": "4234", + "range_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "55", + "strength_level": "77", + "weakness": "6" + }, + { + "id": "4356", + "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "62", + "lifepoints": "55", + "magic_animation": "4234", + "magic_level": "80", + "melee_animation": "4234", + "range_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "55", + "strength_level": "77", + "weakness": "6" + }, + { + "id": "4357", + "name": "Cave horror", + "examine": "A horrible, emaciated ape like creature with beady red eyes.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", + "defence_level": "62", + "lifepoints": "55", + "magic_animation": "4234", + "magic_level": "80", + "melee_animation": "4234", + "range_animation": "4234", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "55", + "strength_level": "77", + "weakness": "6" + }, + { + "id": "4358", + "name": "Cavey Davey" + }, + { + "id": "4359", + "name": "Patchy", + "examine": "Patchy the pirate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4360", + "name": "San Fan" + }, + { + "id": "4361", + "name": "Fancy Dan", + "examine": "A very flamboyant pirate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4362", + "name": "Honest Jimmy", + "examine": "I can't wait to buy from a guy with Honest in his name...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4363", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4364", + "name": "Swarm" + }, + { + "id": "4365", + "name": "Blue Monkey" + }, + { + "id": "4371", + "name": "Blue Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4372", + "name": "Red Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4373", + "name": "Parrot", + "examine": "A colourful bird.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4374", + "name": "Parrot" + }, + { + "id": "4375", + "name": "Security Guard", + "examine": "A retired Gielinor security guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4376", + "name": "Litara", + "examine": "Litara, a barbarian explorer." + }, + { + "id": "4377", + "name": "Gate of War" + }, + { + "id": "4378", + "name": "Ricketty door" + }, + { + "id": "4379", + "name": "Oozing barrier" + }, + { + "id": "4380", + "name": "Portal of Death" + }, + { + "id": "4381", + "name": "Ankou", + "examine": "A boney ghost.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "924,926,925", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "50", + "lifepoints": "60", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "slayer_exp": "60", + "strength_level": "69" + }, + { + "id": "4382", + "name": "Ankou", + "examine": "A boney ghost.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "924,926,925", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "50", + "lifepoints": "66", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "slayer_exp": "60", + "strength_level": "69" + }, + { + "id": "4383", + "name": "Ankou", + "examine": "A boney ghost.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "924,926,925", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "55", + "lifepoints": "66", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "slayer_exp": "60", + "strength_level": "74" + }, + { + "id": "4384", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "68", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4385", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "34", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "34", + "weakness": "7" + }, + { + "id": "4386", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4387", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "1", + "lifepoints": "36", + "melee_animation": "5540", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "4388", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "magic_level": "30", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4389", + "name": "Flesh Crawler", + "examine": "I don't think insect repellent will work.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "3", + "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", + "combat_audio": "571,573,572", + "death_animation": "1190", + "defence_animation": "1186", + "defence_level": "10", + "lifepoints": "25", + "magic_animation": "1184", + "melee_animation": "1184", + "range_animation": "1184", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "25", + "strength_level": "2", + "weakness": "0" + }, + { + "id": "4390", + "name": "Flesh Crawler", + "examine": "I don't think insect repellent will work.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "3", + "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", + "combat_audio": "571,573,572", + "death_animation": "1190", + "defence_animation": "1186", + "defence_level": "10", + "lifepoints": "25", + "magic_animation": "1184", + "melee_animation": "1184", + "range_animation": "1184", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "25", + "strength_level": "2" + }, + { + "id": "4391", + "name": "Flesh Crawler", + "examine": "I don't think insect repellent will work.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "3", + "bonuses": "0,0,0,0,0,15,15,15,15,15,15,15,15,15,15", + "combat_audio": "571,573,572", + "death_animation": "1190", + "defence_animation": "1186", + "defence_level": "10", + "lifepoints": "25", + "magic_animation": "1184", + "melee_animation": "1184", + "range_animation": "1184", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "25", + "strength_level": "2" + }, + { + "id": "4392", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "24", + "attack_speed": "5", + "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "25", + "lifepoints": "23", + "magic_animation": "5568", + "melee_animation": "5568", + "range_animation": "5568", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "4393", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "35", + "attack_speed": "5", + "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "28", + "lifepoints": "18", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "4394", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "5", + "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "48", + "lifepoints": "40", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "4395", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "21", + "attack_speed": "5", + "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "23", + "lifepoints": "26", + "melee_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "9" + }, + { + "id": "4396", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "710,713,711", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4397", + "name": "Catablepon", + "examine": "Big Cow-like... But cows don't have serpent tails!", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "5", + "bonuses": "5,20,15,3,10,20,10,10,20,15,23,25,15,25,10", + "clue_level": "1", + "combat_audio": "511,330,329", + "death_animation": "4270", + "defence_animation": "4273", + "defence_level": "38", + "lifepoints": "50", + "magic_animation": "4272", + "magic_level": "38", + "melee_animation": "4271", + "range_level": "1", + "respawn_delay": "74", + "safespot": null, + "slayer_exp": "50", + "spell_id": "7", + "strength_level": "38" + }, + { + "id": "4398", + "name": "Catablepon", + "examine": "Big Cow-like... But cows don't have serpent tails!", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "5", + "bonuses": "20,25,10,20,25,10,10,11,0,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "511,330,329", + "death_animation": "4270", + "defence_animation": "4273", + "defence_level": "38", + "lifepoints": "70", + "magic_animation": "4272", + "magic_level": "38", + "melee_animation": "4271", + "range_level": "1", + "respawn_delay": "74", + "safespot": null, + "slayer_exp": "50", + "spell_id": "7", + "strength_level": "38" + }, + { + "id": "4399", + "name": "Catablepon", + "examine": "Big Cow-like... But cows don't have serpent tails!", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "50,45,50,40,45,20,30,25,33,25,25,20,20,40,0", + "clue_level": "1", + "combat_audio": "511,330,329", + "death_animation": "4270", + "defence_animation": "4273", + "defence_level": "38", + "lifepoints": "50", + "magic_animation": "4272", + "magic_level": "45", + "melee_animation": "4271", + "range_level": "1", + "respawn_delay": "74", + "safespot": null, + "slayer_exp": "50", + "spell_id": "7", + "strength_level": "58" + }, + { + "id": "4400", + "name": "Giant spider", + "examine": "I think this spider has been genetically modified.", + "aggressive": "true", + "attack_level": "48", + "bonuses": "19,21,19,62,45,51,54,63,60,18,0,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "51", + "lifepoints": "50", + "magic_animation": "5327", + "melee_animation": "5327", + "range_animation": "5327", + "range_level": "1", + "safespot": null, + "strength_level": "45" + }, + { + "id": "4401", + "name": "Spider", + "aggressive": "false", + "attack_level": "1", + "combat_audio": "3604,3609,3608", + "death_animation": "6251", + "defence_animation": "6250", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "6249", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4402", + "name": "Scorpion", + "examine": "An extremely vicious scorpion.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "12,23,10,51,32,62,29,54,48,0,0,0,0,0,0", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "6255", + "defence_level": "49", + "lifepoints": "55", + "magic_animation": "6254", + "melee_animation": "6254", + "range_animation": "6254", + "range_level": "1", + "safespot": null, + "slayer_exp": "17", + "strength_level": "51" + }, + { + "id": "4403", + "name": "Scorpion", + "examine": "An extremely vicious scorpion.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "5", + "bonuses": "15,16,12,32,19,38,18,34,32,0,0,0,0,0,0", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "6255", + "defence_level": "35", + "lifepoints": "37", + "magic_animation": "6254", + "melee_animation": "6254", + "range_animation": "6254", + "range_level": "1", + "safespot": null, + "slayer_exp": "17", + "strength_level": "31" + }, + { + "id": "4404", + "name": "Minotaur", + "examine": "He doesn't look very pleased to see you.", + "aggressive": "false", + "attack_level": "12", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "626,628,627", + "death_animation": "4265", + "defence_animation": "4267", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "4266", + "magic_level": "1", + "melee_animation": "4266", + "range_animation": "4266", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "10", + "strength_level": "10", + "weakness": "7" + }, + { + "id": "4405", + "name": "Minotaur", + "examine": "He doesn't look very pleased to see you.", + "aggressive": "false", + "attack_level": "12", + "attack_speed": "4", + "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", + "clue_level": "0", + "combat_audio": "626,628,627", + "death_animation": "4265", + "defence_animation": "4267", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "4266", + "magic_level": "1", + "melee_animation": "4266", + "range_animation": "4266", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "4406", + "name": "Minotaur", + "examine": "He doesn't look very pleased to see you.", + "aggressive": "false", + "attack_level": "23", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-10,-10,-10,21,-10,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "626,628,627", + "death_animation": "4265", + "defence_animation": "4267", + "defence_level": "25", + "lifepoints": "22", + "magic_animation": "4266", + "magic_level": "1", + "melee_animation": "4266", + "range_animation": "4266", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "22", + "strength_level": "25" + }, + { + "id": "4407", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "5", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4408", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "11", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "2" + }, + { + "id": "4409", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "6,6,6,6,6,6,6,6,6,6,6,6,6,6,6", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "9", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "7", + "strength_level": "2" + }, + { + "id": "4410", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4411", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "10,10,10,10,10,10,10,10,10,10,10,10,10,10,10", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "17", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4412", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "8,8,8,8,8,8,8,8,8,8,8,8,8,8,8", + "clue_level": "0", + "combat_audio": "3520,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "10", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4413", + "name": "Wolf", + "examine": "They say inside you there are two wolves...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "7,7,7,7,7,7,7,7,7,7,7,7,7,7,7", + "combat_audio": "481,491,490", + "death_animation": "6558", + "defence_animation": "6557", + "defence_level": "9", + "lifepoints": "15", + "magic_animation": "6559", + "melee_animation": "6559", + "range_animation": "6559", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "15", + "strength_level": "1" + }, + { + "id": "4414", + "name": "Wolf", + "examine": "They say inside you there are two wolves...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "5,5,5,5,5,5,5,5,5,5,5,5,5,5,5", + "combat_audio": "481,491,490", + "death_animation": "6558", + "defence_animation": "6557", + "defence_level": "7", + "lifepoints": "10", + "magic_animation": "6559", + "melee_animation": "6559", + "range_animation": "6559", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "10", + "strength_level": "1" + }, + { + "id": "4415", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "-47,-47,-47,0,0,-42,-42,-42,-42,-42,-42,-53,0,0,0", + "combat_audio": "710,713,711", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "2705", + "magic_level": "1", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "respawn_delay": "3", + "safespot": null, + "slayer_exp": "2", + "strength_level": "1" + }, + { + "id": "4416", + "name": "Bee keeper" + }, + { + "id": "4417", + "name": "Bees!" + }, + { + "id": "4418", + "name": "Gorak", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "4302", + "defence_animation": "4301", + "defence_level": "1", + "lifepoints": "112", + "melee_animation": "4300", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "112", + "strength_level": "1" + }, + { + "id": "4419", + "name": "Cosmic Being", + "examine": "A highly enlightened being.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4420", + "name": "Fairy Godfather" + }, + { + "id": "4434", + "name": "Fairy Nuff", + "examine": "A healing fairy." + }, + { + "id": "4435", + "name": "Fairy Queen" + }, + { + "id": "4437", + "name": "Fairy Queen", + "examine": "Looks otherworldy..." + }, + { + "id": "4438", + "name": "Centaur", + "examine": "Horseplay.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4439", + "name": "Centaur", + "examine": "Horseplay.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4440", + "name": "Stag", + "examine": "A noble creature!", + "aggressive": "false", + "attack_level": "11", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "812,814,813", + "death_animation": "6377", + "defence_animation": "0", + "defence_level": "13", + "lifepoints": "19", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6376", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "90", + "safespot": null, + "strength_level": "13", + "weakness": "7" + }, + { + "id": "4441", + "name": "Wood Dryad", + "examine": "Twiggy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4442", + "name": "Fairy Very Wise" + }, + { + "id": "4443", + "name": "Fairy", + "examine": "A delicate creature from this strange realm." + }, + { + "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": "4455", + "name": "Fairy Fixit", + "examine": "Fairy ring maintenance division.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4456", + "name": "Ork" + }, + { + "id": "4457", + "name": "Ork", + "examine": "He works in the bank.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4458", + "name": "Ork", + "examine": "She works in the bank.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4459", + "name": "Ork", + "examine": "She works in the bank.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4470", + "name": "Tree spirit", + "examine": "A very angry nymph.", + "aggressive": "true", + "attack_level": "9", + "attack_speed": "6", + "death_animation": "97", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "12", + "magic_animation": "0", + "melee_animation": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "6" + }, + { + "id": "4471", + "name": "Bogrog" + }, + { + "id": "4473", + "name": "Woman" + }, + { + "id": "4474", + "name": "Magic dummy", + "examine": "A magic training dummy", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "97", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "id": "4475", + "name": "Ned" + }, + { + "id": "4476", + "name": "Guardian mummy", + "examine": "Who's your mummy?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4477", + "name": "Annoyed guardian mummy" + }, + { + "id": "4478", + "name": "Tarik" + }, + { + "id": "4479", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4480", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4481", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4482", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4483", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4484", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4485", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4486", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4487", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4488", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4489", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4490", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4491", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4492", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "strength_level": "2" + }, + { + "id": "4493", + "name": "General Bentnoze" + }, + { + "id": "4494", + "name": "General Wartface", + "examine": "An ugly green creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4495", + "name": "Grubfoot" + }, + { + "id": "4497", + "name": "Grubfoot" + }, + { + "id": "4498", + "name": "Grubfoot" + }, + { + "id": "4499", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "safespot": null, + "slayer_exp": "5", + "strength_level": "2" + }, + { + "id": "4500", + "name": "Scarab swarm" + }, + { + "id": "4501", + "name": "Ethereal Man", + "examine": "Is he real or is it just my imagination?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4502", + "name": "Ethereal Lady", + "examine": "Does she really exist?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4503", + "name": "Ethereal Numerator", + "examine": "He reminds me of my old mathematics teacher.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4504", + "name": "Ethereal Expert", + "examine": "The master of accomplishment.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4505", + "name": "Ethereal Perceptive", + "examine": "He knows what is possible.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4506", + "name": "Ethereal Guide", + "examine": "He knows the past, present and future. But what of my shoe size?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4507", + "name": "Ethereal Fluke", + "examine": "Luck is probably on his side.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4508", + "name": "Ethereal Mimic" + }, + { + "id": "4509", + "name": "Me", + "examine": "It's like looking in the mirror.", + "attack_level": "60", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "171", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "414", + "range_animation": "0", + "range_level": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "4510", + "name": "Me", + "examine": "It's like looking in the mirror.", + "attack_level": "60", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "171", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "414", + "range_animation": "0", + "range_level": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "4511", + "name": "Oneiromancer", + "examine": "Spiritual leader of the Moonclan.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4512", + "name": "House", + "examine": "The craziest house I ever did see!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4521", + "name": "Enchanted Broom", + "examine": "How does it see where to sweep?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4522", + "name": "Enchanted Broom" + }, + { + "id": "4523", + "name": "Enchanted Broom" + }, + { + "id": "4524", + "name": "Enchanted Bucket", + "examine": "I'm not sure if it's actually doing anything of use.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4525", + "name": "Enchanted Bucket" + }, + { + "id": "4526", + "name": "Bouquet Mac Hyacinth" + }, + { + "id": "4527", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4384", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4528", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4384", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4529", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4384", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4530", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4385", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4531", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,70,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4385", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4532", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4384", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4533", + "name": "Suqah", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,50,70,70,90,50,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4389", + "defence_animation": "4384", + "defence_level": "95", + "lifepoints": "105", + "melee_animation": "4388", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "95" + }, + { + "id": "4534", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "85", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "85", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "4535", + "name": "Parrot" + }, + { + "id": "4536", + "name": "Lokar Searunner" + }, + { + "id": "4537", + "name": "Lokar Searunner", + "examine": "Either a very fremennikey pirate, or a very piratey fremnnik.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4538", + "name": "Cabin boy" + }, + { + "id": "4539", + "name": "Cabin boy", + "examine": "I always wondered what that job description actually meant...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4540", + "name": "Captain Bentley", + "examine": "The High Priest has been transformed into an avatar of Bandos.", + "aggressive": "true", + "attack_level": "55", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "214", + "magic_animation": "0", + "magic_level": "55", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "4541", + "name": "'Beefy' Burns", + "examine": "A stickler for hygiene in the kitchen.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4542", + "name": "'Eagle-eye' Shultz", + "examine": "A keen-eyed lookout with a telescope.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4543", + "name": "First mate 'Davey-boy", + "examine": "First mate to Captain Bentley.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4544", + "name": "'Bird's-Eye' Jack" + }, + { + "id": "4545", + "name": "'Picarron' Pete", + "examine": "A pirate through and through.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4546", + "name": "Jake", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4547", + "name": "Bedread the bold", + "examine": "He got his name from his favourite hobby. Reading about beds.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4548", + "name": "Wilson", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4549", + "name": "Tommy 2-times", + "examine": "Nobody really knows why he's called Tommy 2-times...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4550", + "name": "Murky Pat", + "examine": "Don't look him straight in the eyes. He'll eat you alive!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4551", + "name": "Jack Sails", + "examine": "Nice beard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4552", + "name": "Palmer" + }, + { + "id": "4553", + "name": "'Betty' B.Boppin" + }, + { + "id": "4554", + "name": "'Beedy-eye' Jones", + "examine": "Is he looking at me?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4555", + "name": "Jenny Blade", + "examine": "Yes", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4556", + "name": "'Lecherous' Lee", + "examine": "A low-down", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4557", + "name": "'Sticky' Sanders", + "examine": "Apparently his nickname comes from his fondness for jam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4558", + "name": "Hirko" + }, + { + "id": "4559", + "name": "Holoy" + }, + { + "id": "4560", + "name": "Frizzy Skernip", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "4561", + "name": "Yulf Squecks", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "4562", + "name": "Praistan Ebola", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "id": "4563", + "name": "Hura" + }, + { + "id": "4564", + "name": "Digsite workman", + "examine": "This person is working on the site.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4565", + "name": "Digsite workman", + "examine": "This person is working on the site.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4566", + "name": "Examiner", + "examine": "Upon examining the examiner you examine it is indeed an examiner!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4567", + "name": "Examiner", + "examine": "Upon examining the examiner you examine it is indeed an examiner!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4568", + "name": "Researcher", + "examine": "Must be hard at work.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "examine": "He looks like he's a long way from home." + }, + { + "id": "4579", + "name": "Generic Diplomat", + "examine": "He makes pots.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4580", + "name": "Ambassador Gimblewap", + "examine": "He's very diplomatic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4581", + "name": "Ambassador Spanfipple", + "examine": "Peaceful man!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4582", + "name": "Ambassador Ferrnook", + "examine": "Peaceful man!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4583", + "name": "Professor Manglethorp" + }, + { + "id": "4584", + "name": "Damwin" + }, + { + "id": "4585", + "name": "Professor Onglewip", + "examine": "A gnome mage on sabbatical." + }, + { + "id": "4586", + "name": "Professor Imblewyn" + }, + { + "id": "4587", + "name": "Perrdur" + }, + { + "id": "4588", + "name": "Dalila" + }, + { + "id": "4589", + "name": "Sorrn", + "examine": "Your common man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4590", + "name": "Mimm", + "examine": "Your common woman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4591", + "name": "Eebel" + }, + { + "id": "4592", + "name": "Ermin" + }, + { + "id": "4593", + "name": "Portobello", + "examine": "Your common man.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4594", + "name": "Captain Ninto", + "examine": "A gnome pilot off duty", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4595", + "name": "Captain Daerkin", + "examine": "A gnome pilot off duty", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4596", + "name": "Captain Lamdoo" + }, + { + "id": "4597", + "name": "Meegle" + }, + { + "id": "4598", + "name": "Wurbel" + }, + { + "id": "4599", + "name": "Sarble" + }, + { + "id": "4600", + "name": "Guard Vemmeldo", + "examine": "He looks alert and ready for action.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4601", + "name": "Burkor" + }, + { + "id": "4602", + "name": "Froono" + }, + { + "id": "4603", + "name": "Fortress Guard", + "examine": "A generic evil henchman.", + "attack_level": "26", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "4604", + "name": "Fortress Guard", + "examine": "A generic evil henchman.", + "attack_level": "26", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "4605", + "name": "Fortress Guard", + "examine": "A generic evil henchman.", + "attack_level": "26", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "428", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "4606", + "name": "Fortress Guard", + "examine": "A generic evil henchman.", + "attack_level": "26", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "4607", + "name": "Black Cat", + "examine": "A witch's black cat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "4608", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4609", + "name": "Brimstail" + }, + { + "id": "4611", + "name": "Saboteur", + "examine": "Swine.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4612", + "name": "Gnome shop keeper" + }, + { + "id": "4613", + "name": "Cute creature" + }, + { + "id": "4615", + "name": "Evil creature", + "examine": "What is that thing!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "0", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "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": "4633", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4634", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4635", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4636", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4637", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4638", + "name": "Gnome soldier", + "examine": "A little warrior.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4639", + "name": "Gnome soldier", + "examine": "A little warrior.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4640", + "name": "Gnome soldier", + "examine": "A little warrior.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4641", + "name": "Gnome soldier", + "examine": "A little warrior.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4642", + "name": "Gnome soldier", + "examine": "A little warrior.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4643", + "name": "Healthorg and tortoise", + "examine": "King Healthorg riding his War Tortoise.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4644", + "name": "Oaknock the Engineer" + }, + { + "id": "4645", + "name": "Glouphrie the Untrusted", + "examine": "A weasly, shifty-looking Gnome Mage.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4646", + "name": "King Healthorg" + }, + { + "id": "4647", + "name": "Hazelmere" + }, + { + "id": "4648", + "name": "Nisha" + }, + { + "id": "4649", + "name": "Tyras guard" + }, + { + "id": "4650", + "name": "Trader Stan", + "examine": "With the prices he charges, no wonder he can afford to look so sharp.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4651", + "name": "Trader Crewmember", + "examine": "Looks very stylish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4652", + "name": "Trader Crewmember", + "examine": "That suit looks a little briny around the edges.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4653", + "name": "Trader Crewmember", + "examine": "First storm he is in that hat will blow away.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4654", + "name": "Trader Crewmember", + "examine": "She'll never be able to climb rigging in that.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4655", + "name": "Trader Crewmember", + "examine": "High heels on a ship? What is she thinking?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4656", + "name": "Trader Crewmember", + "examine": "The effect is sort of spoiled by all those tattoos.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "movement_radius": "710", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4657", + "name": "Sir Prysin" + }, + { + "id": "4658", + "name": "Dark wizard" + }, + { + "id": "4659", + "name": "Dark wizard", + "examine": "He works evil magic.", + "aggressive": "true", + "attack_level": "5", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "5", + "end_gfx": "95", + "lifepoints": "12", + "magic_animation": "711", + "magic_level": "6", + "melee_animation": "810", + "projectile": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "93", + "start_height": "80", + "strength_level": "2" + }, + { + "id": "4660", + "name": "Dark wizard", + "examine": "He works evil magic.", + "aggressive": "true", + "attack_level": "17", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "14", + "end_gfx": "98", + "lifepoints": "24", + "magic_animation": "711", + "magic_level": "22", + "melee_animation": "810", + "projectile": "97", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "96", + "start_height": "80", + "strength_level": "17" + }, + { + "id": "4661", + "name": "Dark wizard", + "examine": "He works evil magic.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "4", + "combat_audio": "511,513,512", + "combat_style": "2", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "15", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "15", + "projectile": "98", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4662", + "name": "Denath" + }, + { + "id": "4663", + "name": "Denath" + }, + { + "id": "4664", + "name": "Wally" + }, + { + "id": "4665", + "name": "Baby blue dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,50,50,40,30,0,0,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "40", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "1", + "melee_animation": "25", + "range_animation": "25", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "50", + "strength_level": "40", + "weakness": "3" + }, + { + "id": "4666", + "name": "Baby blue dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "45", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,30,50,50,40,30,0,0,0,0,0", + "combat_audio": "405,407,406", + "death_animation": "28", + "defence_animation": "26", + "defence_level": "40", + "lifepoints": "50", + "magic_animation": "25", + "magic_level": "1", + "melee_animation": "25", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "50", + "strength_level": "40", + "weakness": "3" + }, + { + "id": "4667", + "name": "Baby red dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "405,407,406", + "defence_level": "1", + "lifepoints": "50", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4668", + "name": "Baby red dragon", + "examine": "Young but still dangerous.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "405,407,406", + "defence_level": "1", + "lifepoints": "50", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4669", + "name": "Red dragon", + "examine": "A big powerful dragon.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "130", + "lifepoints": "140", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "143", + "strength_level": "130", + "weakness": "3" + }, + { + "id": "4670", + "name": "Red dragon", + "examine": "A big powerful dragon.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "130", + "lifepoints": "140", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "143", + "strength_level": "130", + "weakness": "3" + }, + { + "id": "4671", + "name": "Red dragon", + "examine": "A big powerful dragon.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "130", + "lifepoints": "140", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "143", + "strength_level": "130", + "weakness": "3" + }, + { + "id": "4672", + "name": "Red dragon", + "examine": "A big powerful dragon.", + "aggressive": "true", + "attack_level": "130", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "130", + "lifepoints": "140", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "143", + "strength_level": "130", + "weakness": "3" + }, + { + "id": "4673", + "name": "Black dragon", + "examine": "A fierce dragon with black scales!", + "aggressive": "true", + "attack_level": "200", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "200", + "lifepoints": "190", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "200", + "strength_level": "200", + "weakness": "3" + }, + { + "id": "4674", + "name": "Black dragon", + "examine": "A fierce dragon with black scales!", + "aggressive": "true", + "attack_level": "200", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "200", + "lifepoints": "190", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "200", + "strength_level": "200", + "weakness": "3" + }, + { + "id": "4675", + "name": "Black dragon", + "examine": "A fierce dragon with black scales!", + "aggressive": "true", + "attack_level": "200", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "200", + "lifepoints": "190", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "200", + "strength_level": "200", + "weakness": "3" + }, + { + "id": "4676", + "name": "Black dragon", + "examine": "A fierce dragon with black scales!", + "aggressive": "true", + "attack_level": "200", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "200", + "lifepoints": "190", + "magic_animation": "80", + "magic_level": "100", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "200", + "strength_level": "200", + "weakness": "3" + }, + { + "id": "4677", + "name": "Green dragon", + "examine": "Must be related to Elvarg.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "68", + "lifepoints": "75", + "magic_animation": "80", + "magic_level": "68", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" + }, + { + "id": "4678", + "name": "Green dragon", + "examine": "Must be related to Elvarg.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "68", + "lifepoints": "75", + "magic_animation": "80", + "magic_level": "68", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" + }, + { + "id": "4679", + "name": "Green dragon", + "examine": "Must be related to Elvarg.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "68", + "lifepoints": "75", + "magic_animation": "80", + "magic_level": "68", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" + }, + { + "id": "4680", + "name": "Green dragon", + "examine": "Must be related to Elvarg.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,40,40,30,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "68", + "lifepoints": "75", + "magic_animation": "80", + "magic_level": "68", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" + }, + { + "id": "4681", + "name": "Blue dragon", + "examine": "A mother dragon.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" + }, + { + "id": "4682", + "name": "Blue dragon", + "examine": "A mother dragon.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" + }, + { + "id": "4683", + "name": "Blue dragon", + "examine": "A mother dragon.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" + }, + { + "id": "4684", + "name": "Blue dragon", + "examine": "A mother dragon.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" + }, + { + "id": "4685", + "name": "Ice giant", + "examine": "He's got icicles in his beard.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4673", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4672", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "70", + "strength_level": "40", + "weakness": "9" + }, + { + "id": "4686", + "name": "Ice giant", + "examine": "He's got icicles in his beard.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4673", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4672", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "70", + "strength_level": "40", + "weakness": "9" + }, + { + "id": "4687", + "name": "Ice giant", + "examine": "He's got icicles in his beard.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4673", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4672", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "70", + "strength_level": "40", + "weakness": "9" + }, + { + "id": "4688", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "60", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "60", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "4689", + "name": "Hill Giant", + "examine": "A very large foe.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "4652", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "22", + "weakness": "6" + }, + { + "id": "4690", + "name": "Hill Giant", + "examine": "A very large foe.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "4652", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "22", + "weakness": "6" + }, + { + "id": "4691", + "name": "Hill Giant", + "examine": "A very large foe.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "4652", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "22", + "weakness": "6" + }, + { + "id": "4692", + "name": "Hill Giant", + "examine": "A very large foe.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "4652", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "22", + "weakness": "6" + }, + { + "id": "4693", + "name": "Hill Giant", + "examine": "A very large foe.", + "aggressive": "true", + "attack_level": "18", + "attack_speed": "6", + "bonuses": "18,18,18,0,0,0,0,0,0,0,0,16,0,0,0", + "combat_audio": "448,451,450", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "26", + "lifepoints": "35", + "magic_animation": "4652", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "4652", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "35", + "strength_level": "22", + "weakness": "6" + }, + { + "id": "4694", + "name": "Lesser demon", + "examine": "Lesser, but still pretty big.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", + "melee_animation": "4630", + "range_animation": "4630", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" + }, + { + "id": "4695", + "name": "Lesser demon", + "examine": "Lesser, but still pretty big.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", + "melee_animation": "4630", + "range_animation": "4630", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" + }, + { + "id": "4696", + "name": "Lesser demon", + "examine": "Lesser, but still pretty big.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", + "melee_animation": "4630", + "range_animation": "4630", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" + }, + { + "id": "4697", + "name": "Lesser demon", + "examine": "Lesser, but still pretty big.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", + "melee_animation": "4630", + "range_animation": "4630", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" + }, + { + "id": "4698", + "name": "Greater demon", + "examine": "Big, red, and incredibly evil.", + "aggressive": "true", + "attack_level": "59", + "attack_speed": "5", + "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "59", + "lifepoints": "87", + "magic_animation": "64", + "magic_level": "59", + "melee_animation": "64", + "range_animation": "64", + "range_level": "59", + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "87", + "strength_level": "59", + "weakness": "4" + }, + { + "id": "4699", + "name": "Greater demon", + "examine": "Big, red, and incredibly evil.", + "aggressive": "true", + "attack_level": "59", + "attack_speed": "5", + "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "59", + "lifepoints": "87", + "magic_animation": "64", + "magic_level": "59", + "melee_animation": "64", + "range_animation": "64", + "range_level": "59", + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "87", + "strength_level": "59", + "weakness": "4" + }, + { + "id": "4700", + "name": "Greater demon", + "examine": "Big, red, and incredibly evil.", + "aggressive": "true", + "attack_level": "59", + "attack_speed": "5", + "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "59", + "lifepoints": "87", + "magic_animation": "64", + "magic_level": "59", + "melee_animation": "64", + "range_animation": "64", + "range_level": "59", + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "87", + "strength_level": "59", + "weakness": "4" + }, + { + "id": "4701", + "name": "Greater demon", + "examine": "Big, red, and incredibly evil.", + "aggressive": "true", + "attack_level": "59", + "attack_speed": "5", + "bonuses": "30,20,20,30,20,50,50,50,10,50,20,0,0,0,0", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "68", + "defence_animation": "65", + "defence_level": "59", + "lifepoints": "87", + "magic_animation": "64", + "magic_level": "59", + "melee_animation": "64", + "range_animation": "64", + "range_level": "59", + "respawn_delay": "32", + "safespot": null, + "slayer_exp": "87", + "strength_level": "59", + "weakness": "4" + }, + { + "id": "4702", + "name": "Black demon", + "examine": "A big, scary, jet-black demon.", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", + "melee_animation": "64", + "range_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "157", + "strength_level": "148", + "weakness": "4" + }, + { + "id": "4703", + "name": "Black demon", + "examine": "A big, scary, jet-black demon.", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", + "melee_animation": "64", + "range_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "157", + "strength_level": "148", + "weakness": "4" + }, + { + "id": "4704", + "name": "Black demon", + "examine": "A big, scary, jet-black demon.", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", + "melee_animation": "64", + "range_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "157", + "strength_level": "148", + "weakness": "4" + }, + { + "id": "4705", + "name": "Black demon", + "examine": "A big, scary, jet-black demon.", + "aggressive": "true", + "attack_level": "145", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "397,399,398", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "152", + "lifepoints": "157", + "magic_animation": "64", + "magic_level": "1", + "melee_animation": "64", + "range_animation": "64", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "157", + "strength_level": "148", + "weakness": "4" + }, + { + "id": "4706", + "name": "Moss giant", + "examine": "His beard seems to have a life of its own.", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "6", + "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", + "combat_audio": "449,451,450", + "death_animation": "4668", + "defence_animation": "4665", + "defence_level": "30", + "lifepoints": "85", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "85", + "strength_level": "30", + "weakness": "1" + }, + { + "id": "4707", + "name": "Magic Tutor", + "examine": "Mikasi looks ready to teach you about magic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4708", + "name": "Old Man Ral", + "examine": "A flea-infested", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4709", + "name": "Vertida Sefalatis" + }, + { + "id": "4710", + "name": "Aeonisig Raispher" + }, + { + "id": "4711", + "name": "Safalaan" + }, + { + "id": "4714", + "name": "Sarius Guile" + }, + { + "id": "4716", + "name": "Trader Sven", + "examine": "A flea-infested black market trader.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4717", + "name": "Meiyerditch citizen", + "examine": "A flea-infested", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4718", + "name": "Meiyerditch citizen", + "examine": "A flea-infested", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4719", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4720", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4721", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4722", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4723", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4724", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4725", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4726", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4733", + "name": "Meiyerditch citizen", + "examine": "Cowardly mage.", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "28", + "magic_animation": "0", + "magic_level": "90", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4734", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4735", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4736", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4737", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4738", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4739", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4740", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4741", + "name": "Meiyerditch citizen", + "examine": "This citizen looks pale", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4742", + "name": "Meiyerditch citizen" + }, + { + "id": "4743", + "name": "Meiyerditch citizen" + }, + { + "id": "4744", + "name": "Meiyerditch citizen" + }, + { + "id": "4745", + "name": "Meiyerditch citizen" + }, + { + "id": "4746", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4747", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4748", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4749", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4750", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4751", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4752", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4753", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4754", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4755", + "name": "A Meiyerditch child", + "examine": "A poor street urchin!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4756", + "name": "Meiyerditch miner", + "examine": "This citizen looks dirty and tired!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4757", + "name": "Meiyerditch miner", + "examine": "This citizen looks dirty and tired!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4758", + "name": "Meiyerditch miner" + }, + { + "id": "4759", + "name": "Meiyerditch miner", + "examine": "This citizen looks dirty and tired!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4765", + "name": "null", + "examine": "Some rubbish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4766", + "name": "Stray dog", + "examine": "Looks like it's got fleas!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4767", + "name": "Stray dog" + }, + { + "id": "4768", + "name": "Cat", + "examine": "A smelly cat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4769", + "name": "Cat" + }, + { + "id": "4770", + "name": "Boat" + }, + { + "id": "4771", + "name": "Boat" + }, + { + "id": "4772", + "name": "Juvinate guard", + "examine": "An initiate juvenile vampyre; a guard for the mining area.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "4773", + "name": "Juvinate guard", + "examine": "An initiate juvenile vampyre; a guard for the mining area.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "4774", + "name": "Vampyre juvenile", + "examine": "A juvenile vampyre.", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "6276", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4775", + "name": "Vampyre juvenile", + "examine": "A juvenile vampyre.", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "6276", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4776", + "name": "Vampyre juvinate", + "examine": "An initiate juvenile vampyre; seems to be a servant.", + "attack_level": "1", + "death_animation": "5798", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "5783", + "range_animation": "0", + "range_level": "44", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "4777", + "name": "Vampyre juvinate", + "examine": "An initiate juvenile vampyre.", + "attack_level": "1", + "death_animation": "5798", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "5783", + "range_animation": "0", + "range_level": "44", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "4778", + "name": "Held vampyre juvenile", + "examine": "A juvenile vampyre.", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4779", + "name": "Held vampyre juvenile", + "examine": "A juvenile vampyre.", + "attack_level": "40", + "death_animation": "6781", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4780", + "name": "Held vampyre juvinate", + "examine": "An initiate juvenile vampyre; seems to be a servant.", + "attack_level": "1", + "death_animation": "5798", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "44", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "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": "4789", + "name": "Angry vampyre", + "examine": "An enraged vampyre!", + "attack_level": "42", + "death_animation": "6031", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "6016", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "42", + "weakness": "7" + }, + { + "id": "4790", + "name": "Angry vampyre" + }, + { + "id": "4791", + "name": "Vanstrom Klause" + }, + { + "id": "4792", + "name": "Vanstrom Klause" + }, + { + "id": "4793", + "name": "Vanstrom Klause", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "155", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4794", + "name": "Vanstrom Klause" + }, + { + "id": "4795", + "name": "Vanstrom Klause" + }, + { + "id": "4796", + "name": "Vanstrom Klause", + "examine": "Not as strong as Thok.", + "aggressive": "true", + "attack_level": "90", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "28", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "90" + }, + { + "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": "4805", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4806", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4807", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "54", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4808", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "56", + "lifepoints": "80", + "magic_animation": "0", + "magic_level": "56", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4809", + "name": "Flying female vampire" + }, + { + "id": "4810", + "name": "Flying female vampire", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4811", + "name": "Flying female vampire", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4812", + "name": "Flying female vampire", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4813", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4814", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4815", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "54", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4816", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "56", + "lifepoints": "80", + "magic_animation": "0", + "magic_level": "56", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4817", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4818", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4819", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "54", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4820", + "name": "Vyrewatch", + "examine": "An evil", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "56", + "lifepoints": "80", + "magic_animation": "0", + "magic_level": "56", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4821", + "name": "Vyrewatch", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4822", + "name": "Vyrewatch", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4823", + "name": "Vyrewatch", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4824", + "name": "Vyrewatch", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4825", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4826", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4827", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "54", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4828", + "name": "Vyrewatch", + "examine": "A thirsty-looking", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "6861", + "defence_animation": "0", + "defence_level": "56", + "lifepoints": "80", + "magic_animation": "0", + "magic_level": "56", + "melee_animation": "6783", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "4829", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4830", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4831", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4832", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4833", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4834", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4835", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4836", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4837", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4838", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4839", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4840", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4841", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4842", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4843", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4844", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4845", + "name": "Flying female vampire", + "examine": "A very high-ranking vampyre.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7524", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7435", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "4846", + "name": "Flying female vampire" + }, + { + "id": "4847", + "name": "Flying female vampire", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4848", + "name": "Flying female vampire", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4849", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4850", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4851", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4852", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4853", + "name": "Ezekial Lovecraft" + }, + { + "id": "4856", + "name": "Ezekial Lovecraft", + "examine": "Smells a bit fishy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4868", + "name": "Holgart", + "examine": "Her knives tickle Thok.", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "28", + "magic_animation": "0", + "range_level": "90", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4869", + "name": "Fisherman" + }, + { + "id": "4870", + "name": "Fisherman", + "examine": "Not Thok's pretty lass.", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "28", + "magic_animation": "0", + "magic_level": "90", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4871", + "name": "Col. O'Niall" + }, + { + "id": "4872", + "name": "Col. O'Niall", + "examine": "A (semi) retired member of the Temple Knights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4873", + "name": "Col. O'Niall" + }, + { + "id": "4874", + "name": "Mayor Hobb", + "examine": "There's something fishy about him.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4875", + "name": "Mayor Hobb" + }, + { + "id": "4876", + "name": "Brother Maledict" + }, + { + "id": "4878", + "name": "Brother Maledict", + "examine": "A fresh-faced and innocent priest.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4879", + "name": "Brother Maledict" + }, + { + "id": "4880", + "name": "Witchaven villager" + }, + { + "id": "4883", + "name": "Witchaven villager", + "examine": "A down on his luck fisherman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4884", + "name": "Witchaven villager" + }, + { + "id": "4885", + "name": "Witchaven villager", + "examine": "A down on her luck fisherman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4886", + "name": "Witchaven villager" + }, + { + "id": "4887", + "name": "Witchaven villager", + "examine": "A down on his luck fisherman.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4888", + "name": "Witchaven villager" + }, + { + "id": "4889", + "name": "Mother Mallum" + }, + { + "id": "4890", + "name": "Slug Prince", + "examine": "A child of aquatic evil.", + "aggressive": "true", + "attack_level": "37", + "death_animation": "4830", + "defence_animation": "0", + "defence_level": "37", + "lifepoints": "105", + "magic_animation": "0", + "melee_animation": "4829", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "37", + "weakness": "9" + }, + { + "id": "4891", + "name": "Giant lobster" + }, + { + "id": "4893", + "name": "Giant Lobster", + "examine": "An extremely vicious lobster.", + "aggressive": "true", + "attack_level": "29", + "death_animation": "6267", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "82", + "magic_animation": "0", + "melee_animation": "6265", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "29", + "weakness": "9" + }, + { + "id": "4894", + "name": "Sea slug", + "examine": "A rather nasty looking crustacean.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4895", + "name": "Jeb", + "examine": "A villager named Jeb.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4896", + "name": "Jeb" + }, + { + "id": "4897", + "name": "Sir Tinley" + }, + { + "id": "4898", + "name": "Hobgoblin", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "3520,472,471", + "death_animation": "167", + "defence_animation": "163", + "defence_level": "1", + "lifepoints": "29", + "melee_animation": "164", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4899", + "name": "Cooking Tutor", + "examine": "Cordero looks ready to teach you how to cook.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4900", + "name": "Crafting Tutor", + "examine": "Cadmus, looking a little bit crafty.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4901", + "name": "Fishing Tutor", + "examine": "Finlay, mending a crayfish cage.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4902", + "name": "Mining Tutor", + "examine": "Monlum, surveying the rocks.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4903", + "name": "Prayer Tutor", + "examine": "Yauchomi, follower of Saradomin.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4904", + "name": "Smelting Tutor", + "examine": "Feoras looks a bit fiery.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4905", + "name": "Smithing Tutor" + }, + { + "id": "4906", + "name": "Woodcutting Tutor", + "examine": "Wilfred, a chip off the old block.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4907", + "name": "Bank Tutor", + "examine": "Barb, ready to teach you about banking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4908", + "name": "Fishing spot" + }, + { + "id": "4909", + "name": "Fritz the Glassblower", + "examine": "His motives are see-through.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4910", + "name": "Earth elemental", + "examine": "An earth elemental.", + "aggressive": "false", + "attack_level": "20", + "attack_speed": "6", + "combat_audio": "1531,1533,1532", + "death_animation": "4870", + "defence_animation": "4869", + "defence_level": "35", + "lifepoints": "35", + "magic_level": "10", + "melee_animation": "4868", + "range_level": "30", + "respawn_delay": "5", + "safespot": null, + "strength_level": "35" + }, + { + "id": "4911", + "name": "Elemental rock", + "examine": "An elemental rock.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "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": "4920", + "name": "Giant crypt rat", + "examine": "A nasty overgrown rodent.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "65", + "lifepoints": "70", + "magic_animation": "4933", + "magic_level": "1", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "4921", + "name": "Giant crypt rat", + "examine": "A nasty overgrown rodent.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "65", + "lifepoints": "70", + "magic_animation": "4933", + "magic_level": "1", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "4922", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "id": "4923", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "id": "4924", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "5", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "id": "4925", + "name": "Giant rat", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4926", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "7", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "7", + "weakness": "8" + }, + { + "id": "4927", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "7", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "7", + "weakness": "8" + }, + { + "id": "4928", + "name": "Dungeon rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "8" + }, + { + "id": "4929", + "name": "Dungeon rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "13", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "13", + "lifepoints": "18", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "8" + }, + { + "id": "4930", + "name": "Goat", + "examine": "They'll eat anything!", + "attack_level": "5", + "death_animation": "5343", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "5341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "4931", + "name": "Goat", + "examine": "They'll eat anything!", + "attack_level": "5", + "death_animation": "5343", + "defence_animation": "0", + "defence_level": "5", + "lifepoints": "7", + "magic_animation": "0", + "melee_animation": "5341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "6" + }, + { + "id": "4932", + "name": "Billy Goat", + "examine": "They'll eat anything!", + "attack_level": "11", + "death_animation": "5343", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "5341", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "11", + "weakness": "6" + }, + { + "id": "4933", + "name": "Goat", + "attack_level": "1", + "death_animation": "5343", + "defence_animation": "5342", + "defence_level": "1", + "lifepoints": "21", + "melee_animation": "5341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4934", + "name": "Goat", + "attack_level": "1", + "death_animation": "5343", + "defence_animation": "5342", + "defence_level": "1", + "lifepoints": "21", + "melee_animation": "5341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4935", + "name": "Billy Goat", + "attack_level": "1", + "death_animation": "5343", + "defence_animation": "5342", + "defence_level": "1", + "lifepoints": "28", + "melee_animation": "5341", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4936", + "name": "Dungeon rat", + "examine": "A dirty rat.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "9" + }, + { + "id": "4937", + "name": "Dungeon rat", + "examine": "A dirty rat.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "9" + }, + { + "id": "4938", + "name": "Angry giant rat", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4939", + "name": "Angry giant rat", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4940", + "name": "Angry giant rat", + "examine": "He looks a little on the cross side!", + "attack_level": "25", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4941", + "name": "Blessed giant rat", + "examine": "It's one of Iban's pet vermin.", + "attack_level": "12", + "combat_audio": "703,705,704", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "9" + }, + { + "id": "4942", + "name": "Giant rat", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "4933", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4943", + "name": "Giant rat", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "4933", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4944", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "21", + "attack_speed": "5", + "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "23", + "lifepoints": "26", + "melee_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "20" + }, + { + "id": "4945", + "name": "Giant rat", + "examine": "Overgrown vermin.", + "aggressive": "true", + "attack_level": "21", + "attack_speed": "5", + "bonuses": "19,15,15,34,36,38,33,31,30,14,0,0,0,0,0", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "23", + "lifepoints": "26", + "melee_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "9" + }, + { + "id": "4946", + "name": "Ignatius Vulcan" + }, + { + "id": "4947", + "name": "My Arm", + "examine": "Trollish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4962", + "name": "Captain Barnaby", + "examine": "An old sailor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4963", + "name": "Murcaily" + }, + { + "id": "4964", + "name": "Jagbakoba" + }, + { + "id": "4965", + "name": "Tool Leprechaun", + "examine": "It's a leprechaun sunbathing on a mountain.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4966", + "name": "Flies" + }, + { + "id": "4968", + "name": "Unnamed troll child" + }, + { + "id": "4969", + "name": "Drunken dwarf's leg" + }, + { + "id": "4970", + "name": "Baby Roc" + }, + { + "id": "4971", + "name": "Baby Roc", + "examine": "A mountain-dwelling bird. Cute", + "aggressive": "true", + "attack_level": "40", + "death_animation": "5033", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "5031", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "4972", + "name": "Giant Roc", + "examine": "A very", + "aggressive": "true", + "attack_level": "55", + "death_animation": "5027", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "285", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "5024", + "range_animation": "5025", + "range_level": "55", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "4973", + "name": "Shadow" + }, + { + "id": "4974", + "name": "Captain Barnaby" + }, + { + "id": "4975", + "name": "Male slave", + "examine": "It looks like he's been here a long time.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "4976", + "name": "Male slave" + }, + { + "id": "4977", + "name": "Female slave", + "examine": "She looks like she's been down here a long time.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "4989", + "name": "Mercenary", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4990", + "name": "Mercenary", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4991", + "name": "Mercenary", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4992", + "name": "Mercenary", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4993", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4994", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4995", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4996", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4997", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4998", + "name": "Guard", + "examine": "He looks a bit aggressive.", + "attack_level": "25", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "25", + "weakness": "9" + }, + { + "id": "4999", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5000", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5001", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5002", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5026", + "name": "Egg launcher", + "examine": "An apprentice.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5027", + "name": "Egg launcher", + "examine": "A tired old wizard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5028", + "name": "Egg launcher", + "examine": "An egg launcher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5029", + "name": "Commander Connad", + "examine": "A slightly more approachable barbarian.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5030", + "name": "Captain Cain", + "examine": "He looks pretty mean.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5031", + "name": "Private Paldo", + "examine": "A stressed out barbarian private.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5032", + "name": "Private Pendron", + "examine": "A barbarian private.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5033", + "name": "Private Pierreb", + "examine": "A barbarian private.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5034", + "name": "Private Paldon", + "examine": "A barbarian private.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5035", + "name": "Major Attack", + "examine": "A barbarian army teacher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5036", + "name": "Major Collect", + "examine": "A barbarian army teacher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5037", + "name": "Major Defend", + "examine": "A barbarian army teacher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5038", + "name": "Major Heal", + "examine": "A barbarian army teacher.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5039", + "name": "Sergeant Sambur", + "examine": "He's keeping a close eye on that nearby door.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5040", + "name": "Penance Fighter", + "examine": "What on Gielinor is that?", + "attack_level": "1", + "combat_audio": "670,672,671", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5041", + "name": "Penance Ranger", + "examine": "Shooty-shooty.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5042", + "name": "Penance Runner", + "examine": "He's making a run for it!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5043", + "name": "Penance Healer", + "examine": "A nasty piece of work.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5044", + "name": "Penance Fighter" + }, + { + "id": "5045", + "name": "Penance Fighter" + }, + { + "id": "5046", + "name": "Jack" + }, + { + "id": "5047", + "name": "Jill" + }, + { + "id": "5048", + "name": "Jeff", + "examine": "Not as strong as Thok.", + "aggressive": "true", + "attack_level": "80", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "22", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "401", + "range_animation": "0", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "10" + }, + { + "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": "5067", + "name": "Shark", + "examine": "Time to run away...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5068", + "name": "Shark" + }, + { + "id": "5069", + "name": "Shark" + }, + { + "id": "5070", + "name": "Tropical wagtail" + }, + { + "id": "5072", + "name": "Tropical wagtail", + "examine": "It blends in very well with its surroundings.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5073", + "name": "Crimson swift", + "examine": "This bird obviously doesn't believe in subtlety.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5074", + "name": "Cerulean twitch", + "examine": "Best served ice cold.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5075", + "name": "Golden warbler", + "examine": "Actually", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5076", + "name": "Copper longtail", + "examine": "Nothing much to get in a flap about.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5077", + "name": "Chinchompa" + }, + { + "id": "5079", + "name": "Chinchompa", + "examine": "Handle with care.", + "attack_level": "1", + "death_animation": "5183", + "defence_level": "1", + "lifepoints": "1", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5080", + "name": "Carnivorous chinchompa", + "examine": "It looks fluffy and cute; it's probably deadly.", + "attack_level": "2", + "death_animation": "5183", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "2", + "melee_animation": "0", + "range_animation": "0", + "range_level": "2", + "safespot": null, + "strength_level": "2" + }, + { + "id": "5081", + "name": "Ferret", + "examine": "Wild.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5082", + "name": "Black warlock", + "examine": "A black warlock. The air seems to distort wherever it passes.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5083", + "name": "Snowy knight", + "examine": "It's a snowy knight butterfly.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5084", + "name": "Sapphire glacialis", + "examine": "A sapphire glacialis. It doesn't look as pretentious as its name sounds.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5085", + "name": "Ruby harvest", + "examine": "It's a ruby harvest butterfly.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5086", + "name": "Prickly kebbit", + "examine": "Curls up into a ball to protect itself from attack.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5087", + "name": "Sabre-toothed kebbit", + "examine": "Now that's a big overbite.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5088", + "name": "Barb-tailed kebbit", + "examine": "It uses its tail to hunt and skewer fish.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5089", + "name": "Wild kebbit", + "examine": "That's a mean looking set of claws.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5090", + "name": "Matthias" + }, + { + "id": "5093", + "name": "Matthias" + }, + { + "id": "5094", + "name": "Gyr Falcon" + }, + { + "id": "5095", + "name": "Gyr Falcon" + }, + { + "id": "5096", + "name": "Gyr Falcon" + }, + { + "id": "5097", + "name": "Gyr Falcon", + "examine": "Ha! Thok much stronger.", + "aggressive": "true", + "attack_level": "80", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "22", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "395", + "range_animation": "0", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "10" + }, + { + "id": "5098", + "name": "Spotted kebbit", + "examine": "It seems to be on a permanent sugar rush.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5099", + "name": "Dark kebbit", + "examine": "Quieter than a ninja mouse with slippers on.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5100", + "name": "Dashing kebbit", + "examine": "Now you see it; now you don't.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5101", + "name": "Sabre-toothed kyatt" + }, + { + "id": "5103", + "name": "Sabre-toothed kyatt", + "examine": "It's just like a big, white, furry, deadly can opener.", + "attack_level": "25", + "attack_speed": "5", + "death_animation": "5234", + "defence_animation": "404", + "defence_level": "99", + "lifepoints": "10", + "magic_animation": "5228", + "melee_animation": "5228", + "range_animation": "5228", + "range_level": "1", + "respawn_delay": "10", + "safespot": null, + "strength_level": "25" + }, + { + "id": "5104", + "name": "Spined larupia", + "examine": "If you tried to ride that, you'd just impale yourself!", + "attack_level": "15", + "attack_speed": "5", + "death_animation": "5234", + "defence_animation": "404", + "defence_level": "99", + "lifepoints": "10", + "magic_animation": "5228", + "melee_animation": "5228", + "range_animation": "5228", + "range_level": "1", + "safespot": null, + "strength_level": "15" + }, + { + "id": "5105", + "name": "Horned graahk", + "examine": "Get in a graahk's way and you're going to know about it... however briefly.", + "attack_level": "25", + "attack_speed": "5", + "death_animation": "5234", + "defence_animation": "404", + "defence_level": "99", + "lifepoints": "10", + "magic_animation": "5228", + "melee_animation": "5228", + "range_animation": "5228", + "range_level": "1", + "safespot": null, + "strength_level": "25" + }, + { + "id": "5106", + "name": "null", + "examine": "It's just like a big, white, furry, deadly can opener.", + "attack_level": "20", + "attack_speed": "5", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "40", + "lifepoints": "10", + "magic_animation": "5228", + "melee_animation": "5228", + "range_animation": "5228", + "range_level": "1", + "safespot": null, + "strength_level": "20" + }, + { + "id": "5110", + "name": "Aleck" + }, + { + "id": "5111", + "name": "Leon", + "examine": "He seems to be making odd sucking noises with his teeth." + }, + { + "id": "5112", + "name": "Hunting expert", + "examine": "He must be good at hunting; even his hair blends in with the surroundings.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5113", + "name": "Hunting expert", + "examine": "With all the furs", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5114", + "name": "Orange salamander", + "examine": "Slightly slimy but kind of cute.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5115", + "name": "Red salamander", + "examine": "Slightly slimy but certainly striking.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5116", + "name": "Black salamander", + "examine": "Slightly slimy and somewhat menacing.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5117", + "name": "Swamp lizard", + "examine": "Very slimy and generally disgusting.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5118", + "name": "Eagle" + }, + { + "id": "5120", + "name": "Eagle", + "examine": "A very large", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5130", + "name": "Desert eagle", + "examine": "A very large", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5131", + "name": "Jungle eagle", + "examine": "A very large", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5132", + "name": "Polar eagle", + "examine": "A very large", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5133", + "name": "Eagle", + "examine": "It seems to be protecting the nest.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5134", + "name": "Kebbit" + }, + { + "id": "5137", + "name": "Kebbit", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "5305", + "defence_level": "1", + "lifepoints": "40", + "melee_animation": "5304", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5138", + "name": "Charlie" + }, + { + "id": "5139", + "name": "Boulder" + }, + { + "id": "5140", + "name": "Boulder", + "examine": "Now", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5141", + "name": "Uri" + }, + { + "id": "5142", + "name": "Uri" + }, + { + "id": "5143", + "name": "Uri" + }, + { + "id": "5144", + "name": "Double agent", + "examine": "He's playing both sides!", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "75", + "lifepoints": "85", + "magic_animation": "0", + "magic_level": "75", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "75", + "weakness": "6" + }, + { + "id": "5145", + "name": "Double agent", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5146", + "name": "Li'l lamb", + "examine": "How cute!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5147", + "name": "Lamb", + "examine": "The black sheep of the family.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5162", + "name": "Sheep", + "examine": "White and fluffy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5163", + "name": "Sheep", + "examine": "White and fluffy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5164", + "name": "Sheep", + "examine": "White and fluffy" + }, + { + "id": "5165", + "name": "Sheep" + }, + { + "id": "5166", + "name": "Mountain Goat", + "examine": "This goat belongs to the mountain camp people.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5167", + "name": "Mountain Goat", + "examine": "This goat belongs to the mountain camp people.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5168", + "name": "Ram", + "examine": "White and fluffy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "757,759,758", + "death_animation": "5336", + "defence_animation": "5337", + "defence_level": "1", + "lifepoints": "8", + "melee_animation": "5338", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5169", + "name": "Ram", + "examine": "White and fluffy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "757,759,758", + "death_animation": "5336", + "defence_animation": "5337", + "defence_level": "1", + "lifepoints": "8", + "melee_animation": "5338", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5170", + "name": "Ram", + "examine": "White and fluffy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "757,759,758", + "death_animation": "5336", + "defence_animation": "5337", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "0", + "melee_animation": "5338", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5171", + "name": "Mountain goat", + "examine": "This beast doesn't need climbing boots.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5172", + "name": "Golden sheep", + "examine": "Freshly shorn.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5173", + "name": "Golden sheep", + "examine": "Lovely thick wool.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5174", + "name": "Ogre chieftain" + }, + { + "id": "5175", + "name": "Ogre shaman" + }, + { + "id": "5176", + "name": "Ogre shaman", + "examine": "Seems intelligent... for an ogre.", + "aggressive": "true", + "attack_level": "65", + "combat_style": "2", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "371", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "5177", + "name": "Ogre shaman" + }, + { + "id": "5178", + "name": "Blue dragon", + "examine": "A mother dragon.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,70,70,60,50,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "95", + "lifepoints": "105", + "magic_animation": "80", + "magic_level": "1", + "melee_animation": "80", + "range_animation": "80", + "range_level": "1", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "108", + "strength_level": "95", + "weakness": "3" + }, + { + "id": "5179", + "name": "Elkoy" + }, + { + "id": "5180", + "name": "Ogre shaman" + }, + { + "id": "5181", + "name": "Ogre shaman", + "examine": "Seems intelligent... for an ogre.", + "attack_level": "65", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "371", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "5182", + "name": "Elkoy" + }, + { + "id": "5183", + "name": "Ogre shaman" + }, + { + "id": "5184", + "name": "Ogre shaman", + "examine": "Seems intelligent... for an ogre.", + "aggressive": "true", + "attack_level": "65", + "combat_style": "2", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "371", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "5185", + "name": "Biggleswade" + }, + { + "id": "5186", + "name": "Ogre shaman" + }, + { + "id": "5187", + "name": "Ogre shaman", + "examine": "Seems intelligent... for an ogre.", + "aggressive": "true", + "attack_level": "65", + "combat_style": "2", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "371", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" + }, + { + "id": "5188", + "name": "Saff Waldon", + "examine": "It's Nial", + "aggressive": "true", + "attack_level": "58", + "death_animation": "8948", + "defence_animation": "0", + "defence_level": "58", + "lifepoints": "165", + "magic_animation": "0", + "magic_level": "58", + "melee_animation": "8946", + "range_animation": "0", + "range_level": "58", + "respawn_delay": "60", + "safespot": null, + "strength_level": "58", + "weakness": "10" + }, + { + "id": "5189", + "name": "Ogre shaman" + }, + { + "id": "5190", + "name": "Ogre shaman", + "examine": "Seems intelligent. For an ogre.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "defence_level": "1", + "lifepoints": "1", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5191", + "name": "Blaze Sharpeye" + }, + { + "id": "5192", + "name": "Ogre shaman" + }, + { + "id": "5193", + "name": "Ogre shaman", + "examine": "Seems intelligent. For an ogre.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "defence_level": "1", + "lifepoints": "1", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5194", + "name": "Blaze Sharpeye" + }, + { + "id": "5195", + "name": "Wizard", + "examine": "A wizard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5196", + "name": "Wizard", + "examine": "A wizard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5197", + "name": "Wizard", + "examine": "A wizard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5198", + "name": "Ava", + "examine": "She's watching you." + }, + { + "id": "5199", + "name": "Witch" + }, + { + "id": "5200", + "name": "Witch", + "examine": "The hat's a dead giveaway." + }, + { + "id": "5201", + "name": "Alice's husband" + }, + { + "id": "5203", + "name": "Alice's husband" + }, + { + "id": "5204", + "name": "Alice's husband", + "examine": "She needs to work out before facing Thok.", + "aggressive": "true", + "attack_level": "80", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "22", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "7041", + "range_animation": "0", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "10" + }, + { + "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": "5211", + "name": "Undead cow", + "examine": "It's an undead cow.", + "aggressive": "false", + "attack_level": "1", + "attack_speed": "4", + "bonuses": "-15,-15,-15,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "death_animation": "5851", + "defence_animation": "5850", + "defence_level": "1", + "lifepoints": "8", + "magic_animation": "5849", + "magic_level": "1", + "melee_animation": "5849", + "range_animation": "5849", + "range_level": "1", + "respawn_delay": "5", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5229", + "name": "Penance Ranger", + "examine": "Shooty-shooty.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "5937", + "defence_animation": "5396", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "5395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5237", + "name": "Penance Ranger", + "examine": "Shooty-shooty.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "5937", + "defence_animation": "5396", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "5395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5247", + "name": "Penance Queen", + "examine": "Run away! Run away!", + "aggressive": "true", + "attack_level": "260", + "death_animation": "5412", + "defence_animation": "0", + "defence_level": "132", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5411", + "range_animation": "0", + "range_level": "116", + "respawn_delay": "60", + "safespot": null, + "strength_level": "260" + }, + { + "id": "5248", + "name": "Queen spawn", + "examine": "What's it looking at?", + "aggressive": "true", + "attack_level": "60", + "death_animation": "5093", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "450", + "magic_animation": "0", + "melee_animation": "5092", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60" + }, + { + "id": "5249", + "name": "Captain Errdo", + "examine": "He looks embarrassed", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5250", + "name": "Scarab mage", + "examine": "Part scarab, part man.", + "aggressive": "true", + "attack_level": "40", + "combat_style": "2", + "death_animation": "7616", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "54", + "melee_animation": "7615", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "3" + }, + { + "id": "5251", + "name": "Locust rider", + "examine": "A mounted lancer.", + "aggressive": "true", + "attack_level": "54", + "death_animation": "7581", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "melee_animation": "7584", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "54", + "weakness": "7" + }, + { + "id": "5252", + "name": "Locust rider", + "examine": "A mounted archer.", + "aggressive": "true", + "attack_level": "40", + "death_animation": "7581", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "77", + "magic_animation": "0", + "melee_animation": "5451", + "range_animation": "5451", + "range_level": "54", + "safespot": null, + "strength_level": "40", + "weakness": "2" + }, + { + "id": "5253", + "name": "Giant scarab", + "examine": "A huge scarab beast.", + "aggressive": "true", + "attack_level": "62", + "death_animation": "5456", + "defence_animation": "0", + "defence_level": "62", + "lifepoints": "571", + "magic_animation": "0", + "magic_level": "62", + "melee_animation": "5457", + "range_animation": "0", + "range_level": "62", + "safespot": null, + "strength_level": "62", + "weakness": "10" + }, + { + "id": "5254", + "name": "Scarab mage", + "examine": "Part scarab, part man.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "death_animation": "7616", + "defence_animation": "7617", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "7615", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5255", + "name": "Locust rider" + }, + { + "id": "5256", + "name": "Locust rider" + }, + { + "id": "5257", + "name": "Banker" + }, + { + "id": "5258", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5259", + "name": "Banker" + }, + { + "id": "5260", + "name": "Banker", + "examine": "She can look after my money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5261", + "name": "Stonemason" + }, + { + "id": "5263", + "name": "Nathifa" + }, + { + "id": "5265", + "name": "Urbi" + }, + { + "id": "5267", + "name": "Jamila" + }, + { + "id": "5269", + "name": "Sophanem guard" + }, + { + "id": "5270", + "name": "Sophanem guard", + "examine": "A guard of Sophanem.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5271", + "name": "Sophanem guard" + }, + { + "id": "5273", + "name": "Sophanem guard" + }, + { + "id": "5274", + "name": "Sophanem guard", + "examine": "A guard of Sophanem.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5275", + "name": "Sophanem guard" + }, + { + "id": "5277", + "name": "Menaphite guard", + "examine": "A guard of Menaphos.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5278", + "name": "Coenus" + }, + { + "id": "5279", + "name": "Jex" + }, + { + "id": "5280", + "name": "Maisa" + }, + { + "id": "5282", + "name": "Osman" + }, + { + "id": "5285", + "name": "Osman", + "examine": "Flings annoying splinters at Thok.", + "aggressive": "true", + "attack_level": "80", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "22", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "426", + "range_animation": "426", + "range_level": "80", + "safespot": null, + "strength_level": "80", + "weakness": "10" + }, + { + "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": "5293", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5294", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5295", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5296", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5297", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5298", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5299", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5300", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5301", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5302", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5303", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5304", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5305", + "name": "Zombie", + "examine": "Brains!", + "attack_level": "11", + "attack_speed": "5", + "bonuses": "6,15,15,15,15,15,15,6,6,15,15,15,15,15,15", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "10", + "lifepoints": "18", + "magic_animation": "5568", + "melee_animation": "5568", + "range_animation": "5568", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5306", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5307", + "name": "Zombie", + "examine": "Brains!", + "attack_level": "11", + "attack_speed": "5", + "bonuses": "6,15,15,15,15,15,15,6,6,15,15,15,15,15,15", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "10", + "lifepoints": "18", + "magic_animation": "5568", + "melee_animation": "5568", + "range_animation": "5568", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5308", + "name": "Zombie", + "examine": "Brains!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "24", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5309", + "name": "Zombie", + "examine": "Brains!", + "attack_level": "15", + "attack_speed": "5", + "bonuses": "10,15,16,16,15,16,16,10,10,16,15,15,15,15,15", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "16", + "lifepoints": "40", + "magic_animation": "5571", + "melee_animation": "5571", + "range_animation": "5571", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5310", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "24", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5311", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "24", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5312", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "24", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5313", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "24", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5314", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5315", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5316", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5317", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5318", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5319", + "name": "Zombie", + "examine": "Examine not added", + "attack_level": "15", + "attack_speed": "5", + "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "20", + "lifepoints": "40", + "magic_animation": "5571", + "melee_animation": "5571", + "range_animation": "5571", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5320", + "name": "Zombie", + "examine": "Examine not added", + "attack_level": "18", + "attack_speed": "5", + "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "20", + "lifepoints": "40", + "magic_animation": "5571", + "melee_animation": "5571", + "range_animation": "5571", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5321", + "name": "Zombie", + "examine": "Examine not added", + "attack_level": "18", + "attack_speed": "5", + "bonuses": "12,20,20,20,20,20,20,12,12,20,20,20,20,20,20", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "20", + "lifepoints": "40", + "magic_animation": "5571", + "melee_animation": "5571", + "range_animation": "5571", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5322", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "20", + "attack_speed": "5", + "bonuses": "21,19,20,42,44,34,41,38,40,21,0,0,0,0,0", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "23", + "lifepoints": "30", + "magic_animation": "5571", + "melee_animation": "5571", + "range_animation": "5571", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "19" + }, + { + "id": "5323", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5324", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5325", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5326", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5327", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5328", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5329", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5330", + "name": "Zombie", + "examine": "", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5331", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5332", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "15", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "17", + "lifepoints": "29", + "magic_level": "1", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "70", + "safespot": null, + "strength_level": "18", + "weakness": "8" + }, + { + "id": "5333", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "15", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "17", + "lifepoints": "29", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "70", + "safespot": null, + "strength_level": "18", + "weakness": "8" + }, + { + "id": "5334", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "15", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "17", + "lifepoints": "29", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "70", + "safespot": null, + "strength_level": "18", + "weakness": "8" + }, + { + "id": "5335", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "11", + "attack_speed": "6", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "1", + "lifepoints": "24", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "11" + }, + { + "id": "5336", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "11", + "attack_speed": "6", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "1", + "lifepoints": "24", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "11" + }, + { + "id": "5337", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "24", + "attack_speed": "4", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "24", + "lifepoints": "17", + "magic_animation": "5485", + "magic_level": "1", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "8" + }, + { + "id": "5338", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "12", + "attack_speed": "6", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "23", + "lifepoints": "17", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "8" + }, + { + "id": "5339", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "12", + "attack_speed": "6", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "23", + "lifepoints": "17", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "8" + }, + { + "id": "5340", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "attack_level": "12", + "attack_speed": "6", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5489", + "defence_level": "23", + "lifepoints": "17", + "magic_animation": "5485", + "melee_animation": "5485", + "range_animation": "5485", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "8" + }, + { + "id": "5341", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "42", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5503", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "5499", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "42", + "weakness": "8" + }, + { + "id": "5342", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5343", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5344", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5345", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5346", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5347", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5348", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5349", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5350", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5351", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5352", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5353", + "name": "Undead one", + "examine": "A minion of Rashiliyia.", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "5", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "8" + }, + { + "id": "5354", + "name": "Undead one", + "examine": "A minion of Rashiliyia.", + "attack_level": "33", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "33", + "weakness": "8" + }, + { + "id": "5355", + "name": "Undead one", + "examine": "The animated dead; one of Rashiliyia's minions.", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "5568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "8" + }, + { + "id": "5356", + "name": "Undead one", + "examine": "The animated dead; one of Rashiliyia's minions.", + "aggressive": "true", + "attack_level": "32", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "melee_animation": "5568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "32", + "weakness": "8" + }, + { + "id": "5357", + "name": "Undead one", + "examine": "The animated dead; one of Rashiliyia's minions.", + "aggressive": "true", + "attack_level": "33", + "attack_speed": "5", + "death_animation": "5575", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "5571", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "8" + }, + { + "id": "5358", + "name": "Undead one", + "examine": "The animated dead; one of Rashiliyia's minions.", + "aggressive": "true", + "attack_level": "33", + "attack_speed": "5", + "death_animation": "5575", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", + "melee_animation": "5571", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "33", + "weakness": "8" + }, + { + "id": "5359", + "name": "Giant skeleton", + "examine": "A giant skeleton.", + "attack_level": "60", + "death_animation": "5503", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "5499", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "60", + "weakness": "8" + }, + { + "id": "5360", + "name": "Mummy ashes" + }, + { + "id": "5361", + "name": "Waterfiend", + "examine": "A fiendish embodiment of water.", + "aggressive": "true", + "attack_level": "0", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,100,100,10,100,100,0,0,0,0,0", + "combat_audio": "3774,3773,3772", + "combat_style": "1", + "death_animation": "300", + "defence_animation": "301", + "defence_level": "128", + "lifepoints": "128", + "magic_animation": "299", + "magic_level": "105", + "melee_animation": "299", + "poison_immune": "true", + "projectile": "16", + "range_animation": "299", + "range_level": "105", + "safespot": null, + "strength_level": "0", + "weakness": "4" + }, + { + "id": "5362", + "name": "Brutal green dragon", + "examine": "It appears to be intelligent and savage.", + "aggressive": "true", + "attack_level": "268", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "168", + "lifepoints": "175", + "magic_animation": "91", + "magic_level": "168", + "melee_animation": "91", + "poison_immune": "true", + "range_animation": "91", + "range_level": "0", + "safespot": null, + "slayer_exp": "183", + "strength_level": "168", + "weakness": "3" + }, + { + "id": "5363", + "name": "Mithril dragon", + "examine": "Experimenting with mithril gone bad!", + "aggressive": "true", + "attack_level": "268", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,50,100,70,30,90,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "92", + "defence_animation": "89", + "defence_level": "268", + "lifepoints": "254", + "magic_animation": "91", + "magic_level": "168", + "melee_animation": "91", + "range_animation": "91", + "range_level": "168", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "273", + "strength_level": "268", + "weakness": "8" + }, + { + "id": "5364", + "name": "Confused barbarian", + "examine": "It appears to be intelligent and savage.", + "attack_level": "70", + "attack_speed": "6", + "defence_level": "50", + "lifepoints": "175", + "range_level": "167", + "safespot": null, + "strength_level": "167", + "weakness": "7" + }, + { + "id": "5365", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "34", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "34", + "weakness": "7" + }, + { + "id": "5366", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5367", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "85", + "melee_animation": "5485", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5368", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "85", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5369", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "36", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5370", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "36", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5371", + "name": "Ghost", + "examine": "Eeek! A ghost!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "36", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5372", + "name": "Ghost", + "examine": "Why does it attack with twigs? Swords and axes much better.", + "aggressive": "true", + "attack_level": "80", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "40", + "lifepoints": "36", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "5532", + "range_animation": "426", + "range_level": "80", + "safespot": null, + "slayer_exp": "25", + "strength_level": "80", + "weakness": "10" + }, + { + "id": "5373", + "name": "Ghost", + "examine": "Are these twigs meant to hurt Thok?", + "aggressive": "true", + "attack_level": "80", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "40", + "lifepoints": "36", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "5532", + "range_animation": "426", + "range_level": "80", + "safespot": null, + "slayer_exp": "25", + "strength_level": "80", + "weakness": "10" + }, + { + "id": "5374", + "name": "Ghost", + "examine": "It thinks it scary. Thok show it scary.", + "aggressive": "true", + "attack_level": "80", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "70", + "lifepoints": "36", + "magic_animation": "0", + "magic_level": "80", + "melee_animation": "5532", + "range_animation": "0", + "range_level": "80", + "safespot": null, + "slayer_exp": "25", + "strength_level": "80", + "weakness": "10" + }, + { + "id": "5375", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", + "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "25", + "lifepoints": "23", + "magic_animation": "5568", + "melee_animation": "5568", + "range_animation": "5568", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5376", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", + "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "25", + "lifepoints": "23", + "magic_animation": "5568", + "melee_animation": "5568", + "range_animation": "5568", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5377", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "5", + "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "28", + "lifepoints": "30", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5378", + "name": "Zombie", + "examine": "Dead man walking.", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "5", + "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "28", + "lifepoints": "30", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5379", + "name": "Zombie", + "examine": "The walking dead.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "5", + "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "48", + "lifepoints": "40", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5380", + "name": "Zombie", + "examine": "The walking dead.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "5", + "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", + "combat_audio": "918,923,922", + "death_animation": "5580", + "defence_animation": "5579", + "defence_level": "48", + "lifepoints": "40", + "magic_animation": "5578", + "melee_animation": "5578", + "range_animation": "5578", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5381", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "62", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "62", + "lifepoints": "88", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "62", + "weakness": "8" + }, + { + "id": "5382", + "name": "Animated steel armour", + "examine": "Animated steel armour.", + "attack_level": "28", + "death_animation": "4167", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "28", + "weakness": "7" + }, + { + "id": "5383", + "name": "Odovacar" + }, + { + "id": "5384", + "name": "Giant skeleton", + "examine": "Big and bony, just how I like them.", + "aggressive": "true", + "attack_level": "95", + "attack_speed": "2", + "bonuses": "25,25,25,25,25,25,25,25,25,25,25,25,25,25,25", + "death_animation": "5503", + "defence_animation": "5489", + "defence_level": "85", + "lifepoints": "100", + "magic_animation": "5499", + "melee_animation": "5499", + "poison_immune": "true", + "projectile": "2718", + "range_animation": "5499", + "range_level": "1", + "safespot": null, + "start_gfx": "2713", + "strength_level": "95" + }, + { + "id": "5385", + "name": "Skeleton", + "examine": "A skeleton in a dress!", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "57", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "57", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "5386", + "name": "Skeleton", + "examine": "Achingly thin.", + "aggressive": "true", + "attack_level": "55", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "8" + }, + { + "id": "5387", + "name": "Skeleton", + "examine": "Look: another skeleton.", + "aggressive": "true", + "attack_level": "55", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "8" + }, + { + "id": "5388", + "name": "Skeleton", + "examine": "That skeleton's grinning at me.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "8" + }, + { + "id": "5389", + "name": "Skeleton", + "examine": "He needs a tan.", + "aggressive": "true", + "attack_level": "51", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "51", + "lifepoints": "72", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "51", + "weakness": "8" + }, + { + "id": "5390", + "name": "Skeleton", + "examine": "How do you know if a skeleton's male or female?", + "aggressive": "true", + "attack_level": "36", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "420", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36" + }, + { + "id": "5391", + "name": "Skeleton", + "examine": "He obviously hasn't realised he's dead.", + "aggressive": "true", + "attack_level": "55", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "8" + }, + { + "id": "5392", + "name": "Skeleton", + "examine": "Put some meat on those bones!", + "aggressive": "true", + "attack_level": "52", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "52", + "weakness": "8" + }, + { + "id": "5393", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "56", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5394", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "40", + "melee_animation": "5578", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5395", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5396", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5397", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5398", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5399", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5400", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5401", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5402", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5403", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5404", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5569", + "defence_animation": "5567", + "defence_level": "1", + "lifepoints": "92", + "melee_animation": "5568", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5405", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5406", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5407", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5408", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5409", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5410", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "5411", + "name": "Skeleton", + "examine": "An angry skeleton.", + "aggressive": "true", + "attack_level": "55", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "8" + }, + { + "id": "5412", + "name": "Skeleton", + "examine": "Cross bones.", + "aggressive": "true", + "attack_level": "57", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "57", + "lifepoints": "81", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "57", + "weakness": "8" + }, + { + "id": "5413", + "name": "Possessed pickaxe", + "examine": "How does it move of its own accord?", + "aggressive": "true", + "attack_level": "55", + "death_animation": "5592", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5591", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "6" + }, + { + "id": "5414", + "name": "Animated spade", + "examine": "How does it move of its own accord?", + "attack_level": "52", + "death_animation": "5596", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "74", + "magic_animation": "0", + "melee_animation": "5597", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "52", + "weakness": "6" + }, + { + "id": "5415", + "name": "Terror dog statue" + }, + { + "id": "5416", + "name": "Terror dog statue" + }, + { + "id": "5417", + "name": "Terror dog", + "examine": "A terrifying dog beast.", + "aggressive": "true", + "attack_level": "47", + "attack_speed": "5", + "death_animation": "5628", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "134", + "magic_animation": "0", + "melee_animation": "5625", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "1" + }, + { + "id": "5418", + "name": "Terror dog", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "5627", + "defence_animation": "5626", + "defence_level": "1", + "lifepoints": "82", + "melee_animation": "5625", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5419", + "name": "Tarn" + }, + { + "id": "5420", + "name": "Tarn", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "5619", + "defence_animation": "5618", + "defence_level": "1", + "lifepoints": "65", + "melee_animation": "5617", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5421", + "name": "Mutant tarn", + "examine": "I preferred him when he was human.", + "aggressive": "true", + "attack_level": "57", + "attack_speed": "6", + "death_animation": "5619", + "defence_animation": "0", + "defence_level": "57", + "lifepoints": "325", + "magic_animation": "0", + "melee_animation": "5617", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "57", + "weakness": "6" + }, + { + "id": "5422", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "63", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "5485", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "8" + }, + { + "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": "5439", + "name": "null", + "examine": "What restful music!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5442", + "name": "KGP Agent", + "examine": "What restful music!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5443", + "name": "Noodle" + }, + { + "id": "5445", + "name": "Penguin" + }, + { + "id": "5446", + "name": "Penguin suit" + }, + { + "id": "5447", + "name": "Agility Instructor", + "examine": "Instructs agility.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5448", + "name": "Army Commander", + "examine": "An army commander.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5449", + "name": "Penguin" + }, + { + "id": "5450", + "name": "Penguin" + }, + { + "id": "5451", + "name": "Penguin" + }, + { + "id": "5452", + "name": "Icelord", + "examine": "Sturdy cold being.", + "aggressive": "true", + "attack_level": "22", + "death_animation": "5726", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "5724", + "range_animation": "5725", + "range_level": "30", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "0" + }, + { + "id": "5453", + "name": "Icelord", + "examine": "Sturdy cold being.", + "aggressive": "true", + "attack_level": "30", + "death_animation": "5726", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "5724", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "9" + }, + { + "id": "5454", + "name": "Icelord", + "examine": "Sturdy cold being.", + "aggressive": "true", + "attack_level": "30", + "death_animation": "5726", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "5724", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "9" + }, + { + "id": "5455", + "name": "Icelord", + "examine": "Sturdy cold being.", + "aggressive": "true", + "attack_level": "30", + "death_animation": "5726", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "5724", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "9" + }, + { + "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": "5472", + "name": "Ice Troll King", + "examine": "An impressive-looking troll.", + "aggressive": "true", + "attack_level": "65", + "death_animation": "5209", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "185", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "5374", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "5473", + "name": "Ice troll runt", + "examine": "An ice troll youngling.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "70", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "9" + }, + { + "id": "5474", + "name": "Ice troll male", + "examine": "A male troll wielding a large club.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "9" + }, + { + "id": "5475", + "name": "Ice troll female", + "examine": "An ice troll with a bag of rocks.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "1142", + "projectile": "276", + "range_animation": "1142", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "0" + }, + { + "id": "5476", + "name": "Ice troll grunt", + "examine": "A large ice troll.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "4332", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "9" + }, + { + "id": "5477", + "name": "Bork Sigmundson" + }, + { + "id": "5478", + "name": "King Gjuki Sorvott IV", + "examine": "An ill-tempered king.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5479", + "name": "HRH Hrafn", + "examine": "A regal cat with an evil glint in its eye.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5480", + "name": "Thorkel Silkbeard", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5481", + "name": "Mord Gunnars" + }, + { + "id": "5482", + "name": "Mord Gunnars" + }, + { + "id": "5483", + "name": "Hring Hring", + "examine": "Harder than the rocks he sells.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5484", + "name": "Flossi Dalksson", + "examine": "Jatizso's fishmonger.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5485", + "name": "Raum Urda-Stein", + "examine": "A guy that will sell you armour.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5486", + "name": "Skuli Myrka", + "examine": "Jatizso's armour merchant.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5487", + "name": "Keepa Kettilon", + "examine": "A rough-looking chef.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5488", + "name": "Magnus Gram", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "facing_booth": "true", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5489", + "name": "Guard", + "examine": "A guard on insult duty.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5490", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5491", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5492", + "name": "Guard", + "examine": "One of King Sorvott's militia.", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5493", + "name": "Freygerd", + "examine": "One of Jatizso's many citizens.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5494", + "name": "Lensa", + "examine": "One of Jatizso's many citizens.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5495", + "name": "Vanligga Gastfrihet", + "examine": "A happy, friendly landlady.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5496", + "name": "Sassilik", + "examine": "One of Jatizso's many citizens.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5497", + "name": "Miner", + "examine": "A miner at work.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5498", + "name": "Miner" + }, + { + "id": "5499", + "name": "Eric", + "examine": "A bedraggled-looking tramp.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5500", + "name": "Gruva Patrull", + "examine": "Mina guards the mine entrance.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5501", + "name": "Brendt", + "examine": "A dwarven trader from Keldagrim.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5514", + "name": "Honour guard", + "examine": "The Burgher's protectors.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5515", + "name": "Honour Guard", + "examine": "One of the Burgher's Protectors", + "death_animation": "836", + "defence_animation": "404", + "melee_animation": "395" + }, + { + "id": "5516", + "name": "Honour guard", + "examine": "The Burgher's protectors.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5517", + "name": "Honour guard", + "examine": "One of your militia.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "5518", + "name": "Kjedelig Uppsen" + }, + { + "id": "5519", + "name": "Trogen Konungarde" + }, + { + "id": "5520", + "name": "Slug Hemligssen" + }, + { + "id": "5521", + "name": "Ice troll runt", + "examine": "An ice troll youngling.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "70", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "9" + }, + { + "id": "5522", + "name": "Ice troll male", + "examine": "A male troll wielding a large club.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "9" + }, + { + "id": "5523", + "name": "Ice troll female", + "examine": "An ice troll with a bag of rocks.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "1142", + "projectile": "276", + "range_animation": "1142", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "0" + }, + { + "id": "5524", + "name": "Ice troll grunt", + "examine": "A large ice troll.", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "4332", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "100", + "weakness": "9" + }, + { + "id": "5525", + "name": "Ice troll runt", + "examine": "An ice troll youngling.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "70", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "9" + }, + { + "id": "5526", + "name": "Ice troll male", + "examine": "A male troll wielding a large club.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "9" + }, + { + "id": "5527", + "name": "Ice troll female", + "examine": "An ice troll with a bag of rocks.", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "4", + "bonuses": "60,60,60,0,0,30,60,30,0,0,0,60,0,0,0", + "combat_style": "1", + "death_animation": "287", + "defence_animation": "285", + "defence_level": "40", + "lifepoints": "80", + "magic_animation": "0", + "melee_animation": "1142", + "projectile": "276", + "range_animation": "1142", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "80", + "weakness": "0" + }, + { + "id": "5528", + "name": "Ice troll grunt" + }, + { + "id": "5529", + "name": "Yak", + "examine": "A hairy, smelly, grazing animal.", + "attack_level": "20", + "attack_speed": "4", + "combat_audio": "3378,3380,3379", + "death_animation": "5784", + "defence_animation": "5783", + "defence_level": "1", + "lifepoints": "50", + "magic_animation": "5782", + "melee_animation": "5782", + "range_animation": "5782", + "range_level": "1", + "safespot": null, + "strength_level": "10", + "weakness": "7" + }, + { + "id": "5530", + "name": "Sorceress" + }, + { + "id": "5531", + "name": "Sorceress", + "examine": "Antisocial.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5532", + "name": "Apprentice", + "examine": "Come", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5533", + "name": "Autumn Elemental", + "examine": "An autumn elemental.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5539", + "name": "Spring Elemental", + "examine": "A spring elemental.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5547", + "name": "Summer Elemental", + "examine": "A summer elemental.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5553", + "name": "Winter Elemental", + "examine": "A winter elemental.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5560", + "name": "Osman" + }, + { + "id": "5561", + "name": "Osman", + "examine": "It dare attack Pretty Lass? Thok crush its puny skull!", + "aggressive": "true", + "attack_level": "95", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "157", + "magic_animation": "0", + "magic_level": "95", + "range_animation": "0", + "range_level": "95", + "safespot": null, + "strength_level": "95", + "weakness": "10" + }, + { + "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": "5571", + "name": "Sin Seer", + "examine": "She's honest about the things you aren't.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5572", + "name": "Ghost", + "examine": "A Shadow Realm guardian.", + "attack_level": "1", + "combat_audio": "436,439,438", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "5573", + "name": "Effigy" + }, + { + "id": "5579", + "name": "Effigy" + }, + { + "id": "5580", + "name": "Bonafido" + }, + { + "id": "5581", + "name": "Homunculus", + "examine": "Is it male or female?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5582", + "name": "Homunculus" + }, + { + "id": "5583", + "name": "Homunculus" + }, + { + "id": "5584", + "name": "Cage", + "examine": "Strong", + "aggressive": "true", + "attack_level": "95", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "85", + "magic_animation": "0", + "magic_level": "95", + "range_animation": "0", + "range_level": "95", + "safespot": null, + "strength_level": "95", + "weakness": "10" + }, + { + "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": "5589", + "name": "'Black-eye", + "examine": "Famous for his fights.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5590", + "name": "'No fingers", + "examine": "Count them pinkies!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5591", + "name": "'Gummy", + "examine": "He wishes he had teeth.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5592", + "name": "'The Guns'" + }, + { + "id": "5593", + "name": "Frogeel", + "examine": "Is it a frog", + "aggressive": "true", + "attack_level": "36", + "attack_speed": "5", + "death_animation": "5841", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "melee_animation": "5842", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "60", + "safespot": null, + "strength_level": "36", + "weakness": "2" + }, + { + "id": "5594", + "name": "Spidine", + "attack_level": "1", + "death_animation": "146", + "defence_animation": "144", + "defence_level": "1", + "lifepoints": "35", + "melee_animation": "143", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5595", + "name": "Swordchick", + "examine": "Definitely not a chicken or a swordfish.", + "attack_level": "45", + "attack_speed": "5", + "death_animation": "5389", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "5387", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "7" + }, + { + "id": "5596", + "name": "Jubster", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6801", + "defence_animation": "6802", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "6800", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5597", + "name": "Newtroost", + "examine": "Since when did newts have beaks?", + "attack_level": "40", + "death_animation": "2301", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "2299", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40", + "weakness": "7" + }, + { + "id": "5598", + "name": "Unicow" + }, + { + "id": "5603", + "name": "Unicow", + "examine": "Half unicorn", + "aggressive": "true", + "attack_level": "30", + "attack_speed": "5", + "death_animation": "5851", + "defence_animation": "0", + "defence_level": "41", + "lifepoints": "58", + "magic_animation": "0", + "magic_level": "41", + "melee_animation": "5849", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "4" + }, + { + "id": "5604", + "name": "Elfinlocks" + }, + { + "id": "5605", + "name": "Clockwork cat" + }, + { + "id": "5606", + "name": "Clockwork cat" + }, + { + "id": "5608", + "name": "Monk", + "examine": "Holier than thou.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5609", + "name": "Monk", + "examine": "Holier than thou.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5610", + "name": "Monk", + "examine": "Holier than thou.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5611", + "name": "Monk", + "examine": "Holier than thou.", + "attack_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5612", + "name": "Rufus" + }, + { + "id": "5613", + "name": "Mi-Gor" + }, + { + "id": "5614", + "name": "Puffin", + "examine": "I'd prefer it if it were a muffin...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5615", + "name": "Puffin" + }, + { + "id": "5616", + "name": "Brother Tranquility" + }, + { + "id": "5617", + "name": "Brother Tranquility" + }, + { + "id": "5618", + "name": "Brother Tranquility" + }, + { + "id": "5619", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5620", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5621", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5622", + "name": "Monk", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5623", + "name": "Zombie monk" + }, + { + "id": "5624", + "name": "Zombie monk" + }, + { + "id": "5625", + "name": "Zombie monk" + }, + { + "id": "5626", + "name": "Zombie monk" + }, + { + "id": "5627", + "name": "Sorebones", + "examine": "Bedside manner is a little lacking", + "attack_level": "35", + "death_animation": "5658", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5643", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5628", + "name": "Sorebones", + "examine": "I hope his hands don't shake.", + "attack_level": "35", + "death_animation": "5658", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5643", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5629", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5630", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5631", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5632", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5633", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5634", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5635", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5647", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5636", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5637", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5638", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5639", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5640", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5641", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5651", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5642", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5880", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5643", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5880", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5644", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5880", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5645", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5880", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5646", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5654", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5880", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5647", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5886", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5884", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5648", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5886", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5884", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5649", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5650", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5886", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5884", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5651", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5652", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5886", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5884", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5653", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5654", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5886", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5884", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5655", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5656", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5657", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5658", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5659", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5660", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5661", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5662", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5663", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5664", + "name": "Zombie pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "5649", + "defence_animation": "5648", + "defence_level": "1", + "lifepoints": "52", + "melee_animation": "5647", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5665", + "name": "Zombie pirate", + "examine": "Aaaarg", + "aggressive": "true", + "attack_level": "35", + "combat_audio": "703,705,704", + "death_animation": "5891", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "5889", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "6" + }, + { + "id": "5666", + "name": "Barrelchest", + "examine": "It's trying to mash you flat! Less examine", + "aggressive": "true", + "attack_level": "52", + "death_animation": "5898", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "285", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "5895", + "range_animation": "0", + "range_level": "52", + "respawn_delay": "60", + "safespot": null, + "strength_level": "52", + "weakness": "10" + }, + { + "id": "5667", + "name": "Undead Lumberjack" + }, + { + "id": "5679", + "name": "Undead Lumberjack" + }, + { + "id": "5680", + "name": "Undead Lumberjack", + "examine": "He is one", + "attack_level": "73", + "death_animation": "5972", + "defence_animation": "0", + "defence_level": "10", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "5970", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "73" + }, + { + "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": "5750", + "name": "Cave bug", + "examine": "A big", + "attack_level": "82", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,72,59,35,25,95,0,0,0,0,0", + "death_animation": "6081", + "defence_animation": "0", + "defence_level": "84", + "lifepoints": "93", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "6079", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "93", + "strength_level": "80", + "weakness": "2" + }, + { + "id": "5751", + "name": "Molanisk", + "examine": "A strange mole-like being.", + "aggressive": "true", + "attack_level": "40", + "attack_speed": "4", + "clue_level": "1", + "combat_audio": "3492,3494,3493", + "death_animation": "6014", + "defence_animation": "6013", + "defence_level": "50", + "lifepoints": "52", + "magic_level": "1", + "melee_animation": "6012", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "52", + "strength_level": "40" + }, + { + "id": "5752", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5753", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5754", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5755", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5756", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5757", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5758", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5759", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5760", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5761", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5762", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5763", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5764", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5765", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5766", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5767", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5768", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5769", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5776", + "name": "Banker", + "examine": "Looks after cave goblin money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5777", + "name": "Banker", + "examine": "Looks after cave goblin money.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5778", + "name": "Bartak" + }, + { + "id": "5779", + "name": "Turgall" + }, + { + "id": "5780", + "name": "Reldak" + }, + { + "id": "5781", + "name": "Miltog" + }, + { + "id": "5782", + "name": "Mernik" + }, + { + "id": "5783", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5784", + "name": "Crate goblin", + "examine": "Taking a terribly important box from one place to another.", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5785", + "name": "Cave goblin", + "examine": "One of the many citizens of Dorgesh-Kaan.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5786", + "name": "Goblin scribe", + "examine": "Good at shorthand.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5800", + "name": "Guard", + "examine": "He keeps order in the city.", + "attack_level": "42", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "42", + "weakness": "6" + }, + { + "id": "5801", + "name": "Guard", + "examine": "He keeps order in the city.", + "attack_level": "42", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "6003", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "6007", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "42", + "weakness": "6" + }, + { + "id": "5802", + "name": "Young 'un" + }, + { + "id": "5803", + "name": "Young 'un", + "examine": "A goblin baby.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5804", + "name": "Tyke" + }, + { + "id": "5805", + "name": "Nipper", + "examine": "A goblin baby.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5806", + "name": "Nipper" + }, + { + "id": "5807", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5808", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5809", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5810", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5811", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5812", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5813", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5814", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5815", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5816", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5817", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5818", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5819", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5820", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5821", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5822", + "name": "Cave goblin child", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5823", + "name": "Spit goblin", + "examine": "Don't spit", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5824", + "name": "Goblin fish", + "examine": "A bug-eyed little goblin fish. How cute.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5825", + "name": "Movario" + }, + { + "id": "5826", + "name": "Darve" + }, + { + "id": "5827", + "name": "Moths", + "examine": "Annoying little flappy things.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5828", + "name": "Barlak" + }, + { + "id": "5829", + "name": "Rabbit", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "6091", + "defence_level": "1", + "lifepoints": "5", + "melee_animation": "6090", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5830", + "name": "Rat Burgiss" + }, + { + "id": "5833", + "name": "Rat Burgiss", + "attack_level": "1", + "combat_audio": "703,705,704", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5834", + "name": "Surok Magis", + "examine": "A powerful wizard." + }, + { + "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": "5842", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5843", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5844", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5845", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5846", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5847", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5848", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5849", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5850", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5851", + "name": "Outlaw", + "examine": "A mean looking outlaw. Don't get too close!", + "attack_level": "23", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "23", + "lifepoints": "32", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23", + "weakness": "9" + }, + { + "id": "5852", + "name": "Monkey", + "examine": "Weird eyeball thing. Reminds Thok of breakfast.", + "aggressive": "true", + "attack_level": "60", + "combat_audio": "629,631,630", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "85", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "0", + "range_animation": "0", + "range_level": "60", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "5853", + "name": "Bench" + }, + { + "id": "5854", + "name": "Bench", + "examine": "For sitting on.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5855", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5856", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5857", + "name": "Zanik" + }, + { + "id": "5858", + "name": "Ur-tag" + }, + { + "id": "5859", + "name": "H.A.M. Archer", + "examine": "He looks pretty skilled with that bow.", + "aggressive": "true", + "attack_level": "15", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "57", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "0", + "range_level": "20", + "safespot": null, + "strength_level": "15", + "weakness": "0" + }, + { + "id": "5860", + "name": "H.A.M. Mage", + "examine": "He bristles with arcane power.", + "aggressive": "true", + "attack_level": "15", + "clue_level": "0", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "57", + "magic_animation": "0", + "magic_level": "20", + "melee_animation": "429", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "3" + }, + { + "id": "5861", + "name": "Zanik", + "examine": "No match for Thok. Silly demon.", + "aggressive": "true", + "attack_level": "85", + "defence_animation": "0", + "defence_level": "85", + "lifepoints": "71", + "magic_animation": "0", + "magic_level": "85", + "range_level": "85", + "safespot": null, + "strength_level": "85", + "weakness": "10" + }, + { + "id": "5862", + "name": "Sigmund and Zanik" + }, + { + "id": "5863", + "name": "Ambassador Alvijar" + }, + { + "id": "5864", + "name": "Builder", + "examine": "A worker on the train link.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5865", + "name": "Builder", + "examine": "A worker on the train link.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5866", + "name": "Builder", + "examine": "A worker on the train link.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5867", + "name": "Builder", + "examine": "A worker on the train link.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5868", + "name": "Tegdak" + }, + { + "id": "5869", + "name": "Zanik" + }, + { + "id": "5871", + "name": "Sergeant Mossfists" + }, + { + "id": "5872", + "name": "Sergeant Slimetoes" + }, + { + "id": "5873", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5874", + "name": "Cave goblin", + "examine": "A goblin with big", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5875", + "name": "Cave goblin", + "examine": "A goblin with big", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5876", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5877", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5878", + "name": "Cave goblin", + "attack_level": "1", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5879", + "name": "Ticket goblin", + "examine": "He sells tickets to Keldagrim.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5880", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5881", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5882", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5883", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5884", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5885", + "name": "Dwarf", + "examine": "A dwarf from Keldagrim.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "16", + "strength_level": "1" + }, + { + "id": "5886", + "name": "Ticket dwarf", + "examine": "He sells tickets to Dorgesh-Kaan.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5902", + "name": "The Inadequacy", + "examine": "Are you good enough to fight?", + "aggressive": "true", + "attack_level": "70", + "death_animation": "6322", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "6318", + "range_animation": "0", + "range_level": "70", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "10" + }, + { + "id": "5903", + "name": "The Everlasting", + "examine": "Can you endure long enough?", + "aggressive": "true", + "attack_level": "67", + "death_animation": "6347", + "defence_animation": "0", + "defence_level": "67", + "lifepoints": "191", + "magic_animation": "0", + "magic_level": "67", + "melee_animation": "6345", + "range_animation": "0", + "range_level": "67", + "respawn_delay": "60", + "safespot": null, + "strength_level": "67", + "weakness": "10" + }, + { + "id": "5904", + "name": "The Untouchable", + "examine": "Can you bring yourself to hurt another?", + "aggressive": "true", + "attack_level": "65", + "death_animation": "6331", + "defence_animation": "0", + "defence_level": "75", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "6329", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "5905", + "name": "The Illusive", + "examine": "What if you don't know how to win?", + "aggressive": "true", + "attack_level": "63", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "63", + "melee_animation": "6342", + "range_animation": "0", + "range_level": "63", + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "10" + }, + { + "id": "5906", + "name": "A Doubt", + "examine": "You can't escape your inadequacy!", + "aggressive": "true", + "attack_level": "55", + "death_animation": "6315", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "6310", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "8" + }, + { + "id": "5907", + "name": "The Illusive" + }, + { + "id": "5908", + "name": "Dying tree", + "examine": "Dead stone defending a dead throne room.", + "aggressive": "true", + "attack_level": "50", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "142", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "7" + }, + { + "id": "5909", + "name": "Barbarian", + "examine": "Don't let her arm-wrestle you for money.", + "attack_level": "20", + "clue_level": "0", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "20", + "lifepoints": "28", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "5910", + "name": "Cook", + "examine": "A greasy, grimy gourmet.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5911", + "name": "Cook", + "examine": "He's suspiciously talented with a meat-cleaver.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5912", + "name": "Banker" + }, + { + "id": "5913", + "name": "Banker" + }, + { + "id": "5914", + "name": "Iffie", + "examine": "This grizzled soldier is here to distribute Rated Clan Wars badges.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5915", + "name": "Elsie" + }, + { + "id": "5916", + "name": "Cleaner", + "examine": "Does what too many people aren't interested in doing.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5917", + "name": "Stray dog", + "examine": "A mangy mutt.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5918", + "name": "Stray dog" + }, + { + "id": "5919", + "name": "Guard", + "examine": "He tries to keep order around here. His bizarre uniform isn't helping.", + "attack_level": "18", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "2548,1979,512", + "death_animation": "6490", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "6489", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "18", + "weakness": "6" + }, + { + "id": "5920", + "name": "Guard", + "examine": "He tries to keep order around here. His bizarre uniform isn't helping.", + "attack_level": "18", + "attack_speed": "5", + "clue_level": "1", + "combat_audio": "2548,1979,512", + "death_animation": "6490", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "6489", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "18", + "weakness": "6" + }, + { + "id": "5921", + "name": "Trainee Guard", + "examine": "He's studying to be a guard.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5922", + "name": "Captain", + "examine": "Trains the guards of the future.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5923", + "name": "Man", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" + }, + { + "id": "5924", + "name": "Woman", + "examine": "One of Gielinor's many citizens.", + "attack_level": "3", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "511,506,505", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "9" + }, + { + "id": "5925", + "name": "Benny", + "examine": "Newspaper seller." + }, + { + "id": "5926", + "name": "Thief", + "examine": "Known for his light-fingered qualities.", + "attack_level": "9", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "9" + }, + { + "id": "5927", + "name": "Thief", + "examine": "Known for his light-fingered qualities.", + "attack_level": "9", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "9" + }, + { + "id": "5928", + "name": "Thief", + "examine": "Known for her light-fingered qualities.", + "attack_level": "9", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "24", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "9" + }, + { + "id": "5929", + "name": "Thief", + "examine": "Known for her light-fingered qualities.", + "attack_level": "9", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "24", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "9" + }, + { + "id": "5930", + "name": "Art Critic Jacques", + "examine": "Jacques Netis - a slightly pompous art expert.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5931", + "name": "Historian Minas", + "examine": "Seth Minas - an aged expert in Gielinor history.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "5938", + "name": "Information clerk", + "examine": "A lady with lots of information about the Museum.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5939", + "name": "Torrcs" + }, + { + "id": "5940", + "name": "Marfet" + }, + { + "id": "5941", + "name": "Museum guard" + }, + { + "id": "5942", + "name": "Museum guard" + }, + { + "id": "5943", + "name": "Museum guard" + }, + { + "id": "5944", + "name": "Teacher and pupil", + "examine": "A teacher and one of his pupils.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5945", + "name": "Schoolboy", + "examine": "Looks kind of familiar.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5946", + "name": "Schoolboy" + }, + { + "id": "5947", + "name": "Teacher and pupil", + "examine": "A teacher and one of her pupils.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5948", + "name": "Teacher and pupil" + }, + { + "id": "5949", + "name": "Schoolboy" + }, + { + "id": "5950", + "name": "Teacher" + }, + { + "id": "5951", + "name": "Schoolgirl" + }, + { + "id": "5952", + "name": "Workman", + "examine": "Looks hard at work.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5953", + "name": "Workman" + }, + { + "id": "5954", + "name": "Workman", + "examine": "Could there be something exciting in his wheelbarrow?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5955", + "name": "Workman" + }, + { + "id": "5956", + "name": "Void Knight", + "examine": "A powerful knight of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5957", + "name": "Schoolgirl" + }, + { + "id": "5958", + "name": "Digsite workman", + "examine": "Wheelbarrow loader.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5959", + "name": "Barge workman", + "examine": "Working hard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5960", + "name": "Barge workman", + "examine": "Working hard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5961", + "name": "Barge workman", + "examine": "Working hard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5962", + "name": "Barge workman", + "examine": "Working hard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5963", + "name": "Barge foreman", + "examine": "Working hard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5964", + "name": "Ed Wood" + }, + { + "id": "5965", + "name": "Orlando Smith" + }, + { + "id": "5966", + "name": "Natural historian", + "examine": "An aged expert in natural history.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5967", + "name": "Natural historian" + }, + { + "id": "5968", + "name": "Natural historian" + }, + { + "id": "5969", + "name": "Natural historian" + }, + { + "id": "5970", + "name": "Natural historian" + }, + { + "id": "5971", + "name": "Leech display", + "examine": "Bloodsuckers!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5972", + "name": "Sea slugs display", + "examine": "Slugs of the sea variety.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5973", + "name": "Snail display", + "examine": "A house on its back.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5974", + "name": "Monkey display", + "examine": "A cheeky little monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5975", + "name": "Lizard display", + "examine": "A scaly little fellow.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5976", + "name": "Penguin display", + "examine": "Nice suit.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5977", + "name": "Camel display", + "examine": "He's got the hump.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5978", + "name": "Terrorbird display", + "examine": "Terrifying!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "34", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "1010", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "strength_level": "23" + }, + { + "id": "5979", + "name": "Dragon display", + "examine": "Looks like a fire-breather.", + "attack_level": "1", + "combat_audio": "408,410,409", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5980", + "name": "Wyvern display", + "examine": "Needs a good square meal.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5981", + "name": "Battle tortoise display", + "examine": "A huge beast.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5982", + "name": "Mole display", + "examine": "Loves making molehills.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5983", + "name": "Schoolgirl" + }, + { + "id": "5984", + "name": "Schoolgirl" + }, + { + "id": "5985", + "name": "Schoolgirl" + }, + { + "id": "5986", + "name": "Workman", + "examine": "It hides in stone", + "aggressive": "true", + "attack_level": "95", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "34", + "magic_animation": "0", + "magic_level": "95", + "range_animation": "0", + "range_level": "95", + "safespot": null, + "strength_level": "95", + "weakness": "10" + }, + { + "id": "5987", + "name": "Sylas", + "examine": "A gnarly old man." + }, + { + "id": "5988", + "name": "Miazrqa" + }, + { + "id": "5989", + "name": "Grimgnash" + }, + { + "id": "5990", + "name": "Rupert the Beard", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5991", + "name": "Drain pipe" + }, + { + "id": "5992", + "name": "Rupert the Beard", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5993", + "name": "Experiment No.2", + "examine": "A very odd looking creature.", + "attack_level": "46", + "death_animation": "6512", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "131", + "magic_animation": "0", + "magic_level": "46", + "melee_animation": "6513", + "range_animation": "0", + "range_level": "46", + "respawn_delay": "60", + "safespot": null, + "strength_level": "46", + "weakness": "10" + }, + { + "id": "5994", + "name": "Mouse", + "examine": "A huge mouse. It looks hungry...", + "aggressive": "true", + "attack_level": "44", + "death_animation": "6521", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "6519", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "44", + "weakness": "9" + }, + { + "id": "5995", + "name": "Mouse" + }, + { + "id": "5996", + "name": "Glod", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6502", + "defence_animation": "6503", + "defence_level": "1", + "lifepoints": "160", + "melee_animation": "6501", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "5997", + "name": "Rupert the Beard" + }, + { + "id": "5999", + "name": "Rupert the Beard", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6000", + "name": "Rupert the Beard" + }, + { + "id": "6001", + "name": "Rupert the Beard", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6002", + "name": "Gnome" + }, + { + "id": "6003", + "name": "Winkin" + }, + { + "id": "6004", + "name": "Gnome" + }, + { + "id": "6005", + "name": "Cage" + }, + { + "id": "6006", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6007", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6008", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6009", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6010", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6011", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6012", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6013", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6014", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6015", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6016", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6017", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6018", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6019", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6020", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6021", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6022", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6023", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6024", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6025", + "name": "Werewolf", + "examine": "Eek! A werewolf!", + "aggressive": "false", + "attack_level": "70", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,60,0,0,0,0,0,0", + "clue_level": "1", + "combat_audio": "481,491,490", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "", + "magic_level": "1", + "melee_animation": "6536", + "range_animation": "", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "70", + "weakness": "" + }, + { + "id": "6026", + "name": "Boris", + "examine": "A traveling man. Are those fleas?", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6027", + "name": "Imre", + "examine": "A well organised shop keeper.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6028", + "name": "Yuri", + "examine": "He's staring at the moon.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6029", + "name": "Joseph", + "examine": "He keeps calling me comrade.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6030", + "name": "Nikolai", + "examine": "There's a set of brows that mean business.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6031", + "name": "Eduard", + "examine": "This is one feral looking innkeeper.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6032", + "name": "Lev", + "examine": "A tough looking villager.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6033", + "name": "Georgy", + "examine": "A simple villager.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6034", + "name": "Svetlana", + "examine": "Seems a bit of a drama queen.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6035", + "name": "Irina", + "examine": "I bet she works out.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6036", + "name": "Alexis", + "examine": "She doesn't say much.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6037", + "name": "Milla", + "examine": "She looks like she can handle herself.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6038", + "name": "Galina", + "examine": "She seems light on her feet.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6039", + "name": "Sofiya", + "examine": "A wise villager.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6040", + "name": "Ksenia", + "examine": "Not exactly a warrior princess.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6041", + "name": "Yadviga", + "examine": "A well off villager.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6042", + "name": "Nikita", + "examine": "The woman.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6043", + "name": "Vera", + "examine": "She seems sure of herself.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6044", + "name": "Zoja", + "examine": "A healthy villager.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6045", + "name": "Liliya", + "examine": "She's looking for flowers.", + "attack_level": "10", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "10", + "lifepoints": "60", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, + "strength_level": "10" + }, + { + "id": "6046", + "name": "Big Wolf", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "909,912,911", + "death_animation": "6558", + "defence_animation": "6557", + "defence_level": "1", + "lifepoints": "74", + "melee_animation": "6559", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "74", + "strength_level": "1" + }, + { + "id": "6047", + "name": "Wolf", + "examine": "A vicious mountain wolf.", + "aggressive": "true", + "attack_level": "22", + "attack_speed": "6", + "combat_audio": "909,912,911", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "22", + "lifepoints": "69", + "magic_animation": "6579", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "safespot": null, + "slayer_exp": "34", + "strength_level": "1" + }, + { + "id": "6048", + "name": "Wolf", + "examine": "Now it has no armour", + "aggressive": "true", + "attack_level": "95", + "combat_audio": "481,491,490", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "157", + "magic_animation": "0", + "magic_level": "95", + "range_animation": "0", + "range_level": "95", + "safespot": null, + "slayer_exp": "34", + "strength_level": "95", + "weakness": "10" + }, + { + "id": "6049", + "name": "Wolf", + "examine": "A big coward. Thok mocks scaredy man.", + "aggressive": "true", + "attack_level": "75", + "combat_audio": "481,491,490", + "defence_animation": "0", + "defence_level": "75", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "75", + "range_level": "75", + "safespot": null, + "slayer_exp": "34", + "strength_level": "75", + "weakness": "10" + }, + { + "id": "6050", + "name": "Desert Wolf", + "examine": "A vicious desert wolf.", + "attack_level": "20", + "attack_speed": "5", + "death_animation": "6558", + "defence_animation": "6578", + "defence_level": "5", + "lifepoints": "55", + "magic_animation": "6579", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "safespot": null, + "strength_level": "20", + "weakness": "9" + }, + { + "id": "6051", + "name": "Desert Wolf", + "examine": "A vicious desert wolf.", + "attack_level": "20", + "attack_speed": "5", + "death_animation": "6558", + "defence_animation": "6578", + "defence_level": "5", + "lifepoints": "55", + "magic_animation": "6579", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "safespot": null, + "strength_level": "20" + }, + { + "id": "6052", + "name": "Ice wolf", + "examine": "Not man's best friend.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "6558", + "defence_animation": "6557", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "6559", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6053", + "name": "Ninja Impling", + "examine": "A very stealthy impling." + }, + { + "id": "6054", + "name": "Dragon impling", + "examine": "An impling who likes the word \"dragon\".", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6055", + "name": "Baby Impling Puro", + "examine": "A baby impling. Ahhh.", + "respawn_delay": "7" + }, + { + "id": "6056", + "name": "Young Impling Puro", + "examine": "A young impling. It's not fair!", + "respawn_delay": "7" + }, + { + "id": "6057", + "name": "Gourmet Impling Puro", + "examine": "An impling gourmet. Mmmm, tasty.", + "respawn_delay": "7" + }, + { + "id": "6058", + "name": "Earth Impling Puro", + "examine": "An earth impling. Rock on.", + "respawn_delay": "50" + }, + { + "id": "6059", + "name": "Essence Impling Puro", + "examine": "An impling who likes magic. Magic!", + "respawn_delay": "50" + }, + { + "id": "6060", + "name": "Eclectic Impling Puro", + "examine": "An impling with varied tastes.", + "respawn_delay": "7" + }, + { + "id": "6061", + "name": "Nature Impling Puro", + "examine": "A nature impling. Right on, maaan.", + "respawn_delay": "9999" + }, + { + "id": "6062", + "name": "Magpie Impling Puro", + "examine": "Ooh, shiny things!", + "respawn_delay": "9999" + }, + { + "id": "6063", + "name": "Ninja Impling Puro", + "examine": "A very stealthy impling.", + "respawn_delay": "9999" + }, + { + "id": "6064", + "name": "Dragon impling", + "examine": "An impling who likes the word \"dragon.\"", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6065", + "name": "Puro Spawn (Low)", + "respawn_delay": "1" + }, + { + "id": "6066", + "name": "Puro Spawn (Mid)", + "respawn_delay": "1" + }, + { + "id": "6067", + "name": "Puro Spawn (High)", + "respawn_delay": "1" + }, + { + "id": "6071", + "name": "Immenizz" + }, + { + "id": "6072", + "name": "Fairy Aeryka", + "examine": "She tends the wheat.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6073", + "name": "Wandering impling", + "examine": "He looks a little lost.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6074", + "name": "Imp defender", + "examine": "An angry little imp. Grrr.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6075", + "name": "Cyclops" + }, + { + "id": "6078", + "name": "Cyclops", + "examine": "A one-eyed man eater.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "35", + "lifepoints": "75", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "75", + "strength_level": "65", + "weakness": "8" + }, + { + "id": "6079", + "name": "Cyclops", + "examine": "A one-eyed woman eater.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "65", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "100", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "6080", + "name": "Cyclops", + "examine": "A one-eyed man eater.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "35", + "lifepoints": "75", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "75", + "strength_level": "65", + "weakness": "8" + }, + { + "id": "6081", + "name": "Cyclops", + "examine": "A one-eyed woman eater.", + "aggressive": "true", + "attack_level": "65", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "65", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "100", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "6082", + "name": "Captain Ned" + }, + { + "id": "6085", + "name": "Cabin boy Jenkins" + }, + { + "id": "6086", + "name": "Cabin boy Jenkins" + }, + { + "id": "6087", + "name": "Elvarg" + }, + { + "id": "6088", + "name": "Zombie rat", + "examine": "Overgrown undead vermin.", + "attack_level": "2", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2", + "weakness": "7" + }, + { + "id": "6089", + "name": "Zombie rat", + "examine": "Overgrown undead vermin.", + "attack_level": "2", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2", + "weakness": "7" + }, + { + "id": "6090", + "name": "Zombie rat", + "examine": "Overgrown undead vermin.", + "attack_level": "2", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "2", + "magic_animation": "0", + "melee_animation": "4933", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "2", + "weakness": "7" + }, + { + "id": "6091", + "name": "Skeleton", + "examine": "Ey.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "22", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6092", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "22", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6093", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "9", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "12", + "magic_animation": "0", + "melee_animation": "5487", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "9", + "weakness": "8" + }, + { + "id": "6094", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5542", + "defence_animation": "5541", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5540", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6095", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6096", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6097", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6098", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5532", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6099", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5571", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "6100", + "name": "Zombie", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "1", + "lifepoints": "30", + "melee_animation": "5571", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "6101", + "name": "Lesser demon", + "examine": "Lesser, but still pretty big.", + "aggressive": "true", + "attack_level": "68", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", + "combat_audio": "400,404,403", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "71", + "lifepoints": "79", + "magic_animation": "4630", + "magic_level": "1", + "melee_animation": "4630", + "range_animation": "4630", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "79", + "strength_level": "70", + "weakness": "4" + }, + { + "id": "6102", + "name": "Lost barbarian", + "examine": "His face is expressionless.", + "attack_level": "60", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "7" + }, + { + "id": "6103", + "name": "Skeleton hero", + "examine": "An opponent from the grave. He seems unimpressed by your bone rummaging.", + "aggressive": "true", + "attack_level": "53", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "53", + "lifepoints": "75", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "53", + "weakness": "8" + }, + { + "id": "6104", + "name": "Skeleton brute", + "examine": "An ex-barbarian, willing to make you ex too.", + "aggressive": "true", + "attack_level": "63", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "63", + "lifepoints": "90", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "8" + }, + { + "id": "6105", + "name": "Skeleton warlord", + "examine": "He's heartless.", + "aggressive": "true", + "attack_level": "65", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "92", + "magic_animation": "0", + "melee_animation": "2067", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "8" + }, + { + "id": "6106", + "name": "Skeleton heavy", + "examine": "He's less heavy now.", + "aggressive": "true", + "attack_level": "64", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "64", + "lifepoints": "91", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "8" + }, + { + "id": "6107", + "name": "Skeleton thug", + "examine": "Floats like an anvil, hits like a hammer.", + "aggressive": "true", + "attack_level": "61", + "combat_audio": "774,775,777", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "61", + "lifepoints": "87", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "61", + "weakness": "8" + }, + { + "id": "6108", + "name": "Entrana firebird", + "examine": "Probably not a chicken.", + "attack_level": "1", + "death_animation": "6812", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "0", + "melee_animation": "6811", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "6109", + "name": "Mounted terrorbird gnome", + "examine": "These gnomes know how to get around!", + "attack_level": "38", + "death_animation": "6791", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "6790", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "38", + "weakness": "8" + }, + { + "id": "6110", + "name": "Mounted terrorbird gnome", + "examine": "These gnomes know how to get around!", + "attack_level": "1", + "death_animation": "6791", + "defence_animation": "6792", + "defence_level": "1", + "lifepoints": "55", + "melee_animation": "6790", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6111", + "name": "Mounted terrorbird gnome", + "examine": "These gnomes know how to get around!", + "attack_level": "1", + "death_animation": "6791", + "defence_animation": "6792", + "defence_level": "1", + "lifepoints": "55", + "melee_animation": "6790", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6112", + "name": "Ducklings", + "examine": "Aww, cute.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "water_npc": "true" + }, + { + "id": "6113", + "name": "Duck", + "examine": "Quackers.", + "attack_level": "1", + "death_animation": "750", + "defence_animation": "3268", + "defence_level": "1", + "lifepoints": "3", + "melee_animation": "747", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6114", + "name": "Drake" + }, + { + "id": "6115", + "name": "Seagull", + "examine": "A messy bird.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "309,311,310", + "death_animation": "3468", + "defence_animation": "1014", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "3467", + "melee_animation": "3467", + "range_animation": "3467", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "id": "6116", + "name": "Seagull", + "examine": "A messy bird.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "309,311,310", + "death_animation": "3468", + "defence_animation": "1014", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "3467", + "melee_animation": "3467", + "range_animation": "3467", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6117", + "name": "Mr. Mordaut", + "examine": "Do not upset this teacher. You have been warned!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6118", + "name": "Observatory assistant", + "examine": "He helps the professor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6119", + "name": "Observatory professor", + "examine": "A man, learned in the ways of the stars." + }, + { + "id": "6122", + "name": "Sleeping guard", + "examine": "Sleeping like an ugly baby.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6123", + "name": "Naghead", + "examine": "An old goblin hag.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6124", + "name": "Wagchin", + "examine": "A young goblin 'beauty'.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6125", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6126", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6127", + "name": "Greasycheeks", + "examine": "Big appetite for a small creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6128", + "name": "Smellytoes", + "examine": "Likes drink a little too much.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6129", + "name": "Creakyknees", + "examine": "Lean and green.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6130", + "name": "Clothears" + }, + { + "id": "6131", + "name": "Zombie", + "attack_level": "1", + "combat_audio": "931,923,922", + "defence_level": "1", + "lifepoints": "40", + "range_level": "1", + "safespot": null, + "slayer_exp": "30", + "strength_level": "1" + }, + { + "id": "6132", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6133", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6134", + "name": "Dunce", + "examine": "This imp is clearly the class clown.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6135", + "name": "Town crier", + "examine": "Get useful information from this guy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6136", + "name": "Town crier", + "examine": "Get useful information from this guy." + }, + { + "id": "6137", + "name": "Town crier", + "examine": "Get useful information from this guy." + }, + { + "id": "6138", + "name": "Town crier" + }, + { + "id": "6139", + "name": "Town crier" + }, + { + "id": "6140", + "name": "Squire (Intermediate)", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6141", + "name": "Squire (Veteran)", + "examine": "A squire of balance.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6142", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "50", + "lifepoints": "200", + "magic_level": "50", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6143", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", + "defence_level": "50", + "lifepoints": "200", + "magic_level": "25", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6144", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "50", + "lifepoints": "200", + "magic_level": "50", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6145", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "50", + "lifepoints": "200", + "magic_level": "50", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6146", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "200", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6147", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "200", + "magic_level": "15", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6148", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "200", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6149", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "200", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6150", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "150,150,150,75,75,0,0,0,0,0,0,0,0,0,0", + "defence_level": "70", + "lifepoints": "250", + "magic_level": "70", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6151", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "150,150,150,150,37,0,0,0,0,0,0,0,0,0,0", + "defence_level": "70", + "lifepoints": "250", + "magic_level": "40", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6152", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "150,150,75,150,75,0,0,0,0,0,0,0,0,0,0", + "defence_level": "70", + "lifepoints": "250", + "magic_level": "70", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6153", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "75,75,150,150,75,0,0,0,0,0,0,0,0,0,0", + "defence_level": "70", + "lifepoints": "250", + "magic_level": "70", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6154", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6155", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "15", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6156", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6157", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6158", + "name": "Sir Lucan" + }, + { + "id": "6159", + "name": "Workman", + "examine": "It is cold and solid. Thok will strap it to Marmaros's leg.", + "aggressive": "true", + "attack_level": "50", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "114", + "magic_animation": "0", + "magic_level": "50", + "range_animation": "0", + "range_level": "50", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "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": "6169", + "name": "Squire", + "examine": "Squire to the Knights of the Round Table.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6170", + "name": "Sir Lancelot", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "422", + "defence_level": "1", + "lifepoints": "118", + "melee_animation": "400", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6183", + "name": "Guard", + "examine": "An officer of the law.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6184", + "name": "Guard", + "examine": "An officer of the law.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6185", + "name": "Prosecutor" + }, + { + "id": "6186", + "name": "Morgan Le Faye" + }, + { + "id": "6188", + "name": "Renegade knight", + "examine": "He isn't very friendly.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "8" + }, + { + "id": "6189", + "name": "Black Knight", + "examine": "A dark-hearted knight.", + "aggressive": "true", + "attack_level": "25", + "attack_speed": "5", + "bonuses": "18,18,18,0,0,73,76,70,-11,72,0,16,0,0,0", + "combat_audio": "2503,15,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "25", + "weakness": "8" + }, + { + "id": "6190", + "name": "Guard", + "examine": "Primping with combs and hair clips.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6191", + "name": "Sinclair" + }, + { + "id": "6198", + "name": "Sinclair", + "examine": "The name isn't just for show.", + "aggressive": "true", + "attack_level": "55", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "85", + "magic_animation": "0", + "magic_level": "55", + "range_animation": "0", + "range_level": "55", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6199", + "name": "Banker" + }, + { + "id": "6200", + "name": "Banker", + "examine": "He can look after my money.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6201", + "name": "Knight", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6202", + "name": "K'ril Tsutsaroth" + }, + { + "id": "6203", + "name": "K'ril Tsutsaroth", + "examine": "A servant of the god Zamorak. ", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "340", + "attack_speed": "6", + "bonuses": "160,160,160,0,0,80,80,80,130,80,0,31,0,0,0", + "clue_level": "2", + "death_animation": "6946", + "defence_animation": "6944", + "defence_level": "270", + "lifepoints": "255", + "magic_animation": "6945", + "magic_level": "200", + "melee_animation": "6945", + "poison_amount": "", + "poison_immune": "true", + "poisonous": "", + "range_animation": "6945", + "range_level": "1", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "350", + "strength_level": "300", + "weakness": "9" + }, + { + "id": "6204", + "name": "Tstanon Karlak", + "examine": "Destroyer of 1000 planes!", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "124", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,-5,0,0,14,0,0,0", + "death_animation": "6946", + "defence_animation": "6944", + "defence_level": "125", + "lifepoints": "142", + "magic_animation": "6945", + "magic_level": "50", + "melee_animation": "6945", + "range_animation": "6945", + "range_level": "50", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "142", + "strength_level": "118", + "weakness": "9" + }, + { + "id": "6205", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6206", + "name": "Zakl'n Gritch", + "examine": "Scourge of light.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "83", + "attack_speed": "5", + "bonuses": "0,0,0,0,20,0,0,0,-5,0,0,0,0,0,20", + "combat_style": "1", + "death_animation": "6946", + "defence_animation": "6945", + "defence_level": "127", + "lifepoints": "150", + "magic_animation": "7033", + "magic_level": "50", + "melee_animation": "7033", + "projectile": "1209", + "range_animation": "7033", + "range_level": "150", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "150", + "start_gfx": "1208", + "strength_level": "76", + "weakness": "0" + }, + { + "id": "6207", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6208", + "name": "Balfrug Kreeyath", + "examine": "Despoiler of Ullek.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "115", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,10,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "6946", + "defence_animation": "6944", + "defence_level": "153", + "lifepoints": "161", + "magic_animation": "7033", + "magic_level": "150", + "melee_animation": "7033", + "projectile": "1213", + "range_animation": "7033", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "161", + "start_gfx": "1212", + "strength_level": "60" + }, + { + "id": "6209", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6210", + "name": "Hellhound", + "examine": "From the maws of hell.", + "aggressive": "true", + "attack_level": "105", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "3717,3719,3718", + "death_animation": "6576", + "defence_animation": "6578", + "defence_level": "102", + "lifepoints": "116", + "magic_animation": "6579", + "melee_animation": "6579", + "range_animation": "6579", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "116", + "strength_level": "104", + "weakness": "7" + }, + { + "id": "6211", + "name": "Imp", + "examine": "Have you checked your pockets lately?", + "aggressive": "true", + "attack_level": "4", + "bonuses": "10,5,5,10,10,10,10,10,10,0,0,0,0,0,0", + "combat_audio": "534,536,535", + "death_animation": "172", + "defence_animation": "170", + "defence_level": "4", + "lifepoints": "10", + "magic_animation": "169", + "melee_animation": "169", + "movement_radius": "25", + "range_animation": "169", + "range_level": "1", + "safespot": null, + "strength_level": "4", + "weakness": "7" + }, + { + "id": "6212", + "name": "Werewolf", + "examine": "He doesn't look pleased to see me.", + "aggressive": "true", + "attack_level": "64", + "bonuses": "50,40,45,60,70,70,70,40,40,50,0,0,0,0,0", + "clue_level": "1", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "64", + "lifepoints": "100", + "magic_animation": "6536", + "melee_animation": "6536", + "range_animation": "6536", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "64", + "weakness": "7" + }, + { + "id": "6213", + "name": "Werewolf", + "examine": "He doesn't look pleased to see me.", + "aggressive": "true", + "attack_level": "64", + "bonuses": "50,40,45,60,70,70,70,40,40,50,0,0,0,0,0", + "clue_level": "1", + "death_animation": "6537", + "defence_animation": "6538", + "defence_level": "64", + "lifepoints": "100", + "magic_animation": "6536", + "melee_animation": "6536", + "range_animation": "6536", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "64", + "weakness": "7" + }, + { + "id": "6214", + "name": "Vampire", + "examine": "It looks really hungry!", + "aggressive": "true", + "attack_level": "44", + "bonuses": "30,35,30,40,50,50,50,25,30,30,0,0,0,0,0", + "clue_level": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "44", + "lifepoints": "60", + "magic_animation": "810", + "melee_animation": "810", + "range_animation": "810", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "44" + }, + { + "id": "6215", + "name": "Bloodveld", + "examine": "The tongue of evil.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "4", + "clue_level": "2", + "combat_audio": "312,314,313", + "death_animation": "9131", + "defence_animation": "9132", + "defence_level": "30", + "lifepoints": "120", + "magic_animation": "1552", + "melee_animation": "9130", + "range_animation": "1552", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "120", + "strength_level": "45" + }, + { + "id": "6216", + "name": "Pyrefiend", + "examine": "A small fire demon.", + "aggressive": "true", + "attack_level": "25", + "bonuses": "25,25,20,40,40,40,15,30,20,60,20,0,0,0,0", + "clue_level": "1", + "combat_audio": "696,698,697", + "death_animation": "1580", + "defence_animation": "1581", + "defence_level": "25", + "lifepoints": "48", + "magic_animation": "1582", + "melee_animation": "1582", + "range_animation": "1582", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "25", + "weakness": "7" + }, + { + "id": "6217", + "name": "Icefiend", + "examine": "A small ice demon.", + "attack_level": "5", + "bonuses": "5,5,5,5,10,10,10,10,10,10,5,0,0,0,0", + "clue_level": "0", + "combat_audio": "531,533,532", + "death_animation": "8078", + "defence_animation": "8079", + "defence_level": "5", + "lifepoints": "15", + "magic_animation": "8080", + "melee_animation": "8080", + "range_animation": "8080", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "6218", + "name": "Gorak", + "examine": "Those horns look pretty sharp...", + "aggressive": "true", + "attack_level": "70", + "bonuses": "30,30,50,70,40,70,70,50,60,55,0,0,0,0,0", + "death_animation": "4302", + "defence_animation": "4301", + "defence_level": "70", + "lifepoints": "112", + "magic_animation": "4300", + "melee_animation": "4300", + "range_animation": "4300", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "112", + "strength_level": "70", + "weakness": "9" + }, + { + "id": "6219", + "name": "Spiritual warrior", + "examine": "Warrior of Zamorak.", + "aggressive": "true", + "attack_level": "66", + "attack_speed": "5", + "bonuses": "40,60,60,60,60,20,50,55,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "9055", + "defence_animation": "1156", + "defence_level": "66", + "lifepoints": "102", + "magic_animation": "390", + "melee_animation": "390", + "range_animation": "390", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "66", + "weakness": "6" + }, + { + "id": "6220", + "name": "Spiritual ranger", + "examine": "A ranger spirit dedicated to Zamorak.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "50,70,50,50,50,70,50,85,85,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "70", + "lifepoints": "120", + "magic_animation": "426", + "melee_animation": "426", + "projectile": "9", + "range_animation": "426", + "range_level": "70", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "18", + "start_height": "96", + "strength_level": "1", + "weakness": "1" + }, + { + "id": "6221", + "name": "Spiritual mage", + "examine": "A deadly servant of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "2", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "61", + "end_gfx": "78", + "end_height": "0", + "lifepoints": "75", + "magic_animation": "811", + "magic_level": "180", + "melee_animation": "811", + "projectile": "78", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "3" + }, + { + "id": "6222", + "name": "Kree'arra", + "examine": "Graceful avatar of Armadyl.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "300", + "attack_speed": "3", + "bonuses": "136,136,136,0,120,180,180,180,200,200,0,12,0,0,0", + "clue_level": "2", + "death_animation": "6975", + "defence_animation": "6974", + "defence_level": "260", + "lifepoints": "255", + "magic_animation": "6977", + "magic_level": "200", + "melee_animation": "6977", + "poison_immune": "true", + "range_animation": "6977", + "range_level": "380", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "357", + "strength_level": "200", + "weakness": "10" + }, + { + "id": "6223", + "name": "Wingman Skree", + "examine": "Graceful, bird-like creature.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "45,45,45,0,0,0,0,0,0,0,0,25,0,0,0", + "combat_style": "2", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "160", + "lifepoints": "121", + "magic_animation": "6952", + "magic_level": "150", + "melee_animation": "6952", + "poison_immune": "false", + "prj_height": "92", + "projectile": "1199", + "range_animation": "6952", + "range_level": "100", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "124", + "strength_level": "50", + "weakness": "3" + }, + { + "id": "6224", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6225", + "name": "Flockleader Geerin", + "examine": "Graceful, bird-like creature.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "0,0,0,0,60,0,0,0,0,0,0,0,0,0,0", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "175", + "lifepoints": "132", + "magic_animation": "6953", + "magic_level": "50", + "melee_animation": "6953", + "poison_immune": "false", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "150", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "133", + "strength_level": "80", + "weakness": "4" + }, + { + "id": "6226", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6227", + "name": "Flight Kilisa", + "examine": "Graceful, bird-like creature.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "124", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,14,0,0,0", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "175", + "lifepoints": "133", + "magic_animation": "6954", + "magic_level": "50", + "melee_animation": "6954", + "range_animation": "6954", + "range_level": "169", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "133", + "strength_level": "118", + "weakness": "6" + }, + { + "id": "6228", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6229", + "name": "Spiritual warrior", + "examine": "A servant of Armadyl.", + "aggressive": "true", + "attack_level": "70", + "bonuses": "40,40,50,50,20,50,50,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "70", + "lifepoints": "98", + "magic_animation": "6954", + "melee_animation": "6954", + "range_animation": "6954", + "range_level": "68", + "respawn_delay": "25", + "safespot": null, + "strength_level": "70", + "weakness": "6" + }, + { + "id": "6230", + "name": "Spiritual ranger", + "examine": "Armadyl's favourite servant.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "70", + "lifepoints": "89", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "70", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "6231", + "name": "Spiritual mage", + "examine": "A mage who serves Armadyl above all else - even death.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,9,12,5,45,28,0,0,0,0,0", + "clue_level": "2", + "combat_style": "2", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "111", + "lifepoints": "75", + "magic_animation": "6952", + "magic_level": "150", + "melee_animation": "6952", + "prj_height": "92", + "projectile": "1199", + "range_animation": "6952", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "6232", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "40", + "lifepoints": "70", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1191", + "range_animation": "6953", + "range_level": "40", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6233", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "45", + "lifepoints": "83", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "45", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6234", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "50", + "lifepoints": "86", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "50", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6235", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "50", + "lifepoints": "86", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "50", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6236", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "55", + "lifepoints": "95", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "55", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6237", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "55", + "lifepoints": "98", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1191", + "range_animation": "6953", + "range_level": "55", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6238", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "65", + "lifepoints": "124", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "65", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6239", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "65", + "lifepoints": "139", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1191", + "range_animation": "6953", + "range_level": "65", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6240", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "40", + "lifepoints": "63", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1191", + "range_animation": "6953", + "range_level": "40", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6241", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "40", + "lifepoints": "65", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "40", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6242", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "40", + "lifepoints": "83", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "40", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6243", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "50", + "lifepoints": "69", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "50", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6244", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "55", + "lifepoints": "75", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "55", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6245", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "55", + "lifepoints": "98", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1190", + "range_animation": "6953", + "range_level": "55", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6246", + "name": "Aviansie", + "examine": "Graceful, bird-like creature.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "60,50,60,50,65,65,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "6956", + "defence_animation": "6955", + "defence_level": "65", + "lifepoints": "73", + "magic_animation": "6953", + "melee_animation": "6953", + "prj_height": "92", + "projectile": "1191", + "range_animation": "6953", + "range_level": "65", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6247", + "name": "Commander Zilyana", + "examine": "Commander of Saradomin's forces. ", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "280", + "attack_speed": "2", + "bonuses": "195,195,195,200,0,100,100,100,100,100,0,20,0,0,0", + "clue_level": "2", + "death_animation": "6965", + "defence_animation": "6966", + "defence_level": "300", + "lifepoints": "255", + "magic_animation": "6964", + "magic_level": "300", + "melee_animation": "6964", + "poison_immune": "true", + "range_animation": "6964", + "range_level": "250", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "360", + "strength_level": "196", + "weakness": "7" + }, + { + "id": "6248", + "name": "Starlight", + "examine": "The bane of darkness.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "120", + "attack_speed": "5", + "bonuses": "60,60,60,0,0,12,14,13,5,13,0,10,0,0,0", + "combat_audio": "3829,3866,3849", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "120", + "lifepoints": "160", + "magic_animation": "6376", + "magic_level": "125", + "melee_animation": "6376", + "poison_immune": "false", + "range_animation": "6376", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "125", + "weakness": "7" + }, + { + "id": "6249", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6250", + "name": "Growler", + "examine": "Defender of the faithful.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "100", + "attack_speed": "5", + "bonuses": "10,10,10,0,0,12,14,14,18,5,0,7,0,0,0", + "combat_audio": "3830,3869,3867", + "combat_style": "2", + "death_animation": "7016", + "defence_animation": "7017", + "defence_level": "120", + "end_gfx": "1186", + "lifepoints": "146", + "magic_animation": "7019", + "magic_level": "150", + "melee_animation": "7019", + "prj_height": "0", + "projectile": "1185", + "range_animation": "7019", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "start_gfx": "1184", + "strength_level": "101" + }, + { + "id": "6251", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6252", + "name": "Bree", + "examine": "Champion of Truth.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "110", + "attack_speed": "5", + "bonuses": "10,10,10,0,0,12,14,14,18,5,0,7,0,0,0", + "combat_style": "1", + "death_animation": "7011", + "defence_animation": "7010", + "defence_level": "130", + "end_gfx": "24", + "lifepoints": "162", + "magic_animation": "7009", + "magic_level": "80", + "melee_animation": "7009", + "prj_height": "50", + "projectile": "1188", + "range_animation": "7009", + "range_level": "150", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "80", + "weakness": "0" + }, + { + "id": "6253", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6254", + "name": "Saradomin priest", + "examine": "A holy man wearing ancient clothing.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "40,40,40,40,20,50,40,55,0,0,0,0,0,0,0", + "combat_style": "2", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "60", + "end_gfx": "76", + "lifepoints": "89", + "magic_animation": "811", + "magic_level": "60", + "melee_animation": "811", + "poisonous": "", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "3" + }, + { + "id": "6255", + "name": "Spiritual warrior", + "examine": "Warrior of Saradomin.", + "aggressive": "true", + "attack_level": "66", + "attack_speed": "5", + "bonuses": "40,60,60,60,60,20,50,45,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "9055", + "defence_animation": "1156", + "defence_level": "66", + "lifepoints": "100", + "magic_animation": "390", + "melee_animation": "390", + "range_animation": "390", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "66", + "weakness": "8" + }, + { + "id": "6256", + "name": "Spiritual ranger", + "examine": "A ranger spirit dedicated to Saradomin.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "50,70,50,50,50,70,50,65,65,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "70", + "lifepoints": "106", + "magic_animation": "426", + "melee_animation": "426", + "projectile": "9", + "range_animation": "426", + "range_level": "70", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "18", + "start_height": "96", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "6257", + "name": "Spiritual mage", + "examine": "Saradomin's holy mage.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,8,7,3,16,2,0,0,0,0,0", + "clue_level": "2", + "combat_style": "2", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "86", + "end_gfx": "76", + "end_height": "0", + "lifepoints": "85", + "magic_animation": "811", + "magic_level": "160", + "melee_animation": "811", + "projectile": "76", + "range_animation": "811", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "6258", + "name": "Knight of Saradomin", + "examine": "A valiant knight.", + "aggressive": "true", + "attack_level": "50", + "bonuses": "40,40,40,40,50,20,40,50,0,0,0,0,0,0,0", + "death_animation": "9055", + "defence_animation": "410", + "defence_level": "50", + "lifepoints": "135", + "magic_animation": "407", + "melee_animation": "407", + "range_animation": "407", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "50", + "weakness": "7" + }, + { + "id": "6259", + "name": "Knight of Saradomin", + "examine": "A valiant knight.", + "aggressive": "true", + "attack_level": "50", + "bonuses": "40,40,40,40,50,20,40,65,0,0,0,0,0,0,0", + "death_animation": "9055", + "defence_animation": "424", + "defence_level": "50", + "lifepoints": "108", + "magic_animation": "401", + "melee_animation": "401", + "range_animation": "401", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "50", + "weakness": "7" + }, + { + "id": "6260", + "name": "General Graardor", + "examine": "A huge war chief.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "280", + "attack_speed": "6", + "bonuses": "120,120,120,0,100,90,90,90,65,90,0,43,0,0,0", + "clue_level": "2", + "death_animation": "7062", + "defence_animation": "7061", + "defence_level": "250", + "lifepoints": "255", + "magic_animation": "7060", + "magic_level": "280", + "melee_animation": "7060", + "poison_immune": "true", + "range_animation": "7060", + "range_level": "350", + "respawn_delay": "150", + "safespot": null, + "slayer_exp": "0", + "strength_level": "350", + "weakness": "10" + }, + { + "id": "6261", + "name": "Sergeant Strongstack", + "examine": "A battle-honed goblin. ", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "124", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,14,0,0,0", + "death_animation": "6156", + "defence_animation": "6155", + "defence_level": "126", + "lifepoints": "128", + "magic_animation": "6154", + "magic_level": "50", + "melee_animation": "6154", + "range_animation": "6154", + "range_level": "50", + "respawn_delay": "50", + "safespot": null, + "strength_level": "118", + "weakness": "8" + }, + { + "id": "6262", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6263", + "name": "Sergeant Steelwill", + "examine": "A battle-honed goblin. ", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,6,0,0,0", + "combat_style": "2", + "death_animation": "6156", + "defence_animation": "6155", + "defence_level": "150", + "lifepoints": "127", + "magic_animation": "6154", + "magic_level": "150", + "melee_animation": "6154", + "projectile": "1203", + "range_animation": "6154", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "start_gfx": "1202", + "strength_level": "50", + "weakness": "3" + }, + { + "id": "6264", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6265", + "name": "Sergeant Grimspike", + "examine": "A battle-honed goblin. ", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "80", + "attack_speed": "5", + "bonuses": "0,0,0,0,20,0,0,0,0,0,0,0,0,0,20", + "combat_style": "1", + "death_animation": "6156", + "defence_animation": "6155", + "defence_level": "132", + "lifepoints": "146", + "magic_animation": "6154", + "magic_level": "50", + "melee_animation": "6154", + "projectile": "1206", + "range_animation": "6154", + "range_level": "150", + "respawn_delay": "50", + "safespot": null, + "strength_level": "80", + "weakness": "2" + }, + { + "id": "6266", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6267", + "name": "Fishing spot", + "examine": "Big, ugly, and smelly.", + "aggressive": "true", + "attack_level": "27", + "attack_speed": "6", + "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", + "death_animation": "361", + "defence_animation": "360", + "defence_level": "27", + "lifepoints": "60", + "magic_animation": "359", + "magic_level": "27", + "melee_animation": "359", + "range_animation": "359", + "range_level": "27", + "respawn_delay": "25", + "safespot": null, + "strength_level": "27" + }, + { + "id": "6268", + "name": "Jogre", + "examine": "Green and aggressive.", + "aggressive": "true", + "attack_level": "62", + "attack_speed": "6", + "bonuses": "10,20,20,20,20,20,10,20,0,0,0,0,0,0,0", + "clue_level": "1", + "death_animation": "2938", + "defence_animation": "2937", + "defence_level": "62", + "lifepoints": "70", + "magic_animation": "2936", + "melee_animation": "2936", + "range_animation": "2936", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "62", + "weakness": "8" + }, + { + "id": "6269", + "name": "Cyclops", + "examine": "A one-eyed man-eater.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "22,22,22,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "48", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "110", + "strength_level": "70", + "weakness": "8" + }, + { + "id": "6270", + "name": "Cyclops", + "examine": "A one-eyed man-eater.", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "4", + "bonuses": "22,22,22,0,0,0,0,0,0,0,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4653", + "defence_animation": "4651", + "defence_level": "48", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4652", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "110", + "strength_level": "70", + "weakness": "8" + }, + { + "id": "6271", + "name": "Ork", + "examine": "Ugly, fierce and with a bad attitude.", + "aggressive": "true", + "attack_level": "68", + "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "68", + "lifepoints": "110", + "magic_animation": "4320", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "68", + "weakness": "8" + }, + { + "id": "6272", + "name": "Ork", + "examine": "Ugly, fierce and with a bad attitude.", + "aggressive": "true", + "attack_level": "68", + "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "68", + "lifepoints": "110", + "magic_animation": "4320", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "68" + }, + { + "id": "6273", + "name": "Ork", + "examine": "Ugly, fierce and with a bad attitude.", + "aggressive": "true", + "attack_level": "68", + "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "68", + "lifepoints": "110", + "magic_animation": "4320", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "68" + }, + { + "id": "6274", + "name": "Ork", + "examine": "Ugly, fierce and with a bad attitude.", + "aggressive": "true", + "attack_level": "68", + "bonuses": "40,20,30,45,45,45,45,20,45,35,0,0,0,0,0", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "68", + "lifepoints": "110", + "magic_animation": "4320", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "68" + }, + { + "id": "6275", + "name": "Hobgoblin", + "examine": "An ugly, smelly creature.", + "aggressive": "true", + "attack_level": "40", + "bonuses": "20,30,30,30,30,30,10,30,0,0,0,0,0,0,0", + "combat_audio": "469,472,471", + "death_animation": "167", + "defence_animation": "165", + "defence_level": "40", + "lifepoints": "52", + "magic_animation": "164", + "melee_animation": "164", + "range_animation": "164", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "40", + "weakness": "8" + }, + { + "id": "6276", + "name": "Spiritual ranger", + "examine": "The spirit of a ranger, serving bandos beyond death.", + "aggressive": "true", + "attack_level": "1", + "bonuses": "30,50,30,30,50,60,50,40,40,0,0,0,0,0,0", + "clue_level": "2", + "combat_style": "1", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "70", + "lifepoints": "130", + "magic_animation": "4320", + "melee_animation": "4320", + "projectile": "1195", + "range_animation": "4320", + "range_level": "70", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "95", + "strength_level": "1", + "weakness": "0" + }, + { + "id": "6277", + "name": "Spiritual warrior", + "examine": "A true servant of Bandos.", + "aggressive": "true", + "attack_level": "70", + "bonuses": "50,40,45,60,60,60,60,20,50,60,0,0,0,0,0", + "clue_level": "2", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "70", + "lifepoints": "131", + "magic_animation": "4320", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "70", + "weakness": "8" + }, + { + "id": "6278", + "name": "Spiritual mage", + "examine": "One of Bandos' chosen.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "0,0,0,0,0,12,14,13,35,13,0,0,0,0,0", + "clue_level": "2", + "combat_style": "2", + "death_animation": "4321", + "defence_animation": "4322", + "defence_level": "103", + "lifepoints": "106", + "magic_animation": "4320", + "magic_level": "142", + "melee_animation": "4320", + "range_animation": "4320", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "1", + "weakness": "4" + }, + { + "id": "6279", + "name": "Goblin", + "examine": "An ancient goblin.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "5", + "lifepoints": "20", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "6280", + "name": "Goblin", + "examine": "A warrior for Bandos.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "5", + "lifepoints": "15", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "6281", + "name": "Goblin", + "examine": "A soldier to the death.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "5,10,10,10,10,10,5,10,5,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6190", + "defence_animation": "6189", + "defence_level": "5", + "lifepoints": "15", + "magic_animation": "6188", + "melee_animation": "6188", + "range_animation": "6188", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "6282", + "name": "Goblin", + "examine": "A soldier to the death.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "5,10,10,10,10,10,5,10,5,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6190", + "defence_animation": "6189", + "defence_level": "5", + "lifepoints": "18", + "magic_animation": "6188", + "melee_animation": "6188", + "range_animation": "6188", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "5" + }, + { + "id": "6283", + "name": "Goblin", + "examine": "A warrior for Bandos.", + "aggressive": "true", + "attack_level": "5", + "bonuses": "10,20,20,20,20,5,20,10,0,0,0,0,0,0,0", + "clue_level": "0", + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "5", + "lifepoints": "16", + "magic_animation": "6185", + "melee_animation": "6185", + "range_animation": "6185", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "16", + "strength_level": "5" + }, + { + "id": "6284", + "name": "Warped terrorbird" + }, + { + "id": "6285", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6286", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6287", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6288", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6289", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6290", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6291", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6292", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "168", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6293", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6294", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "308", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6295", + "name": "Warped terrorbird", + "examine": "You don't think you can harm this terrorbird.", + "aggressive": "true", + "attack_level": "59", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "0", + "defence_level": "59", + "lifepoints": "308", + "magic_animation": "0", + "magic_level": "59", + "melee_animation": "7108", + "projectile": "1468", + "range_animation": "0", + "range_level": "59", + "respawn_delay": "60", + "safespot": null, + "strength_level": "59", + "weakness": "1" + }, + { + "id": "6296", + "name": "Warped tortoise", + "examine": "You don't think you can harm this creature.", + "attack_level": "39", + "attack_speed": "5", + "death_animation": "7091", + "defence_animation": "0", + "defence_level": "52", + "lifepoints": "148", + "magic_animation": "0", + "magic_level": "52", + "melee_animation": "7093", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "39", + "weakness": "4" + }, + { + "id": "6297", + "name": "Warped tortoise", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7091", + "defence_animation": "7092", + "defence_level": "1", + "lifepoints": "87", + "melee_animation": "7093", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6306", + "name": "Bolrie", + "examine": "Annoyingly easy to squish.", + "aggressive": "true", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "2", + "lifepoints": "0", + "magic_animation": "0", + "magic_level": "2", + "melee_animation": "0", + "range_animation": "0", + "range_level": "2", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6314", + "name": "Guard no. 21", + "examine": "She looks bored.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6315", + "name": "Guard no. 72", + "examine": "He looks even more bored.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6322", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6323", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6324", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6325", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6326", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6327", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6328", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6329", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6330", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6331", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6332", + "name": "Warped terrorbird", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7109", + "defence_animation": "7110", + "defence_level": "1", + "lifepoints": "154", + "melee_animation": "7108", + "projectile": "1468", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6333", + "name": "Child" + }, + { + "id": "6334", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6335", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6336", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6337", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6338", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6339", + "name": "Child", + "examine": "A child of West Ardougne.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6340", + "name": "Child" + }, + { + "id": "6341", + "name": "Child" + }, + { + "id": "6342", + "name": "Child" + }, + { + "id": "6343", + "name": "Child" + }, + { + "id": "6344", + "name": "Child", + "examine": "Duck!", + "aggressive": "true", + "attack_level": "48", + "attack_speed": "5", + "combat_style": "1", + "death_animation": "3922", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "92", + "magic_animation": "0", + "melee_animation": "3920", + "range_animation": "3920", + "range_level": "65", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "1" + }, + { + "id": "6345", + "name": "Child" + }, + { + "id": "6346", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6347", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6348", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6349", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6350", + "name": "Pirate", + "examine": "This one's had too much to drink!", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6351", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6352", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6353", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6354", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6355", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6356", + "name": "Pirate", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "23", + "melee_animation": "390", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6357", + "name": "Street urchin" + }, + { + "id": "6358", + "name": "Street urchin", + "examine": "The mother of all pests!", + "aggressive": "true", + "attack_level": "70", + "attack_speed": "6", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "1428", + "magic_animation": "0", + "magic_level": "70", + "range_level": "70", + "respawn_delay": "60", + "safespot": null, + "strength_level": "70", + "weakness": "10" + }, + { + "id": "6359", + "name": "Street urchin" + }, + { + "id": "6360", + "name": "Street urchin" + }, + { + "id": "6361", + "name": "Street urchin" + }, + { + "id": "6362", + "name": "Eniola" + }, + { + "id": "6363", + "name": "Zamorak warrior", + "examine": "A warrior of Zamorak.", + "aggressive": "true", + "attack_level": "44", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "44", + "lifepoints": "62", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "44", + "weakness": "7" + }, + { + "id": "6364", + "name": "Zamorak warrior", + "examine": "A warrior of Zamorak.", + "aggressive": "true", + "attack_level": "47", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" + }, + { + "id": "6365", + "name": "Zamorak ranger", + "examine": "A ranger of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", + "range_level": "46", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "6366", + "name": "Zamorak ranger", + "examine": "A ranger of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "melee_animation": "426", + "range_animation": "426", + "range_level": "46", + "safespot": null, + "strength_level": "1", + "weakness": "1" + }, + { + "id": "6367", + "name": "Zamorak mage", + "examine": "A mage of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "49", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "6368", + "name": "Zamorak mage", + "examine": "A mage of Zamorak.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "49", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "6369", + "name": "Cave lizard", + "examine": "A cave dweller.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7205", + "defence_animation": "0", + "defence_level": "47", + "lifepoints": "67", + "magic_animation": "0", + "melee_animation": "7207", + "range_animation": "0", + "range_level": "47", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "0" + }, + { + "id": "6370", + "name": "Mage of Zamorak", + "examine": "A representative of the Z.M.I.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6371", + "name": "Zamorak crafter", + "examine": "A Z.M.I. runecrafter.", + "attack_level": "19", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "19", + "lifepoints": "27", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "19", + "weakness": "7" + }, + { + "id": "6372", + "name": "Zamorak crafter", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6373", + "name": "Kennith" + }, + { + "id": "6374", + "name": "Guard dog", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "49", + "strength_level": "1" + }, + { + "id": "6375", + "name": "Wizard Cromperty" + }, + { + "id": "6376", + "name": "Jungle spider", + "examine": "A barely visible deadly spider.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5327", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6377", + "name": "Jungle spider", + "examine": "A barely visible deadly spider.", + "aggressive": "true", + "attack_level": "1", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "1", + "lifepoints": "25", + "melee_animation": "5327", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6378", + "name": "Tenacious toucan", + "examine": "I wouldn't want to be footing that bill.", + "attack_level": "60", + "attack_speed": "5", + "death_animation": "6779", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "6775", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "6379", + "name": "Giant ant worker", + "examine": "I wonder how much he can lift.", + "attack_level": "35", + "death_animation": "7238", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "50", + "magic_animation": "0", + "melee_animation": "7237", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "35", + "weakness": "9" + }, + { + "id": "6380", + "name": "Giant ant soldier", + "examine": "Gi-ant-ant-ant-ant...", + "aggressive": "true", + "attack_level": "50", + "death_animation": "7238", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "71", + "magic_animation": "0", + "melee_animation": "7237", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" + }, + { + "id": "6381", + "name": "Giant wasp", + "examine": "That stinger has to hurt...", + "aggressive": "true", + "attack_level": "60", + "attack_speed": "5", + "death_animation": "7244", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "85", + "magic_animation": "0", + "melee_animation": "7242", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" + }, + { + "id": "6382", + "name": "Pernicious parrot", + "examine": "Polynomial - a parrot that goes without breakfast", + "attack_level": "45", + "attack_speed": "5", + "death_animation": "6777", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "6775", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "9" + }, + { + "id": "6383", + "name": "Giant wasp", + "examine": "That stinger has to hurt...", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "7244", + "defence_animation": "7243", + "defence_level": "1", + "lifepoints": "37", + "melee_animation": "7242", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6384", + "name": "Karamjan Jungle Eagle" + }, + { + "id": "6385", + "name": "Karamjan Jungle eagle", + "examine": "A very large", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6386", + "name": "Bandit" + }, + { + "id": "6389", + "name": "Gargoyle", + "examine": "Flies like a rock.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,20,20,0,20,20,0,0,0,0,0", + "clue_level": "2", + "combat_audio": "428,430,429", + "death_animation": "1518", + "defence_animation": "9455", + "defence_level": "107", + "lifepoints": "105", + "magic_animation": "1516", + "magic_level": "1", + "melee_animation": "9454", + "range_animation": "1516", + "range_level": "1", + "safespot": null, + "slayer_exp": "105", + "strength_level": "95" + }, + { + "id": "6390", + "name": "Grim Reaper", + "examine": "I wish I could tell where he was looking. What could be beneath that hood?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6402", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6403", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6404", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6405", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6406", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6407", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6408", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6409", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6410", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6411", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6412", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6413", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6414", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6415", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6416", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6417", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6418", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6419", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6420", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6421", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6422", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6423", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6424", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6425", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6426", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6427", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6428", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6429", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6430", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6431", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6432", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6433", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6434", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6435", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6436", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6437", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6438", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6439", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6440", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6441", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6442", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6443", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6444", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6445", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6446", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6447", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6448", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6449", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6450", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6451", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6452", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6453", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6454", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6455", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6456", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6457", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6458", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6459", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6460", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6461", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6462", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6463", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6464", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6465", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6466", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6467", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6468", + "name": "Skoblin" + }, + { + "id": "6469", + "name": "Snothead", + "examine": "A goblin high priest", + "aggressive": "true", + "attack_level": "14", + "death_animation": "6182", + "defence_animation": "0", + "defence_level": "14", + "lifepoints": "20", + "magic_animation": "0", + "melee_animation": "7317", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "14", + "weakness": "8" + }, + { + "id": "6470", + "name": "Snailfeet", + "examine": "A goblin high priest", + "aggressive": "true", + "attack_level": "13", + "death_animation": "6182", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "7317", + "range_animation": "0", + "range_level": "18", + "respawn_delay": "60", + "safespot": null, + "strength_level": "13", + "weakness": "2" + }, + { + "id": "6471", + "name": "Mosschin", + "examine": "A goblin high priest", + "aggressive": "true", + "attack_level": "16", + "death_animation": "6182", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "magic_level": "22", + "melee_animation": "7317", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "16", + "weakness": "5" + }, + { + "id": "6472", + "name": "Redeyes", + "examine": "A goblin high priest", + "aggressive": "true", + "attack_level": "19", + "death_animation": "6182", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "magic_level": "26", + "melee_animation": "7317", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "19", + "weakness": "5" + }, + { + "id": "6473", + "name": "Strongbones", + "examine": "A goblin high priest", + "aggressive": "true", + "attack_level": "19", + "death_animation": "6182", + "defence_animation": "0", + "defence_level": "26", + "lifepoints": "37", + "magic_animation": "0", + "magic_level": "26", + "melee_animation": "7317", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "19", + "weakness": "5" + }, + { + "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": "6482", + "name": "Priest", + "examine": "Priest of the Ekeleshuun tribe.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6483", + "name": "Priest" + }, + { + "id": "6484", + "name": "Priest" + }, + { + "id": "6485", + "name": "Priest" + }, + { + "id": "6486", + "name": "Priest" + }, + { + "id": "6487", + "name": "Priest" + }, + { + "id": "6488", + "name": "Preacher", + "examine": "He is expounding the word of the Big High War God.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6489", + "name": "High priest" + }, + { + "id": "6490", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6491", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6492", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6493", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6494", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6495", + "name": "Goblin", + "attack_level": "1", + "clue_level": "0", + "combat_audio": "469,472,471", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6496", + "name": "Goblin guard", + "examine": "He doesn't look very welcoming.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6497", + "name": "Goblin guard", + "examine": "He doesn't look very welcoming.", + "attack_level": "1", + "combat_audio": "469,472,471", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6498", + "name": "Guard", + "examine": "A guard wearing blue armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6499", + "name": "Guard", + "examine": "A guard wearing orange armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6500", + "name": "Guard", + "examine": "A guard wearing black armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6501", + "name": "Guard", + "examine": "A guard wearing white armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6502", + "name": "Guard", + "examine": "A guard wearing purple armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6503", + "name": "Guard", + "examine": "A guard wearing yellow armour.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6504", + "name": "Ghost", + "attack_level": "1", + "combat_audio": "436,439,438", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "25", + "strength_level": "1" + }, + { + "id": "6505", + "name": "Registrar 1" + }, + { + "id": "6507", + "name": "Registrar 2", + "examine": "A very noble spider and attendant to the Queen.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6508", + "name": "Registrar 3", + "examine": "The highest ranking of the Spider Queen's servants.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6509", + "name": "Registrar 4", + "examine": "The head of the Spider Queen's army.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6510", + "name": "Jury", + "examine": "Trying hard to hide his identity.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6511", + "name": "Jury" + }, + { + "id": "6512", + "name": "Spectator", + "examine": "Sir Amik's loyal squire.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6513", + "name": "Spectator" + }, + { + "id": "6514", + "name": "Spectator", + "examine": "A being of ore and minerals.", + "attack_level": "1", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6515", + "name": "Spectator" + }, + { + "id": "6516", + "name": "Spectator", + "examine": "A monkey on a mission.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6517", + "name": "Spectator" + }, + { + "id": "6518", + "name": "Spectator", + "examine": "He works for Doric & Son.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6519", + "name": "Spectator" + }, + { + "id": "6520", + "name": "Head Registrar" + }, + { + "id": "6521", + "name": "Grand Exchange Tutor", + "examine": "Gives an introduction to the Grand Exchange.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6522", + "examine": "Director of the Grand Exchange." + }, + { + "id": "6523", + "name": "Farid Morrisane (ores)", + "examine": "The son of Ali M!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6524", + "name": "Bob Barter (herbs)", + "examine": "He's probably seen better days.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6525", + "name": "Murky Matt (runes)", + "examine": "You can never miss a pirate in disguise.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6526", + "name": "Relobo Blinyo (logs)", + "examine": "A tribal man", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6527", + "name": "Hofuthand (armour and weapons)", + "examine": "He's travelled a long way.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6528", + "examine": "There to help me make my bids." + }, + { + "id": "6529", + "examine": "There to help me make my bids." + }, + { + "id": "6530", + "examine": "There to help me make my bids." + }, + { + "id": "6531", + "examine": "There to help me make my bids." + }, + { + "id": "6532", + "examine": "She can look after my money. Good with money." + }, + { + "id": "6533", + "examine": "She can look after my money. Good with money." + }, + { + "id": "6534", + "examine": "He can look after my money. Good with money." + }, + { + "id": "6535", + "examine": "He can look after my money. Good with money." + }, + { + "id": "6536", + "name": "Registrar 5", + "examine": "Chief architect of the Spider Realm.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6543", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6544", + "name": "", + "attack_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6558", + "name": "null", + "examine": "Tapping his feet and looking very bored.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6560", + "name": "null", + "examine": "A huge war chief. ", + "aggressive": "true", + "attack_level": "250", + "attack_speed": "5", + "bonuses": "300,300,300,300,300,300,150,300,300,250,43,96,43,30,0", + "death_animation": "7062", + "defence_animation": "7061", + "defence_level": "250", + "lifepoints": "254", + "magic_animation": "422", + "melee_animation": "422", + "range_animation": "422", + "range_level": "200", + "respawn_delay": "20", + "safespot": null, + "strength_level": "251" + }, + { + "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": "6604", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6605", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6606", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6607", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6608", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6609", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6610", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6611", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6612", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6613", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6614", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6615", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6616", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6617", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6618", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6619", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6620", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6621", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6622", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6623", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6624", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6625", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6626", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6627", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6628", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6629", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6630", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6631", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6632", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6633", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6634", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6635", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6636", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6637", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6638", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6639", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6640", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6641", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6642", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6643", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6644", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6645", + "name": "Revenant cyclops", + "examine": "The ghost of a cyclops slain during the god wars.", + "attack_level": "43", + "clue_level": "2", + "death_animation": "7454", + "defence_animation": "7455", + "defence_level": "43", + "lifepoints": "110", + "magic_animation": "7497", + "magic_level": "43", + "melee_animation": "7453", + "range_animation": "7510", + "range_level": "43", + "safespot": null, + "slayer_exp": "110", + "spawn_animation": "7457", + "strength_level": "43", + "weakness": "0" + }, + { + "id": "6646", + "name": "Revenant hellhound", + "examine": "The essence of a hellhound slain during the god wars.", + "attack_level": "50", + "clue_level": "2", + "combat_audio": "3717,3719,3718", + "death_animation": "7461", + "defence_animation": "7462", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "7515", + "magic_level": "50", + "melee_animation": "7460", + "range_animation": "7501", + "range_level": "50", + "safespot": null, + "slayer_exp": "80", + "spawn_animation": "7464", + "strength_level": "50" + }, + { + "id": "6647", + "name": "Revenant demon", + "examine": "The essence of a demon slain during the god wars.", + "attack_level": "60", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "7475", + "defence_animation": "7476", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "7498", + "magic_level": "60", + "melee_animation": "7474", + "range_animation": "7512", + "range_level": "60", + "safespot": null, + "slayer_exp": "80", + "spawn_animation": "7478", + "strength_level": "60" + }, + { + "id": "6648", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6649", + "name": "Revenant dark beast", + "examine": "The ghost of a dark beast slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7468", + "defence_animation": "7469", + "defence_level": "80", + "lifepoints": "140", + "magic_animation": "7515", + "melee_animation": "7467", + "range_animation": "7514", + "range_level": "80", + "safespot": null, + "slayer_exp": "140", + "spawn_animation": "7471", + "strength_level": "80" + }, + { + "id": "6650", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6651", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6652", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6653", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6654", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6655", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6656", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6657", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "22", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "22", + "lifepoints": "34", + "magic_animation": "7499", + "magic_level": "22", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "22", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "22" + }, + { + "id": "6658", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6659", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6660", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6661", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6662", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6663", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6664", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "poisonous": "", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6665", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6666", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6667", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6668", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6669", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6670", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6671", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6672", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6673", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "prj_height": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6674", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6675", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6676", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6677", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6678", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6679", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6680", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6681", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6682", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6683", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6684", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6685", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6686", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6687", + "name": "Revenant cyclops", + "examine": "The ghost of a cyclops slain during the god wars.", + "attack_level": "43", + "clue_level": "2", + "death_animation": "7454", + "defence_animation": "7455", + "defence_level": "43", + "lifepoints": "110", + "magic_animation": "7497", + "magic_level": "43", + "melee_animation": "7453", + "range_animation": "7510", + "range_level": "43", + "safespot": null, + "slayer_exp": "110", + "spawn_animation": "7457", + "strength_level": "43", + "weakness": "0" + }, + { + "id": "6688", + "name": "Revenant hellhound", + "examine": "The essence of a hellhound slain during the god wars.", + "attack_level": "50", + "clue_level": "2", + "combat_audio": "3717,3719,3718", + "death_animation": "7461", + "defence_animation": "7462", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "7515", + "magic_level": "50", + "melee_animation": "7460", + "range_animation": "7501", + "range_level": "50", + "safespot": null, + "slayer_exp": "80", + "spawn_animation": "7464", + "strength_level": "50" + }, + { + "id": "6689", + "name": "Revenant demon", + "examine": "The essence of a demon slain during the god wars.", + "attack_level": "60", + "clue_level": "2", + "combat_audio": "400,404,403", + "death_animation": "7475", + "defence_animation": "7476", + "defence_level": "60", + "lifepoints": "80", + "magic_animation": "7498", + "magic_level": "60", + "melee_animation": "7474", + "range_animation": "7512", + "range_level": "60", + "safespot": null, + "slayer_exp": "80", + "spawn_animation": "7478", + "strength_level": "60" + }, + { + "id": "6690", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6691", + "name": "Revenant dark beast", + "examine": "The ghost of a dark beast slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7468", + "defence_animation": "7469", + "defence_level": "80", + "lifepoints": "140", + "magic_animation": "7515", + "melee_animation": "7467", + "range_animation": "7514", + "range_level": "80", + "safespot": null, + "slayer_exp": "140", + "spawn_animation": "7471", + "strength_level": "80" + }, + { + "id": "6692", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6693", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6694", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6695", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6696", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6697", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6698", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6699", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6700", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6701", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6702", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6703", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6704", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6705", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6706", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6707", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6708", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6709", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6710", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6711", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6712", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6713", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6714", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6715", + "name": "Revenant imp", + "examine": "The essence of an imp slain during the god wars.", + "attack_level": "10", + "combat_audio": "0,0,0", + "death_animation": "7408", + "defence_animation": "7404", + "defence_level": "10", + "lifepoints": "20", + "magic_animation": "7500", + "magic_level": "10", + "melee_animation": "7407", + "range_animation": "7501", + "range_level": "10", + "safespot": null, + "slayer_exp": "10", + "spawn_animation": "7410", + "strength_level": "10" + }, + { + "id": "6716", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6717", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6718", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6719", + "name": "Revenant goblin", + "examine": "The ghost of a goblin slain during the god wars.", + "attack_level": "12", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7448", + "defence_animation": "7450", + "defence_level": "12", + "lifepoints": "24", + "magic_animation": "7499", + "magic_level": "12", + "melee_animation": "7449", + "range_animation": "7513", + "range_level": "12", + "safespot": null, + "spawn_animation": "7447", + "strength_level": "12" + }, + { + "id": "6720", + "name": "Revenant icefiend", + "examine": "The ghost of an icefiend slain during the God Wars.", + "attack_level": "27", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "27", + "lifepoints": "40", + "magic_animation": "7506", + "magic_level": "27", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "27", + "safespot": null, + "slayer_exp": "40", + "spawn_animation": "7485", + "strength_level": "27" + }, + { + "id": "6721", + "name": "Revenant pyrefiend", + "examine": "The essence of a pyrefiend slain during the god wars.", + "attack_level": "29", + "attack_speed": "3", + "clue_level": "1", + "death_animation": "7482", + "defence_animation": "7483", + "defence_level": "29", + "lifepoints": "48", + "magic_animation": "7506", + "magic_level": "29", + "melee_animation": "7481", + "range_animation": "7519", + "range_level": "29", + "safespot": null, + "slayer_exp": "48", + "spawn_animation": "7485", + "strength_level": "29" + }, + { + "id": "6722", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6723", + "name": "Revenant vampire", + "examine": "The ghost of a vampire slain in the god wars.", + "attack_level": "34", + "clue_level": "1", + "death_animation": "7428", + "defence_animation": "7429", + "defence_level": "34", + "lifepoints": "60", + "magic_animation": "7507", + "magic_level": "34", + "melee_animation": "7427", + "range_animation": "7520", + "range_level": "34", + "safespot": null, + "slayer_exp": "60", + "spawn_animation": "7426", + "strength_level": "34" + }, + { + "id": "6724", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6725", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6726", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6727", + "name": "Revenant hobgoblin", + "examine": "The ghost of a hobgoblin slain during the God Wars.", + "attack_level": "32", + "clue_level": "1", + "combat_audio": "469,472,471", + "death_animation": "7419", + "defence_animation": "7420", + "defence_level": "32", + "lifepoints": "72", + "magic_animation": "7503", + "magic_level": "32", + "melee_animation": "7418", + "range_animation": "7516", + "range_level": "32", + "safespot": null, + "slayer_exp": "72", + "strength_level": "32" + }, + { + "id": "6728", + "name": "Revenant werewolf", + "examine": "The ghost of a werewolf slain during the god wars.", + "attack_level": "38", + "clue_level": "2", + "death_animation": "7398", + "defence_animation": "7399", + "defence_level": "38", + "lifepoints": "65", + "magic_animation": "7496", + "magic_level": "38", + "melee_animation": "7397", + "range_animation": "7521", + "range_level": "38", + "safespot": null, + "spawn_animation": "7403", + "strength_level": "38" + }, + { + "id": "6729", + "name": "Revenant ork", + "examine": "A ghost of an ork slain during the god wars.", + "attack_level": "70", + "clue_level": "2", + "death_animation": "7412", + "defence_animation": "7413", + "defence_level": "70", + "lifepoints": "105", + "magic_animation": "7505", + "magic_level": "70", + "melee_animation": "7411", + "range_animation": "7518", + "range_level": "70", + "safespot": null, + "strength_level": "70" + }, + { + "id": "6730", + "name": "Revenant knight", + "examine": "A ghost of a knight slain during the god wars.", + "attack_level": "80", + "clue_level": "2", + "death_animation": "7442", + "defence_animation": "7443", + "defence_level": "80", + "lifepoints": "143", + "magic_animation": "7508", + "magic_level": "80", + "melee_animation": "7441", + "poison_amount": "", + "poisonous": "", + "range_animation": "7522", + "range_level": "80", + "safespot": null, + "slayer_exp": "143", + "spawn_animation": "7440", + "strength_level": "80" + }, + { + "id": "6731", + "name": "Queen of Snow", + "examine": "Her lack of body heat is unsettling.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "movement_radius": "5", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6739", + "name": "Snow imp", + "examine": "It looks right at home here.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6740", + "name": "Snow", + "examine": "Snowflakes swirling in the wind.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6741", + "name": "Snow" + }, + { + "id": "6742", + "name": "Barbarian Snowman", + "examine": "A helmetted, barbarian snowman.", + "agg_radius": "", + "combat_audio": "12,3295,3287", + "death_animation": "7560", + "defence_animation": "7559", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "7565" + }, + { + "id": "6743", + "name": "Dragon snowman", + "examine": "A snowman in fake dragon armour.", + "attack_level": "1", + "combat_audio": "29,3295,3287", + "death_animation": "7560", + "defence_animation": "7559", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "7558", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6744", + "name": "Dwarf Snowman", + "examine": "A snow dwarf.", + "combat_audio": "12,3295,3287", + "death_animation": "7560", + "defence_animation": "7559", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "7565" + }, + { + "id": "6745", + "name": "Pirate snowman", + "examine": "Arr!", + "attack_level": "1", + "combat_audio": "29,3295,3287", + "death_animation": "7560", + "defence_animation": "7559", + "defence_level": "1", + "lifepoints": "20", + "melee_animation": "7558", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6746", + "name": "Snowman", + "examine": "A traditional snowman." + }, + { + "id": "6747", + "name": "Snow ranger", + "examine": "A snowman armed with a holly bow.", + "attack_level": "30", + "defence_level": "10", + "lifepoints": "30", + "range_level": "30", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6748", + "name": "Snow ranger", + "examine": "A snowman armed with an ice sword.", + "attack_level": "30", + "defence_level": "10", + "lifepoints": "20", + "magic_level": "30", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6749", + "name": "Snow mage", + "examine": "A snowman armed with a winter staff.", + "attack_level": "30", + "defence_level": "10", + "lifepoints": "20", + "range_level": "30", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6750", + "name": "Pet shop owner", + "examine": "The lady of the pet shop." + }, + { + "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": "6761", + "name": "Dried zombie", + "examine": "Cut-down and dried.", + "attack_level": "48", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "48", + "lifepoints": "68", + "magic_animation": "0", + "melee_animation": "5568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "7" + }, + { + "id": "6762", + "name": "Dried zombie", + "examine": "Needs moisturising.", + "attack_level": "48", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "48", + "lifepoints": "68", + "magic_animation": "0", + "melee_animation": "5568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "7" + }, + { + "id": "6763", + "name": "Dried zombie", + "examine": "I bet it's parched.", + "attack_level": "48", + "attack_speed": "5", + "death_animation": "5569", + "defence_animation": "0", + "defence_level": "48", + "lifepoints": "68", + "magic_animation": "0", + "melee_animation": "5568", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "7" + }, + { + "id": "6764", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "97", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6765", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "108", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6766", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6767", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "115", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6768", + "name": "Skeleton", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", + "defence_level": "1", + "lifepoints": "140", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6769", + "name": "Doorman", + "examine": "A guard.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6770", + "name": "Giant scarab", + "examine": "A huge beast of a beetle.", + "aggressive": "true", + "attack_level": "49", + "death_animation": "7595", + "defence_animation": "0", + "defence_level": "66", + "lifepoints": "285", + "magic_animation": "0", + "melee_animation": "7596", + "range_animation": "0", + "range_level": "66", + "safespot": null, + "strength_level": "49", + "weakness": "2" + }, + { + "id": "6771", + "name": "Giant scarab" + }, + { + "id": "6772", + "name": "Scarab larva", + "examine": "It's not quite small enough to stamp upon.", + "attack_level": "18", + "death_animation": "7656", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "melee_animation": "7657", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "18", + "weakness": "9" + }, + { + "id": "6773", + "name": "Scabaras ranger", + "examine": "Bred to shoot you down.", + "aggressive": "true", + "attack_level": "41", + "combat_style": "1", + "death_animation": "7609", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "157", + "magic_animation": "0", + "melee_animation": "7607", + "projectile": "11", + "range_animation": "7607", + "range_level": "55", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "18", + "strength_level": "41", + "weakness": "2" + }, + { + "id": "6774", + "name": "Scabaras lancer", + "examine": "It might be angry...it's hard to tell.", + "aggressive": "true", + "attack_level": "55", + "death_animation": "7614", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "7612", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "6775", + "name": "Scabaras priest" + }, + { + "id": "6776", + "name": "Scabaras locust", + "examine": "A giant, mountable locust.", + "aggressive": "true", + "attack_level": "41", + "death_animation": "7581", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "7586", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "60", + "safespot": null, + "strength_level": "41", + "weakness": "2" + }, + { + "id": "6777", + "name": "Locust lancer", + "examine": "Bouncy", + "aggressive": "true", + "attack_level": "55", + "death_animation": "7581", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "7584", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "55", + "weakness": "9" + }, + { + "id": "6778", + "name": "Locust ranger", + "examine": "He'll have someone's eye out with that.", + "aggressive": "true", + "attack_level": "41", + "combat_style": "1", + "death_animation": "7581", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "melee_animation": "5451", + "projectile": "11", + "range_animation": "5451", + "range_level": "55", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "18", + "strength_level": "41", + "weakness": "2" + }, + { + "id": "6779", + "name": "Crocodile", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "combat_audio": "386,388,387", + "death_animation": "7591", + "defence_animation": "7590", + "defence_level": "1", + "lifepoints": "72", + "melee_animation": "7588", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "62", + "strength_level": "1" + }, + { + "id": "6780", + "name": "Scabaras mage", + "examine": "Part scarab, part man.", + "aggressive": "true", + "attack_level": "41", + "combat_style": "2", + "death_animation": "7616", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "78", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7615", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "41", + "weakness": "4" + }, + { + "id": "6781", + "name": "Scabaras mage", + "examine": "Part scarab, part man.", + "aggressive": "true", + "attack_level": "1", + "combat_style": "2", + "death_animation": "7616", + "defence_animation": "7617", + "defence_level": "1", + "lifepoints": "70", + "melee_animation": "7620", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6782", + "name": "Maisa" + }, + { + "id": "6783", + "name": "Maisa" + }, + { + "id": "6784", + "name": "Priest" + }, + { + "id": "6785", + "name": "Clay golem", + "examine": "An animated clay statue.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6786", + "name": "Lead archaeologist Abigail", + "examine": "She looks a bit dirty from digging.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6787", + "name": "Assistant archaeologist Kerner", + "examine": "He looks a bit dusty from troweling.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6788", + "name": "High Priest of Scabaras", + "examine": "He exudes something like holiness.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6789", + "name": "High Priest of Scabaras", + "examine": "He exudes something like holiness.", + "attack_level": "66", + "death_animation": "7614", + "defence_animation": "0", + "defence_level": "66", + "lifepoints": "94", + "magic_animation": "0", + "magic_level": "66", + "melee_animation": "7612", + "range_animation": "0", + "range_level": "66", + "safespot": null, + "strength_level": "66", + "weakness": "10" + }, + { + "id": "6790", + "name": "High Priest of Scabaras", + "examine": "He exudes something like holiness.", + "attack_level": "66", + "death_animation": "7609", + "defence_animation": "0", + "defence_level": "66", + "lifepoints": "94", + "magic_animation": "0", + "magic_level": "66", + "melee_animation": "7607", + "range_animation": "7607", + "range_level": "66", + "safespot": null, + "strength_level": "66", + "weakness": "10" + }, + { + "id": "6791", + "name": "High Priest of Scabaras" + }, + { + "id": "6792", + "name": "Vulture" + }, + { + "id": "6793", + "name": "Spirit terrorbird" + }, + { + "id": "6794", + "name": "Spirit terrorbird", + "examine": "A bird. Literally terrifying.", + "attack_level": "50", + "death_animation": "1013", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "1010", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6795", + "name": "Spirit terrorbird", + "examine": "A bird. Literally terrifying.", + "attack_level": "50", + "death_animation": "1013", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "74", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "1010", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6796", + "name": "Granite crab", + "examine": "Is it a stone? Is it a crab? No! It's Granite Crab!", + "attack_level": "18", + "attack_speed": "3", + "bonuses": "16,9,5,12,4,11,57,53,61,12,45,22,0,0,0", + "death_animation": "8106", + "defence_animation": "8105", + "defence_level": "22", + "lifepoints": "39", + "magic_animation": "8104", + "magic_level": "13", + "melee_animation": "8104", + "range_animation": "8104", + "range_level": "16", + "respawn_delay": "50", + "safespot": null, + "strength_level": "17" + }, + { + "id": "6797", + "name": "Granite crab", + "examine": "Is it a stone? Is it a crab? No! It's Granite Crab!", + "attack_level": "18", + "attack_speed": "3", + "bonuses": "16,9,5,12,4,11,57,53,61,12,45,22,0,0,0", + "death_animation": "8106", + "defence_animation": "8105", + "defence_level": "22", + "lifepoints": "39", + "magic_animation": "8104", + "magic_level": "13", + "melee_animation": "8104", + "range_animation": "8104", + "range_level": "16", + "respawn_delay": "50", + "safespot": null, + "strength_level": "17", + "weakness": "10" + }, + { + "id": "6798", + "name": "Praying mantis", + "examine": "Wax on", + "attack_level": "60", + "death_animation": "8065", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "107", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "8069", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6799", + "name": "Praying mantis", + "examine": "Wax on", + "attack_level": "60", + "death_animation": "8065", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "107", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "8069", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6800", + "name": "Giant ent", + "examine": "He ent such a bad guy.", + "attack_level": "60", + "death_animation": "7854", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "111", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7853", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6801", + "name": "Giant ent", + "examine": "He ent such a bad guy.", + "attack_level": "60", + "death_animation": "7854", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "111", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7853", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6802", + "name": "Spirit cobra", + "examine": "Serpentine.", + "attack_level": "55", + "death_animation": "8153", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8152", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6803", + "name": "Spirit cobra", + "examine": "Serpentine.", + "attack_level": "55", + "death_animation": "8153", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "90", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8152", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6804", + "name": "Spirit dagannoth", + "examine": "Face the thing that should not be!", + "attack_level": "65", + "death_animation": "7780", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "115", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7786", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "6805", + "name": "Spirit dagannoth", + "examine": "Face the thing that should not be!", + "attack_level": "65", + "death_animation": "7780", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "115", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7786", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "6806", + "name": "Thorny snail", + "examine": "A hermit slug.", + "attack_level": "19", + "attack_speed": "5", + "bonuses": "47,50,52,52,54,49,13,3,13,4,0,0,0,0,0", + "combat_style": "1", + "death_animation": "8143", + "defence_animation": "8145", + "defence_level": "22", + "end_gfx": "1387", + "lifepoints": "28", + "magic_animation": "8143", + "magic_level": "18", + "melee_animation": "8143", + "poison_immune": "true", + "projectile": "1386", + "range_animation": "8143", + "range_level": "21", + "respawn_delay": "55", + "safespot": null, + "strength_level": "18" + }, + { + "id": "6807", + "name": "Thorny snail", + "examine": "A hermit slug.", + "attack_level": "19", + "attack_speed": "5", + "bonuses": "47,50,52,52,54,49,13,3,13,4,0,0,0,0,0", + "combat_style": "1", + "death_animation": "8143", + "defence_animation": "8145", + "defence_level": "22", + "end_gfx": "1387", + "lifepoints": "28", + "magic_animation": "8143", + "magic_level": "18", + "melee_animation": "8143", + "poison_immune": "true", + "projectile": "1386", + "range_animation": "8143", + "range_level": "21", + "respawn_delay": "55", + "safespot": null, + "strength_level": "18", + "weakness": "10" + }, + { + "id": "6808", + "name": "Beaver" + }, + { + "id": "6809", + "name": "Karamthulhu overlord", + "examine": "Kneel before squid!", + "attack_level": "50", + "combat_style": "2", + "death_animation": "7964", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "82", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "7963", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6810", + "name": "Karamthulhu overlord", + "examine": "Kneel before squid!", + "attack_level": "50", + "combat_style": "2", + "death_animation": "7964", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "82", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "7963", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6811", + "name": "Hydra", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7937", + "defence_animation": "7936", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "7935", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6812", + "name": "Hydra", + "attack_level": "1", + "combat_style": "1", + "death_animation": "7937", + "defence_animation": "7936", + "defence_level": "1", + "lifepoints": "10", + "melee_animation": "7935", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6813", + "name": "Bunyip", + "attack_level": "1", + "death_animation": "7740", + "defence_animation": "7742", + "defence_level": "1", + "lifepoints": "40", + "melee_animation": "7741", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6814", + "name": "Bunyip", + "attack_level": "1", + "death_animation": "7740", + "defence_animation": "7742", + "defence_level": "1", + "lifepoints": "40", + "melee_animation": "7741", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6815", + "name": "War tortoise", + "examine": "Definitely not teenaged", + "attack_level": "55", + "combat_style": "0", + "death_animation": "8285", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "95", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8286", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6816", + "name": "War tortoise", + "examine": "Definitely not teenaged", + "attack_level": "55", + "combat_style": "0", + "death_animation": "8285", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "95", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8286", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6817", + "name": "Fruit bat", + "examine": "Fruity and bat-shaped. A winning combination!.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6818", + "name": "Abyssal parasite", + "examine": "It's an extra-planar little blood hoover.", + "attack_level": "50", + "combat_style": "2", + "death_animation": "7671", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "8910", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6819", + "name": "Abyssal parasite", + "examine": "It's an extra-planar little blood hoover.", + "attack_level": "50", + "combat_style": "2", + "death_animation": "7671", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "77", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "8910", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6820", + "name": "Abyssal lurker", + "examine": "Lurking like only a lurker can.", + "attack_level": "55", + "death_animation": "7684", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "88", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7680", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6821", + "name": "Abyssal lurker", + "examine": "Lurking like only a lurker can.", + "attack_level": "55", + "death_animation": "7684", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "88", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7680", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6822", + "name": "Unicorn stallion", + "examine": "The pointiest horse on the planet.", + "attack_level": "64", + "bonuses": "48,59,43,52,104,52,116,123,134,46,127,4,0,0,0", + "combat_audio": "4135,4196,4195", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "6376", + "magic_level": "69", + "melee_animation": "6376", + "poison_immune": "true", + "range_animation": "6376", + "range_level": "72", + "safespot": null, + "strength_level": "62" + }, + { + "id": "6823", + "name": "Unicorn stallion", + "examine": "The pointiest horse on the planet.", + "attack_level": "64", + "bonuses": "48,59,43,52,104,52,116,123,134,46,127,4,0,0,0", + "combat_audio": "4135,4196,4195", + "death_animation": "6377", + "defence_animation": "6375", + "defence_level": "70", + "lifepoints": "100", + "magic_animation": "6376", + "magic_level": "69", + "melee_animation": "6376", + "poison_immune": "true", + "range_animation": "6376", + "range_level": "72", + "safespot": null, + "strength_level": "62", + "weakness": "10" + }, + { + "id": "6824", + "name": "Magpie", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "2", + "death_animation": "8012", + "defence_animation": "5388", + "defence_level": "1", + "lifepoints": "16", + "melee_animation": "7810", + "projectile": "1318", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6825", + "name": "Dreadfowl", + "examine": "Southern fried please!", + "attack_level": "17", + "attack_speed": "5", + "bonuses": "16,15,29,12,39,13,41,35,29,39,47,2,2,2,7", + "combat_style": "2", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "22", + "lifepoints": "16", + "magic_animation": "7810", + "magic_level": "23", + "melee_animation": "7810", + "poison_immune": "true", + "projectile": "1318", + "range_animation": "7810", + "range_level": "16", + "respawn_delay": "50", + "safespot": null, + "strength_level": "15" + }, + { + "id": "6826", + "name": "Dreadfowl", + "examine": "Southern fried please!", + "attack_level": "17", + "attack_speed": "5", + "bonuses": "16,15,29,12,39,13,41,35,29,39,47,2,2,2,7", + "combat_style": "2", + "death_animation": "5389", + "defence_animation": "5388", + "defence_level": "22", + "lifepoints": "16", + "magic_animation": "7810", + "magic_level": "23", + "melee_animation": "7810", + "poison_immune": "true", + "projectile": "1318", + "range_animation": "7810", + "range_level": "16", + "respawn_delay": "50", + "safespot": null, + "strength_level": "15", + "weakness": "10" + }, + { + "id": "6827", + "name": "Stranger plant", + "examine": "It's mean and green!", + "attack_level": "55", + "attack_speed": "5", + "death_animation": "8209", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "91", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8208", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6828", + "name": "Stranger plant", + "examine": "It's mean and green!", + "attack_level": "55", + "attack_speed": "5", + "death_animation": "8209", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "91", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "8208", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6829", + "name": "Spirit wolf", + "examine": "Man's significantly less-domesticated friend.", + "attack_level": "16", + "attack_speed": "5", + "bonuses": "12,7,4,10,23,5,29,27,16,12,13,17,0,0,0", + "death_animation": "8295", + "defence_animation": "8294", + "defence_level": "21", + "lifepoints": "15", + "magic_animation": "8292", + "magic_level": "7", + "melee_animation": "8292", + "range_animation": "8292", + "range_level": "9", + "safespot": null, + "strength_level": "18" + }, + { + "id": "6830", + "name": "Spirit wolf", + "examine": "Man's significantly less-domesticated friend.", + "attack_level": "16", + "attack_speed": "5", + "bonuses": "12,7,4,10,23,5,29,27,16,12,13,17,0,0,0", + "death_animation": "8295", + "defence_animation": "8294", + "defence_level": "21", + "lifepoints": "15", + "magic_animation": "8292", + "magic_level": "7", + "melee_animation": "8292", + "range_animation": "8292", + "range_level": "9", + "safespot": null, + "strength_level": "18", + "weakness": "10" + }, + { + "id": "6831", + "name": "Desert wyrm", + "examine": "Surprisingly slime-free.", + "attack_level": "18", + "combat_style": "1", + "death_animation": "7797", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "7795", + "range_animation": "0", + "range_level": "18", + "respawn_delay": "0", + "safespot": null, + "strength_level": "18", + "weakness": "10" + }, + { + "id": "6832", + "name": "Desert wyrm", + "examine": "Surprisingly slime-free.", + "attack_level": "18", + "combat_style": "1", + "death_animation": "7797", + "defence_animation": "0", + "defence_level": "18", + "lifepoints": "25", + "magic_animation": "0", + "magic_level": "18", + "melee_animation": "7795", + "range_animation": "0", + "range_level": "18", + "respawn_delay": "0", + "safespot": null, + "strength_level": "18", + "weakness": "10" + }, + { + "id": "6833", + "name": "Evil turnip", + "examine": "If you think he's evil", + "attack_level": "42", + "combat_style": "1", + "death_animation": "8250", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "42", + "melee_animation": "8248", + "range_animation": "0", + "range_level": "42", + "safespot": null, + "strength_level": "42", + "weakness": "10" + }, + { + "id": "6834", + "name": "Evil turnip", + "examine": "If you think he's evil", + "attack_level": "42", + "combat_style": "1", + "death_animation": "8250", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "magic_level": "42", + "melee_animation": "8248", + "range_animation": "0", + "range_level": "42", + "safespot": null, + "strength_level": "42", + "weakness": "10" + }, + { + "id": "6835", + "name": "Vampire bat", + "examine": "It vants to suck my blood!", + "attack_level": "31", + "death_animation": "8276", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "44", + "magic_animation": "0", + "magic_level": "31", + "melee_animation": "8275", + "range_animation": "0", + "range_level": "31", + "respawn_delay": "0", + "safespot": null, + "strength_level": "31", + "weakness": "10" + }, + { + "id": "6836", + "name": "Vampire bat", + "examine": "It vants to suck my blood!", + "attack_level": "31", + "death_animation": "8276", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "44", + "magic_animation": "0", + "magic_level": "31", + "melee_animation": "8275", + "range_animation": "0", + "range_level": "31", + "respawn_delay": "0", + "safespot": null, + "strength_level": "31", + "weakness": "10" + }, + { + "id": "6837", + "name": "Spirit scorpion", + "examine": "Salt and vinegaroon.", + "attack_level": "19", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "0", + "defence_level": "19", + "lifepoints": "27", + "magic_animation": "0", + "magic_level": "19", + "melee_animation": "6254", + "range_animation": "0", + "range_level": "19", + "respawn_delay": "0", + "safespot": null, + "strength_level": "19", + "weakness": "10" + }, + { + "id": "6838", + "name": "Spirit scorpion", + "examine": "Salt and vinegaroon.", + "attack_level": "19", + "combat_audio": "3611,3612,3610", + "death_animation": "6256", + "defence_animation": "0", + "defence_level": "19", + "lifepoints": "27", + "magic_animation": "0", + "magic_level": "19", + "melee_animation": "6254", + "range_animation": "0", + "range_level": "19", + "respawn_delay": "0", + "safespot": null, + "strength_level": "19", + "weakness": "10" + }, + { + "id": "6839", + "name": "Arctic bear", + "examine": "Crikey! Look at the size of those teeth!", + "attack_level": "60", + "bonuses": "90,50,50,60,90,80,50,30,40,100,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "4929", + "defence_animation": "4928", + "defence_level": "60", + "lifepoints": "10", + "magic_level": "60", + "melee_animation": "4925", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60" + }, + { + "id": "6840", + "name": "Arctic bear", + "examine": "Crikey! Look at the size of those teeth!", + "attack_level": "60", + "bonuses": "90,50,50,60,90,80,50,30,40,100,0,0,0,0,0", + "combat_audio": "498,500,499", + "death_animation": "4929", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "101", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "4925", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6841", + "name": "Spirit spider", + "examine": "Would scare anyone off their tuffet.", + "attack_level": "17", + "bonuses": "15,16,11,14,34,12,24,36,46,16,37,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "17", + "lifepoints": "18", + "magic_animation": "5327", + "magic_level": "15", + "melee_animation": "5327", + "range_animation": "5327", + "range_level": "12", + "respawn_delay": "50", + "safespot": null, + "strength_level": "16" + }, + { + "id": "6842", + "name": "Spirit spider", + "examine": "Would scare anyone off their tuffet.", + "attack_level": "17", + "bonuses": "15,16,11,14,34,12,24,36,46,16,37,0,0,0,0", + "combat_audio": "537,539,538", + "death_animation": "5329", + "defence_animation": "5328", + "defence_level": "17", + "lifepoints": "18", + "magic_animation": "5327", + "magic_level": "15", + "melee_animation": "5327", + "range_animation": "5327", + "range_level": "12", + "respawn_delay": "50", + "safespot": null, + "strength_level": "16", + "weakness": "10" + }, + { + "id": "6843", + "name": "Bloated leech", + "examine": "It's like a little suction pump with eyes.", + "attack_level": "49", + "death_animation": "7656", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "49", + "melee_animation": "7657", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "0", + "safespot": null, + "strength_level": "49", + "weakness": "10" + }, + { + "id": "6844", + "name": "Bloated leech", + "examine": "It's like a little suction pump with eyes.", + "attack_level": "49", + "death_animation": "7656", + "defence_animation": "0", + "defence_level": "49", + "lifepoints": "70", + "magic_animation": "0", + "magic_level": "49", + "melee_animation": "7657", + "range_animation": "0", + "range_level": "49", + "respawn_delay": "0", + "safespot": null, + "strength_level": "49", + "weakness": "10" + }, + { + "id": "6845", + "name": "Honey badger", + "examine": "Violent little so-and-so.", + "attack_level": "32", + "death_animation": "7925", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "32", + "melee_animation": "7928", + "range_animation": "0", + "range_level": "32", + "respawn_delay": "0", + "safespot": null, + "strength_level": "32", + "weakness": "10" + }, + { + "id": "6846", + "name": "Honey badger", + "examine": "Violent little so-and-so.", + "attack_level": "32", + "death_animation": "7925", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "45", + "magic_animation": "0", + "magic_level": "32", + "melee_animation": "7928", + "range_animation": "0", + "range_level": "32", + "respawn_delay": "0", + "safespot": null, + "strength_level": "32", + "weakness": "10" + }, + { + "id": "6847", + "name": "Albino rat", + "examine": "The big cheese.", + "attack_level": "31", + "bonuses": "64,42,40,57,64,50,76,82,72,24,75,5,15,5,5", + "combat_audio": "703,705,704", + "defence_level": "32", + "lifepoints": "68", + "magic_level": "27", + "poison_immune": "true", + "range_level": "31", + "respawn_delay": "50", + "safespot": null, + "strength_level": "28" + }, + { + "id": "6848", + "name": "Albino rat", + "examine": "The big cheese.", + "attack_level": "31", + "bonuses": "64,42,40,57,64,50,76,82,72,24,75,5,15,5,5", + "combat_audio": "703,705,704", + "defence_level": "32", + "lifepoints": "68", + "magic_level": "27", + "poison_immune": "true", + "range_level": "31", + "respawn_delay": "50", + "safespot": null, + "strength_level": "28", + "weakness": "10" + }, + { + "id": "6849", + "name": "Granite lobster", + "examine": "Puts the 'crust' in 'crustacean'.", + "attack_level": "60", + "bonuses": "93,90,65,90,30,40,50,50,60,40,0,0,0,0,0", + "death_animation": "8113", + "defence_animation": "8114", + "defence_level": "60", + "lifepoints": "10", + "melee_animation": "8112", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60" + }, + { + "id": "6850", + "name": "Granite lobster", + "examine": "Puts the 'crust' in 'crustacean'.", + "attack_level": "60", + "bonuses": "93,90,65,90,30,40,50,50,60,40,0,0,0,0,0", + "death_animation": "8113", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "105", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "8112", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "6851", + "name": "Macaw", + "attack_level": "1", + "death_animation": "8012", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6852", + "name": "Macaw", + "attack_level": "1", + "death_animation": "8012", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6853", + "name": "Bronze minotaur", + "examine": "He's just a big bully.", + "attack_level": "19", + "bonuses": "-2,6,3,0,0,15,14,9,-6,14,5,90,0,0,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "19", + "lifepoints": "133", + "magic_level": "1", + "melee_animation": "8024", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "19" + }, + { + "id": "6854", + "name": "Bronze minotaur", + "examine": "He's just a big bully.", + "attack_level": "19", + "bonuses": "15,20,10,15,10,15,15,15,0,0,0,90,0,0,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "0", + "defence_level": "19", + "lifepoints": "133", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8024", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "19", + "weakness": "10" + }, + { + "id": "6855", + "name": "Iron minotaur", + "examine": "He's just a big bully.", + "attack_level": "25", + "bonuses": "-2,8,5,0,0,21,17,9,-6,20,5,100,0,100,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "25", + "lifepoints": "193", + "magic_animation": "", + "magic_level": "25", + "melee_animation": "8024", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "25" + }, + { + "id": "6856", + "name": "Iron minotaur", + "examine": "He's just a big bully.", + "attack_level": "25", + "bonuses": "25,25,25,25,0,25,25,25,25,0,0,100,0,100,0", + "clue_level": "", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "193", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "8024", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "25", + "weakness": "10" + }, + { + "id": "6857", + "name": "Steel minotaur", + "examine": "He's just a big bully!", + "attack_level": "28", + "bonuses": "-2,16,11,0,0,32,31,24,-6,31,5,122,0,122,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "28", + "lifepoints": "260", + "magic_level": "28", + "melee_animation": "8024", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "28" + }, + { + "id": "6858", + "name": "Steel minotaur", + "examine": "He's just a big bully.", + "attack_level": "28", + "bonuses": "40,30,30,30,30,30,30,30,30,30,0,127,0,0,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "260", + "magic_animation": "0", + "magic_level": "28", + "melee_animation": "8024", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "28", + "weakness": "10" + }, + { + "id": "6859", + "name": "Mithril minotaur", + "examine": "He's just a big bully", + "attack_level": "30", + "bonuses": "-2,22,17,0,0,46,44,38,-6,44,20,146,0,147,0", + "can_tolerate": "", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "30", + "lifepoints": "340", + "magic_level": "30", + "melee_animation": "8024", + "range_level": "0", + "respawn_delay": "0", + "safespot": null, + "strength_level": "30" + }, + { + "id": "6860", + "name": "Mithril minotaur", + "examine": "He's just a big bully.", + "attack_level": "30", + "bonuses": "30,30,30,30,0,30,30,30,30,0,30,146,0,147,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "30", + "lifepoints": "340", + "magic_animation": "0", + "magic_level": "30", + "melee_animation": "8024", + "range_animation": "0", + "range_level": "0", + "respawn_delay": "0", + "safespot": null, + "strength_level": "30", + "weakness": "10" + }, + { + "id": "6861", + "name": "Adamant minotaur", + "examine": "He's just a big bully.", + "attack_level": "35", + "attack_speed": "", + "bonuses": "-2,31,26,-3,-17,65,63,55,-6,63,30,146,0,181,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "35", + "lifepoints": "441", + "magic_level": "35", + "melee_animation": "8024", + "poison_immune": "", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "35" + }, + { + "id": "6862", + "name": "Adamant minotaur", + "examine": "He's just a big bully.", + "attack_level": "35", + "bonuses": "40,50,40,45,35,20,50,36,30,40,15,180,30,10,0", + "combat_style": "0", + "death_animation": "8025", + "defence_animation": "0", + "defence_level": "35", + "lifepoints": "441", + "magic_animation": "0", + "magic_level": "35", + "melee_animation": "8024", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "35", + "weakness": "10" + }, + { + "id": "6863", + "name": "Rune minotaur", + "examine": "He's just a big bully.", + "attack_level": "40", + "bonuses": "-2,48,43,0,-1,207,209,192,-12,205,102,157,0,195,0", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "40", + "lifepoints": "570", + "magic_level": "40", + "melee_animation": "8024", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "40" + }, + { + "id": "6864", + "name": "Rune minotaur", + "examine": "He's just a big bully.", + "attack_level": "40", + "bonuses": "50,40,40,56,40,30,50,30,60,30,40,219,50,40,40", + "death_animation": "8025", + "defence_animation": "8023", + "defence_level": "40", + "lifepoints": "570", + "magic_level": "40", + "melee_animation": "8024", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "slayer_exp": "0", + "strength_level": "40" + }, + { + "id": "6865", + "name": "Smoke devil", + "examine": "Well", + "attack_level": "55", + "death_animation": "7818", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "87", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7816", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6866", + "name": "Smoke devil", + "examine": "Well", + "attack_level": "55", + "death_animation": "7818", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "87", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7816", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6867", + "name": "Bull ant", + "examine": "Putting all three of its best feet forward.", + "attack_level": "40", + "death_animation": "7897", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "magic_level": "40", + "melee_animation": "7896", + "range_animation": "0", + "range_level": "40", + "respawn_delay": "0", + "safespot": null, + "strength_level": "40", + "weakness": "10" + }, + { + "id": "6868", + "name": "Bull ant", + "examine": "Putting all three of its best feet forward.", + "attack_level": "40", + "death_animation": "7897", + "defence_animation": "0", + "defence_level": "40", + "lifepoints": "57", + "magic_animation": "0", + "magic_level": "40", + "melee_animation": "7896", + "range_animation": "0", + "range_level": "40", + "respawn_delay": "0", + "safespot": null, + "strength_level": "40", + "weakness": "10" + }, + { + "id": "6869", + "name": "Wolpertinger", + "examine": "It's just a harmless wee rabbit. ", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "59,50,50,50,50,50,50,50,50,50,50,50,50,68,0", + "combat_style": "2", + "death_animation": "8305", + "defence_animation": "8306", + "defence_level": "95", + "lifepoints": "619", + "magic_animation": "8304", + "magic_level": "95", + "melee_animation": "8304", + "poison_immune": "true", + "range_animation": "8304", + "range_level": "95", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6870", + "name": "Wolpertinger", + "examine": "It's just a harmless wee rabbit. ", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "59,50,50,50,50,50,50,50,50,50,50,50,50,68,0", + "combat_style": "2", + "death_animation": "8305", + "defence_animation": "8306", + "defence_level": "95", + "lifepoints": "619", + "magic_animation": "8304", + "magic_level": "95", + "melee_animation": "8304", + "poison_immune": "true", + "range_animation": "8304", + "range_level": "95", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6871", + "name": "Compost mound", + "examine": "I suppose it could smell worse", + "attack_level": "28", + "attack_speed": "5", + "death_animation": "7770", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "magic_level": "28", + "melee_animation": "7769", + "range_animation": "0", + "range_level": "28", + "respawn_delay": "0", + "safespot": null, + "strength_level": "28", + "weakness": "10" + }, + { + "id": "6872", + "name": "Compost mound", + "examine": "I suppose it could smell worse", + "attack_level": "28", + "attack_speed": "5", + "death_animation": "7770", + "defence_animation": "0", + "defence_level": "28", + "lifepoints": "40", + "magic_animation": "0", + "magic_level": "28", + "melee_animation": "7769", + "range_animation": "0", + "range_level": "28", + "respawn_delay": "0", + "safespot": null, + "strength_level": "28", + "weakness": "10" + }, + { + "id": "6873", + "name": "Pack yak", + "examine": "Bulk haulage never smelled so bad.", + "attack_level": "112", + "attack_speed": "5", + "bonuses": "64,67,5,72,128,49,264,234,164,49,132,40,35,40,40", + "death_animation": "5784", + "defence_animation": "5783", + "defence_level": "121", + "lifepoints": "710", + "magic_animation": "5782", + "magic_level": "91", + "melee_animation": "5782", + "poison_immune": "true", + "range_animation": "5782", + "range_level": "86", + "safespot": null, + "strength_level": "104" + }, + { + "id": "6874", + "name": "Pack yak", + "examine": "Bulk haulage never smelled so bad.", + "attack_level": "112", + "attack_speed": "5", + "bonuses": "64,67,5,72,128,49,264,234,164,49,132,40,35,40,40", + "death_animation": "5784", + "defence_animation": "5783", + "defence_level": "121", + "lifepoints": "710", + "magic_animation": "5782", + "magic_level": "91", + "melee_animation": "5782", + "poison_immune": "true", + "range_animation": "5782", + "range_level": "86", + "safespot": null, + "strength_level": "104", + "weakness": "10" + }, + { + "id": "6875", + "name": "Spirit cockatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6876", + "name": "Spirit cockatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6877", + "name": "Spirit guthatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6878", + "name": "Spirit guthatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6879", + "name": "Spirit saratrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_audio": "703,705,704", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6880", + "name": "Spirit saratrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_audio": "703,705,704", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6881", + "name": "Spirit zamatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6882", + "name": "Spirit zamatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6883", + "name": "Spirit pengatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6884", + "name": "Spirit pengatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6885", + "name": "Spirit coraxatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6886", + "name": "Spirit coraxatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6887", + "name": "Spirit vulatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6888", + "name": "Spirit vulatrice", + "examine": "If looks could kill... Wait", + "attack_level": "43", + "combat_style": "2", + "death_animation": "7763", + "defence_animation": "0", + "defence_level": "43", + "lifepoints": "61", + "magic_animation": "0", + "magic_level": "43", + "melee_animation": "7762", + "projectile": "1468", + "range_animation": "0", + "range_level": "43", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1467", + "strength_level": "43", + "weakness": "10" + }, + { + "id": "6889", + "name": "Barker toad", + "examine": "The only creature with a mouth big enough to fit a cannon ball into.", + "attack_level": "55", + "death_animation": "7256", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "94", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7260", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6890", + "name": "Barker toad", + "examine": "The only creature with a mouth big enough to fit a cannon ball into.", + "attack_level": "55", + "death_animation": "7256", + "defence_animation": "0", + "defence_level": "55", + "lifepoints": "94", + "magic_animation": "0", + "magic_level": "55", + "melee_animation": "7260", + "range_animation": "0", + "range_level": "55", + "respawn_delay": "0", + "safespot": null, + "strength_level": "55", + "weakness": "10" + }, + { + "id": "6891", + "name": "Penguin keeper", + "examine": "He looks like the type of guy who would keep penguins.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6892", + "name": "Pet shop owner", + "examine": "The lady of the pet shop.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6893", + "name": "Pet shop owner", + "examine": "The man of the pet shop.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6894", + "name": "Terrier", + "examine": "A terrier puppy." + }, + { + "id": "6895", + "name": "Labrador", + "examine": "A labrador puppy." + }, + { + "id": "6896", + "name": "Dalmatian", + "examine": "A dalmatian puppy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6897", + "name": "Bulldog", + "examine": "A bulldog puppy.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6898", + "name": "Pet shop owner" + }, + { + "id": "6899", + "name": "Bulldog" + }, + { + "id": "6900", + "name": "Hatchling dragon", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6901", + "name": "Baby dragon", + "attack_level": "1", + "combat_audio": "407,405,406", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6902", + "name": "Hatchling dragon", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6903", + "name": "Baby dragon", + "attack_level": "1", + "combat_audio": "407,405,406", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6904", + "name": "Hatchling dragon", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6905", + "name": "Baby dragon", + "attack_level": "1", + "combat_audio": "407,405,406", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6906", + "name": "Hatchling dragon", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6907", + "name": "Baby dragon", + "attack_level": "1", + "combat_audio": "407,405,406", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "examine": "A stripy little baby raccoon." + }, + { + "id": "6914", + "name": "Raccoon", + "examine": "He can run with us." + }, + { + "id": "6915", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "6916", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "6917", + "name": "Baby gecko", + "examine": "It's so adorably tiny!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6918", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "6919", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "6920", + "name": "Squirrel", + "examine": "An experienced nut-thief." + }, + { + "id": "6921", + "name": "Baby squirrel", + "examine": "A tiny nut-thief.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6942", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6943", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6944", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "6958", + "name": "Terrier puppy", + "examine": "It's lively and energetic." + }, + { + "id": "6959", + "name": "Terrier", + "examine": "It's lively and energetic." + }, + { + "id": "6960", + "name": "Greyhound puppy", + "examine": "This is one fast little pooch." + }, + { + "id": "6961", + "name": "Greyhound", + "examine": "An aerodynamic doggy." + }, + { + "id": "6962", + "name": "Labrador puppy", + "examine": "Soft but strong... and cute." + }, + { + "id": "6963", + "name": "Labrador", + "examine": "My most faithful friend." + }, + { + "id": "6964", + "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred..." + }, + { + "id": "6965", + "name": "Dalmatian", + "examine": "A purebred doggy." + }, + { + "id": "6966", + "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet." + }, + { + "id": "6967", + "name": "Sheepdog", + "examine": "Come by! Come by!" + }, + { + "id": "6968", + "name": "Bulldog", + "examine": "Looks like it's chewing a wasp." + }, + { + "id": "6969", + "name": "Bulldog puppy", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "2", + "death_animation": "8305", + "defence_animation": "8306", + "defence_level": "1", + "lifepoints": "651", + "magic_level": "28", + "melee_animation": "8304", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6970", + "name": "Pikkupstix", + "examine": "He's a very important druid." + }, + { + "id": "6971", + "name": "Pikkupstix", + "examine": "He's a very important druid." + }, + { + "id": "6972", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "7186", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6973", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6974", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6975", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "7186", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6976", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6977", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6978", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6979", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "7186", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28" + }, + { + "id": "6980", + "name": "Druid", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,513,512", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6981", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6982", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "7186", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6983", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6984", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "7186", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7181", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6985", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6986", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6987", + "name": "Druidess", + "examine": "A nature lover.", + "attack_level": "28", + "attack_speed": "4", + "combat_audio": "511,506,505", + "death_animation": "7185", + "defence_animation": "0", + "defence_level": "32", + "lifepoints": "30", + "magic_animation": "711", + "magic_level": "25", + "melee_animation": "7182", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "strength_level": "28", + "weakness": "3" + }, + { + "id": "6988", + "name": "Pikkupstix", + "examine": "He's a very important druid." + }, + { + "id": "6989", + "name": "Pikkupstix", + "examine": "He's a very important druid." + }, + { + "id": "6990", + "name": "Giant wolpertinger" + }, + { + "id": "6991", + "name": "Ibis" + }, + { + "id": "6992", + "name": "Spirit jelly", + "examine": "On Gielinor", + "attack_level": "50", + "death_animation": "8570", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "78", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "8569", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6993", + "name": "Spirit jelly", + "examine": "On Gielinor", + "attack_level": "50", + "death_animation": "8570", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "78", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "8569", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "6994", + "name": "Spirit kalphite", + "examine": "Hail to the Queen", + "attack_level": "25", + "combat_style": "1", + "death_animation": "6228", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "6223", + "range_animation": "0", + "range_level": "25", + "respawn_delay": "0", + "safespot": null, + "strength_level": "25", + "weakness": "10" + }, + { + "id": "6995", + "name": "Spirit kalphite", + "examine": "Hail to the Queen", + "attack_level": "25", + "combat_style": "1", + "death_animation": "6228", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "6223", + "range_animation": "0", + "range_level": "25", + "respawn_delay": "0", + "safespot": null, + "strength_level": "25", + "weakness": "10" + }, + { + "id": "6996", + "name": "Fishing spot" + }, + { + "id": "6997", + "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "6998", + "name": "Revenant dragon", + "examine": "A ghost of a dragon slain during the god wars.", + "attack_level": "85", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "8593", + "defence_animation": "8592", + "defence_level": "85", + "lifepoints": "155", + "magic_animation": "8594", + "melee_animation": "8591", + "poison_amount": "", + "poisonous": "", + "range_animation": "8594", + "range_level": "85", + "safespot": null, + "spawn_animation": "8595", + "strength_level": "85" + }, + { + "id": "6999", + "name": "Revenant dragon", + "examine": "A ghost of a dragon slain during the god wars.", + "attack_level": "85", + "clue_level": "2", + "combat_audio": "408,410,409", + "death_animation": "8593", + "defence_animation": "8592", + "defence_level": "85", + "lifepoints": "155", + "magic_animation": "8594", + "melee_animation": "8591", + "poison_amount": "", + "poisonous": "", + "range_animation": "8594", + "range_level": "85", + "safespot": null, + "spawn_animation": "8595", + "strength_level": "85" + }, + { + "id": "7000", + "name": "Dark Squall" + }, + { + "id": "7002", + "name": "Surok Magis" + }, + { + "id": "7003", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "7004", + "name": "Fire giant", + "examine": "A very large elemental adversary.", + "attack_level": "65", + "attack_speed": "5", + "bonuses": "29,29,29,0,0,0,3,2,0,0,0,31,0,0,0", + "combat_audio": "447,451,450", + "death_animation": "4668", + "defence_animation": "4664", + "defence_level": "65", + "lifepoints": "111", + "magic_animation": "4666", + "magic_level": "1", + "melee_animation": "4666", + "range_animation": "4666", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "slayer_exp": "111", + "strength_level": "65", + "weakness": "1" + }, + { + "id": "7005", + "name": "Big wolf", + "examine": "Must be the pack leader.", + "aggressive": "true", + "attack_level": "60", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "62", + "lifepoints": "21", + "magic_animation": "0", + "melee_animation": "6559", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "74", + "strength_level": "60" + }, + { + "id": "7006", + "name": "Grenwall" + }, + { + "id": "7010", + "name": "Grenwall", + "examine": "An active spiny creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7011", + "name": "Grenwall", + "examine": "A hiding spiny creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7012", + "name": "Pawya", + "examine": "What a strange little creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7013", + "name": "Earth Mound", + "examine": "Dug by a Pawya (Not actual exam).", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7014", + "name": "Pawya", + "examine": "What a strange little creature.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7021", + "name": "Platypus", + "examine": "A most unlikely creature.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7022", + "name": "Platypus" + }, + { + "id": "7023", + "name": "Platypus" + }, + { + "id": "7024", + "name": "Baby platypus", + "examine": "A most unlikely creature.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7031", + "name": "Wimpy bird", + "examine": "An ogre snack with wings.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7032", + "name": "Diseased kebbit" + }, + { + "id": "7039", + "name": "Diseased kebbit", + "examine": "A shy creature with a toxic bite.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7040", + "name": "Fishing spot" + }, + { + "id": "7045", + "name": "Fishing spot" + }, + { + "id": "7046", + "name": "Fishing spot" + }, + { + "id": "7047", + "name": "Balnea", + "examine": "She looks like she means business." + }, + { + "id": "7048", + "name": "Frawd" + }, + { + "id": "7049", + "name": "Ogress banker", + "examine": "Fit to wear the uniform?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7050", + "name": "Ogress banker" + }, + { + "id": "7051", + "name": "Chief Tess", + "examine": "She walks as if she owns the place.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7078", + "name": "Ogress champion", + "examine": "A fearsome adversary with no sense of humour.", + "aggressive": "true", + "attack_level": "48", + "death_animation": "8640", + "defence_animation": "0", + "defence_level": "48", + "lifepoints": "68", + "magic_animation": "0", + "melee_animation": "8636", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "6" + }, + { + "id": "7079", + "name": "Ogress warrior", + "examine": "Irascible, belligerent and stupefyingly impolite.", + "aggressive": "true", + "attack_level": "45", + "death_animation": "8640", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "8636", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "6" + }, + { + "id": "7080", + "name": "Ogress warrior", + "examine": "Irascible, beliggerent and stupefyingly impolite.", + "aggressive": "true", + "attack_level": "45", + "death_animation": "8641", + "defence_animation": "0", + "defence_level": "45", + "lifepoints": "64", + "magic_animation": "0", + "melee_animation": "8637", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "6" + }, + { + "id": "7081", + "name": "Ogress", + "examine": "A large and contentious lady ogre.", + "aggressive": "true", + "attack_level": "42", + "attack_speed": "5", + "death_animation": "8640", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "8636", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "42", + "weakness": "6" + }, + { + "id": "7082", + "name": "Ogress", + "examine": "A large and contentious lady ogre.", + "aggressive": "true", + "attack_level": "42", + "attack_speed": "5", + "death_animation": "8641", + "defence_animation": "0", + "defence_level": "42", + "lifepoints": "60", + "magic_animation": "0", + "melee_animation": "8637", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "42", + "weakness": "6" + }, + { + "id": "7083", + "name": "Flying bugs" + }, + { + "id": "7084", + "name": "Eucalyptus ent", + "examine": "Dead stone defending a dead throne room.", + "aggressive": "true", + "attack_level": "51", + "attack_speed": "5", + "defence_animation": "0", + "defence_level": "51", + "lifepoints": "145", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "51", + "weakness": "7" + }, + { + "id": "7085", + "name": "Eucalyptus ent", + "examine": "Dead stone defending a dead throne room.", + "aggressive": "true", + "attack_level": "54", + "attack_speed": "5", + "defence_animation": "0", + "defence_level": "54", + "lifepoints": "142", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "54", + "weakness": "7" + }, + { + "id": "7086", + "name": "Eucalyptus ent", + "examine": "Dead stone defending a dead throne room.", + "aggressive": "true", + "attack_level": "57", + "defence_animation": "0", + "defence_level": "57", + "lifepoints": "162", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "57", + "weakness": "7" + }, + { + "id": "7087", + "name": "Balnea" + }, + { + "id": "7088", + "name": "Chief Tess" + }, + { + "id": "7089", + "name": "Chargurr" + }, + { + "id": "7090", + "name": "Wise Old Man" + }, + { + "id": "7091", + "name": "Dawg" + }, + { + "id": "7105", + "name": "Chaos druid", + "examine": "A crazy, evil druid.", + "aggressive": "true", + "attack_level": "8", + "attack_speed": "4", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "12", + "end_gfx": "107", + "lifepoints": "20", + "magic_animation": "422", + "magic_level": "10", + "melee_animation": "422", + "projectile": "106", + "range_animation": "422", + "range_level": "1", + "respawn_delay": "25", + "safespot": null, + "start_gfx": "105", + "strength_level": "8", + "weakness": "3" + }, + { + "id": "7106", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7107", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7108", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7109", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7110", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7111", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7112", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7113", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7114", + "name": "Thug", + "examine": "Low on brains, high on aggression.", + "aggressive": "true", + "attack_level": "7", + "attack_speed": "4", + "bonuses": "5,5,5,0,0,0,2,3,3,0,0,5,0,0,0", + "clue_level": "0", + "death_animation": "9055", + "defence_animation": "404", + "defence_level": "9", + "lifepoints": "18", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "5" + }, + { + "id": "7115", + "name": "Thaki the delivery dwarf", + "examine": "He seems to be enjoying his time in the bar.", + "attack_level": "1", + "bonuses": "5,5,0,0,0,0,2,3,3,0,0,0,0,0,0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7116", + "name": "Drunken sailor" + }, + { + "id": "7117", + "name": "Drunken sailor" + }, + { + "id": "7118", + "name": "Catapult engineer" + }, + { + "id": "7119", + "name": "Tyras guard" + }, + { + "id": "7120", + "name": "Tyras guard", + "examine": "One of King Tyras's men.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7121", + "name": "General Hining" + }, + { + "id": "7122", + "name": "Githan" + }, + { + "id": "7123", + "name": "Amulet of Nature" + }, + { + "id": "7124", + "name": "Mud" + }, + { + "id": "7125", + "name": "Rabbit", + "examine": "A hungry-looking rabbit.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7126", + "name": "Rabbit", + "examine": "A hungry-looking rabbit.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7127", + "name": "Rabbit", + "examine": "A hungry-looking rabbit.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7128", + "name": "Farmer", + "examine": "A gnome farmer.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7129", + "name": "Farmer", + "examine": "A gnome farmer.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7130", + "name": "Farmer", + "examine": "A gnome farmer.", + "aggressive": "true", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7131", + "name": "Farmer Blinkin", + "examine": "The head farmer, apparently." + }, + { + "id": "7132", + "name": "Mrs. Winkin", + "examine": "Owner of this homestead.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7133", + "name": "Bork", + "examine": "That's one big Ork... ", + "agg_radius": "64", + "attack_level": "100", + "attack_speed": "6", + "bonuses": "200,120,100,300,300,120,400,500,300,200,300,400,200,90,0", + "death_animation": "8756", + "defence_animation": "8755", + "defence_level": "80", + "lifepoints": "300", + "melee_animation": "8754", + "poison_immune": "true", + "range_level": "1", + "respawn_delay": "60", + "strength_level": "90" + }, + { + "id": "7134", + "name": "Bork", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "8", + "death_animation": "8756", + "defence_animation": "8755", + "defence_level": "1", + "lifepoints": "300", + "melee_animation": "8754", + "poison_immune": "true", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7135", + "name": "Ork legion", + "examine": "Aww, aren't they the cutest li - Argh!", + "aggressive": "true", + "attack_level": "68", + "bonuses": "5,10,3,5,3,5,5,5,10,0,0,0,0,0,0", + "death_animation": "8761", + "defence_animation": "0", + "defence_level": "57", + "lifepoints": "100", + "magic_animation": "0", + "melee_animation": "8760", + "poison_immune": "true", + "range_animation": "0", + "range_level": "68", + "respawn_delay": "60", + "safespot": null, + "strength_level": "68" + }, + { + "id": "7136", + "name": "Surok Magis" + }, + { + "id": "7137", + "name": "Dagon'hai Elite", + "examine": "A powerful wizard.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "25", + "bonuses": "220,190,140,120,120,130,90,120,120,200,100,154,150,93,190", + "combat_style": "2", + "defence_level": "40", + "lifepoints": "70", + "magic_level": "90", + "range_level": "1", + "safespot": null, + "spell_id": "11", + "strength_level": "90" + }, + { + "id": "7138", + "name": "Dagon'hai Monk", + "examine": "A monk of the Dagon'hai.", + "aggressive": "true", + "attack_level": "84", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "80", + "lifepoints": "88", + "magic_animation": "0", + "magic_level": "98", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84" + }, + { + "id": "7139", + "name": "Dagon'hai Monk", + "examine": "A monk of the Dagon'hai.", + "aggressive": "true", + "attack_level": "84", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "80", + "lifepoints": "88", + "magic_animation": "0", + "magic_level": "98", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84" + }, + { + "id": "7140", + "name": "Dagon'hai Monk", + "examine": "A monk of the Dagon'hai.", + "aggressive": "true", + "attack_level": "84", + "attack_speed": "5", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "80", + "lifepoints": "88", + "magic_animation": "0", + "magic_level": "98", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "84" + }, + { + "id": "7141", + "name": "null", + "examine": "He seems to have gone mad.", + "aggressive": "true", + "attack_level": "25", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "4", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "426", + "range_animation": "426", + "range_level": "25", + "safespot": null, + "strength_level": "25", + "weakness": "1" + }, + { + "id": "7142", + "name": "Guard", + "examine": "He thumps people who cheat.", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7143", + "name": "Professor Henry" + }, + { + "id": "7144", + "name": "Gublinch jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7145", + "name": "Gublinchette jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7146", + "name": "Gublinchette jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7147", + "name": "Gublinch jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7148", + "name": "Gublinch jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7149", + "name": "Gublinch jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7150", + "name": "Gublinch jailmate", + "examine": "An imprisoned gublinch.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7151", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "7152", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7153", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "7154", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "7155", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "7156", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7157", + "name": "Student", + "examine": "A person sitting an exam.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "strength_level": "1" + }, + { + "id": "7158", + "name": "Cockroach drone", + "examine": "A household pest.", + "attack_level": "3", + "attack_speed": "5", + "death_animation": "8788", + "defence_animation": "0", + "defence_level": "3", + "lifepoints": "4", + "magic_animation": "0", + "melee_animation": "8785", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "3", + "weakness": "8" + }, + { + "id": "7159", + "name": "Cockroach worker", + "examine": "Eurgh! A big bug.", + "attack_level": "22", + "attack_speed": "5", + "death_animation": "8790", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "31", + "magic_animation": "0", + "melee_animation": "8787", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "22", + "weakness": "8" + }, + { + "id": "7160", + "name": "Cockroach soldier", + "examine": "Euww! A giant bug.", + "attack_level": "38", + "attack_speed": "5", + "bonuses": "10,10,10,5,0,0,0,0,0,0,0,0,0,0,0", + "death_animation": "8789", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "97", + "magic_animation": "0", + "melee_animation": "8786", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "26", + "weakness": "8" + }, + { + "id": "7161", + "name": "Mugger", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "8", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7162", + "name": "Mugger", + "aggressive": "true", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "8", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7163", + "name": "Villager" + }, + { + "id": "7165", + "name": "Villager" + }, + { + "id": "7166", + "name": "Villager" + }, + { + "id": "7167", + "name": "Villager" + }, + { + "id": "7168", + "name": "Kimberly", + "examine": "A little girl. She looks terrified.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7202", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "4933", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7203", + "name": "Void Knight" + }, + { + "id": "7204", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "4933", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7205", + "name": "Mouse" + }, + { + "id": "7206", + "name": "Cockroach", + "examine": "It seems to have eaten a lot of chocolate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7207", + "name": "Spider", + "examine": "It's searching for crumbs of chocolate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7208", + "name": "Rabbit" + }, + { + "id": "7209", + "name": "Snail", + "examine": "It seems to have eaten a lot of chocolate.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7210", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7211", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7212", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7213", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7214", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7215", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7216", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7217", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7218", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7219", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7220", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7221", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7222", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7223", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7224", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7225", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7226", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7227", + "name": "Monkey", + "examine": "Hide the bananas!", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7228", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7229", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7230", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7231", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7232", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7233", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7234", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7235", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7236", + "name": "Baby monkey", + "examine": "It's a tiny, cheeky monkey.", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7237", + "name": "Terrier puppy", + "examine": "A hyperactive little pup." + }, + { + "id": "7238", + "name": "Terrier", + "examine": "A hyperactive little pup." + }, + { + "id": "7239", + "name": "Terrier puppy", + "examine": "Such a brave doggy." + }, + { + "id": "7240", + "name": "Terrier", + "examine": "Such a brave doggy." + }, + { + "id": "7241", + "name": "Greyhound puppy", + "examine": "This is one fast little pooch." + }, + { + "id": "7242", + "name": "Greyhound", + "examine": "An aerodynamic doggy." + }, + { + "id": "7243", + "name": "Greyhound puppy", + "examine": "This is one fast little pooch." + }, + { + "id": "7244", + "name": "Greyhound", + "examine": "An aerodynamic doggy." + }, + { + "id": "7245", + "name": "Labrador puppy", + "examine": "Soft but strong... and cute." + }, + { + "id": "7246", + "name": "Labrador", + "examine": "My most faithful friend." + }, + { + "id": "7247", + "name": "Labrador puppy", + "examine": "Soft but strong... and cute." + }, + { + "id": "7248", + "name": "Labrador", + "examine": "My most faithful friend." + }, + { + "id": "7249", + "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred..." + }, + { + "id": "7250", + "name": "Dalmatian", + "examine": "A purebred doggy." + }, + { + "id": "7251", + "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred..." + }, + { + "id": "7252", + "name": "Dalmatian", + "examine": "A purebred doggy." + }, + { + "id": "7253", + "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet." + }, + { + "id": "7254", + "name": "Sheepdog", + "examine": "Come by! Come by!" + }, + { + "id": "7255", + "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet." + }, + { + "id": "7256", + "name": "Sheepdog", + "examine": "Come by! Come by!" + }, + { + "id": "7257", + "name": "Bulldog", + "examine": "Looks like it's chewing a wasp." + }, + { + "id": "7258", + "name": "Bulldog", + "examine": "Looks like it's chewing a wasp." + }, + { + "id": "7259", + "name": "Bulldog puppy", + "examine": "One proud little puppy." + }, + { + "id": "7260", + "name": "Bulldog puppy", + "examine": "One proud little puppy." + }, + { + "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", + "examine": "A stripy little baby raccoon." + }, + { + "id": "7272", + "name": "Raccoon", + "examine": "He can run with us." + }, + { + "id": "7273", + "name": "Baby raccoon", + "examine": "A stripy little baby raccoon." + }, + { + "id": "7274", + "name": "Raccoon", + "examine": "He can run with us." + }, + { + "id": "7275", + "name": "Baby Raccoon", + "examine": "A stripy little baby raccoon.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7276", + "name": "Baby raccoon", + "examine": "A stripy little baby raccoon." + }, + { + "id": "7277", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7278", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7279", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7280", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7281", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7282", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7283", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7284", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7285", + "name": "Baby gecko", + "examine": "It's so adorably tiny!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7286", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7287", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7288", + "name": "Baby gecko", + "examine": "It's so adorably tiny!" + }, + { + "id": "7289", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7290", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7291", + "name": "Gecko", + "examine": "How cute!" + }, + { + "id": "7292", + "name": "Gecko", + "examine": "How cute!" + }, + { + "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", + "examine": "A tiny nut-thief." + }, + { + "id": "7302", + "name": "Squirrel", + "examine": "An experienced nut-thief." + }, + { + "id": "7303", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7304", + "name": "Squirrel", + "examine": "An experienced nut-thief." + }, + { + "id": "7305", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7306", + "name": "Squirrel", + "examine": "An experienced nut-thief." + }, + { + "id": "7307", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7308", + "name": "Squirrel", + "examine": "An experienced nut-thief." + }, + { + "id": "7309", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7310", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7311", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "id": "7312", + "name": "Baby squirrel", + "examine": "A tiny nut-thief." + }, + { + "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": "7329", + "name": "Swamp titan", + "attack_level": "1", + "bonuses": "60,60,60,60,50,50,100,200,200,28,100,100,70,70,70", + "combat_style": "0", + "death_animation": "8226", + "defence_animation": "8224", + "defence_level": "78", + "lifepoints": "556", + "magic_level": "70", + "melee_animation": "8222", + "range_level": "70", + "respawn_delay": "0", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7330", + "name": "Swamp titan", + "examine": "Do you hear duelling banjos?", + "attack_level": "60", + "bonuses": "60,60,60,60,50,50,100,200,200,28,100,100,70,70,70", + "combat_style": "0", + "death_animation": "8226", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "117", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "8222", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7331", + "name": "Spirit mosquito", + "examine": "It buzzes and bites. Nasty.", + "attack_level": "28", + "bonuses": "45,49,42,39,47,46,29,43,51,16,53,10,0,0,0", + "death_animation": "8033", + "defence_animation": "8034", + "defence_level": "25", + "lifepoints": "43", + "magic_animation": "8032", + "magic_level": "21", + "melee_animation": "8032", + "poison_immune": "true", + "range_animation": "8032", + "range_level": "25", + "respawn_delay": "50", + "safespot": null, + "strength_level": "24" + }, + { + "id": "7332", + "name": "Spirit mosquito", + "examine": "It buzzes and bites. Nasty.", + "attack_level": "28", + "bonuses": "45,49,42,39,47,46,29,43,51,16,53,10,0,0,0", + "death_animation": "8033", + "defence_animation": "8034", + "defence_level": "25", + "lifepoints": "43", + "magic_animation": "8032", + "magic_level": "21", + "melee_animation": "8032", + "poison_immune": "true", + "range_animation": "8032", + "range_level": "25", + "respawn_delay": "50", + "safespot": null, + "strength_level": "24", + "weakness": "10" + }, + { + "id": "7333", + "name": "Void spinner", + "examine": "It spins.", + "attack_level": "34", + "death_animation": "8176", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8172", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7334", + "name": "Void spinner", + "examine": "It spins.", + "attack_level": "34", + "death_animation": "8176", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8172", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7335", + "name": "Forge regent", + "examine": "This one will burn right through the net!", + "attack_level": "60", + "combat_style": "1", + "death_animation": "7864", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "108", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7863", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7336", + "name": "Forge regent", + "examine": "This one will burn right through the net!", + "attack_level": "60", + "combat_style": "1", + "death_animation": "7864", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "108", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7863", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7337", + "name": "Spirit larupia", + "examine": "Fast cat is fast.", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5228", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7338", + "name": "Spirit larupia", + "examine": "Fast cat is fast.", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5228", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7339", + "name": "Geyser titan", + "examine": "It'll kill your enemies, and makes a great cup of tea.", + "attack_level": "70", + "bonuses": "115,115,115,110,110,102,95,110,110,105,78,120,115,120,120", + "combat_style": "1", + "death_animation": "7880", + "defence_animation": "7878", + "defence_level": "70", + "lifepoints": "620", + "magic_animation": "422", + "magic_level": "70", + "melee_animation": "7879", + "poison_immune": "true", + "range_animation": "422", + "range_level": "70", + "respawn_delay": "50", + "safespot": null, + "strength_level": "70", + "weakness": "0" + }, + { + "id": "7340", + "name": "Geyser titan", + "examine": "It'll kill your enemies, and makes a great cup of tea.", + "attack_level": "70", + "bonuses": "115,115,115,110,110,102,95,110,110,105,78,120,115,120,120", + "combat_style": "1", + "death_animation": "7880", + "defence_animation": "7878", + "defence_level": "70", + "lifepoints": "620", + "magic_animation": "422", + "magic_level": "70", + "melee_animation": "7879", + "poison_immune": "true", + "range_animation": "422", + "range_level": "70", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "70", + "weakness": "0" + }, + { + "id": "7341", + "name": "Lava titan", + "examine": "Made of lava.", + "attack_level": "65", + "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", + "combat_style": "0", + "death_animation": "7979", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "115", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7980", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "7342", + "name": "Lava titan", + "examine": "Made of lava.", + "attack_level": "65", + "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", + "combat_style": "0", + "death_animation": "7979", + "defence_animation": "0", + "defence_level": "65", + "lifepoints": "115", + "magic_animation": "0", + "magic_level": "65", + "melee_animation": "7980", + "range_animation": "0", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "7343", + "name": "Steel titan", + "examine": "The King of the Titans!", + "attack_level": "70", + "bonuses": "105,105,105,100,110,102,95,110,110,105,78,120,115,120,120", + "combat_style": "1", + "death_animation": "8184", + "defence_animation": "8185", + "defence_level": "70", + "lifepoints": "750", + "magic_animation": "8183", + "magic_level": "70", + "melee_animation": "8183", + "poison_immune": "true", + "range_animation": "8183", + "range_level": "70", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "0", + "strength_level": "70", + "weakness": "10" + }, + { + "id": "7344", + "name": "Steel titan", + "examine": "The King of the Titans!", + "attack_level": "70", + "bonuses": "105,105,105,100,110,102,95,110,110,105,78,120,115,120,120", + "combat_style": "1", + "death_animation": "8184", + "defence_animation": "8185", + "defence_level": "70", + "lifepoints": "750", + "magic_animation": "8183", + "magic_level": "70", + "melee_animation": "8183", + "poison_immune": "true", + "range_animation": "8183", + "range_level": "70", + "respawn_delay": "15", + "safespot": null, + "slayer_exp": "0", + "strength_level": "70", + "weakness": "10" + }, + { + "id": "7345", + "name": "Obsidian golem", + "examine": "Four fists can make quite an impression IN someone...", + "attack_level": "60", + "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", + "death_animation": "8052", + "defence_animation": "8051", + "defence_level": "60", + "lifepoints": "10", + "magic_level": "60", + "melee_animation": "8050", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60" + }, + { + "id": "7346", + "name": "Obsidian golem", + "examine": "Four fists can make quite an impression IN someone...", + "attack_level": "60", + "bonuses": "100,120,90,100,90,90,40,50,0,0,0,0,0,0,0", + "death_animation": "8052", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "104", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "8050", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7347", + "name": "Talon beast", + "examine": "If a normal black cat is bad luck", + "attack_level": "60", + "death_animation": "5990", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "5989", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7348", + "name": "Talon beast", + "examine": "If a normal black cat is bad luck", + "attack_level": "60", + "death_animation": "5990", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "110", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "5989", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7349", + "name": "Abyssal titan", + "examine": "Big", + "attack_level": "70", + "death_animation": "7692", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "125", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7693", + "range_animation": "0", + "range_level": "70", + "respawn_delay": "0", + "safespot": null, + "strength_level": "70", + "weakness": "10" + }, + { + "id": "7350", + "name": "Abyssal titan", + "examine": "Big", + "attack_level": "70", + "death_animation": "7692", + "defence_animation": "0", + "defence_level": "70", + "lifepoints": "125", + "magic_animation": "0", + "magic_level": "70", + "melee_animation": "7693", + "range_animation": "0", + "range_level": "70", + "respawn_delay": "0", + "safespot": null, + "strength_level": "70", + "weakness": "10" + }, + { + "id": "7351", + "name": "Void torcher", + "examine": "It torches.", + "attack_level": "34", + "combat_style": "2", + "death_animation": "8236", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8235", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7352", + "name": "Void torcher", + "examine": "It torches.", + "attack_level": "34", + "combat_style": "2", + "death_animation": "8236", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8235", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7353", + "name": "Giant chinchompa", + "examine": "Looks a little...volatile.", + "attack_level": "29", + "combat_style": "1", + "death_animation": "7758", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "41", + "magic_animation": "0", + "magic_level": "29", + "melee_animation": "7755", + "range_animation": "0", + "range_level": "29", + "respawn_delay": "0", + "safespot": null, + "strength_level": "29", + "weakness": "10" + }, + { + "id": "7354", + "name": "Giant chinchompa", + "examine": "Looks a little...volatile.", + "attack_level": "29", + "combat_style": "1", + "death_animation": "7758", + "defence_animation": "0", + "defence_level": "29", + "lifepoints": "41", + "magic_animation": "0", + "magic_level": "29", + "melee_animation": "7755", + "range_animation": "0", + "range_level": "29", + "respawn_delay": "0", + "safespot": null, + "strength_level": "29", + "weakness": "10" + }, + { + "id": "7355", + "name": "Fire titan", + "examine": "Scorching!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", + "combat_style": "2", + "death_animation": "7833", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7834", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7356", + "name": "Fire titan", + "examine": "Scorching!", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,208,84,221,231,179,89,113,23,0,0,0", + "combat_style": "2", + "death_animation": "7833", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7834", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7357", + "name": "Moss titan", + "examine": "Gathers rolling stones to bash people with.", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", + "death_animation": "7843", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7844", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7358", + "name": "Moss titan", + "examine": "Gathers rolling stones to bash people with.", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", + "death_animation": "7843", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7844", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7359", + "name": "Ice titan", + "examine": "Frosty the highly violent snowman.", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", + "death_animation": "7846", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7845", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7360", + "name": "Ice titan", + "examine": "Frosty the highly violent snowman.", + "attack_level": "40", + "attack_speed": "5", + "bonuses": "64,45,76,82,120,84,140,120,106,89,113,80,0,0,0", + "death_animation": "7846", + "defence_animation": "7832", + "defence_level": "40", + "lifepoints": "476", + "magic_animation": "7834", + "magic_level": "40", + "melee_animation": "7845", + "poison_immune": "true", + "range_animation": "7834", + "range_level": "30", + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7361", + "name": "Spirit Tz-Kih", + "examine": "This bat burned down the belfry.", + "attack_level": "22", + "attack_speed": "3", + "combat_style": "2", + "death_animation": "8258", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "magic_level": "22", + "melee_animation": "8257", + "range_animation": "0", + "range_level": "22", + "respawn_delay": "0", + "safespot": null, + "strength_level": "22", + "weakness": "10" + }, + { + "id": "7362", + "name": "Spirit Tz-Kih", + "examine": "This bat burned down the belfry.", + "attack_level": "22", + "attack_speed": "3", + "combat_style": "2", + "death_animation": "8258", + "defence_animation": "0", + "defence_level": "22", + "lifepoints": "31", + "magic_animation": "0", + "magic_level": "22", + "melee_animation": "8257", + "range_animation": "0", + "range_level": "22", + "respawn_delay": "0", + "safespot": null, + "strength_level": "22", + "weakness": "10" + }, + { + "id": "7363", + "name": "Spirit graahk", + "examine": "Those spikes are pretty big!", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5229", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1367", + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7364", + "name": "Spirit graahk", + "examine": "Those spikes are pretty big!", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5229", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1367", + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7365", + "name": "Spirit kyatt", + "examine": "Those teeth are pretty big!", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5228", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1365", + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7366", + "name": "Spirit kyatt", + "examine": "Those teeth are pretty big!", + "attack_level": "50", + "death_animation": "5230", + "defence_animation": "0", + "defence_level": "50", + "lifepoints": "81", + "magic_animation": "0", + "magic_level": "50", + "melee_animation": "5228", + "range_animation": "0", + "range_level": "50", + "respawn_delay": "0", + "safespot": null, + "start_gfx": "1365", + "strength_level": "50", + "weakness": "10" + }, + { + "id": "7367", + "name": "Void shifter", + "examine": "It shifts.", + "attack_level": "34", + "death_animation": "8133", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8131", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7368", + "name": "Void shifter", + "examine": "It shifts.", + "attack_level": "34", + "death_animation": "8133", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8131", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7369", + "name": "Void shifter" + }, + { + "id": "7370", + "name": "Void ravager", + "examine": "It ravages.", + "attack_level": "34", + "death_animation": "8087", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8086", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7371", + "name": "Void ravager", + "examine": "It ravages.", + "attack_level": "34", + "death_animation": "8087", + "defence_animation": "0", + "defence_level": "34", + "lifepoints": "48", + "magic_animation": "0", + "magic_level": "34", + "melee_animation": "8086", + "range_animation": "0", + "range_level": "34", + "safespot": null, + "strength_level": "34", + "weakness": "10" + }, + { + "id": "7372", + "name": "Ravenous locust" + }, + { + "id": "7373", + "name": "Ravenous locust", + "examine": "It's like a little stomach on wings.", + "attack_level": "60", + "death_animation": "7996", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7994", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7374", + "name": "Ravenous locust", + "examine": "It's like a little stomach on wings.", + "attack_level": "60", + "death_animation": "7996", + "defence_animation": "0", + "defence_level": "60", + "lifepoints": "100", + "magic_animation": "0", + "magic_level": "60", + "melee_animation": "7994", + "range_animation": "0", + "range_level": "60", + "respawn_delay": "0", + "safespot": null, + "strength_level": "60", + "weakness": "10" + }, + { + "id": "7375", + "name": "Iron titan", + "examine": "He is an iron man!", + "attack_level": "65", + "bonuses": "120,130,100,109,80,120,102,90,103,80,100,108,105,66,90", + "combat_style": "0", + "death_animation": "7947", + "defence_animation": "7948", + "defence_level": "65", + "lifepoints": "694", + "magic_level": "65", + "melee_animation": "7946", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "strength_level": "65", + "weakness": "10" + }, + { + "id": "7376", + "name": "Iron titan", + "examine": "He is an iron man!", + "attack_level": "65", + "bonuses": "120,130,100,109,80,120,102,90,103,80,100,108,105,66,90", + "combat_audio": "0,0,0", + "combat_style": "0", + "death_animation": "7947", + "defence_animation": "7948", + "defence_level": "65", + "lifepoints": "694", + "magic_level": "65", + "melee_animation": "7946", + "range_level": "65", + "respawn_delay": "0", + "safespot": null, + "slayer_exp": "0", + "strength_level": "65", + "weakness": "10" + }, + { + "id": "7377", + "name": "Pyrelord", + "examine": "Where did I put the marshmallows?", + "attack_level": "46", + "death_animation": "8078", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "magic_level": "46", + "melee_animation": "8080", + "range_animation": "0", + "range_level": "46", + "safespot": null, + "strength_level": "46", + "weakness": "10" + }, + { + "id": "7378", + "name": "Pyrelord", + "examine": "Where did I put the marshmallows?", + "attack_level": "46", + "death_animation": "8078", + "defence_animation": "0", + "defence_level": "46", + "lifepoints": "65", + "magic_animation": "0", + "magic_level": "46", + "melee_animation": "8080", + "range_animation": "0", + "range_level": "46", + "safespot": null, + "strength_level": "46", + "weakness": "10" + }, + { + "id": "7379", + "name": "Elfinlocks", + "aggressive": "true", + "attack_level": "1", + "death_animation": "8967", + "defence_animation": "8966", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "8965", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7380", + "name": "Missi Sissi", + "aggressive": "true", + "attack_level": "1", + "death_animation": "8967", + "defence_animation": "8966", + "defence_level": "1", + "lifepoints": "120", + "melee_animation": "8965", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7381", + "name": "Missi Sissi", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "2", + "death_animation": "8956", + "defence_animation": "8957", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "8955", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7382", + "name": "Uberlass", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "combat_style": "2", + "death_animation": "8956", + "defence_animation": "8957", + "defence_level": "1", + "lifepoints": "75", + "melee_animation": "8955", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7398", + "name": "Elisabeta", + "examine": "The vengeful spirit of one who died within Daemonheim.", + "aggressive": "true", + "attack_level": "25", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "7", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "0", + "range_animation": "0", + "range_level": "25", + "safespot": null, + "strength_level": "25", + "weakness": "3" + }, + { + "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": "7417", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "3102,3104,3103", + "death_animation": "2707", + "defence_animation": "2706", + "defence_level": "1", + "lifepoints": "2", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7425", + "name": "Wigglewoo", + "examine": "An impling manager: what a terrifying thought!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7426", + "name": "Orangeowns", + "examine": "This worker looks after the incubator.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7438", + "name": "Elf warrior", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7439", + "name": "Elf warrior", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7440", + "name": "Elf warrior", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7441", + "name": "Elf warrior", + "attack_level": "1", + "clue_level": "2", + "combat_audio": "425,427,426", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7442", + "name": "Arianwyn" + }, + { + "id": "7443", + "name": "Eudav", + "examine": "He holds up passers by.", + "aggressive": "true", + "attack_level": "8", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "8", + "lifepoints": "11", + "magic_animation": "0", + "melee_animation": "386", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "8", + "weakness": "7" + }, + { + "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": "7459", + "name": "Fairtrade", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "75", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7460", + "name": "Teapotspout", + "attack_level": "1", + "attack_speed": "5", + "defence_level": "1", + "lifepoints": "75", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7461", + "name": "Rat", + "examine": "A popular dwarven delicacy.", + "attack_level": "1", + "attack_speed": "5", + "combat_audio": "703,705,704", + "death_animation": "4935", + "defence_animation": "4934", + "defence_level": "1", + "lifepoints": "5", + "magic_animation": "4933", + "melee_animation": "4933", + "range_animation": "4933", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7462", + "name": "Hotwater", + "examine": "It's bubbling, gross.", + "attack_level": "99", + "attack_speed": "5", + "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,45,20,0", + "death_animation": "9131", + "defence_animation": "9132", + "defence_level": "99", + "lifepoints": "120", + "magic_animation": "9130", + "melee_animation": "9130", + "range_animation": "9130", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "99" + }, + { + "id": "7463", + "name": "Hotwater", + "examine": "It's bubbling, gross.", + "attack_level": "99", + "attack_speed": "5", + "bonuses": "20,20,20,20,20,20,20,20,20,20,20,20,45,20,0", + "death_animation": "9131", + "defence_animation": "9132", + "defence_level": "99", + "lifepoints": "120", + "magic_animation": "9130", + "melee_animation": "9130", + "range_animation": "9130", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "99" + }, + { + "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": "7475", + "name": "Lady Seenit", + "examine": "His usual sunny disposition is not in evidence.", + "attack_level": "30", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "magic_level": "30", + "range_animation": "0", + "range_level": "30", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "10" + }, + { + "id": "7476", + "name": "Berrybree", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "50", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7477", + "name": "Stuffstuffer", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "50", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7478", + "name": "Learking" + }, + { + "id": "7479", + "name": "Learking", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "60", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7480", + "name": "Rachael", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "70", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7481", + "name": "Cool Mom227", + "examine": "You clearly can't live on treasure alone.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7482", + "name": "Purepker895", + "examine": "A testament to the effect of greed.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7483", + "name": "Pkmaster0036", + "examine": "He wanted loot", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7484", + "name": "Cow1337killr", + "examine": "Eternally looking for that big payday.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7485", + "name": "Mathdude", + "examine": "No more tea-breaks for this one.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7486", + "name": "Mathdude", + "examine": "Even in death you can smell his feet.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7487", + "name": "1337sp34kr", + "examine": "An anatomist's dream.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7488", + "name": "1337sp34kr", + "examine": "A hand seems to be gripping his spine through his chest. Ouch.", + "attack_level": "30", + "death_animation": "5491", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7489", + "name": "Moglewee" + }, + { + "id": "7490", + "name": "Moglewee" + }, + { + "id": "7491", + "name": "Sarah Domin" + }, + { + "id": "7492", + "name": "Sarah Domin", + "examine": "A desert dweller taken to banditry.", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "9705", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7493", + "name": "Sarah Domin", + "examine": "Calls himself an archaeologist.", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "9715", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7494", + "name": "Sue Spammers", + "examine": "He hasn't found much of use", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "9705", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7495", + "name": "Killerwail", + "examine": "An amateur historian with added greed.", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "9715", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7496", + "name": "Wise Old Man", + "examine": "It all started with her love of genealogy.", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "melee_animation": "9705", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7497", + "name": "Sabre-toothed kyatt", + "examine": "A rocky horror.", + "attack_level": "27", + "defence_animation": "0", + "defence_level": "27", + "lifepoints": "38", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "27", + "weakness": "6" + }, + { + "id": "7498", + "name": "Snowy knight" + }, + { + "id": "7499", + "name": "Sapphire glacialis" + }, + { + "id": "7500", + "name": "Cerulean twitch", + "examine": "This dung beetle has mistaken you for its staple diet.", + "aggressive": "true", + "attack_level": "30", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7501", + "name": "Abone", + "examine": "It's looking unwell - probably something it ate.", + "aggressive": "true", + "attack_level": "30", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7502", + "name": "Mythmaster", + "examine": "Once a valuable contributor to the ecosystem.", + "aggressive": "true", + "attack_level": "30", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7503", + "name": "Donkey Wrong", + "examine": "This is what happens if you play with your food.", + "aggressive": "true", + "attack_level": "30", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "42", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7504", + "name": "Creapantic", + "examine": "He likes a good fight", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7505", + "name": "Frondlike", + "examine": "He likes a good fight", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7506", + "name": "Happy Spud", + "examine": "His favourite must be winning.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7507", + "name": "Nobodyhere", + "examine": "Blood-thirsty and enthusiastic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7508", + "name": "Bluehairlass", + "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7509", + "name": "Evil Wibbler" + }, + { + "id": "7510", + "name": "Ilikekebabs", + "examine": "His favourite must be winning.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7511", + "name": "Trunka Lex", + "examine": "His favourite must be winning.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7512", + "name": "Val Razz", + "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7513", + "name": "Abstractclas", + "examine": "Probably discussing the strengths and weaknesses of the fighting slaves.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7514", + "name": "Bigbluebox", + "examine": "Bloodthirsty and enthusiastic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7515", + "name": "Funorbrox", + "examine": "Bloodthirsty and enthusiastic.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7516", + "name": "Heresjohnny" + }, + { + "id": "7517", + "name": "Mogglewump" + }, + { + "id": "7518", + "name": "Morrisnorris", + "examine": "He's asleep.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7519", + "name": "Matt Blitzer", + "examine": "He's propping the bar up. Or is it the other way round?", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7520", + "name": "Ketchuppl0x", + "examine": "He demands to have some booze.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7528", + "name": "Redheadmonky", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "22", + "melee_animation": "395", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7529", + "name": "Wallscaler" + }, + { + "id": "7530", + "name": "2scompany" + }, + { + "id": "7531", + "name": "2scompany" + }, + { + "id": "7532", + "name": "3sacrowd", + "examine": "It's an NPC.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "slayer_exp": "0", + "strength_level": "1" + }, + { + "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": "7551", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "90", + "bonuses": "200,200,200,100,100,0,0,0,0,0,0,0,0,0,0", + "defence_level": "90", + "lifepoints": "250", + "magic_level": "70", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7552", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "200,200,200,200,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "90", + "lifepoints": "250", + "magic_level": "50", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7553", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "200,200,100,200,100,0,0,0,0,0,0,0,0,0,0", + "defence_level": "90", + "lifepoints": "250", + "magic_level": "90", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7554", + "name": "Portal", + "examine": "Not somewhere I want to go...", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,200,200,100,0,0,0,0,0,0,0,0,0,0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "90", + "lifepoints": "250", + "magic_animation": "0", + "magic_level": "90", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7555", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,50,50,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "30", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7556", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,100,100,25,0,0,0,0,0,0,0,0,0,0", + "defence_level": "30", + "lifepoints": "250", + "magic_level": "15", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7557", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "100,100,50,100,50,0,0,0,0,0,0,0,0,0,0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "250", + "magic_animation": "0", + "magic_level": "30", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7558", + "name": "Portal", + "examine": "The Void Knight will soon weaken the shield.", + "attack_level": "1", + "attack_speed": "5", + "bonuses": "50,50,100,100,50,0,0,0,0,0,0,0,0,0,0", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "250", + "magic_animation": "0", + "magic_level": "30", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "125", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7559", + "name": "Brawler", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "combat_audio": "323,326,325", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7560", + "name": "Lostme", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7561", + "name": "Chiercat", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7562", + "name": "Skydischarge", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7563", + "name": "Agplus", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7564", + "name": "Distantthin", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7565", + "name": "Allmarshes", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7566", + "name": "Explosive67", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7567", + "name": "Wraithboss" + }, + { + "id": "7568", + "name": "What Goudron" + }, + { + "id": "7569", + "name": "Alpha1beta", + "examine": "Looks after your Farming tools.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7570", + "name": "Solltalk", + "examine": "Enjoys locking up animals in small pens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7571", + "name": "Hm Val", + "examine": "Enjoys locking up animals in small pens.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7572", + "name": "Wizzydumped", + "examine": "He looks like the type of guy who would mind monkeys.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7573", + "name": "Oddskater", + "examine": "He looks like the type of guy who would mind monkeys.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7580", + "name": "Poledragon", + "attack_level": "1", + "combat_audio": "408,410,409", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7581", + "name": "Deepkiwi" + }, + { + "id": "7582", + "name": "Al Truism", + "examine": "He likes inflicting pain.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7583", + "name": "Ohhhhdude", + "examine": "The Don of penguins.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7584", + "name": "Bigface Oz" + }, + { + "id": "7585", + "name": "Torcher", + "examine": "A penguin pushing paper", + "attack_level": "1", + "combat_audio": "845,848,846", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7586", + "name": "Torcher", + "examine": "A penguin pushing paper", + "attack_level": "1", + "combat_audio": "845,848,846", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7593", + "name": "Shifter", + "examine": "He's a little rough around the edges.", + "attack_level": "1", + "combat_audio": "766,768,767", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7605", + "name": "Banker", + "examine": "He has unbelievable strength!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7606", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7607", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7608", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7609", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7614", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7615", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7616", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7617", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7618", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7619", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7620", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7621", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7622", + "name": "Vyrewatch" + }, + { + "id": "7623", + "name": "Vyrewatch" + }, + { + "id": "7624", + "name": "Vyrewatch" + }, + { + "id": "7625", + "name": "Vyrewatch" + }, + { + "id": "7626", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7627", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7628", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7629", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7630", + "name": "Vyrewatch" + }, + { + "id": "7631", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7632", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7633", + "name": "Vyrewatch" + }, + { + "id": "7634", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7635", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7636", + "name": "Fishing spot" + }, + { + "id": "7637", + "name": "Skeletal hand" + }, + { + "id": "7640", + "name": "Skeletal hand", + "examine": "Ew it's still alive.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "5", + "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", + "death_animation": "9126", + "defence_animation": "9127", + "defence_level": "85", + "lifepoints": "90", + "magic_animation": "9125", + "melee_animation": "9125", + "range_animation": "9125", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "85", + "weakness": "9" + }, + { + "id": "7641", + "name": "Zombie hand", + "examine": "Ew it's still alive.", + "aggressive": "true", + "attack_level": "75", + "attack_speed": "5", + "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", + "death_animation": "9126", + "defence_animation": "9127", + "defence_level": "75", + "lifepoints": "90", + "magic_animation": "9125", + "melee_animation": "9125", + "range_animation": "9125", + "range_level": "1", + "respawn_delay": "20", + "safespot": null, + "strength_level": "75", + "weakness": "9" + }, + { + "id": "7642", + "name": "Mutated bloodveld", + "examine": "A bloodveld with a very mixed heritage.", + "aggressive": "true", + "attack_level": "56", + "attack_speed": "5", + "death_animation": "9131", + "defence_animation": "0", + "defence_level": "56", + "lifepoints": "112", + "magic_animation": "0", + "melee_animation": "9102", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "206", + "strength_level": "56", + "weakness": "9" + }, + { + "id": "7643", + "name": "Mutated bloodveld", + "examine": "A bloodveld with a very mixed heritage.", + "aggressive": "true", + "attack_level": "58", + "attack_speed": "5", + "death_animation": "9131", + "defence_animation": "0", + "defence_level": "58", + "lifepoints": "116", + "magic_animation": "0", + "melee_animation": "9102", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "206", + "strength_level": "58", + "weakness": "9" + }, + { + "id": "7644", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7645", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7646", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7647", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7648", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7649", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7650", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7651", + "name": "Zaromark Sliver", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "88", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7652", + "name": "Zaromark Sliver" + }, + { + "id": "7653", + "name": "Zaromark Sliver" + }, + { + "id": "7654", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7655", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7656", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7657", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7658", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7659", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7660", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7661", + "name": "Fistandantilus", + "aggressive": "true", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "65", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7682", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7683", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7684", + "name": "Vyrewatch" + }, + { + "id": "7685", + "name": "Vyrewatch" + }, + { + "id": "7686", + "name": "Safalaan" + }, + { + "id": "7687", + "name": "Spectral Vyrewatch" + }, + { + "id": "7688", + "name": "Drezel" + }, + { + "id": "7690", + "name": "Drezel", + "examine": "A holy man." + }, + { + "id": "7691", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7692", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7693", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7694", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7695", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7696", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7697", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7698", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7699", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7700", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7701", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7702", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7703", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "95", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7704", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "105", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7705", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "120", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7706", + "name": "Vyrewatch", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "defence_level": "1", + "lifepoints": "130", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7707", + "name": "Drezel", + "examine": "A holy man." + }, + { + "id": "7708", + "name": "Temple guardian" + }, + { + "id": "7711", + "name": "Temple guardian", + "examine": "Looks like a big ugly dog.", + "attack_level": "24", + "death_animation": "6564", + "defence_animation": "0", + "defence_level": "24", + "lifepoints": "34", + "magic_animation": "0", + "melee_animation": "6565", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "9" + }, + { + "id": "7712", + "name": "Baby icefiend" + }, + { + "id": "7713", + "name": "Baby icefiend", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "8080", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7714", + "name": "Icefiend", + "examine": "A small ice demon.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "531,533,532", + "death_animation": "8078", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "8080", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "7715", + "name": "Icefiend", + "examine": "The icefiend seems to be melting.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "clue_level": "0", + "combat_audio": "531,533,532", + "death_animation": "8078", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "8080", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "id": "7716", + "name": "null", + "examine": "The icefiend seems to be melting.", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "8078", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "35", + "magic_animation": "0", + "magic_level": "25", + "melee_animation": "8080", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "5" + }, + { + "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": "7727", + "name": "Monk", + "examine": "A holy man.", + "attack_level": "12", + "combat_audio": "511,513,512", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "17", + "magic_animation": "0", + "melee_animation": "422", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "12", + "weakness": "9" + }, + { + "id": "7728", + "name": "Baby icefiend" + }, + { + "id": "7736", + "name": "Baby icefiend", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "8078", + "defence_animation": "8084", + "defence_level": "1", + "lifepoints": "50", + "melee_animation": "8080", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7737", + "name": "Will" + }, + { + "id": "7738", + "name": "Will" + }, + { + "id": "7739", + "name": "Phil" + }, + { + "id": "7740", + "name": "Jiggling crate", + "examine": "Is it full of rats?", + "attack_level": "1", + "combat_audio": "703,705,704", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7741", + "name": "Three little kittens", + "examine": "A warrior who has been long forgotten.", + "aggressive": "true", + "attack_level": "30", + "death_animation": "836", + "defence_animation": "0", + "defence_level": "25", + "lifepoints": "5", + "magic_animation": "0", + "magic_level": "25", + "range_animation": "0", + "range_level": "25", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "7742", + "name": "Fluffs" + }, + { + "id": "7743", + "name": "Fluffs" + }, + { + "id": "7744", + "name": "Kittens" + }, + { + "id": "7746", + "name": "TzHaar-Mej-Malk" + }, + { + "id": "7747", + "name": "TzHaar-Xil-Tog", + "examine": "It looks upset.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7752", + "name": "TzHaar-Mej-Lor", + "examine": "A TzHaar librarian.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7753", + "name": "TzHaar-Mej" + }, + { + "id": "7754", + "name": "TzHaar-Hur" + }, + { + "id": "7755", + "name": "TzHaar-Xil" + }, + { + "id": "7756", + "name": "Library assistant", + "examine": "A TzHaar-Hur stamping stone tablets.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7767", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "1", + "safespot": null, + "strength_level": "50" + }, + { + "id": "7768", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "1", + "safespot": null, + "strength_level": "50" + }, + { + "id": "7769", + "name": "TzHaar-Hur", + "examine": "Looks like a craftsman of some kind.", + "attack_level": "50", + "attack_speed": "5", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "death_animation": "9288", + "defence_animation": "9287", + "defence_level": "50", + "lifepoints": "80", + "magic_animation": "9286", + "magic_level": "50", + "melee_animation": "9286", + "range_animation": "9286", + "range_level": "1", + "safespot": null, + "strength_level": "50" + }, + { + "id": "7770", + "name": "TokTz-Ket-Dill" + }, + { + "id": "7771", + "name": "TokTz-Ket-Dill" + }, + { + "id": "7772", + "name": "Lava monster", + "examine": "A monster made of magma.", + "aggressive": "true", + "attack_level": "28", + "combat_style": "1", + "death_animation": "9340", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "9337", + "range_animation": "0", + "range_level": "38", + "respawn_delay": "60", + "safespot": null, + "strength_level": "28", + "weakness": "0" + }, + { + "id": "7773", + "name": "Fire monster", + "examine": "A monster like many others", + "aggressive": "true", + "attack_level": "28", + "combat_style": "2", + "death_animation": "9344", + "defence_animation": "0", + "defence_level": "38", + "lifepoints": "54", + "magic_animation": "0", + "melee_animation": "9341", + "range_animation": "0", + "range_level": "38", + "respawn_delay": "60", + "safespot": null, + "strength_level": "28", + "weakness": "0" + }, + { + "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": "7786", + "name": "Mighty banshee", + "examine": "Not the best of vocalists.", + "aggressive": "true", + "attack_level": "65", + "combat_audio": "284,286,285", + "combat_style": "1", + "death_animation": "9450", + "defence_animation": "9451", + "defence_level": "65", + "lifepoints": "85", + "magic_animation": "9382", + "magic_level": "65", + "melee_animation": "9449", + "range_animation": "9382", + "range_level": "0", + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "0" + }, + { + "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": "7823", + "name": "Wall Beast", + "examine": "A big, scary hand! ", + "aggressive": "true", + "attack_level": "38", + "attack_speed": "6", + "death_animation": "1804", + "defence_animation": "1803", + "defence_level": "38", + "lifepoints": "105", + "melee_animation": "1802", + "movement_radius": "0", + "range_level": "1", + "respawn_delay": "0", + "safespot": null, + "slayer_exp": "105", + "strength_level": "38" + }, + { + "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": "7891", + "name": "Melee dummy", + "examine": "A melee training dummy", + "aggressive": "true", + "attack_level": "1", + "attack_speed": "6", + "death_animation": "97", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "94", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "6" + }, + { + "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": "7958", + "name": "Smelting Tutor", + "examine": "A man of his craft.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "movement_radius": "5", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "7959", + "name": "Smelting Tutor", + "examine": "A master of his craft.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "movement_radius": "5", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "7969", + "name": "Explorer Jack", + "examine": "He looks like a professional explorer.", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "10", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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": "8041", + "name": "Imiago", + "examine": "Perhaps this gardener might look after your crops for you." + }, + { + "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": "8077", + "name": "Pumpkin", + "examine": "What's black and white and smells of pumpkin?", + "end_height": "" + }, + { + "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", + "examine": "What's black and white and smells like beer?" + }, + { + "id": "8105", + "name": "Bush", + "examine": "What's black and white and down to earth?" + }, + { + "id": "8106", + "name": "Bush" + }, + { + "id": "8107", + "name": "Cactus", + "examine": "What's black and white and covered in spines?" + }, + { + "id": "8108", + "name": "Crate", + "examine": "What's black and white and on a sneaking mission?" + }, + { + "id": "8109", + "name": "Rock", + "examine": "What's black and white and looks like a rock hopper?" + }, + { + "id": "8110", + "name": "Toadstool", + "examine": "What's black and white and covered in mushroom?" + }, + { + "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": "8133", + "name": "Corporeal Beast", + "examine": "A vision of supernatural horror.", + "agg_radius": "64", + "aggressive": "true", + "attack_level": "320", + "attack_speed": "6", + "bonuses": "50,50,50,0,0,25,200,100,150,230,0,0,0,0,0", + "death_animation": "10385", + "defence_animation": "10386", + "defence_level": "310", + "lifepoints": "2000", + "magic_animation": "10053", + "magic_level": "350", + "melee_animation": "10058", + "movement_radius": "64", + "poison_immune": "true", + "range_level": "150", + "respawn_delay": "80", + "safespot": null, + "strength_level": "320" + }, + { + "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": "8149", + "name": "Armoured zombie", + "examine": "He sure looks grave.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "6", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "50", + "lifepoints": "62", + "melee_animation": "5571", + "range_level": "50", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "30", + "strength_level": "50" + }, + { + "id": "8150", + "name": "Armoured zombie", + "examine": "This is a rotten one.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "6", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "50", + "lifepoints": "62", + "melee_animation": "5571", + "range_level": "50", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "30", + "strength_level": "50" + }, + { + "id": "8151", + "name": "Armoured zombie", + "examine": "It hasn't quite gotten around to dying.", + "aggressive": "true", + "attack_level": "50", + "attack_speed": "6", + "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", + "combat_audio": "931,923,922", + "death_animation": "5575", + "defence_animation": "5574", + "defence_level": "50", + "lifepoints": "62", + "melee_animation": "5571", + "range_level": "50", + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "30", + "strength_level": "50" + }, + { + "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": "8273", + "name": "Turael", + "examine": "He looks dangerous!" + }, + { + "id": "8274", + "name": "Mazchna" + }, + { + "id": "8275", + "name": "Duradel", + "examine": "He looks dangerous!" + }, + { + "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": "8349", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8350", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8351", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8352", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8353", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8354", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8355", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8356", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8357", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8358", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8359", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8360", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "2", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8361", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8362", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8363", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "1", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8364", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "0", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8365", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "2", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "id": "8366", + "name": "Tormented demon", + "examine": "Lucien must be incredibly powerful if he can bind such demons to his will.", + "aggressive": "true", + "attack_level": "85", + "attack_speed": "6", + "bonuses": "112,132,113,111,245,167,394,346,213,349,167,65,39,65,58", + "combat_style": "0", + "death_animation": "10924", + "defence_animation": "10923", + "defence_level": "85", + "lifepoints": "326", + "magic_animation": "10918", + "magic_level": "85", + "melee_animation": "10922", + "movement_radius": "40", + "poison_immune": "true", + "protect_style": "1", + "range_animation": "10919", + "range_level": "85", + "respawn_delay": "100", + "safespot": "true", + "slayer_exp": "136", + "strength_level": "85" + }, + { + "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", + "examine": "Are those spines or feathers? Either way, it looks delicious." + }, + { + "id": "8503", + "name": "Cactus", + "examine": "Prickly, but strangely tasty-looking..." + }, + { + "id": "8504", + "name": "Bush", + "examine": "It rustles deliciously." + }, + { + "id": "8505", + "name": "Toadstool", + "examine": "A horrible fungus, but there is something strangely tasty about it." + }, + { + "id": "8506", + "name": "Barrel", + "examine": "The most delicious barrel you have ever seen." + }, + { + "id": "8507", + "name": "Bush", + "examine": "Who would have thought that leaves could smell so edible?" + }, + { + "id": "8508", + "name": "Crate", + "examine": "A crate. But delicious." + }, + { + "id": "8509", + "name": "Bush", + "examine": "You feel a strange urge to pour gravy on that bush." + }, + { + "id": "8510", + "name": "Rock", + "examine": "Now that's a rock I'd try to make into soup!" + }, + { + "id": "8511", + "name": "Bush", + "examine": "Did that bush just pour sauce on itself?" + }, + { + "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", + "examine": "The rebellious teenage son of the Queen of Snow. Interesting outfit!" + }, + { + "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", + "examine": "A jolly-looking present-distributor." + }, + { + "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" } ] \ No newline at end of file diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index 74f4d1946..77ef33f02 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -1,11070 +1,12442 @@ -[ - { - "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}-{3197,3353,0,1,0}-{3196,3358,0,1,0}-{2655,3441,0,1,6}-{2649,3438,0,1,7}-{2649,3446,0,1,6}-{2653,3444,0,1,6}-{2647,3441,0,1,1}-{2674,3652,0,1,3}-{2672,3655,0,1,6}-{2677,3655,0,1,6}-{3234,3297,0,1,3}-{3230,3296,0,1,5}-{3236,3288,0,1,6}-{3236,3300,0,1,2}-{3234,3293,0,1,4}-{3229,3298,0,1,1}-{3228,3296,0,1,4}-{2694,3274,0,1,6}-{2695,3275,0,1,3}-{2965,3336,0,1,4}-{2784,3062,0,1,4}-{2789,3063,0,1,4}-{2815,3560,0,1,7}-" + }, + { + "npc_id": "42", + "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}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3341,3267,0,1,5}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-" + }, + { + "npc_id": "48", + "loc_data": "{2817,2921,0,1,2}-{2775,2929,0,1,2}-{2789,2926,0,1,5}-{2795,2932,0,1,6}-{2775,2933,0,1,0}-{2769,2928,0,1,3}-" + }, + { + "npc_id": "49", + "loc_data": "{3133,3678,0,1,0}-{3127,3677,0,1,0}-{3127,3667,0,1,0}-{3117,3674,0,1,0}-{3121,3679,0,1,0}-{3104,3678,0,1,0}-{3110,3687,0,1,0}-{3116,3688,0,1,0}-{3103,3704,0,1,0}-{2871,9819,0,1,0}-{2871,9822,0,1,0}-{2870,9826,0,1,0}-{2869,9829,0,1,0}-{2867,9834,0,1,0}-{2867,9841,0,1,0}-{2864,9853,0,1,0}-{2859,9852,0,1,0}-{2851,9850,0,1,0}-{2856,9846,0,1,0}-{2857,9841,0,1,0}-{2861,9838,0,1,0}-{2854,9838,0,1,0}-{3178,3929,0,1,0}-{3193,3926,0,1,0}-{3195,3915,0,1,0}-{3186,3913,0,1,0}-{3175,3906,0,1,0}-{2735,9692,0,1,1}-{2732,9687,0,1,1}-{2733,9688,0,1,1}-{2734,9690,0,1,1}-{2733,9686,0,1,1}-{2732,9692,0,1,1}-{2736,9685,0,1,1}-{2730,9691,0,1,1}-" + }, + { + "npc_id": "50", + "loc_data": "{2273,4698,0,0,0}-" + }, + { + "npc_id": "52", + "loc_data": "{2898,9764,0,1,0}-{2892,9800,0,1,0}-{2912,9808,0,1,0}-" + }, + { + "npc_id": "53", + "loc_data": "{3212,3820,0,1,0}-{3222,3829,0,1,0}-{2694,9508,0,0,3}-{2703,9505,0,0,6}-{2710,9499,0,0,1}-{2704,9520,0,0,5}-{2714,9523,0,0,3}-{2722,9520,0,0,7}-" + }, + { + "npc_id": "54", + "loc_data": "{2834,9824,0,1,0}-" + }, + { + "npc_id": "55", + "loc_data": "{2897,9802,0,1,0}-{2909,9906,0,1,4}-" + }, + { + "npc_id": "58", + "loc_data": "{2574,9874,0,1,0}-{2577,9877,0,1,3}-{2574,9878,0,1,0}-{2575,9871,0,1,4}-{3289,5482,0,1,1}-{3296,5472,0,1,0}-{3291,5466,0,1,6}-{3294,5481,0,1,0}-{3300,5479,0,1,7}-{3288,5480,0,1,3}-{3293,5475,0,1,3}-{3293,5481,0,1,2}-{3286,5465,0,1,3}-{3279,5478,0,1,4}-{3270,5464,0,1,2}-{3270,5468,0,1,1}-" + }, + { + "npc_id": "59", + "loc_data": "{3082,3362,0,1,4}-{2602,9640,0,1,0}-{2603,9635,0,1,0}-{2603,9638,0,1,0}-{2605,9637,0,1,0}-{2605,9639,0,1,0}-{2606,9635,0,1,0}-{2606,9646,0,1,0}-{2607,9637,0,1,0}-{2607,9641,0,1,0}-{2607,9644,0,1,0}-{2607,9648,0,1,0}-{2608,9635,0,1,0}-{2608,9642,0,1,0}-{2609,9639,0,1,0}-{2609,9643,0,1,0}-{3102,9881,0,1,3}-{3095,9883,0,1,2}-{3182,3244,0,1,1}-{3162,3223,0,1,7}-{3164,3242,0,1,0}-{3169,3246,0,1,0}-{3170,3250,0,1,0}-{3166,3247,0,1,0}-{3164,3249,0,1,0}-{3157,3226,0,1,0}-{3163,3227,0,1,0}-{3165,3223,0,1,0}-{3194,3236,0,1,0}-{3146,3347,0,1,3}-{2369,3374,0,1,0}-{2372,3379,0,1,0}-{2378,3366,0,1,0}-{2379,3378,0,1,0}-{2387,3370,0,1,0}-{2394,3365,0,1,0}-{2402,3386,0,1,0}-{2405,3381,0,1,0}-{2407,3387,0,1,0}-{2412,3384,0,1,0}-{2648,9766,0,1,4}-{2653,9761,0,1,3}-{2483,2877,0,1,3}-{2481,2876,0,1,4}-{2457,2867,0,1,4}-{2481,2847,0,1,0}-{2475,2854,0,1,3}-{2482,2873,0,1,3}-{2485,2876,0,1,2}-{2449,2865,0,1,1}-{2461,2880,0,1,5}-{2489,2935,0,1,7}-{2492,2907,0,1,1}-{2487,2894,0,1,2}-{2487,2888,0,1,7}-{2478,2916,0,1,5}-{2485,2902,0,1,3}-{2487,2902,0,1,2}-{2489,2894,0,1,2}-{2490,2905,0,1,7}-{2490,2917,0,1,5}-{2484,2890,0,1,1}-{2491,2927,0,1,1}-{3250,3239,0,1,1}-{3241,3241,0,1,1}-{3249,3249,0,1,1}-{3218,9890,0,1,3}-{3220,9887,0,1,0}-{3218,9887,0,1,1}-{3218,9889,0,1,5}-{3213,9890,0,1,1}-{2496,2890,0,1,4}-{2503,2889,0,1,1}-{2497,2939,0,1,2}-" + }, + { + "npc_id": "60", + "loc_data": "{3189,3883,0,1,5}-{3182,3882,0,1,5}-{3177,3871,0,1,0}-{3176,3878,0,1,0}-{3173,3892,0,1,6}-{3171,3884,0,1,4}-{3166,3874,0,1,1}-{3169,3900,0,1,1}-{3165,3881,0,1,3}-{3164,3888,0,1,6}-{3163,3894,0,1,7}-{3162,3894,0,1,4}-{2448,2784,0,1,7}-{2477,2866,0,1,5}-{2455,2857,0,1,6}-{2434,2845,0,1,4}-{2460,2869,0,1,3}-{2484,2897,0,1,3}-{2471,2890,0,1,6}-{2491,2906,0,1,0}-{2544,2904,0,1,2}-{2547,2982,0,1,6}-{2548,2981,0,1,1}-{2523,2975,0,1,1}-" + }, + { + "npc_id": "61", + "loc_data": "{3111,9747,0,1,5}-{3108,9747,0,1,5}-{3156,3248,0,1,4}-{3143,3241,0,1,4}-{3156,3249,0,1,4}-{3175,3248,0,1,7}-{3169,3260,0,1,4}-{3171,3254,0,1,5}-{3162,3246,0,1,6}-{3179,3262,0,1,1}-{3162,3226,0,1,1}-{3166,3222,0,1,1}-{3157,3229,0,1,5}-{3168,3222,0,1,4}-{3182,3878,0,1,6}-{3182,3886,0,1,7}-{3182,3888,0,1,5}-{3175,3888,0,1,4}-{3175,3879,0,1,7}-{3174,3879,0,1,3}-{3174,3882,0,1,0}-{3175,3885,0,1,3}-{3172,3876,0,1,2}-{3172,3894,0,1,1}-{3172,3885,0,1,3}-{3165,3886,0,1,3}-{3165,3896,0,1,1}-{3166,3886,0,1,1}-{3164,3893,0,1,4}-{3163,3881,0,1,4}-{3159,3889,0,1,0}-{2893,9835,0,1,0}-{2895,9828,0,1,0}-{2891,9831,0,1,0}-{3211,9622,0,1,2}-{3215,9620,0,1,4}-{3212,9618,0,1,3}-{3217,9617,0,1,3}-{3214,9620,0,1,7}-{3213,9890,0,1,5}-{3210,9886,0,1,4}-{3213,9887,0,1,1}-{3207,9880,0,1,0}-{3255,9870,0,1,4}-{3258,9872,0,1,3}-{3261,9870,0,1,4}-{3327,3131,0,1,5}-{3321,3145,0,1,3}-{3322,3143,0,1,3}-{2541,3189,0,1,3}-{2542,3187,0,1,6}-{2543,3186,0,1,4}-{2545,3183,0,1,3}-{2545,3183,0,1,3}-{2545,3182,0,1,3}-{2543,3183,0,1,0}-{2543,3182,0,1,3}-{2539,3182,0,1,0}-" + }, + { + "npc_id": "62", + "loc_data": "{2850,3060,0,1,6}-{2857,3044,0,1,7}-{2856,3066,0,1,4}-{2860,3094,0,1,6}-{2854,3086,0,1,6}-{2863,3083,0,1,3}-{2853,3081,0,1,4}-{2859,3079,0,1,4}-{2859,3075,0,1,6}-{2871,3092,0,1,1}-{2867,3094,0,1,1}-{2861,3082,0,1,3}-{2861,3094,0,1,3}-{2863,3081,0,1,2}-{2864,3089,0,1,3}-{2859,3084,0,1,0}-{2885,2950,0,1,4}-{2891,2951,0,1,6}-{2894,2946,0,1,4}-{2928,3022,0,1,3}-{2930,3029,0,1,4}-{2927,3039,0,1,6}-{2928,3043,0,1,0}-{2929,3036,0,1,7}-{2919,3040,0,1,4}-{2934,3045,0,1,2}-{2929,3035,0,1,4}-{2914,3055,0,1,4}-{2922,3059,0,1,3}-{2930,3049,0,1,4}-{2914,3047,0,1,4}-{2935,3049,0,1,1}-{2927,3058,0,1,3}-{2811,2975,0,1,7}-{2815,2987,0,1,5}-{2808,3007,0,1,6}-{2812,3007,0,1,1}-{2784,3030,0,1,4}-{2783,3017,0,1,3}-{2776,3107,0,1,1}-{2794,3121,0,1,3}-{2795,3118,0,1,1}-{2796,3109,0,1,1}-{2808,3112,0,1,6}-{2791,3124,0,1,1}-{2769,3198,0,1,7}-{2768,3206,0,1,1}-" + }, + { + "npc_id": "63", + "loc_data": "{3074,3892,0,1,3}-{2841,9583,0,1,4}-{2841,9582,0,1,4}-{2839,9582,0,1,1}-{2837,9572,0,1,6}-{2832,9580,0,1,1}-{3128,9954,0,1,0}-{3127,9956,0,1,0}-{3125,9958,0,1,0}-{3117,9956,0,1,0}-{3122,9956,0,1,0}-{3120,9952,0,1,0}-{3123,9951,0,1,0}-{3125,9948,0,1,0}-{3118,9950,0,1,0}-{3165,9891,0,1,6}-{3177,9886,0,1,4}-{3175,9889,0,1,4}-{3177,9879,0,1,4}-{3177,9888,0,1,1}-{3178,9895,0,1,4}-{3182,9884,0,1,3}-{3164,9888,0,1,3}-{3169,9892,0,1,6}-{3213,3728,0,1,0}-{3211,3734,0,1,0}-{3214,3738,0,1,0}-{3215,3748,0,1,0}-{3218,3746,0,1,0}-{3226,3750,0,1,0}-{3239,3749,0,1,0}-{3247,3748,0,1,0}-{3248,3746,0,1,0}-{3244,3743,0,1,0}-{3245,3737,0,1,0}-{3258,5560,0,1,2}-{3258,5555,0,1,3}-{3260,5557,0,1,3}-{3257,5552,0,1,1}-{3260,5558,0,1,5}-{3258,5556,0,1,6}-{3257,5555,0,1,6}-{3260,5559,0,1,7}-{3257,5561,0,1,1}-{3071,3894,0,1,7}-{3062,3897,0,1,3}-{3058,3889,0,1,4}-{3054,3880,0,1,2}-{3054,3895,0,1,7}-{3051,3879,0,1,6}-{3051,3886,0,1,6}-{3047,3893,0,1,1}-{3047,3881,0,1,4}-{3044,3893,0,1,5}-{3041,3894,0,1,1}-{3038,3890,0,1,1}-{3046,3872,0,1,4}-" + }, + { + "npc_id": "64", + "loc_data": "{2869,9912,0,1,0}-{2819,9950,0,1,0}-{2824,9934,0,1,0}-{2827,9925,0,1,0}-{2828,9956,0,1,0}-{2857,9969,0,1,0}-{2872,9972,0,1,0}-{2684,9798,0,1,2}-{2885,9962,0,1,0}-{2887,9936,0,1,0}-{2959,3917,0,1,0}-{2960,3925,0,1,0}-{2961,3920,0,1,0}-{2962,3915,0,1,0}-{2962,3933,0,1,0}-{2963,3923,0,1,0}-{2963,3929,0,1,0}-{2965,3927,0,1,0}-{2965,3931,0,1,0}-{2691,9814,0,1,5}-{2694,9819,0,1,6}-{2696,9831,0,1,3}-{2702,9837,0,1,6}-{2709,9844,0,1,3}-{2720,9847,0,1,4}-{2724,9843,0,1,6}-{2733,9846,0,1,4}-{2740,9841,0,1,3}-{2745,9836,0,1,4}-{2745,9828,0,1,5}-{2747,9826,0,1,1}-" + }, + { + "npc_id": "66", + "loc_data": "{2399,3356,0,1,0}-{2383,3452,0,1,0}-{2401,3417,0,1,0}-{2402,3422,0,1,0}-{2402,3441,0,1,0}-{2403,3430,0,1,0}-{2408,3441,0,1,0}-{2409,3430,0,1,0}-{2423,3426,0,1,0}-{2427,3428,0,1,0}-{2427,3440,0,1,0}-{2417,3493,1,1,0}-{2418,3495,1,1,0}-{2435,3460,0,1,0}-{2440,3470,0,1,0}-{2447,3502,0,1,0}-{2457,3462,0,1,0}-{2478,3502,0,1,0}-{2449,3492,1,1,0}-{2450,3490,1,1,0}-{2474,3490,1,1,0}-{2482,3492,1,1,0}-{2482,3498,1,1,0}-{2526,3168,0,1,0}-{2529,3163,0,1,0}-{2530,3172,0,1,0}-{2536,3169,0,1,0}-" + }, + { + "npc_id": "67", + "loc_data": "{2400,3356,0,1,0}-{2377,3442,0,1,0}-{2380,3425,0,1,0}-{2383,3433,0,1,0}-{2405,3447,0,1,0}-{2406,3440,0,1,0}-{2393,3451,1,1,0}-{2408,3437,1,1,0}-{2394,3500,1,1,0}-{2417,3483,1,1,0}-{2456,3425,0,1,0}-{2459,3421,0,1,0}-{2462,3431,0,1,0}-{2437,3478,0,1,0}-{2442,3465,0,1,0}-{2456,3467,0,1,0}-{2471,3496,0,1,0}-{2474,3508,0,1,0}-{2475,3471,0,1,0}-{2479,3468,0,1,0}-{2486,3470,0,1,0}-{2492,3474,0,1,0}-{2443,3464,1,1,0}-{2443,3502,1,1,0}-{2448,3496,1,1,0}-{2449,3506,1,1,0}-{2457,3498,1,1,0}-{2474,3498,1,1,0}-{2480,3502,1,1,0}-{2481,3482,1,1,0}-{2490,3503,1,1,0}-{2521,3169,0,1,0}-{2521,3171,0,1,0}-{2556,3226,0,1,0}-{2033,5530,1,1,0}-" + }, + { + "npc_id": "68", + "loc_data": "{2401,3357,0,1,0}-{2378,3423,0,1,0}-{2393,3435,0,1,0}-{2394,3425,0,1,0}-{2395,3449,0,1,0}-{2403,3433,0,1,0}-{2420,3429,0,1,0}-{2420,3438,0,1,0}-{2400,3514,1,1,0}-{2418,3485,1,1,0}-{2433,3474,0,1,0}-{2433,3492,0,1,0}-{2458,3497,0,1,0}-{2464,3505,0,1,0}-{2476,3458,0,1,0}-{2445,3502,1,1,0}-{2453,3488,1,1,0}-{2475,3502,1,1,0}-{2465,3490,2,1,0}-{2522,3172,0,1,0}-" + }, + { + "npc_id": "73", + "loc_data": "{3123,9657,0,1,2}-{3087,9672,0,1,1}-{3103,9672,0,1,1}-{3119,9670,0,1,1}-{3251,9896,0,1,6}-{3268,9893,0,1,3}-" + }, + { + "npc_id": "74", + "loc_data": "{3124,9660,0,1,3}-{3122,9656,0,1,4}-{3117,9646,0,1,1}-{3124,9651,0,1,2}-{3117,9645,0,1,3}-{3082,9671,0,1,3}-{3105,9672,0,1,1}-{3113,9673,0,1,3}-{3094,9671,0,1,3}-{2843,9767,0,1,3}-{2845,9762,0,1,7}-{2842,9759,0,1,6}-{3126,9865,0,1,7}-{3129,9859,0,1,3}-{3121,9863,0,1,3}-{3126,9862,0,1,5}-{3148,9899,0,1,3}-{3147,9905,0,1,6}-{3141,9901,0,1,5}-{3142,9891,0,1,6}-{3139,9886,0,1,2}-{3148,9883,0,1,1}-{3151,9889,0,1,3}-" + }, + { + "npc_id": "75", + "loc_data": "{2588,9493,0,1,3}-{2584,9491,0,1,3}-{2589,9492,0,1,4}-{2591,9492,0,1,6}-{2593,9491,0,1,6}-{2588,9491,0,1,3}-{2839,9774,0,1,4}-{2931,9641,0,1,6}-{2931,9643,0,1,6}-{3226,9909,0,1,4}-{3225,9905,0,1,0}-{3234,9907,0,1,4}-" + }, + { + "npc_id": "76", + "loc_data": "{2843,9774,0,1,6}-{2842,9769,0,1,4}-{2842,9764,0,1,7}-{2845,9754,0,1,4}-{2849,9751,0,1,1}-{2850,9747,0,1,3}-{2844,9758,0,1,7}-{2540,9814,0,1,6}-{2547,9817,0,1,6}-" + }, + { + "npc_id": "78", + "loc_data": "{2585,3479,0,1,1}-{2589,3480,0,1,2}-{2582,3476,0,1,3}-{2588,3485,0,1,4}-{2580,3490,0,1,5}-{2577,3482,0,1,6}-{2583,3484,0,1,7}-{3073,3961,0,1,0}-{3074,3952,0,1,0}-{3075,3955,0,1,0}-{3076,3961,0,1,0}-{3077,3949,0,1,0}-{3079,3957,0,1,0}-{3080,3961,0,1,0}-{2568,9528,0,1,5}-{2573,9530,0,1,3}-{2575,9525,0,1,6}-{2571,9528,0,1,7}-{2862,9569,0,1,4}-{2857,9571,0,1,7}-{2837,9561,0,1,1}-{2608,9818,0,1,7}-{2609,9825,0,1,5}-{2607,9830,0,1,1}-{2605,9837,0,1,1}-{2607,9833,0,1,6}-{2905,9832,0,1,0}-{2907,9831,0,1,0}-{2913,9828,0,1,0}-{2921,9826,0,1,0}-{2924,9831,0,1,0}-{2921,9834,0,1,0}-{2918,9831,0,1,0}-{2908,9838,0,1,0}-{2913,9841,0,1,0}-{2672,9805,0,1,3}-{2663,9804,0,1,4}-{2658,9809,0,1,5}-{3725,9369,0,1,1}-{3737,9390,0,1,4}-{3738,9399,0,1,0}-{3740,9407,0,1,1}-{3765,9407,0,1,1}-{3762,9395,0,1,3}-{3725,9390,0,1,3}-{3753,9387,0,1,0}-{3728,9382,0,1,3}-{3729,9360,0,1,6}-{3755,9424,0,1,0}-{3760,9440,0,1,4}-{3737,9425,0,1,7}-{3720,9430,0,1,4}-{3736,9451,0,1,3}-{3728,9463,0,1,2}-{3740,9462,0,1,4}-{3770,9463,0,1,1}-{3752,9450,0,1,6}-{3762,9432,0,1,1}-{3733,9421,0,1,7}-{2759,3402,0,1,2}-{2756,3400,0,1,0}-{3787,9459,0,1,1}-{3803,9447,0,1,4}-{3778,9438,0,1,1}-{3786,9422,0,1,4}-{3800,9419,0,1,3}-{3797,9427,0,1,4}-{3820,9432,0,1,5}-{3825,9414,0,1,3}-{3833,9415,0,1,0}-{3825,9449,0,1,3}-{3827,9459,0,1,1}-{3818,9466,0,1,4}-{3799,9460,0,1,1}-{3794,9451,0,1,1}-{2543,9564,0,1,1}-{2543,9562,0,1,1}-{2542,9557,0,1,1}-{2542,9569,0,1,4}-{2540,9566,0,1,0}-{2549,9568,0,1,6}-{2538,9568,0,1,3}-" + }, + { + "npc_id": "80", + "loc_data": "{2619,3265,0,1,0}-{2619,3265,0,1,3}-{2615,3267,0,1,4}-" + }, + { + "npc_id": "81", + "loc_data": "{2589,3120,0,1,4}-{2605,3116,0,1,5}-{2923,3277,0,1,1}-{2921,3289,0,1,1}-{2923,3280,0,1,6}-{3255,3255,0,1,3}-{3255,3256,0,1,4}-{3259,3261,0,1,1}-{3263,3283,0,1,6}-{3257,3268,0,1,4}-{2436,4443,0,1,3}-{2441,4449,0,1,6}-{3038,3310,0,1,5}-{3028,3312,0,1,6}-{3028,3299,0,1,6}-{3042,3300,0,1,6}-" + }, + { + "npc_id": "82", + "loc_data": "{3110,3157,2,1,5}-{3096,3703,0,1,0}-{2841,9557,0,1,3}-{2831,9562,0,1,3}-{2838,9605,0,1,3}-{2936,9652,0,1,3}-{2926,9803,0,1,0}-{2935,9794,0,1,0}-{3052,3117,2,1,1}-{3311,3845,0,1,0}-" + }, + { + "npc_id": "83", + "loc_data": "{3169,3711,0,1,7}-{2639,9477,2,0,3}-{2635,9497,2,0,2}-" + }, + { + "npc_id": "84", + "loc_data": "{2875,9776,0,1,0}-{2862,9776,0,1,0}-{2860,9762,0,1,0}-{2854,9780,0,1,0}-{2866,9781,0,1,0}-{2700,9481,0,0,5}-{2729,9483,0,0,5}-" + }, + { + "npc_id": "86", + "loc_data": "{3105,9517,0,1,7}-{3101,9517,0,1,6}-{3102,9521,0,1,4}-{3106,9519,0,1,1}-{3104,9514,0,1,4}-{3104,9518,0,1,1}-{3107,9519,0,1,1}-{3109,9516,0,1,1}-{3107,9515,0,1,7}-{3105,9512,0,1,6}-{3102,9513,0,1,3}-{3100,9514,0,1,1}-{3100,9516,0,1,7}-{3099,9521,0,1,0}-{3103,9523,0,1,1}-{3098,9880,0,1,5}-{3094,9883,0,1,1}-{3119,9891,0,1,4}-{3188,3181,0,1,7}-{3181,3191,0,1,7}-{3185,3199,0,1,4}-{3176,3198,0,1,6}-{3168,3188,0,1,4}-{3172,3178,0,1,1}-{3179,3174,0,1,1}-{3189,3168,0,1,5}-{3196,3208,0,1,1}-{3196,3205,0,1,0}-{3195,3212,0,1,0}-{2632,9696,0,1,1}-{2639,9697,0,1,3}-{2646,9696,0,1,3}-{2650,9697,0,1,4}-{2646,9702,0,1,6}-{2647,9713,0,1,3}-{2641,9719,0,1,1}-{2650,9716,0,1,1}-{3212,3191,0,1,6}-{3207,3192,0,1,2}-{3216,3184,0,1,2}-{3224,3174,0,1,4}-{3229,3183,0,1,5}-{3222,3185,0,1,3}-{3216,3177,0,1,4}-{3200,3182,0,1,5}-{3202,3189,0,1,6}-{2977,3700,0,1,0}-{2967,3694,0,1,0}-{2973,3694,0,1,0}-{3495,9812,0,1,1}-{3502,9807,0,0,4}-{3249,9868,0,1,4}-{3237,9868,0,1,3}-{3221,9873,0,1,6}-{3225,9862,0,1,2}-{3237,9866,0,1,6}-{3232,9867,0,1,7}-{3219,9862,0,1,7}-{3227,9875,0,1,4}-{2518,3187,0,1,3}-{3291,3388,0,1,6}-{3049,3691,0,1,0}-{3276,9870,0,1,3}-" + }, + { + "npc_id": "87", + "loc_data": "{3103,9881,0,1,3}-{3122,9889,0,1,2}-{3117,9890,0,1,2}-{3242,3530,0,1,7}-{3256,3541,0,1,7}-{3248,3553,0,1,7}-{2520,3188,0,1,3}-{3293,3382,0,1,4}-" + }, + { + "npc_id": "88", + "loc_data": "{2573,9630,0,1,0}-{2601,9802,0,1,6}-{2594,9805,0,1,1}-{2587,9806,0,1,2}-{2585,9803,0,1,1}-{2584,9801,0,1,5}-" + }, + { + "npc_id": "89", + "loc_data": "{2575,3058,0,0,6}-{3080,3451,0,1,1}-{3096,3460,0,1,3}-{3142,3211,0,1,6}-{2629,3266,0,1,2}-{2634,3265,0,1,1}-{2557,3065,0,0,1}-{3294,3344,0,1,3}-{3289,3349,0,1,6}-{3286,3345,0,1,1}-{2783,3463,0,1,7}-" + }, + { + "npc_id": "90", + "loc_data": "{3110,3367,0,0,3}-{3116,3534,0,0,4}-{3072,3531,0,1,0}-{3112,3538,0,1,0}-{2852,9577,0,1,1}-{2865,9567,0,1,3}-{3373,9748,0,1,0}-{3378,9748,0,1,0}-{3382,9753,0,1,0}-{3373,9812,0,1,0}-{3378,9812,0,1,0}-{3382,9817,0,1,0}-{3110,9909,0,1,4}-{3121,9910,0,1,4}-{3133,9909,0,1,5}-{3133,9904,0,1,6}-{3129,9916,0,1,3}-{3133,9916,0,1,0}-{2925,3254,2,0,3}-{2924,3252,2,1,3}-{2926,3255,2,1,4}-{2932,3255,2,1,3}-{2931,3251,2,1,4}-{3194,5448,0,1,3}-{3183,5447,0,1,3}-{3177,5457,0,1,4}-{3187,5456,0,1,1}-{2885,9812,0,1,0}-{2883,9836,0,1,0}-{3141,9875,0,1,6}-{3208,9905,0,1,4}-{3011,3590,0,1,0}-{3016,3596,0,1,0}-{3022,3586,0,1,0}-" + }, + { + "npc_id": "91", + "loc_data": "{3099,9910,0,1,3}-{3100,9906,0,1,6}-{3194,5454,0,1,4}-{3212,9906,0,1,6}-{3252,9916,0,1,3}-{3259,9915,0,1,3}-{3272,9913,0,1,6}-{3274,9914,0,1,1}-{3278,9908,0,1,1}-{3275,9911,0,1,5}-" + }, + { + "npc_id": "92", + "loc_data": "{2848,3248,0,1,3}-{2850,3235,0,1,3}-{2819,3288,0,1,1}-{3111,3561,0,1,3}-{2840,9652,0,1,1}-{2829,9657,0,1,4}-{3122,9661,0,1,6}-{3123,9648,0,1,6}-{3117,9671,0,1,1}-{2593,9883,0,1,1}-{2590,9885,0,1,4}-{2586,9883,0,1,1}-{2585,9878,0,1,3}-{3132,9915,0,1,5}-{3095,9909,0,1,3}-{3143,3802,0,1,0}-{3145,3779,0,1,0}-{3149,9869,0,1,6}-{3138,9878,0,1,5}-{3137,9869,0,1,3}-{2664,9877,0,1,0}-{2980,3763,0,1,0}-{2977,3753,0,1,0}-{2992,3942,0,1,4}-{3005,10362,0,1,1}-{3003,10358,0,1,1}-{3005,10351,0,1,1}-{3003,10345,0,1,5}-{2537,9845,0,1,1}-{2540,9846,0,1,4}-{2552,9844,0,1,6}-{2556,9846,0,1,4}-{2535,9844,0,1,3}-{2532,9843,0,1,4}-{2530,9845,0,1,3}-{2540,9820,0,1,4}-{2544,9824,0,1,4}-" + }, + { + "npc_id": "93", + "loc_data": "{2566,9507,0,1,3}-{2562,9505,0,1,1}-{2568,9509,0,1,3}-{2571,9509,0,1,1}-{2564,9504,0,1,1}-{2566,9505,0,1,1}-{2841,9625,0,1,4}-{2845,9628,0,1,3}-{2841,9638,0,1,6}-{2845,9645,0,1,1}-{3101,9956,0,1,3}-{3104,9948,0,1,0}-{3104,9955,0,1,4}-{3108,9951,0,1,4}-{3108,9954,0,1,1}-{3111,9954,0,1,6}-{3112,9958,0,1,4}-{3194,5460,0,1,6}-{3192,5466,0,1,7}-{3185,5493,0,1,4}-{3182,5494,0,1,6}-{3185,5486,0,1,7}-{3175,5492,0,1,3}-{3169,5496,0,1,4}-{3170,5492,0,1,3}-{3186,5487,0,1,4}-{3169,5491,0,1,2}-{2655,9824,0,1,1}-{2669,9824,0,1,4}-{2667,9823,0,1,5}-{2658,9830,0,1,6}-{2666,9829,0,1,6}-{2637,9891,0,1,0}-{2645,9891,0,1,0}-{2653,9892,0,1,0}-{2653,9892,0,1,0}-{2544,9841,0,1,1}-{2536,9844,0,1,6}-{2527,9844,0,1,3}-{2530,9842,0,1,4}-{2543,9815,0,1,3}-" + }, + { + "npc_id": "94", + "loc_data": "{2589,9881,0,1,6}-{2591,9882,0,1,4}-" + }, + { + "npc_id": "95", + "loc_data": "{2602,2955,0,1,0}-{2605,2963,0,1,0}-{2607,2967,0,1,0}-{2610,2958,0,1,0}-{2610,2961,0,1,0}-{2610,2965,0,1,0}-{2616,3283,0,1,0}-{2620,3283,0,1,0}-{2379,3344,0,1,0}-{2395,3340,0,1,0}-{2398,3330,0,1,0}-{2406,3329,0,1,0}-{2647,3584,0,1,0}-{2680,3585,0,1,0}-{2682,3592,0,1,0}-{2482,2923,0,1,0}-{2462,3274,0,1,0}-{2465,3272,0,1,0}-{2465,3275,0,1,0}-{2470,3272,0,1,0}-{2472,3274,0,1,0}-{2477,3275,0,1,0}-{2437,3336,0,1,0}-{2439,3333,0,1,0}-{2439,3335,0,1,0}-{2440,3333,0,1,0}-{2440,3335,0,1,0}-{2706,3580,0,1,0}-{2717,3569,0,1,0}-{3207,3919,0,1,0}-{3218,3924,0,1,0}-{3229,3917,0,1,0}-{3241,3921,0,1,0}-{2496,2959,0,1,0}-{2498,2968,0,1,0}-{2500,2959,0,1,0}-{2502,2969,0,1,0}-{2509,2964,0,1,0}-{2516,3197,0,1,0}-{2548,3179,0,1,0}-" + }, + { + "npc_id": "96", + "loc_data": "{2842,3450,0,1,0}-{2864,3453,0,1,0}-{2870,3438,0,1,0}-{2872,3445,0,1,0}-{2846,3477,0,1,0}-{2847,3474,0,1,0}-{2860,3492,0,1,0}-{2866,3498,0,1,0}-" + }, + { + "npc_id": "97", + "loc_data": "{2831,3515,0,1,0}-{2833,3513,0,1,0}-{2848,3487,0,1,0}-{2850,3484,0,1,0}-{2854,3509,0,1,0}-{2856,3508,0,1,0}-{2988,3921,0,1,0}-{2991,3924,0,1,0}-{2992,3917,0,1,0}-{2994,3921,0,1,0}-{3002,3924,0,1,0}-{3004,3917,0,1,0}-{3005,3926,0,1,0}-{3006,3921,0,1,0}-" + }, + { + "npc_id": "98", + "loc_data": "{3014,3294,0,1,1}-" + }, + { + "npc_id": "99", + "loc_data": "{2624,3319,0,1,1}-{2624,3327,0,1,6}-{2642,3314,0,1,4}-{2646,3325,0,1,1}-{2646,3504,0,1,4}-{2634,3476,0,1,5}-{2651,3477,0,1,5}-{2665,3472,0,1,5}-{2662,3488,0,1,5}-{2672,3505,0,1,5}-{2790,3199,0,1,2}-{2784,3188,0,1,1}-{2779,3207,0,1,1}-" + }, + { + "npc_id": "100", + "loc_data": "{2567,3440,0,1,3}-{2316,9392,0,1,0}-{2319,9367,0,1,0}-{2321,9402,0,1,0}-{2322,9387,0,1,0}-{2325,9360,0,1,0}-{2333,9346,0,1,0}-{2335,9382,0,1,0}-{2337,9387,0,1,0}-{2343,9360,0,1,0}-{2344,9369,0,1,0}-{2348,9390,0,1,0}-{2349,9380,0,1,0}-{2349,9402,0,1,0}-{2359,9345,0,1,0}-{2359,9359,0,1,0}-{2359,9374,0,1,0}-{2359,9382,0,1,0}-{2363,9403,0,1,0}-{2564,9653,0,1,0}-{2565,9655,0,1,0}-{2566,9626,0,1,0}-{2567,9653,0,1,0}-{2569,9633,0,1,0}-{2607,9621,0,1,0}-{2551,3408,0,1,6}-{2553,3405,0,1,3}-{2559,3452,0,1,3}-" + }, + { + "npc_id": "101", + "loc_data": "{2622,3389,0,1,4}-{2619,3390,0,1,4}-{2573,3437,0,1,3}-{2305,9385,0,1,0}-{2307,9359,0,1,0}-{2307,9403,0,1,0}-{2310,9396,0,1,0}-{2317,9371,0,1,0}-{2317,9383,0,1,0}-{2324,9404,0,1,0}-{2333,9366,0,1,0}-{2335,9393,0,1,0}-{2339,9403,0,1,0}-{2342,9347,0,1,0}-{2351,9359,0,1,0}-{2359,9392,0,1,0}-{2562,9659,0,1,0}-{2563,9661,0,1,0}-{2599,9626,0,1,0}-{2601,9627,0,1,0}-{2605,9621,0,1,0}-{2610,9620,0,1,0}-{2624,3391,0,1,1}-{2555,3407,0,1,3}-{2553,3457,0,1,3}-" + }, + { + "npc_id": "102", + "loc_data": "{2622,3390,0,1,7}-{2622,3390,0,1,7}-{2562,9657,0,1,0}-{2571,9653,0,1,0}-" + }, + { + "npc_id": "103", + "loc_data": "{2928,3251,1,1,3}-{2928,3244,1,1,4}-{2924,3253,1,1,3}-{2927,3252,1,1,4}-{2927,3254,1,1,3}-{2888,9850,0,1,0}-{2901,9848,0,1,0}-{2909,9848,0,1,0}-{2919,9848,0,1,0}-{2937,9837,0,1,0}-{2906,9818,0,1,0}-{2920,9818,0,1,0}-{2968,3761,0,1,0}-{2985,3761,0,1,0}-{2962,3755,0,1,0}-{2990,3755,0,1,0}-{2968,3749,0,1,0}-{2985,3749,0,1,0}-{2713,4892,0,1,3}-{2711,4899,0,1,3}-{2732,4901,0,1,3}-{2729,4893,0,1,3}-{2713,4907,0,1,3}-{3241,9907,0,1,4}-{3243,9914,0,1,1}-{3242,9913,0,1,6}-{3242,9916,0,1,5}-{3239,9914,0,1,1}-{2501,3289,0,1,1}-{3272,3664,0,1,1}-" + }, + { + "npc_id": "104", + "loc_data": "{3093,3357,1,1,6}-{2894,9849,0,1,0}-{2905,9851,0,1,0}-{2915,9852,0,1,0}-{2934,9837,0,1,0}-{2935,9828,0,1,0}-{2912,9820,0,1,0}-{2900,9819,0,1,0}-" + }, + { + "npc_id": "105", + "loc_data": "{3100,3594,0,1,6}-{3107,3608,0,1,2}-{3099,3602,0,1,0}-{2632,3280,0,1,1}-{2633,3274,0,1,3}-{2387,3376,0,1,0}-{2398,3366,0,1,0}-{2412,3378,0,1,0}-{2419,3372,0,1,0}-{2696,3329,0,1,4}-{2708,3336,0,1,4}-{3230,3500,0,1,5}-{2988,3671,0,1,0}-{3001,3674,0,1,0}-{2497,3164,0,1,2}-" + }, + { + "npc_id": "106", + "loc_data": "{2959,3445,0,1,3}-{2968,3480,0,1,6}-{2968,3490,0,1,3}-{2978,3507,0,1,1}-{2807,3377,0,1,6}-{3289,3353,0,1,3}-" + }, + { + "npc_id": "107", + "loc_data": "{2870,3166,0,1,3}-{2868,3159,0,1,4}-{2867,3172,0,1,5}-{2858,3167,0,1,3}-{2846,3153,0,1,3}-{2851,3149,0,1,1}-{2602,3267,0,1,6}-{2603,3270,0,1,4}-{2607,3268,0,1,5}-{2645,9823,0,1,1}-{2641,9819,0,1,7}-{2641,9817,0,1,2}-{2642,9813,0,1,5}-{2639,9809,0,1,2}-{3256,3954,0,1,1}-{3249,3949,0,1,1}-{3246,3945,0,1,1}-{3239,3943,0,1,1}-{3231,3940,0,1,1}-{3225,3943,0,1,1}-{3234,3945,0,1,1}-{3253,9909,0,1,4}-{2787,2943,0,1,3}-{2789,2949,0,1,3}-{2781,2949,0,1,3}-{2813,3112,0,1,4}-{3275,3154,0,1,6}-{3300,3312,0,1,1}-{3300,3305,0,1,3}-{3298,3300,0,1,5}-{3298,3294,0,1,0}-{3299,3290,0,1,6}-{3025,3568,0,1,0}-{3028,3577,0,1,0}-{3032,3570,0,1,0}-{3040,3581,0,1,0}-{3041,3572,0,1,0}-{3047,3567,0,1,0}-{3055,3574,0,1,0}-{3056,3566,0,1,0}-{3062,3555,0,1,0}-{3062,3570,0,1,0}-{3055,9776,0,1,4}-{3039,9769,0,1,6}-{3049,9764,0,1,3}-{3050,9770,0,1,1}-{3039,9765,0,1,1}-{3048,9762,0,1,4}-{3044,9760,0,1,6}-{3048,9779,0,1,1}-{3038,9802,0,1,5}-{3041,9804,0,1,6}-{3040,9795,0,1,7}-" + }, + { + "npc_id": "108", + "loc_data": "{2941,9779,0,1,0}-{2934,9776,0,1,0}-{2934,9768,0,1,0}-{2934,9757,0,1,0}-{2930,9752,0,1,0}-{2732,3225,0,1,0}-{2729,3224,0,1,0}-{2719,3223,0,1,0}-{2722,3220,0,1,0}-{2724,3216,0,1,0}-{2721,3213,0,1,0}-{2713,3217,0,1,0}-" + }, + { + "npc_id": "109", + "loc_data": "{2601,3269,0,0,0}-{2604,3272,0,0,0}-{2605,3268,0,0,0}-{2606,3271,0,0,0}-" + }, + { + "npc_id": "110", + "loc_data": "{2564,9887,0,1,4}-{2581,9897,0,1,1}-{2577,9888,0,1,1}-{3234,5497,0,1,1}-{3244,9356,0,1,1}-{3252,9370,0,1,1}-{3305,9400,0,1,1}-{3294,9375,0,1,3}-{3050,10337,0,1,4}-{3047,10340,0,1,4}-{3048,10346,0,1,4}-{3048,10346,0,1,3}-" + }, + { + "npc_id": "111", + "loc_data": "{2920,3800,0,1,3}-{2883,9932,0,1,0}-{2883,9965,0,1,0}-{2952,3902,0,1,6}-{2953,3889,0,1,6}-{2947,3921,0,1,6}-{2811,3506,0,1,0}-{3043,9581,0,1,1}-{3055,9577,0,1,6}-{3061,9576,0,1,4}-{3055,9571,0,1,7}-{3052,9566,0,1,6}-" + }, + { + "npc_id": "112", + "loc_data": "{2825,3249,0,1,6}-{2560,3406,0,1,3}-{3144,3822,0,1,0}-{3165,9880,0,1,3}-{3168,9880,0,1,1}-{3159,9903,0,1,4}-{3156,9907,0,1,0}-{2699,3206,0,1,1}-{3202,5490,0,1,4}-{3237,5555,0,1,5}-{2554,3411,0,1,3}-{2554,3400,0,1,3}-{2551,3401,0,1,4}-{2543,9823,0,1,3}-{2540,9819,0,1,5}-{2543,9817,0,1,4}-" + }, + { + "npc_id": "113", + "loc_data": "{2622,3272,0,0,0}-{2835,9526,0,1,4}-{2832,9514,0,1,4}-{2825,9521,0,1,5}-{2846,9520,0,1,3}-{2838,9508,0,1,0}-{2832,9495,0,1,6}-{2838,9492,0,1,6}-{2851,9485,0,1,3}-{2847,9481,0,1,3}-{2865,9494,0,1,3}-{2855,9502,0,1,2}-{2870,9496,0,1,1}-{2851,9511,0,1,2}-{2853,9519,0,1,3}-{2857,9520,0,1,6}-{2863,9525,0,1,1}-{2869,9530,0,1,5}-{2865,9515,0,1,4}-{2868,9507,0,1,3}-{2874,9512,0,1,6}-{2896,2946,0,0,0}-{2918,2961,0,0,0}-{2926,2986,0,0,0}-{2930,3044,0,0,0}-{2926,3056,0,0,0}-{2916,3065,0,0,0}-" + }, + { + "npc_id": "115", + "loc_data": "{2571,3027,0,1,0}-{2572,3031,0,1,0}-{2575,3024,0,1,0}-{2578,3031,0,1,0}-{2510,3044,0,1,0}-{2520,3046,0,1,0}-{2526,3039,0,1,0}-{2502,3114,0,1,0}-{2505,3111,0,1,0}-{2507,3108,0,1,0}-{2508,3119,0,1,0}-{2509,3087,0,1,0}-{2510,3084,0,1,0}-{2513,3087,0,1,0}-{2514,3081,0,1,0}-" + }, + { + "npc_id": "116", + "loc_data": "{2622,3277,0,1,5}-" + }, + { + "npc_id": "117", + "loc_data": "{3118,9845,0,1,4}-{3111,9844,0,1,6}-{3123,9845,0,1,3}-{3114,9833,0,1,3}-{3110,9841,0,1,1}-{3119,9839,0,1,1}-{3097,9832,0,1,0}-{3101,9832,0,1,1}-{3107,9829,0,1,3}-{3115,9831,0,1,4}-{3109,9835,0,1,4}-{2369,3404,0,1,0}-{2904,9734,0,1,0}-{2439,3214,0,1,0}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-" + }, + { + "npc_id": "118", + "loc_data": "{2603,3056,0,1,0}-{2598,3064,0,1,3}-{2589,3068,0,1,5}-{2930,9703,0,1,0}-{2933,9702,0,1,0}-{3000,9842,0,1,2}-{2996,9844,0,1,4}-{3003,9828,0,1,1}-{2995,9811,0,1,3}-{3002,9811,0,1,3}-{2994,9809,0,1,5}-{3004,9813,0,1,6}-{2998,9828,0,1,4}-{3004,9799,0,1,4}-{2997,9809,0,1,0}-{2983,9807,0,1,6}-{2964,9811,0,1,1}-{3035,3443,0,1,6}-{3008,3449,0,1,4}-{3042,3465,0,1,1}-{3042,3468,0,1,3}-{3019,9813,0,1,4}-{3012,9812,0,1,3}-{3010,9811,0,1,3}-{3025,9801,0,1,4}-{3026,9824,0,1,1}-{3018,9819,0,1,6}-{3028,9815,0,1,3}-{3024,9811,0,1,4}-{3021,9826,0,1,5}-{3022,9832,0,1,1}-{3023,9833,0,1,6}-{3027,9833,0,1,3}-{3028,9828,0,1,6}-{3023,9814,0,1,6}-{3042,9831,0,1,4}-{3024,9824,0,1,6}-{3019,9847,0,1,0}-{3034,9847,0,1,7}-{3021,9851,0,1,5}-{3043,9824,0,1,3}-{3038,9821,0,1,1}-{3044,9818,0,1,4}-{3047,9814,0,1,4}-{3037,9814,0,1,6}-" + }, + { + "npc_id": "119", + "loc_data": "{2921,9757,0,1,0}-{2915,9759,0,1,0}-{2927,9761,0,1,0}-{2925,9769,0,1,0}-{2931,9784,0,1,0}-{2938,9788,0,1,0}-{3233,3783,0,1,0}-{3241,3796,0,1,0}-{3253,3782,0,1,0}-{3019,3760,0,1,0}-{3019,3766,0,1,0}-{3021,3755,0,1,0}-{3025,3763,0,1,0}-{3029,10313,0,1,4}-{3028,10309,0,1,4}-{3033,10311,0,1,6}-{3035,10309,0,1,1}-{3035,10313,0,1,1}-" + }, + { + "npc_id": "120", + "loc_data": "{2874,9880,0,1,0}-{2861,9874,0,1,0}-{2863,9874,0,1,0}-{2866,9874,0,1,0}-{2865,9877,0,1,0}-{2864,9877,0,1,0}-{2861,9877,0,1,0}-{2867,9877,0,1,0}-{2867,9879,0,1,0}-" + }, + { + "npc_id": "122", + "loc_data": "{2566,9632,0,1,0}-{2573,9626,0,1,0}-{2996,9574,0,1,6}-{3007,9579,0,1,7}-{3001,9580,0,1,4}-{2507,3726,0,1,2}-{2523,3724,0,1,3}-{2535,9556,0,1,4}-{3018,9580,0,1,3}-{3021,9585,0,1,4}-{3024,9582,0,1,4}-" + }, + { + "npc_id": "123", + "loc_data": "{2675,3731,0,1,6}-{2669,3726,0,1,6}-{2655,3727,0,1,6}-{2661,3732,0,1,6}-{2656,3720,0,1,6}-{3014,3787,0,1,0}-{3015,3813,0,1,0}-{3017,3781,0,1,0}-{3017,3793,0,1,0}-{3017,3803,0,1,0}-{3019,3789,0,1,0}-{3019,3798,0,1,0}-{3020,3809,0,1,0}-{3022,3805,0,1,0}-{3024,3781,0,1,0}-{3024,3796,0,1,0}-{3024,3812,0,1,0}-{3026,3791,0,1,0}-{3027,3797,0,1,0}-{3027,3804,0,1,0}-{3028,3809,0,1,0}-{3031,3795,0,1,0}-{3031,3802,0,1,0}-{3033,3785,0,1,0}-{3033,3811,0,1,0}-{3035,3806,0,1,0}-{3036,3795,0,1,0}-{3036,3800,0,1,0}-{3038,3809,0,1,0}-{3044,3802,0,1,0}-{3046,3797,0,1,0}-{2533,9554,0,1,4}-{2531,9556,0,1,5}-{2522,9562,0,1,4}-{2524,9558,0,1,4}-{2518,9562,0,1,6}-{2515,9566,0,1,3}-{2517,9573,0,1,1}-" + }, + { + "npc_id": "124", + "loc_data": "{3124,9974,0,1,0}-{3119,9976,0,1,0}-{3116,9974,0,1,0}-{3118,9971,0,1,0}-{3121,9997,0,1,0}-{3124,9996,0,1,0}-{3125,9993,0,1,0}-{3121,9993,0,1,0}-{3115,9992,0,1,0}-{3118,9990,0,1,0}-{3122,9989,0,1,0}-{3142,5532,0,1,6}-{3141,5531,0,1,6}-{3139,5526,0,1,3}-{3143,5524,0,1,3}-{3148,5523,0,1,4}-{3148,5531,0,1,4}-" + }, + { + "npc_id": "125", + "loc_data": "{2845,3517,0,1,4}-{2848,3515,0,1,4}-{2822,9901,0,1,0}-{2836,9905,0,1,0}-{2838,9917,0,1,0}-{2847,9919,0,1,0}-{2848,9912,0,1,0}-{2834,9940,0,1,0}-{2836,9953,0,1,0}-{2844,9944,0,1,0}-{2958,3867,0,1,3}-{2956,3857,0,1,6}-{2952,3862,0,1,3}-{2949,3858,0,1,4}-{2955,3875,0,1,7}-{2954,3874,0,1,5}-{2962,3876,0,1,1}-{2961,3877,0,1,3}-{2947,3878,0,1,6}-{2959,3884,0,1,4}-{2956,3885,0,1,3}-{2956,3886,0,1,3}-{2948,3886,0,1,1}-{2947,3934,0,1,0}-{2948,3917,0,1,0}-{2949,3926,0,1,0}-{2952,3913,0,1,0}-{2952,3936,0,1,0}-{2954,3921,0,1,0}-{2956,3930,0,1,0}-{2964,3944,0,1,0}-{2970,3947,0,1,0}-{2971,3938,0,1,0}-{2977,3953,0,1,0}-{2978,3942,0,1,0}-{2984,3933,0,1,0}-{3227,5443,0,1,5}-{3220,5448,0,1,5}-{3208,5443,0,1,6}-{3207,5448,0,1,4}-{3050,9570,0,1,3}-{3043,9579,0,1,2}-{3056,9585,0,1,0}-{3058,9575,0,1,1}-{3052,9582,0,1,0}-{3049,9577,0,1,4}-{3042,9586,0,1,6}-{3052,9588,0,1,1}-{3049,9590,0,1,5}-{3060,9578,0,1,3}-{3054,9566,0,1,7}-" + }, + { + "npc_id": "126", + "loc_data": "{2381,4422,0,1,5}-{2381,4425,0,1,4}-{2382,4429,0,1,0}-{2385,4419,0,1,0}-{2388,4421,0,1,1}-{2388,4428,0,1,4}-" + }, + { + "npc_id": "127", + "loc_data": "{3193,3959,0,1,0}-{3188,3960,0,1,0}-{3190,3962,0,1,0}-{3187,3959,0,1,0}-{3192,3961,0,1,0}-{3187,3960,0,1,0}-{3193,3959,0,1,0}-{3187,3961,0,1,0}-{3193,3958,0,1,0}-{2956,9791,0,1,0}-{2966,9788,0,1,0}-{2959,9783,0,1,0}-{2964,9775,0,1,0}-{2954,9776,0,1,0}-{2955,9795,0,1,0}-{2962,9792,0,1,0}-" + }, + { + "npc_id": "128", + "loc_data": "{2844,3040,0,1,0}-{2840,3049,0,1,0}-{2837,3043,0,1,0}-{2834,3042,0,1,0}-{2827,3026,0,1,0}-{2824,3018,0,1,0}-{2830,3021,0,1,0}-{2832,3018,0,1,0}-{2824,3084,0,1,0}-{2821,3078,0,1,0}-{2817,3078,0,1,0}-{2837,3076,0,1,0}-{2877,3154,0,1,0}-{2878,3153,0,1,0}-{2870,3161,0,1,0}-{2822,3192,0,1,5}-{2610,3274,0,1,0}-{2610,3275,0,1,0}-{2615,3276,0,1,0}-{2616,3275,0,1,0}-{2616,3276,0,1,0}-{2925,2969,0,1,0}-{2939,2977,0,1,0}-{2936,2980,0,1,0}-{2933,2976,0,1,0}-{2930,2971,0,1,0}-{2928,2984,0,1,0}-{2942,2983,0,1,0}-{2936,2989,0,1,0}-{2887,3145,0,1,0}-{2741,3173,0,0,0}-{2746,3167,0,0,0}-{2746,3147,0,1,0}-{2733,3162,0,1,0}-{2730,3146,0,1,0}-{2782,3104,0,1,0}-{2782,3103,0,1,0}-{2777,3090,0,1,0}-{2773,3089,0,1,0}-{2803,3115,0,1,0}-{2808,3112,0,1,0}-{2759,3179,0,0,0}-{2757,3170,0,0,0}-{2781,3173,0,1,0}-{2770,3213,0,0,0}-" + }, + { + "npc_id": "131", + "loc_data": "{2594,3271,0,1,4}-{2593,3269,0,1,1}-{2592,3272,0,1,6}-" + }, + { + "npc_id": "132", + "loc_data": "{2875,2936,0,1,2}-{2837,3038,0,1,0}-{2837,3041,0,1,3}-{2831,3031,0,1,3}-{2818,3054,0,1,4}-{2832,3050,0,1,6}-{2832,3011,0,1,4}-{2876,3011,0,1,3}-{2863,3021,0,1,3}-{2867,3016,0,1,1}-{2871,3022,0,1,4}-{2877,3028,0,1,3}-{2868,3012,0,1,4}-{2867,3163,0,1,3}-{2856,3144,0,1,7}-{2600,3275,0,1,6}-{2600,3279,0,1,4}-{2603,3279,0,1,3}-{2603,3281,0,1,3}-{2604,3277,0,1,6}-{2897,3064,0,1,0}-{2896,3071,0,1,1}-{2910,3026,0,1,5}-{2902,3010,0,1,1}-{2928,3029,0,1,4}-{2920,3027,0,1,6}-{2932,3023,0,1,3}-{2933,3024,0,1,3}-{2929,3009,0,1,0}-{2927,3022,0,1,1}-{2926,3023,0,1,6}-{2927,3020,0,1,7}-{2891,3079,0,1,1}-{2908,3078,0,1,4}-{2920,3081,0,1,4}-{2890,3082,0,1,1}-{2916,3082,0,1,1}-{2883,3147,0,1,6}-{2945,2979,0,1,3}-{2953,2965,0,1,4}-{2952,2982,0,1,4}-{2955,3003,0,1,3}-{2944,2999,0,1,5}-{2749,3173,0,1,3}-{2784,3005,0,1,1}-{2791,2999,0,1,2}-{2791,2988,0,1,1}-{2795,2987,0,1,6}-{2797,2985,0,1,3}-{2782,3008,0,1,1}-{2812,3180,0,1,1}-{2791,3181,0,1,3}-{2758,3170,0,1,4}-{2754,3167,0,1,3}-{2754,3175,0,1,1}-" + }, + { + "npc_id": "133", + "loc_data": "{3072,3625,0,1,7}-{3079,3627,0,1,3}-{3083,3632,0,1,6}-{3082,3643,0,1,0}-{3079,3637,0,1,5}-{3086,3636,0,1,3}-{3096,3641,0,1,2}-{3115,3644,1,0,5}-{3112,3614,0,1,5}-{3124,3600,0,1,1}-{3132,3597,0,1,0}-{2730,3659,0,1,7}-{2708,3672,0,1,7}-" + }, + { + "npc_id": "134", + "loc_data": "{2598,9558,0,1,6}-{2599,9557,0,1,4}-{2598,9556,0,1,4}-{2600,9555,0,1,3}-{2601,9553,0,1,1}-{2572,9569,0,1,3}-{2599,9552,0,1,6}-{2601,9560,0,1,1}-{2599,9564,0,1,6}-{2600,9563,0,1,4}-{2597,9569,0,1,1}-{2594,9571,0,1,2}-{2589,9571,0,1,4}-{2589,9574,0,1,3}-{2584,9572,0,1,6}-{2581,9575,0,1,6}-{2583,9577,0,1,6}-{2582,9572,0,1,3}-{2572,9568,0,1,1}-{2570,9567,0,1,1}-{2572,9571,0,1,3}-{2575,9572,0,1,0}-{2577,9574,0,1,3}-{2580,9578,0,1,3}-{2597,9572,0,1,6}-{2871,9793,0,1,0}-{2869,9799,0,1,0}-{2876,9806,0,1,0}-{2864,9819,0,1,0}-{2857,9822,0,1,0}-{2858,9814,0,1,0}-{2859,9802,0,1,0}-{2849,9809,0,1,0}-{3088,9945,0,1,3}-{3089,9943,0,1,0}-{3090,9944,0,1,7}-{3278,5477,0,1,1}-{3278,5476,0,1,2}-{3277,5451,0,1,5}-{3283,5449,0,1,4}-{3280,5476,0,1,3}-{3274,5463,0,1,1}-{3272,5477,0,1,1}-{3271,5473,0,1,4}-{3270,5459,0,1,1}-{3271,5454,0,1,3}-{3278,5525,0,1,3}-{3272,5529,0,1,3}-{3270,5536,0,1,6}-{3277,5536,0,1,4}-{3277,5512,0,1,3}-{3279,5516,0,1,6}-{3280,5531,0,1,6}-{3043,10262,0,1,1}-{3044,10252,0,1,4}-{3051,10257,0,1,3}-{3055,10253,0,1,3}-{3067,10255,0,1,3}-{3067,10257,0,1,3}-{3068,10253,0,1,2}-{3069,10257,0,1,4}-{3069,10259,0,1,0}-" + }, + { + "npc_id": "138", + "loc_data": "{2375,3434,0,1,4}-{2379,3438,0,1,6}-" + }, + { + "npc_id": "139", + "loc_data": "{2378,3432,0,1,2}-{2375,3438,0,1,7}-{2376,3431,0,1,1}-{2380,3438,0,1,4}-" + }, + { + "npc_id": "141", + "loc_data": "{3324,5510,0,1,3}-{3286,5512,0,1,1}-{3310,5524,0,1,2}-{3289,5527,0,1,7}-{3296,5519,0,1,4}-" + }, + { + "npc_id": "142", + "loc_data": "{2514,3194,0,1,3}-{2547,3178,0,1,1}-" + }, + { + "npc_id": "143", + "loc_data": "{2835,2934,0,1,4}-{2889,2936,0,1,2}-{2921,2937,0,1,1}-{2787,2929,0,1,6}-" + }, + { + "npc_id": "144", + "loc_data": "{2842,3298,0,1,6}-{2844,3291,0,1,6}-{2851,3298,0,1,4}-{2855,3303,0,1,4}-{2859,3283,0,1,1}-{2861,3295,0,1,5}-{3075,3848,0,1,0}-{3090,3841,0,1,0}-{3070,3829,0,1,0}-{3070,3836,0,1,0}-{3040,9775,0,1,4}-{3048,9770,0,1,1}-" + }, + { + "npc_id": "145", + "loc_data": "{2864,9950,0,1,0}-{2865,9951,0,1,0}-{2866,9948,0,1,0}-{2867,9948,0,1,0}-{2867,9951,0,1,0}-{2868,9950,0,1,0}-" + }, + { + "npc_id": "146", + "loc_data": "{2827,3139,0,1,0}-{2896,2992,0,1,7}-{2976,3053,0,1,0}-{2985,3038,0,1,0}-{2994,3048,0,1,5}-{2992,3030,0,1,0}-" + }, + { + "npc_id": "151", + "loc_data": "{2756,3156,0,0,4}-{2760,3152,0,0,6}-" + }, + { + "npc_id": "152", + "loc_data": "{3103,3347,0,0,0}-{3107,3337,0,0,0}-{3107,3342,0,0,0}-{3107,3344,0,0,0}-{3108,3346,0,0,0}-{3111,3339,0,0,0}-{3111,3348,0,0,0}-{3115,3344,0,0,0}-{3120,3344,0,0,0}-{3122,3335,0,0,0}-" + }, + { + "npc_id": "153", + "loc_data": "{3082,3077,0,1,0}-{3092,3082,0,1,0}-{3109,3090,0,1,0}-{3112,3093,0,1,0}-{2324,4599,0,1,0}-{2325,4597,0,1,0}-{2326,4589,0,1,0}-{2326,4595,0,1,0}-{2327,4591,0,1,0}-{2327,4595,0,1,0}-{2328,4593,0,1,0}-{2329,4593,0,1,0}-{2332,4588,0,1,0}-{2828,5091,0,1,0}-{2832,5108,0,1,0}-{2835,5079,0,1,0}-{2846,5067,0,1,0}-{2850,5111,0,1,0}-{2853,5086,0,1,0}-{2864,5071,0,1,0}-{3175,3226,0,1,0}-{3178,3228,0,1,0}-{3179,3226,0,1,0}-{3182,3251,0,1,0}-{3183,3254,0,1,0}-{2416,3404,0,1,0}-{2424,3410,0,1,0}-{2430,3405,0,1,0}-{2415,4466,0,1,0}-{2419,4427,0,1,0}-{2421,4468,0,1,0}-{2426,4431,0,1,0}-{1906,5223,0,1,0}-{1907,5221,0,1,0}-{2196,3191,0,1,0}-{2197,3189,0,1,0}-{2198,3181,0,1,0}-{2198,3187,0,1,0}-{2199,3183,0,1,0}-{2199,3187,0,1,0}-{2200,3185,0,1,0}-{2201,3185,0,1,0}-{2204,3180,0,1,0}-{2176,3202,0,1,0}-{2178,3206,0,1,0}-{2180,3203,0,1,0}-{2180,3207,0,1,0}-{2207,3215,0,1,0}-{2212,3222,0,1,0}-{2213,3216,0,1,0}-{2216,3240,0,1,0}-{2216,3242,0,1,0}-{2218,3238,0,1,0}-{2218,3241,0,1,0}-{2220,3242,0,1,0}-{2222,3237,0,1,0}-{2478,3373,0,1,0}-{2479,3381,0,1,0}-{2481,3384,0,1,0}-{2490,3371,0,1,0}-{2437,3425,0,1,0}-{2438,3422,0,1,0}-{2450,3419,0,1,0}-{2464,4421,0,1,0}-{2466,4423,0,1,0}-{2486,4464,0,1,0}-{3200,5954,0,1,0}-{3202,5958,0,1,0}-{3204,5955,0,1,0}-{3204,5959,0,1,0}-{3231,5967,0,1,0}-{3236,5974,0,1,0}-{3237,5968,0,1,0}-{3240,5992,0,1,0}-{3240,5994,0,1,0}-{3242,5990,0,1,0}-{3242,5993,0,1,0}-{3244,5994,0,1,0}-{3246,5989,0,1,0}-{2689,6083,0,1,0}-{2689,6088,0,1,0}-{2690,6094,0,1,0}-{2691,6084,0,1,0}-{2692,6081,0,1,0}-{2692,6090,0,1,0}-{2693,6083,0,1,0}-{2693,6085,0,1,0}-{2693,6090,0,1,0}-{2694,6083,0,1,0}-{2694,6086,0,1,0}-{2695,6082,0,1,0}-{2695,6085,0,1,0}-{2695,6088,0,1,0}-{2695,6091,0,1,0}-{2695,6121,0,1,0}-{2698,6121,0,1,0}-{2699,6082,0,1,0}-{2699,6085,0,1,0}-{2700,6084,0,1,0}-{2701,6085,0,1,0}-{2702,6081,0,1,0}-{2702,6083,0,1,0}-{2703,6119,0,1,0}-{2704,6117,0,1,0}-{2705,6121,0,1,0}-{2730,6091,0,1,0}-{2732,6095,0,1,0}-{2734,6090,0,1,0}-{2735,6094,0,1,0}-{2735,6097,0,1,0}-{2738,6098,0,1,0}-{2697,6204,0,1,0}-{2698,6201,0,1,0}-{2698,6203,0,1,0}-{2700,6205,0,1,0}-{2701,6200,0,1,0}-{2710,6170,0,1,0}-{2712,6169,0,1,0}-{2714,6173,0,1,0}-{2716,6171,0,1,0}-{2717,6166,0,1,0}-{2717,6168,0,1,0}-{2718,6167,0,1,0}-{2718,6170,0,1,0}-{2549,3139,0,1,0}-{2241,3139,0,1,0}-{2241,3144,0,1,0}-{2242,3150,0,1,0}-{2243,3140,0,1,0}-{2244,3137,0,1,0}-{2244,3146,0,1,0}-{2245,3139,0,1,0}-{2245,3141,0,1,0}-{2245,3146,0,1,0}-{2246,3139,0,1,0}-{2246,3142,0,1,0}-{2247,3138,0,1,0}-{2247,3141,0,1,0}-{2247,3144,0,1,0}-{2247,3147,0,1,0}-{2247,3177,0,1,0}-{2250,3177,0,1,0}-{2251,3138,0,1,0}-{2251,3141,0,1,0}-{2252,3140,0,1,0}-{2253,3141,0,1,0}-{2254,3137,0,1,0}-{2254,3139,0,1,0}-{2255,3175,0,1,0}-{2256,3173,0,1,0}-{2257,3177,0,1,0}-{2282,3147,0,1,0}-{2284,3151,0,1,0}-{2286,3146,0,1,0}-{2287,3150,0,1,0}-{2287,3153,0,1,0}-{2290,3154,0,1,0}-{2249,3260,0,1,0}-{2250,3257,0,1,0}-{2250,3259,0,1,0}-{2252,3261,0,1,0}-{2253,3256,0,1,0}-{2262,3226,0,1,0}-{2264,3225,0,1,0}-{2266,3229,0,1,0}-{2268,3227,0,1,0}-{2269,3222,0,1,0}-{2269,3224,0,1,0}-{2270,3223,0,1,0}-{2270,3226,0,1,0}-{2556,3444,0,1,0}-{3273,6012,0,1,0}-{3274,6009,0,1,0}-{3274,6011,0,1,0}-{3276,6013,0,1,0}-{3277,6008,0,1,0}-{3286,5978,0,1,0}-{3288,5977,0,1,0}-{3290,5981,0,1,0}-{3292,5979,0,1,0}-{3293,5974,0,1,0}-{3293,5976,0,1,0}-{3294,5975,0,1,0}-{3294,5978,0,1,0}-{2540,9820,0,1,0}-{2543,9813,0,1,0}-{2546,9817,0,1,0}-" + }, + { + "npc_id": "154", + "loc_data": "{3125,3084,0,1,0}-{2308,4581,0,1,0}-{2321,4595,0,1,0}-{2324,4592,0,1,0}-{2328,4589,0,1,0}-{2328,4596,0,1,0}-{2331,4597,0,1,0}-{2831,5070,0,1,0}-{2843,5106,0,1,0}-{2844,5080,0,1,0}-{2860,5079,0,1,0}-{2866,5104,0,1,0}-{2868,5096,0,1,0}-{2378,3418,0,1,0}-{2380,3422,0,1,0}-{2393,3442,0,1,0}-{2397,3440,0,1,0}-{2423,3399,0,1,0}-{2425,3406,0,1,0}-{2427,3402,0,1,0}-{2422,3489,0,1,0}-{2394,4453,0,1,0}-{2394,4457,0,1,0}-{2406,4447,0,1,0}-{1908,5222,0,1,0}-{2180,3173,0,1,0}-{2193,3187,0,1,0}-{2196,3184,0,1,0}-{2200,3181,0,1,0}-{2200,3188,0,1,0}-{2203,3189,0,1,0}-{2178,3203,0,1,0}-{2182,3201,0,1,0}-{2207,3224,0,1,0}-{2209,3222,0,1,0}-{2211,3218,0,1,0}-{2217,3244,0,1,0}-{2218,3236,0,1,0}-{2220,3240,0,1,0}-{2446,3396,0,1,0}-{2470,3397,0,1,0}-{2475,4459,0,1,0}-{2480,4458,0,1,0}-{2481,4454,0,1,0}-{2488,4465,0,1,0}-{3202,5955,0,1,0}-{3206,5953,0,1,0}-{3231,5976,0,1,0}-{3233,5974,0,1,0}-{3235,5970,0,1,0}-{3241,5996,0,1,0}-{3242,5988,0,1,0}-{3244,5992,0,1,0}-{2688,6092,0,1,0}-{2690,6087,0,1,0}-{2690,6095,0,1,0}-{2692,6082,0,1,0}-{2692,6088,0,1,0}-{2692,6123,0,1,0}-{2693,6081,0,1,0}-{2694,6125,0,1,0}-{2695,6087,0,1,0}-{2697,6086,0,1,0}-{2697,6087,0,1,0}-{2698,6081,0,1,0}-{2698,6124,0,1,0}-{2700,6083,0,1,0}-{2702,6084,0,1,0}-{2726,6085,0,1,0}-{2727,6081,0,1,0}-{2729,6083,0,1,0}-{2732,6084,0,1,0}-{2716,6172,0,1,0}-{2240,3148,0,1,0}-{2242,3143,0,1,0}-{2242,3151,0,1,0}-{2244,3138,0,1,0}-{2244,3144,0,1,0}-{2244,3179,0,1,0}-{2245,3137,0,1,0}-{2246,3181,0,1,0}-{2247,3143,0,1,0}-{2249,3142,0,1,0}-{2249,3143,0,1,0}-{2250,3137,0,1,0}-{2250,3180,0,1,0}-{2252,3139,0,1,0}-{2254,3140,0,1,0}-{2278,3141,0,1,0}-{2279,3137,0,1,0}-{2281,3139,0,1,0}-{2284,3140,0,1,0}-{2268,3228,0,1,0}-{3292,5980,0,1,0}-" + }, + { + "npc_id": "155", + "loc_data": "{3093,3086,0,1,0}-{3097,3085,0,1,0}-{3129,3091,0,1,0}-{3135,3084,0,1,0}-{2318,4588,0,1,0}-{2374,3469,0,1,0}-{2376,3466,0,1,0}-{2190,3180,0,1,0}-{3254,3230,0,1,0}-{2434,3516,0,1,0}-{2479,3501,0,1,0}-{1986,5564,0,1,0}-" + }, + { + "npc_id": "156", + "loc_data": "{2371,3460,0,1,0}-{2372,3456,0,1,0}-{2422,3467,0,1,0}-{2444,3491,0,1,0}-{2725,6127,0,1,0}-{2277,3183,0,1,0}-" + }, + { + "npc_id": "157", + "loc_data": "{2104,2873,0,1,0}-{2102,2942,0,1,0}-{2108,2919,0,1,0}-{2326,2894,0,1,0}-{2333,2920,0,1,0}-{2314,2960,0,1,0}-{2110,2955,0,1,0}-{2322,4566,0,1,0}-{2344,4596,0,1,0}-{2345,4598,0,1,0}-{2348,4566,0,1,0}-{2350,4549,0,1,0}-{2357,4546,0,1,0}-{2359,4589,0,1,0}-{2364,4563,0,1,0}-{2050,5860,0,1,0}-{2056,5930,0,1,0}-{2064,5900,0,1,0}-{2092,5946,0,1,0}-{2086,6028,0,1,0}-{2153,2810,0,1,0}-{2172,2806,0,1,0}-{2113,2859,0,1,0}-{2139,2836,0,1,0}-{2147,2877,0,1,0}-{2165,2816,0,1,0}-{2167,2846,0,1,0}-{2123,2966,0,1,0}-{2175,2965,0,1,0}-{3143,3210,0,1,0}-{3155,3253,0,1,0}-{3163,3261,0,1,0}-{3168,3258,0,1,0}-{1912,5945,0,1,0}-{2134,5966,0,1,0}-{2141,5992,0,1,0}-{1910,6014,0,1,0}-{1916,5991,0,1,0}-{2122,6032,0,1,0}-{1918,6027,0,1,0}-{2180,2798,0,1,0}-{2209,2812,0,1,0}-{2217,2780,0,1,0}-{2182,2979,0,1,0}-{2211,2963,0,1,0}-{2194,3158,0,1,0}-{2216,3188,0,1,0}-{2217,3190,0,1,0}-{2220,3158,0,1,0}-{2222,3141,0,1,0}-{2229,3138,0,1,0}-{2231,3181,0,1,0}-{2236,3155,0,1,0}-{2177,3234,0,1,0}-{2230,3220,0,1,0}-{2231,3234,0,1,0}-{2232,3256,0,1,0}-{2233,3221,0,1,0}-{2233,3223,0,1,0}-{2236,3222,0,1,0}-{2237,3225,0,1,0}-{2238,3205,0,1,0}-{2479,3396,0,1,0}-{2447,3467,0,1,0}-{2448,3469,0,1,0}-{1961,5882,0,1,0}-{1980,5878,0,1,0}-{1921,5931,0,1,0}-{1947,5908,0,1,0}-{1955,5949,0,1,0}-{1973,5888,0,1,0}-{1975,5918,0,1,0}-{3201,5986,0,1,0}-{3254,5972,0,1,0}-{3255,5986,0,1,0}-{3256,6008,0,1,0}-{3257,5973,0,1,0}-{3257,5975,0,1,0}-{3260,5974,0,1,0}-{3261,5977,0,1,0}-{3262,5957,0,1,0}-{1931,6038,0,1,0}-{1983,6037,0,1,0}-{2723,6105,0,1,0}-{2739,6123,0,1,0}-{2744,6108,0,1,0}-{2703,6160,0,1,0}-{2710,6154,0,1,0}-{2719,6148,0,1,0}-{2742,6163,0,1,0}-{2744,6150,0,1,0}-{2746,6145,0,1,0}-{2242,2788,0,1,0}-{2248,2858,0,1,0}-{2256,2828,0,1,0}-{2284,2874,0,1,0}-{2278,2956,0,1,0}-{2540,3167,0,1,0}-{2275,3161,0,1,0}-{2291,3179,0,1,0}-{2296,3164,0,1,0}-{2255,3216,0,1,0}-{2262,3210,0,1,0}-{2271,3204,0,1,0}-{2294,3219,0,1,0}-{2296,3206,0,1,0}-{2298,3201,0,1,0}-{1988,5870,0,1,0}-{2017,5884,0,1,0}-{2025,5852,0,1,0}-{3279,5968,0,1,0}-{3286,5962,0,1,0}-{3295,5956,0,1,0}-{3318,5971,0,1,0}-{3320,5958,0,1,0}-{3322,5953,0,1,0}-{1990,6051,0,1,0}-{2019,6035,0,1,0}-" + }, + { + "npc_id": "158", + "loc_data": "{2693,9776,0,1,0}-{2697,9773,0,1,0}-{2697,9779,0,1,0}-{2700,9770,0,1,0}-{2700,9774,0,1,0}-{2700,9777,0,1,0}-{2701,9781,0,1,0}-{2703,9770,0,1,0}-{2703,9774,0,1,0}-{2703,9779,0,1,0}-{2705,9757,0,1,0}-{2705,9760,0,1,0}-{2705,9766,0,1,0}-{2706,9753,0,1,0}-{2706,9763,0,1,0}-{2706,9772,0,1,0}-{2707,9756,0,1,0}-{2707,9761,0,1,0}-{2708,9758,0,1,0}-" + }, + { + "npc_id": "159", + "loc_data": "{2413,3445,1,1,0}-{2415,3435,1,1,0}-{2416,3416,1,1,0}-{2424,3434,1,1,0}-{2392,3475,0,1,0}-{2394,3506,0,1,0}-{2396,3471,0,1,0}-{2405,3499,0,1,0}-{2416,3487,0,1,0}-{2483,3500,1,1,0}-" + }, + { + "npc_id": "160", + "loc_data": "{2424,3430,1,1,0}-{2474,3500,1,1,0}-" + }, + { + "npc_id": "161", + "loc_data": "{2409,3436,1,1,0}-{2484,3501,1,1,0}-" + }, + { + "npc_id": "162", + "loc_data": "{2480,3426,0,1,6}-{2489,3429,0,1,1}-{2480,3431,0,1,0}-{2475,3418,0,1,4}-{2470,3423,0,1,1}-" + }, + { + "npc_id": "163", + "loc_data": "{2392,3454,0,1,0}-{2394,3436,0,1,0}-{2408,3452,0,1,0}-{2421,3413,0,1,0}-{2409,3470,1,1,0}-{2416,3466,1,1,0}-{2420,3466,1,1,0}-{2459,3385,0,1,0}-{2463,3385,0,1,0}-{2451,3414,0,1,0}-{2459,3395,0,1,0}-{2459,3438,0,1,0}-{2462,3395,0,1,0}-{2445,3429,1,1,0}-{2442,3489,0,1,0}-{2450,3485,0,1,0}-{2451,3507,0,1,0}-{2453,3490,0,1,0}-{2459,3503,0,1,0}-{2461,3509,0,1,0}-{2464,3465,0,1,0}-{2464,3468,0,1,0}-{2468,3465,0,1,0}-{2468,3468,0,1,0}-{2468,3506,0,1,0}-{2472,3484,0,1,0}-{2475,3490,0,1,0}-{2475,3502,0,1,0}-{2477,3512,0,1,0}-{2481,3485,0,1,0}-{2482,3501,0,1,0}-{2467,3495,1,1,0}-{2473,3495,1,1,0}-{2448,3497,2,1,0}-{2463,3480,2,1,0}-{2465,3497,2,1,0}-{2465,3510,2,1,0}-{2466,3480,2,1,0}-{2461,9895,0,1,0}-{2465,9894,0,1,0}-{2465,9899,0,1,0}-" + }, + { + "npc_id": "164", + "loc_data": "{2410,3416,0,1,0}-{2420,3435,0,1,0}-{2420,3447,0,1,0}-{2427,3410,0,1,0}-{2460,3382,0,1,0}-{2462,3382,0,1,0}-{2459,3392,0,1,0}-{2461,3422,0,1,0}-{2462,3392,0,1,0}-{2441,3497,0,1,0}-{2447,3510,0,1,0}-{2461,3487,0,1,0}-{2464,3472,0,1,0}-{2464,3489,0,1,0}-{2467,3489,0,1,0}-{2468,3472,0,1,0}-{2473,3503,0,1,0}-{2478,3497,0,1,0}-{2464,3496,1,1,0}-{2448,3495,2,1,0}-{2460,3487,2,1,0}-{2467,3494,2,1,0}-{2467,3510,2,1,0}-{2471,3496,2,1,0}-{2483,3495,2,1,0}-{2483,3497,2,1,0}-{2012,5535,2,1,0}-{2023,5544,2,1,0}-" + }, + { + "npc_id": "166", + "loc_data": "{2448,3427,1,0,6}-{2448,3424,1,0,6}-{2443,3424,1,0,3}-{2443,3425,1,0,3}-{2450,3480,1,0,1}-{2449,3480,1,0,1}-{2448,3480,1,0,1}-{2440,3488,1,0,4}-{2440,3487,1,0,4}-" + }, + { + "npc_id": "168", + "loc_data": "{2398,3451,1,1,0}-{2414,3447,1,1,0}-{2379,3482,0,1,0}-{2381,3496,0,1,0}-{2384,3497,0,1,0}-{2391,3476,0,1,0}-{2410,3496,0,1,0}-{2421,3481,0,1,0}-{2397,3514,1,1,0}-{2434,3436,0,1,0}-{2437,3451,0,1,0}-{2438,3427,0,1,0}-{2441,3411,0,1,0}-{2466,3449,0,1,0}-{2470,3399,0,1,0}-{2472,3400,0,1,0}-{2473,3412,0,1,0}-{2476,3454,0,1,0}-{2482,3397,0,1,0}-{2489,3401,0,1,0}-{2479,3407,1,1,0}-{2438,3465,0,1,0}-{2442,3505,0,1,0}-{2448,3486,0,1,0}-{2449,3487,0,1,0}-{2450,3489,0,1,0}-{2450,3505,0,1,0}-{2454,3465,0,1,0}-{2449,3486,1,1,0}-{2457,3488,1,1,0}-{2476,3488,1,1,0}-{2450,3496,2,1,0}-{2467,3488,2,1,0}-{2470,3503,2,1,0}-{2481,3498,2,1,0}-" + }, + { + "npc_id": "169", + "loc_data": "{2392,3450,1,1,0}-{2415,3415,1,1,0}-{2416,3434,1,1,0}-{2423,3425,1,1,0}-{2424,3442,1,1,0}-{2378,3482,0,1,0}-{2383,3496,0,1,0}-{2402,3507,0,1,0}-{2406,3476,0,1,0}-{2422,3485,0,1,0}-{2382,3506,1,1,0}-{2418,3472,1,1,0}-{2439,3433,0,1,0}-{2441,3449,0,1,0}-{2442,3428,0,1,0}-{2446,3403,0,1,0}-{2450,3416,0,1,0}-{2468,3441,0,1,0}-{2480,3408,0,1,0}-{2480,3406,1,1,0}-{2486,3400,1,1,0}-{2448,3489,0,1,0}-{2450,3488,0,1,0}-{2463,3508,0,1,0}-{2473,3489,0,1,0}-{2474,3457,0,1,0}-{2479,3503,0,1,0}-{2486,3467,0,1,0}-{2437,3463,1,1,0}-{2448,3489,1,1,0}-{2482,3508,1,1,0}-" + }, + { + "npc_id": "170", + "loc_data": "{2890,3175,0,1,4}-" + }, + { + "npc_id": "171", + "loc_data": "{2409,9817,0,1,0}-" + }, + { + "npc_id": "175", + "loc_data": "{3084,3495,0,1,4}-{3179,3215,0,1,0}-{3182,3365,0,1,6}-{2994,9549,0,1,6}-{2993,9551,0,1,6}-{2995,9555,0,1,6}-{2999,9548,0,1,0}-" + }, + { + "npc_id": "178", + "loc_data": "{3096,3220,0,1,1}-{2909,9690,0,1,0}-{2911,9689,0,1,0}-{2905,9687,0,1,0}-{2912,9682,0,1,0}-{2917,9681,0,1,0}-{2918,9679,0,1,0}-{2916,9677,0,1,0}-{2911,9675,0,1,0}-{2906,9675,0,1,0}-{2905,9673,0,1,0}-{2897,9675,0,1,0}-{2899,9678,0,1,0}-{2898,9682,0,1,0}-{2897,9687,0,1,0}-{2903,9690,0,1,0}-{2904,9692,0,1,0}-{2902,9693,0,1,0}-{2915,9691,0,1,0}-{2916,9694,0,1,0}-{2913,9694,0,1,0}-{2908,9694,0,1,0}-{2908,9702,0,1,0}-{2906,9706,0,1,0}-{2915,9703,0,1,0}-{2915,9711,0,1,0}-{2899,9700,0,1,0}-{2898,9703,0,1,0}-{2898,9708,0,1,0}-{2900,9710,0,1,0}-{2907,9711,0,1,0}-{2923,9710,0,1,0}-{2920,9708,0,1,0}-{2919,9703,0,1,0}-{2891,9679,0,1,0}-{2891,9682,0,1,0}-{2890,9692,0,1,0}-{2891,9695,0,1,0}-{2892,9701,0,1,0}-{2893,9701,0,1,0}-{2892,9709,0,1,0}-{2895,9710,0,1,0}-{2942,9801,0,1,0}-{2938,9812,0,1,0}-{3273,3507,0,1,6}-" + }, + { + "npc_id": "179", + "loc_data": "{3025,3511,1,1,3}-{3038,3505,0,1,4}-{3026,3494,0,1,6}-{3026,3505,0,1,4}-{3026,3512,0,1,6}-{3025,3512,0,1,4}-{3027,3512,0,1,6}-{3030,3511,0,1,6}-{3028,3511,0,1,1}-{3023,3514,1,1,3}-{3014,3516,2,1,1}-{3038,3854,0,1,4}-{3029,3852,0,1,1}-{3032,3851,0,1,3}-{3023,3849,0,1,6}-{3042,3856,0,1,0}-{3050,3849,0,1,0}-{3054,3852,0,1,0}-{3057,3842,0,1,0}-{3058,3854,0,1,0}-" + }, + { + "npc_id": "180", + "loc_data": "{3110,3295,0,1,4}-{2957,3238,0,1,4}-{2953,3240,0,1,3}-{2988,3423,0,1,7}-{2744,3519,0,0,0}-{3011,3277,0,1,1}-{3009,3280,0,1,1}-" + }, + { + "npc_id": "181", + "loc_data": "{2563,3355,0,1,4}-{2561,3355,0,1,3}-{2561,3357,0,1,1}-{2563,3358,0,1,4}-{3105,9942,0,1,3}-{3106,9934,0,1,3}-{3109,9930,0,1,1}-{2937,9847,0,1,0}-{2934,9846,0,1,0}-{2929,9848,0,1,0}-{2931,9846,0,1,0}-{2937,9849,0,1,0}-{2936,9850,0,1,0}-" + }, + { + "npc_id": "182", + "loc_data": "{3690,2966,0,1,4}-" + }, + { + "npc_id": "183", + "loc_data": "{3670,2977,0,1,1}-{3654,2960,0,1,0}-{3698,2969,0,1,2}-{3676,2991,0,1,3}-{3666,2994,0,1,7}-{3666,2981,1,1,4}-{2805,3160,0,0,0}-{2803,3190,0,1,4}-{2795,3167,0,0,0}-{2793,3184,0,1,3}-" + }, + { + "npc_id": "184", + "loc_data": "{3659,2952,0,1,6}-{3669,2961,0,1,4}-{3683,2977,0,1,6}-{3668,2981,0,1,3}-{3655,2973,0,1,1}-{3675,2967,0,1,3}-{2994,9584,0,1,6}-{2992,9573,0,1,1}-{2990,9583,0,1,4}-{2998,9572,0,1,2}-{2998,9573,0,1,4}-{2989,9578,0,1,0}-{2993,9566,0,1,4}-{2994,9570,0,1,3}-{3039,3958,0,1,0}-" + }, + { + "npc_id": "186", + "loc_data": "{3160,3568,0,1,0}-{3167,3576,0,1,0}-{3171,3579,0,1,0}-{3172,3561,0,1,0}-{3175,3572,0,1,0}-{3179,3564,0,1,0}-{3184,3580,0,1,0}-{3189,3563,0,1,0}-{3190,3570,0,1,0}-" + }, + { + "npc_id": "187", + "loc_data": "{3076,3916,0,1,0}-{3079,3909,0,1,0}-{3291,3924,0,1,4}-{3294,3930,0,1,4}-{3286,3933,0,1,6}-{3285,3928,0,1,6}-{3287,3935,1,1,6}-{3286,3927,1,1,6}-{3283,3936,2,1,6}-{3292,3932,1,1,4}-{3291,3940,0,1,6}-{3280,3943,0,1,6}-{3278,3926,0,1,4}-" + }, + { + "npc_id": "188", + "loc_data": "{2940,3517,0,1,3}-{2938,3517,0,1,3}-{3158,5497,0,1,6}-{3157,5494,0,1,1}-{3155,5498,0,1,6}-{3140,5494,0,1,6}-{3143,5493,0,1,5}-{3142,5497,0,1,4}-{3156,5494,0,1,5}-{3148,5496,0,1,4}-" + }, + { + "npc_id": "189", + "loc_data": "{2936,3514,0,1,3}-{2931,3514,0,1,3}-{2933,3517,0,1,3}-{2936,3517,0,1,3}-{3151,5489,0,1,7}-{3151,5489,0,1,7}-{3145,5495,0,1,3}-{3142,5493,0,1,3}-{3149,5489,0,1,6}-{3155,5491,0,1,2}-{3142,5497,0,1,3}-" + }, + { + "npc_id": "190", + "loc_data": "{2833,9814,0,1,0}-{2829,9812,0,1,0}-{2825,9811,0,1,0}-{2829,9808,0,1,0}-{2833,9808,0,1,0}-{2827,9805,0,1,0}-" + }, + { + "npc_id": "191", + "loc_data": "{2868,3053,0,1,0}-{2840,3018,0,1,0}-{2919,2983,0,1,0}-{2937,2993,0,1,0}-{2940,2994,0,1,0}-{2940,2997,0,1,0}-{2942,2997,0,1,0}-{2730,3161,0,1,0}-{2729,3159,0,1,0}-{2727,3163,0,1,0}-{2725,3157,0,1,0}-{2726,3161,0,1,0}-{2725,3161,0,1,0}-{2723,3155,0,1,0}-{2723,3156,0,1,0}-{2725,3162,0,1,0}-{2775,3066,0,1,0}-{2802,3071,0,1,0}-{2770,3018,0,1,0}-{2770,3014,0,1,0}-" + }, + { + "npc_id": "192", + "loc_data": "{3022,3624,0,1,0}-{3022,3639,0,1,0}-{3029,3638,0,1,0}-{3030,3626,0,1,0}-{3035,3632,0,1,0}-{3036,3624,0,1,0}-{3036,3639,0,1,0}-{3022,3624,1,1,0}-{3022,3639,1,1,0}-{3023,3632,1,1,0}-{3028,3626,1,1,0}-{3029,3637,1,1,0}-{3035,3631,1,1,0}-{3036,3624,1,1,0}-{3036,3639,1,1,0}-" + }, + { + "npc_id": "193", + "loc_data": "{2581,9505,0,1,4}-{2594,9497,0,1,1}-{2593,9499,0,1,7}-{2587,9498,0,1,3}-{2573,9500,0,1,5}-{2579,9503,0,1,5}-{2580,9500,0,1,4}-{2578,9497,0,1,7}-" + }, + { + "npc_id": "195", + "loc_data": "{3029,3701,0,1,6}-{3031,3685,0,1,6}-{3032,3695,0,1,6}-{3033,3705,0,1,6}-{3037,3683,0,1,6}-{3040,3703,0,1,6}-" + }, + { + "npc_id": "196", + "loc_data": "{3038,3670,0,1,1}-{3036,3672,0,1,2}-{3037,3661,0,1,6}-{3039,3660,0,1,6}-" + }, + { + "npc_id": "198", + "loc_data": "{3190,3358,0,1,1}-" + }, + { + "npc_id": "199", + "loc_data": "{3079,3443,0,1,3}-" + }, + { + "npc_id": "201", + "loc_data": "{2931,9692,0,0,0}-" + }, + { + "npc_id": "202", + "loc_data": "{3039,3700,0,1,0}-" + }, + { + "npc_id": "203", + "loc_data": "{3038,3695,0,1,0}-" + }, + { + "npc_id": "204", + "loc_data": "{3035,3696,0,1,0}-" + }, + { + "npc_id": "205", + "loc_data": "{2613,9523,0,1,0}-" + }, + { + "npc_id": "206", + "loc_data": "{3026,3455,0,1,6}-{3015,3458,0,1,5}-" + }, + { + "npc_id": "208", + "loc_data": "{2568,3459,0,1,4}-" + }, + { + "npc_id": "209", + "loc_data": "{3013,3452,0,1,6}-" + }, + { + "npc_id": "216", + "loc_data": "{2851,3348,0,0,7}-" + }, + { + "npc_id": "217", + "loc_data": "{2852,3343,0,1,2}-" + }, + { + "npc_id": "218", + "loc_data": "{2613,3475,0,1,2}-" + }, + { + "npc_id": "222", + "loc_data": "{2851,3351,0,0,7}-{2849,3348,0,0,7}-" + }, + { + "npc_id": "223", + "loc_data": "{2569,3250,0,1,0}-" + }, + { + "npc_id": "224", + "loc_data": "{2579,9655,0,1,0}-{2581,9656,0,1,0}-{2581,9659,0,1,0}-{2583,9659,0,1,0}-{2584,9656,0,1,0}-{2586,9659,0,1,0}-{2587,9656,0,1,0}-{2589,9654,0,1,0}-{2589,9658,0,1,0}-{2591,9656,0,1,0}-{2591,9659,0,1,0}-" + }, + { + "npc_id": "225", + "loc_data": "{2641,3437,0,0,0}-" + }, + { + "npc_id": "227", + "loc_data": "{2643,3440,0,0,0}-" + }, + { + "npc_id": "228", + "loc_data": "{2634,3427,0,0,0}-" + }, + { + "npc_id": "229", + "loc_data": "{2628,3413,0,1,4}-" + }, + { + "npc_id": "230", + "loc_data": "{2649,3454,0,0,0}-" + }, + { + "npc_id": "231", + "loc_data": "{2650,3468,0,0,0}-" + }, + { + "npc_id": "232", + "loc_data": "{2819,3487,0,0,4}-" + }, + { + "npc_id": "237", + "loc_data": "{2769,3403,0,1,0}-{2766,3402,0,1,3}-{2770,3402,0,1,0}-{2772,3404,0,1,1}-{2767,3405,1,1,0}-{2771,3403,1,1,6}-{2767,3401,1,1,7}-" + }, + { + "npc_id": "239", + "loc_data": "{2758,3516,1,0,0}-" + }, + { + "npc_id": "240", + "loc_data": "{2760,3501,0,1,1}-" + }, + { + "npc_id": "241", + "loc_data": "{2762,3498,0,0,0}-" + }, + { + "npc_id": "242", + "loc_data": "{2764,3509,1,0,0}-" + }, + { + "npc_id": "243", + "loc_data": "{2756,3513,1,0,0}-" + }, + { + "npc_id": "244", + "loc_data": "{2765,3504,1,0,0}-" + }, + { + "npc_id": "245", + "loc_data": "{2755,3517,0,0,0}-" + }, + { + "npc_id": "247", + "loc_data": "{2769,3402,2,1,5}-" + }, + { + "npc_id": "250", + "loc_data": "{2924,3405,0,0,1}-" + }, + { + "npc_id": "251", + "loc_data": "{2761,3512,0,0,0}-" + }, + { + "npc_id": "253", + "loc_data": "{2580,3163,0,1,1}-{2590,3184,0,1,1}-{2589,3140,0,1,1}-{2601,3177,0,1,1}-{2607,3182,0,1,1}-{2612,3187,0,1,1}-" + }, + { + "npc_id": "254", + "loc_data": "{2569,3149,0,0,1}-{2614,3169,0,1,1}-{2564,3153,0,1,1}-{2568,3147,0,1,1}-{2579,3176,0,1,3}-{2580,3174,0,1,1}-{2584,3149,0,1,3}-{2590,3190,0,1,1}-{2594,3181,0,1,1}-{2595,3186,0,1,1}-{2597,3175,0,1,1}-{2597,3181,0,1,1}-{2607,3185,0,1,1}-{2614,3150,0,1,1}-{2619,3168,0,1,1}-" + }, + { + "npc_id": "255", + "loc_data": "{2571,3142,0,1,1}-{2617,3150,0,1,1}-{2609,3171,0,0,1}-{2617,3157,0,1,1}-{2577,3167,0,1,1}-{2597,3176,0,1,1}-{2607,3181,0,1,1}-{2608,3148,0,1,1}-{2600,3190,0,1,1}-{2610,3150,0,1,1}-{2609,3171,0,1,1}-" + }, + { + "npc_id": "256", + "loc_data": "{2609,3194,0,1,2}-" + }, + { + "npc_id": "259", + "loc_data": "{2566,3140,0,0,1}-" + }, + { + "npc_id": "260", + "loc_data": "{2589,3142,0,1,4}-" + }, + { + "npc_id": "261", + "loc_data": "{2587,3142,0,1,3}-" + }, + { + "npc_id": "262", + "loc_data": "{2615,3160,0,1,4}-{2616,3164,0,1,1}-{2594,3143,0,0,2}-" + }, + { + "npc_id": "263", + "loc_data": "{2597,3143,0,0,4}-" + }, + { + "npc_id": "264", + "loc_data": "{2568,3200,0,1,0}-" + }, + { + "npc_id": "266", + "loc_data": "{2601,3154,0,1,0}-" + }, + { + "npc_id": "267", + "loc_data": "{2601,3168,0,1,3}-" + }, + { + "npc_id": "268", + "loc_data": "{2582,3172,0,1,1}-" + }, + { + "npc_id": "269", + "loc_data": "{2608,3163,0,1,3}-" + }, + { + "npc_id": "270", + "loc_data": "{2608,3165,0,1,3}-" + }, + { + "npc_id": "271", + "loc_data": "{2608,3158,0,1,3}-" + }, + { + "npc_id": "272", + "loc_data": "{3127,3485,0,1,1}-" + }, + { + "npc_id": "273", + "loc_data": "{2573,3321,0,1,3}-" + }, + { + "npc_id": "274", + "loc_data": "{2638,9899,0,1,6}-{2639,9911,0,1,6}-{2648,9901,0,1,6}-{2649,9912,0,1,6}-{2649,9905,0,1,6}-{2650,9897,0,1,6}-" + }, + { + "npc_id": "275", + "loc_data": "{2636,9910,0,1,4}-{2637,9897,0,1,4}-{2643,9913,0,1,6}-{2646,9906,0,1,6}-" + }, + { + "npc_id": "276", + "loc_data": "{2656,9875,0,1,3}-" + }, + { + "npc_id": "278", + "loc_data": "{3209,3215,0,1,0}-" + }, + { + "npc_id": "279", + "loc_data": "{2604,3209,0,0,0}-" + }, + { + "npc_id": "280", + "loc_data": "{2615,3258,0,1,6}-" + }, + { + "npc_id": "281", + "loc_data": "{2604,3219,0,1,6}-{2609,3207,0,1,6}-{2618,3211,0,1,6}-" + }, + { + "npc_id": "282", + "loc_data": "{2562,9608,0,1,6}-{2564,9604,0,1,6}-{3280,3494,0,1,6}-" + }, + { + "npc_id": "283", + "loc_data": "{2569,9602,0,1,6}-" + }, + { + "npc_id": "284", + "loc_data": "{2953,3450,0,0,0}-" + }, + { + "npc_id": "285", + "loc_data": "{3110,3330,0,0,0}-" + }, + { + "npc_id": "286", + "loc_data": "{3111,3367,2,0,4}-" + }, + { + "npc_id": "287", + "loc_data": "{2770,3403,2,1,3}-" + }, + { + "npc_id": "288", + "loc_data": "{3110,3366,2,0,2}-{2677,3655,0,1,3}-{2681,3653,0,1,6}-" + }, + { + "npc_id": "289", + "loc_data": "{2616,3302,0,1,3}-" + }, + { + "npc_id": "290", + "loc_data": "{2614,3306,0,1,4}-" + }, + { + "npc_id": "296", + "loc_data": "{3084,3518,0,1,1}-" + }, + { + "npc_id": "297", + "loc_data": "{3093,3518,0,1,5}-" + }, + { + "npc_id": "298", + "loc_data": "{3113,3513,0,1,3}-" + }, + { + "npc_id": "299", + "loc_data": "{3113,3511,0,1,3}-" + }, + { + "npc_id": "300", + "loc_data": "{3105,9569,0,0,0}-" + }, + { + "npc_id": "302", + "loc_data": "{2519,3429,0,1,1}-" + }, + { + "npc_id": "304", + "loc_data": "{2520,3496,0,1,3}-" + }, + { + "npc_id": "305", + "loc_data": "{2511,3484,0,0,4}-" + }, + { + "npc_id": "306", + "loc_data": "{2514,9580,0,1,1}-" + }, + { + "npc_id": "307", + "loc_data": "{2968,3206,0,0,0}-" + }, + { + "npc_id": "308", + "loc_data": "{2611,3393,0,1,4}-" + }, + { + "npc_id": "309", + "loc_data": "{2860,2976,0,0,4}-{2856,2977,0,0,4}-{2852,2973,0,0,4}-{2561,3374,0,0,4}-{2562,3374,0,0,4}-{2566,3370,0,0,0}-{3104,3424,0,0,0}-{3104,3425,0,0,0}-{2637,3444,0,0,0}-{2678,3595,0,0,4}-{2633,3693,0,0,4}-{2633,3694,0,0,4}-{2471,3146,0,0,4}-{2465,3156,0,0,4}-{2461,3150,0,0,4}-{2215,3248,0,0,4}-{2217,3245,0,0,4}-{2213,3241,0,0,4}-{2716,3530,0,0,0}-{2267,3253,0,0,4}-{2266,3253,0,0,4}-{2262,3249,0,0,4}-" + }, + { + "npc_id": "310", + "loc_data": "{2860,2972,0,0,6}-{2865,2972,0,0,4}-{2382,3414,0,0,4}-{2382,3415,0,0,0}-{2392,3420,0,0,0}-{3239,3243,0,0,6}-{3239,3241,0,0,6}-{3238,3252,0,0,6}-{2537,3406,0,0,4}-{2527,3412,0,0,4}-{2507,3421,0,0,4}-{2508,3421,0,0,4}-" + }, + { + "npc_id": "312", + "loc_data": "{2860,3426,0,0,1}-{2421,3789,0,0,0}-{2410,3780,0,0,0}-{2414,3780,0,0,0}-" + }, + { + "npc_id": "313", + "loc_data": "{2840,3356,0,0,1}-{2843,3359,0,0,1}-{2848,3361,0,0,1}-{2879,3339,0,0,1}-{2879,3338,0,0,1}-{2879,3335,0,0,1}-{2879,3334,0,0,1}-{2876,3331,0,0,1}-{2853,3423,0,0,1}-{2401,3781,0,0,0}-" + }, + { + "npc_id": "316", + "loc_data": "{3086,3227,0,0,1}-{3085,3230,0,0,0}-{2986,3176,0,0,4}-{2996,3157,0,0,1}-{2790,3273,0,0,1}-{2790,3276,0,0,6}-{2794,3283,0,0,6}-{2795,3279,0,0,3}-" + }, + { + "npc_id": "322", + "loc_data": "{2641,3700,0,0,4}-{2640,3700,0,0,4}-{2777,2740,0,0,4}-{2780,2741,0,0,4}-{2788,2741,0,0,4}-{2771,2733,0,0,4}-{2771,2734,0,0,4}-" + }, + { + "npc_id": "323", + "loc_data": "{2926,3177,0,0,6}-{3248,3161,0,0,4}-{2516,3575,0,0,7}-{2511,3562,0,0,7}-{2498,3547,0,0,7}-" + }, + { + "npc_id": "324", + "loc_data": "{2632,3427,0,0,0}-{2645,3708,0,0,1}-{2648,3708,0,0,1}-{2647,3711,0,0,1}-" + }, + { + "npc_id": "325", + "loc_data": "{2671,3160,0,0,4}-{2672,3160,0,0,4}-{2678,3160,0,0,4}-{2682,3160,0,0,4}-{2683,3160,0,0,4}-" + }, + { + "npc_id": "326", + "loc_data": "{2840,3431,0,0,0}-{2855,3423,0,0,1}-{2607,3416,0,0,1}-{2604,3417,0,0,1}-{2602,3419,0,0,1}-{3246,3156,0,0,0}-{3245,3152,0,0,0}-{3242,3148,0,0,0}-{3267,3148,0,0,6}-{3276,3140,0,0,6}-{3275,3140,0,0,6}-" + }, + { + "npc_id": "333", + "loc_data": "{2845,3429,0,0,6}-{2602,3411,0,0,3}-{2605,3413,0,0,3}-{2609,3416,0,0,3}-{2610,3413,0,0,3}-{2605,3421,0,0,1}-{2602,3423,0,0,1}-{2603,3426,0,0,1}-{2923,3178,0,0,6}-{2923,3180,0,0,6}-{2926,3180,0,0,6}-" + }, + { + "npc_id": "334", + "loc_data": "{2612,3411,0,0,0}-{2165,3268,0,0,4}-{2162,3274,0,0,4}-{2627,3415,0,0,0}-{2639,3698,0,0,4}-{2642,3698,0,0,4}-{2699,2702,0,0,4}-{2694,2706,0,0,4}-{2700,2702,0,0,4}-{2707,2698,0,0,4}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2700,2702,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}-{2707,2698,0,0,0}-" + }, + { + "npc_id": "335", + "loc_data": "{2592,3336,0,1,0}-" + }, + { + "npc_id": "336", + "loc_data": "{2928,3218,0,1,4}-" + }, + { + "npc_id": "338", + "loc_data": "{2929,3222,0,0,6}-" + }, + { + "npc_id": "340", + "loc_data": "{2929,3217,0,1,6}-" + }, + { + "npc_id": "342", + "loc_data": "{3281,3382,0,0,0}-" + }, + { + "npc_id": "343", + "loc_data": "{3284,3382,0,0,0}-" + }, + { + "npc_id": "344", + "loc_data": "{2510,3378,0,1,0}-{2511,3383,0,1,0}-{2515,3356,0,1,0}-{2519,3356,0,1,0}-{2521,3383,0,1,0}-{2524,3362,0,1,0}-{2525,3383,0,1,0}-" + }, + { + "npc_id": "345", + "loc_data": "{2519,3366,0,1,0}-{2525,3369,0,1,0}-{2531,3368,0,1,0}-" + }, + { + "npc_id": "346", + "loc_data": "{2508,3370,0,1,0}-{2512,3374,0,1,0}-{2518,3371,0,1,0}-{2525,3380,0,1,0}-" + }, + { + "npc_id": "347", + "loc_data": "{2524,3292,0,1,3}-{2536,3294,0,1,3}-{2538,3321,0,1,3}-" + }, + { + "npc_id": "348", + "loc_data": "{2501,3315,0,1,3}-{2513,3325,0,1,3}-{2526,3279,0,1,3}-{2528,3297,0,1,3}-{2535,3288,0,1,3}-{2548,3287,0,1,3}-" + }, + { + "npc_id": "349", + "loc_data": "{2556,3266,0,0,0}-" + }, + { + "npc_id": "350", + "loc_data": "{2559,3266,0,0,0}-" + }, + { + "npc_id": "351", + "loc_data": "{2465,3307,0,1,0}-{2482,3293,0,1,0}-{2509,3325,0,1,0}-" + }, + { + "npc_id": "352", + "loc_data": "{2453,3307,0,1,0}-{2480,3300,0,1,0}-{2511,3322,0,1,0}-{2540,3279,0,1,0}-" + }, + { + "npc_id": "353", + "loc_data": "{2473,3288,0,1,0}-{2504,3326,0,1,0}-{2509,3314,0,1,0}-{2546,3277,0,1,0}-" + }, + { + "npc_id": "354", + "loc_data": "{2470,3290,0,1,0}-{2472,3296,0,1,0}-{2483,3318,0,1,0}-{2483,3323,0,1,0}-{2490,3293,0,1,0}-{2510,3318,0,1,0}-{2524,3271,0,1,0}-" + }, + { + "npc_id": "355", + "loc_data": "{2464,3302,0,1,0}-{2466,3300,0,1,0}-{2466,3307,0,1,0}-{2467,3306,0,1,0}-{2468,3315,0,1,0}-{2469,3319,0,1,0}-{2471,3323,0,1,0}-{2473,3305,0,1,0}-{2474,3296,0,1,0}-{2476,3307,0,1,0}-{2478,3323,0,1,0}-{2480,3296,0,1,0}-{2481,3313,0,1,0}-{2485,3312,0,1,0}-{2504,3318,0,1,0}-{2547,3277,0,1,0}-" + }, + { + "npc_id": "356", + "loc_data": "{2462,3306,0,1,0}-{2465,3305,0,1,0}-{2469,3295,0,1,0}-{2477,3320,0,1,0}-{2482,3311,0,1,0}-{2518,3275,0,1,0}-{2523,3307,0,1,0}-" + }, + { + "npc_id": "357", + "loc_data": "{2518,3309,0,1,3}-{2526,3303,0,1,3}-{2543,3309,0,1,3}-{2552,3320,0,1,3}-" + }, + { + "npc_id": "358", + "loc_data": "{2529,3285,0,1,0}-" + }, + { + "npc_id": "360", + "loc_data": "{2472,3307,0,1,0}-{2492,3314,0,1,0}-{2553,3275,0,1,0}-" + }, + { + "npc_id": "361", + "loc_data": "{2463,3317,0,1,0}-{2482,3296,0,1,0}-{2485,3315,0,1,0}-{2519,3277,0,1,0}-" + }, + { + "npc_id": "362", + "loc_data": "{2482,3286,0,1,0}-{2489,3295,0,1,0}-{2537,3324,0,1,0}-" + }, + { + "npc_id": "363", + "loc_data": "{2468,3290,0,1,0}-{2475,3325,0,1,0}-{2479,3312,0,1,0}-{2513,3315,0,1,0}-" + }, + { + "npc_id": "364", + "loc_data": "{2577,3298,1,0,0}-" + }, + { + "npc_id": "366", + "loc_data": "{2612,3324,0,0,0}-" + }, + { + "npc_id": "367", + "loc_data": "{2935,3210,0,1,2}-" + }, + { + "npc_id": "368", + "loc_data": "{3263,3407,0,1,2}-" + }, + { + "npc_id": "369", + "loc_data": "{2548,3324,0,1,3}-{2550,3326,0,1,3}-{2553,3325,0,1,3}-" + }, + { + "npc_id": "370", + "loc_data": "{2549,3325,1,1,0}-{2550,3327,1,1,0}-" + }, + { + "npc_id": "371", + "loc_data": "{2545,3326,0,1,3}-{2551,3322,0,1,3}-{2555,3324,0,1,3}-" + }, + { + "npc_id": "372", + "loc_data": "{2561,3305,0,1,2}-{2561,3303,0,1,7}-{2559,3304,0,1,3}-" + }, + { + "npc_id": "373", + "loc_data": "{2516,3274,0,1,0}-" + }, + { + "npc_id": "375", + "loc_data": "{3053,3249,0,0,0}-" + }, + { + "npc_id": "376", + "loc_data": "{3027,3217,0,1,4}-" + }, + { + "npc_id": "377", + "loc_data": "{3028,3223,0,1,2}-" + }, + { + "npc_id": "378", + "loc_data": "{3028,3220,0,1,4}-" + }, + { + "npc_id": "379", + "loc_data": "{2939,3156,0,1,4}-" + }, + { + "npc_id": "380", + "loc_data": "{2953,3149,0,0,0}-{2770,3226,0,0,0}-" + }, + { + "npc_id": "381", + "loc_data": "{2684,3272,0,1,4}-" + }, + { + "npc_id": "382", + "loc_data": "{3044,9758,0,1,3}-" + }, + { + "npc_id": "383", + "loc_data": "{2582,3485,0,1,7}-" + }, + { + "npc_id": "384", + "loc_data": "{2545,3568,0,1,7}-{2545,3571,0,1,6}-" + }, + { + "npc_id": "385", + "loc_data": "{2552,3570,0,1,6}-" + }, + { + "npc_id": "386", + "loc_data": "{2877,9797,0,1,0}-" + }, + { + "npc_id": "387", + "loc_data": "{3058,3488,1,0,0}-" + }, + { + "npc_id": "388", + "loc_data": "{2709,3485,0,1,1}-{2716,3475,0,1,3}-{2697,3472,0,1,6}-{2702,3469,0,1,3}-" + }, + { + "npc_id": "389", + "loc_data": "{2704,3405,3,1,3}-" + }, + { + "npc_id": "397", + "loc_data": "{3254,3258,0,1,1}-{3261,3260,0,1,1}-{3259,3279,0,1,6}-{3247,3292,0,1,3}-{3260,3291,0,1,2}-{3247,3282,0,1,5}-{3252,3283,0,1,6}-{3264,3258,0,1,2}-{3026,3303,0,1,4}-" + }, + { + "npc_id": "398", + "loc_data": "{2849,5091,0,0,0}-{2726,3349,0,1,6}-" + }, + { + "npc_id": "399", + "loc_data": "{2846,5081,0,0,0}-{2730,3349,0,1,1}-" + }, + { + "npc_id": "400", + "loc_data": "{2728,3377,0,1,1}-" + }, + { + "npc_id": "401", + "loc_data": "{2823,2941,0,0,0}-{2861,2941,0,0,0}-{2800,2943,0,0,0}-{2762,2944,0,0,0}-" + }, + { + "npc_id": "402", + "loc_data": "{2818,2939,0,0,0}-{2870,2943,0,0,0}-{2933,2944,0,0,0}-{2762,2943,0,0,0}-{2791,2944,0,0,0}-" + }, + { + "npc_id": "407", + "loc_data": "{3059,4507,0,0,3}-" + }, + { + "npc_id": "411", + "loc_data": "{2878,3161,0,0,0}-{2887,3164,0,0,0}-" + }, + { + "npc_id": "412", + "loc_data": "{3341,3478,0,1,0}-{3343,3484,0,1,0}-{3345,3484,0,1,0}-{3350,3493,0,1,0}-{3353,3494,0,1,0}-{3361,3484,0,1,0}-{3364,3489,0,1,0}-{3370,3497,0,1,0}-{3379,3478,0,1,0}-{3379,3484,0,1,0}-{3387,3483,0,1,0}-{3388,3494,0,1,0}-{3389,3483,0,1,0}-{3588,3472,0,1,0}-{3598,3509,0,1,0}-{3599,3486,0,1,0}-{3607,3462,0,1,0}-{3611,3499,0,1,0}-{3613,3482,0,1,0}-{3627,3511,0,1,0}-{3628,3473,0,1,0}-{3636,3466,0,1,0}-{3639,3510,0,1,0}-{3411,3536,0,1,0}-{3418,3522,0,1,0}-{3425,3525,0,1,0}-{3429,3522,0,1,0}-{3432,3527,0,1,0}-{3434,3521,0,1,0}-{3445,3526,0,1,0}-{2728,10122,0,1,1}-{2735,10123,0,1,3}-{2733,10129,0,1,1}-{2728,10133,0,1,6}-{2722,10133,0,1,6}-{2736,10137,0,1,1}-{2732,10142,0,1,0}-{2726,10142,0,1,6}-{2729,10140,0,1,3}-{2720,10144,0,1,6}-{2712,10142,0,1,3}-{3565,3503,0,1,0}-{3567,3481,0,1,0}-{3580,3491,0,1,0}-" + }, + { + "npc_id": "419", + "loc_data": "{3195,5450,0,1,6}-{3181,5449,0,1,1}-" + }, + { + "npc_id": "420", + "loc_data": "{3175,5454,0,1,4}-" + }, + { + "npc_id": "421", + "loc_data": "{3195,5459,0,1,6}-{3186,5454,0,1,5}-{3173,5494,0,1,6}-{3179,5491,0,1,1}-{3185,5492,0,1,4}-{3184,5487,0,1,3}-" + }, + { + "npc_id": "423", + "loc_data": "{2731,5340,0,1,7}-" + }, + { + "npc_id": "424", + "loc_data": "{2724,5359,0,1,3}-" + }, + { + "npc_id": "425", + "loc_data": "{2717,5332,0,1,7}-" + }, + { + "npc_id": "429", + "loc_data": "{2363,5214,0,1,6}-{2358,5214,0,1,7}-{2361,5212,0,1,6}-{2361,5209,0,1,4}-{2359,5213,0,1,1}-{2364,5215,0,1,6}-{2361,5216,0,1,1}-{2361,5212,0,1,0}-" + }, + { + "npc_id": "437", + "loc_data": "{2807,3191,0,0,0}-" + }, + { + "npc_id": "444", + "loc_data": "{2551,3197,0,1,1}-{2555,3194,0,1,4}-{2551,3157,0,1,3}-{2525,3155,0,1,3}-{2518,3149,0,1,3}-{2502,3175,0,1,1}-" + }, + { + "npc_id": "445", + "loc_data": "{2550,3196,0,1,4}-{2557,3197,0,1,1}-{2538,3155,0,1,7}-" + }, + { + "npc_id": "446", + "loc_data": "{2994,3194,0,1,1}-{3285,9896,0,1,1}-{3279,9895,0,1,6}-" + }, + { + "npc_id": "447", + "loc_data": "{3114,3242,0,1,1}-" + }, + { + "npc_id": "448", + "loc_data": "{3120,3238,0,1,6}-" + }, + { + "npc_id": "449", + "loc_data": "{3127,3248,0,1,1}-" + }, + { + "npc_id": "450", + "loc_data": "{3081,3220,0,1,0}-{3081,3222,0,1,0}-" + }, + { + "npc_id": "451", + "loc_data": "{3077,3234,0,1,0}-{2994,3215,0,1,2}-" + }, + { + "npc_id": "452", + "loc_data": "{3223,3293,0,0,0}-" + }, + { + "npc_id": "454", + "loc_data": "{2899,3430,1,1,3}-" + }, + { + "npc_id": "455", + "loc_data": "{2925,3485,0,1,5}-" + }, + { + "npc_id": "456", + "loc_data": "{3244,3205,0,0,0}-" + }, + { + "npc_id": "458", + "loc_data": "{3148,3175,0,1,4}-" + }, + { + "npc_id": "459", + "loc_data": "{2566,9507,0,1,6}-{2565,9506,0,0,5}-{2566,9507,0,1,3}-" + }, + { + "npc_id": "460", + "loc_data": "{2590,9488,0,1,3}-" + }, + { + "npc_id": "461", + "loc_data": "{2588,3091,1,1,3}-" + }, + { + "npc_id": "462", + "loc_data": "{2590,3084,0,1,5}-" + }, + { + "npc_id": "469", + "loc_data": "{2542,3169,0,1,4}-" + }, + { + "npc_id": "470", + "loc_data": "{2519,3212,0,1,3}-" + }, + { + "npc_id": "471", + "loc_data": "{2526,3160,1,1,6}-" + }, + { + "npc_id": "472", + "loc_data": "{2521,3168,0,1,4}-" + }, + { + "npc_id": "473", + "loc_data": "{2503,3192,0,0,0}-{2514,3159,0,0,6}-" + }, + { + "npc_id": "475", + "loc_data": "{2454,3300,0,1,7}-{2455,3301,0,1,7}-{2513,3262,0,1,3}-{2522,3251,0,1,1}-" + }, + { + "npc_id": "477", + "loc_data": "{2455,3301,0,1,6}-" + }, + { + "npc_id": "478", + "loc_data": "{2509,3255,0,1,3}-{2503,3254,1,1,3}-" + }, + { + "npc_id": "479", + "loc_data": "{2415,3433,3,1,0}-{2384,3481,0,1,0}-{2388,3473,0,1,0}-{2418,3474,3,1,0}-{2466,3500,2,1,0}-{1970,5522,3,1,0}-" + }, + { + "npc_id": "480", + "loc_data": "{2409,3507,0,1,0}-{2412,3474,3,1,0}-{2458,3417,1,1,0}-{2460,3417,1,1,0}-{2463,3504,2,1,0}-{1964,5522,3,1,0}-" + }, + { + "npc_id": "481", + "loc_data": "{2499,3266,0,0,4}-" + }, + { + "npc_id": "482", + "loc_data": "{2524,3258,0,1,4}-" + }, + { + "npc_id": "483", + "loc_data": "{2501,3234,0,0,4}-" + }, + { + "npc_id": "484", + "loc_data": "{2537,3168,0,1,5}-{2542,3167,0,0,1}-{2541,3168,0,0,2}-{2541,3171,0,0,3}-{2542,3172,0,0,4}-" + }, + { + "npc_id": "487", + "loc_data": "{1885,5029,0,0,6}-" + }, + { + "npc_id": "490", + "loc_data": "{1887,5026,0,0,6}-" + }, + { + "npc_id": "492", + "loc_data": "{2464,3227,0,1,0}-" + }, + { + "npc_id": "494", + "loc_data": "{2615,3094,0,0,3}-{2615,3092,0,0,3}-{2615,3092,0,0,3}-{2615,3094,0,0,3}-{3122,3125,0,0,6}-{3120,3125,0,0,6}-{3090,3242,0,0,4}-{3090,3245,0,0,4}-{3090,3243,0,0,4}-{2618,3330,0,0,0}-{2619,3330,0,0,0}-{2584,3422,0,0,4}-{2584,3419,0,0,4}-{2584,3418,0,0,4}-{2657,3283,0,0,3}-{2657,3286,0,0,3}-{2807,3443,0,0,6}-{2810,3443,0,0,6}-" + }, + { + "npc_id": "495", + "loc_data": "{2615,3091,0,0,3}-{2615,3091,0,0,3}-{2615,3330,0,0,0}-{2584,3421,0,0,4}-{2809,3443,0,0,6}-{2811,3443,0,0,6}-" + }, + { + "npc_id": "496", + "loc_data": "{3267,3164,0,0,4}-{3267,3167,0,0,4}-{3267,3169,0,0,4}-" + }, + { + "npc_id": "497", + "loc_data": "{3267,3166,0,0,4}-{3267,3168,0,0,4}-" + }, + { + "npc_id": "498", + "loc_data": "{2384,4457,0,0,7}-{2385,4461,0,0,1}-" + }, + { + "npc_id": "499", + "loc_data": "{2853,2953,0,0,1}-{2852,2953,0,0,1}-" + }, + { + "npc_id": "500", + "loc_data": "{2880,2951,0,0,6}-" + }, + { + "npc_id": "504", + "loc_data": "{2870,2950,0,0,0}-{2877,2959,0,0,0}-" + }, + { + "npc_id": "505", + "loc_data": "{2868,2954,0,0,6}-{2876,2948,0,0,1}-" + }, + { + "npc_id": "510", + "loc_data": "{2780,3211,0,0,0}-" + }, + { + "npc_id": "511", + "loc_data": "{2834,2956,0,0,0}-" + }, + { + "npc_id": "512", + "loc_data": "{2862,2995,1,0,0}-" + }, + { + "npc_id": "513", + "loc_data": "{2857,2962,0,0,6}-" + }, + { + "npc_id": "514", + "loc_data": "{2870,2974,1,0,6}-" + }, + { + "npc_id": "515", + "loc_data": "{2834,2987,0,0,0}-" + }, + { + "npc_id": "516", + "loc_data": "{2826,2958,0,1,3}-" + }, + { + "npc_id": "517", + "loc_data": "{2870,2970,0,0,0}-" + }, + { + "npc_id": "518", + "loc_data": "{2764,2959,1,0,0}-" + }, + { + "npc_id": "519", + "loc_data": "{3228,3203,0,1,6}-" + }, + { + "npc_id": "520", + "loc_data": "{3217,3241,0,1,4}-" + }, + { + "npc_id": "521", + "loc_data": "{3217,3240,0,1,3}-" + }, + { + "npc_id": "522", + "loc_data": "{3218,3415,0,1,3}-" + }, + { + "npc_id": "523", + "loc_data": "{3217,3411,0,1,3}-" + }, + { + "npc_id": "524", + "loc_data": "{3316,3184,0,1,3}-" + }, + { + "npc_id": "525", + "loc_data": "{3316,3182,0,1,3}-" + }, + { + "npc_id": "527", + "loc_data": "{2959,3390,0,1,1}-{2957,3388,0,1,3}-" + }, + { + "npc_id": "528", + "loc_data": "{3078,3512,0,1,3}-" + }, + { + "npc_id": "529", + "loc_data": "{3079,3507,0,1,7}-" + }, + { + "npc_id": "530", + "loc_data": "{2947,3217,0,1,1}-" + }, + { + "npc_id": "531", + "loc_data": "{2947,3212,0,1,1}-" + }, + { + "npc_id": "532", + "loc_data": "{2906,3145,0,1,3}-" + }, + { + "npc_id": "533", + "loc_data": "{2906,3144,0,1,3}-" + }, + { + "npc_id": "534", + "loc_data": "{2375,4449,0,1,5}-" + }, + { + "npc_id": "535", + "loc_data": "{2376,4447,0,0,0}-" + }, + { + "npc_id": "536", + "loc_data": "{3192,3359,1,1,3}-" + }, + { + "npc_id": "537", + "loc_data": "{3192,3355,1,1,6}-" + }, + { + "npc_id": "538", + "loc_data": "{3076,3429,0,1,3}-" + }, + { + "npc_id": "539", + "loc_data": "{3299,3204,0,0,0}-" + }, + { + "npc_id": "540", + "loc_data": "{3288,3212,0,1,6}-" + }, + { + "npc_id": "541", + "loc_data": "{3288,3190,0,1,3}-" + }, + { + "npc_id": "542", + "loc_data": "{3316,3175,0,1,3}-" + }, + { + "npc_id": "543", + "loc_data": "{3272,3182,0,1,6}-" + }, + { + "npc_id": "544", + "loc_data": "{3316,3164,0,1,3}-" + }, + { + "npc_id": "545", + "loc_data": "{3322,3195,0,1,3}-" + }, + { + "npc_id": "546", + "loc_data": "{3203,3433,0,1,3}-" + }, + { + "npc_id": "547", + "loc_data": "{3217,3435,0,0,3}-" + }, + { + "npc_id": "548", + "loc_data": "{3205,3416,0,1,6}-" + }, + { + "npc_id": "549", + "loc_data": "{3229,3438,0,1,6}-" + }, + { + "npc_id": "550", + "loc_data": "{3232,3423,0,1,6}-" + }, + { + "npc_id": "551", + "loc_data": "{3210,3400,0,1,3}-" + }, + { + "npc_id": "552", + "loc_data": "{3206,3400,0,1,3}-" + }, + { + "npc_id": "553", + "loc_data": "{3253,3403,0,1,1}-" + }, + { + "npc_id": "554", + "loc_data": "{3281,3398,0,1,3}-" + }, + { + "npc_id": "555", + "loc_data": "{2657,3153,0,1,0}-" + }, + { + "npc_id": "556", + "loc_data": "{3012,3244,0,1,6}-" + }, + { + "npc_id": "557", + "loc_data": "{3012,3203,0,1,1}-" + }, + { + "npc_id": "558", + "loc_data": "{3013,3225,0,1,1}-" + }, + { + "npc_id": "559", + "loc_data": "{3026,3252,0,1,3}-" + }, + { + "npc_id": "560", + "loc_data": "{2767,3122,0,1,6}-" + }, + { + "npc_id": "561", + "loc_data": "{2514,3385,0,1,0}-" + }, + { + "npc_id": "562", + "loc_data": "{2799,3438,0,1,1}-" + }, + { + "npc_id": "563", + "loc_data": "{2803,3430,0,1,6}-" + }, + { + "npc_id": "564", + "loc_data": "{2485,4450,0,1,3}-" + }, + { + "npc_id": "565", + "loc_data": "{2476,4467,0,1,2}-" + }, + { + "npc_id": "566", + "loc_data": "{2480,4471,0,1,4}-" + }, + { + "npc_id": "568", + "loc_data": "{2925,3143,0,1,3}-" + }, + { + "npc_id": "569", + "loc_data": "{2659,3316,0,1,2}-" + }, + { + "npc_id": "570", + "loc_data": "{2669,3303,0,1,5}-" + }, + { + "npc_id": "571", + "loc_data": "{2655,3310,0,1,1}-{2669,3310,0,1,3}-" + }, + { + "npc_id": "572", + "loc_data": "{2659,3296,0,1,4}-" + }, + { + "npc_id": "573", + "loc_data": "{2663,3296,0,1,4}-" + }, + { + "npc_id": "574", + "loc_data": "{2656,3300,0,1,3}-" + }, + { + "npc_id": "575", + "loc_data": "{2821,3442,0,1,6}-" + }, + { + "npc_id": "576", + "loc_data": "{2836,3447,0,1,4}-" + }, + { + "npc_id": "577", + "loc_data": "{2975,3384,0,1,3}-" + }, + { + "npc_id": "578", + "loc_data": "{2807,3342,0,1,6}-" + }, + { + "npc_id": "579", + "loc_data": "{3034,9845,0,1,3}-" + }, + { + "npc_id": "580", + "loc_data": "{2952,3388,0,1,3}-" + }, + { + "npc_id": "581", + "loc_data": "{2975,3314,0,1,3}-" + }, + { + "npc_id": "582", + "loc_data": "{2998,9828,0,1,1}-" + }, + { + "npc_id": "583", + "loc_data": "{3011,3257,0,1,3}-" + }, + { + "npc_id": "584", + "loc_data": "{2944,3332,0,1,3}-" + }, + { + "npc_id": "585", + "loc_data": "{2946,3205,0,1,6}-" + }, + { + "npc_id": "586", + "loc_data": "{2884,3450,0,1,3}-" + }, + { + "npc_id": "587", + "loc_data": "{2897,3428,0,1,3}-" + }, + { + "npc_id": "588", + "loc_data": "{2803,3152,0,1,3}-" + }, + { + "npc_id": "589", + "loc_data": "{2660,3291,0,1,3}-" + }, + { + "npc_id": "590", + "loc_data": "{2613,3294,0,1,3}-" + }, + { + "npc_id": "591", + "loc_data": "{2615,3292,0,1,6}-" + }, + { + "npc_id": "592", + "loc_data": "{2597,3401,0,1,2}-" + }, + { + "npc_id": "593", + "loc_data": "{2569,3098,0,1,3}-" + }, + { + "npc_id": "594", + "loc_data": "{2998,9840,0,1,3}-" + }, + { + "npc_id": "595", + "loc_data": "{3271,3412,0,1,3}-" + }, + { + "npc_id": "596", + "loc_data": "{3037,3705,0,0,0}-" + }, + { + "npc_id": "597", + "loc_data": "{3025,3701,0,1,5}-" + }, + { + "npc_id": "598", + "loc_data": "{2943,3384,0,1,3}-" + }, + { + "npc_id": "600", + "loc_data": "{2449,3511,1,1,6}-" + }, + { + "npc_id": "601", + "loc_data": "{2482,3510,1,1,0}-" + }, + { + "npc_id": "602", + "loc_data": "{2469,3488,2,1,6}-" + }, + { + "npc_id": "603", + "loc_data": "{2491,3488,1,1,6}-" + }, + { + "npc_id": "604", + "loc_data": "{3001,3144,0,0,0}-" + }, + { + "npc_id": "605", + "loc_data": "{2985,3343,2,1,2}-" + }, + { + "npc_id": "606", + "loc_data": "{2974,3343,0,1,0}-" + }, + { + "npc_id": "607", + "loc_data": "{2539,3547,0,1,4}-" + }, + { + "npc_id": "608", + "loc_data": "{2962,3337,2,1,4}-" + }, + { + "npc_id": "610", + "loc_data": "{3027,3506,0,0,0}-" + }, + { + "npc_id": "611", + "loc_data": "{3029,3505,0,1,0}-" + }, + { + "npc_id": "612", + "loc_data": "{3028,3510,0,0,0}-" + }, + { + "npc_id": "613", + "loc_data": "{3370,3387,0,1,6}-{3362,3416,0,1,6}-" + }, + { + "npc_id": "614", + "loc_data": "{3351,9756,0,1,1}-{3351,9820,0,1,1}-" + }, + { + "npc_id": "615", + "loc_data": "{3363,3397,0,1,3}-" + }, + { + "npc_id": "616", + "loc_data": "{3370,3416,0,1,3}-" + }, + { + "npc_id": "617", + "loc_data": "{3348,3424,0,1,3}-" + }, + { + "npc_id": "618", + "loc_data": "{3364,3342,0,1,3}-" + }, + { + "npc_id": "619", + "loc_data": "{3355,3333,0,1,3}-" + }, + { + "npc_id": "620", + "loc_data": "{3376,3377,0,1,0}-" + }, + { + "npc_id": "636", + "loc_data": "{2395,3494,0,0,6}-{2396,3494,0,0,6}-{2397,3494,0,0,6}-{2396,3493,0,0,6}-" + }, + { + "npc_id": "637", + "loc_data": "{3158,3425,1,1,5}-" + }, + { + "npc_id": "638", + "loc_data": "{3195,3404,0,0,0}-" + }, + { + "npc_id": "639", + "loc_data": "{3211,3425,0,1,7}-" + }, + { + "npc_id": "640", + "loc_data": "{3253,3487,0,1,6}-" + }, + { + "npc_id": "641", + "loc_data": "{3207,3392,0,1,6}-" + }, + { + "npc_id": "642", + "loc_data": "{3185,3385,0,1,6}-" + }, + { + "npc_id": "643", + "loc_data": "{3246,3383,1,1,5}-" + }, + { + "npc_id": "644", + "loc_data": "{3247,9781,0,1,6}-" + }, + { + "npc_id": "645", + "loc_data": "{3223,3401,0,1,2}-" + }, + { + "npc_id": "646", + "loc_data": "{3257,3447,0,1,0}-" + }, + { + "npc_id": "648", + "loc_data": "{3220,3471,0,0,6}-" + }, + { + "npc_id": "649", + "loc_data": "{3149,3210,0,1,0}-" + }, + { + "npc_id": "650", + "loc_data": "{3148,3205,0,1,0}-" + }, + { + "npc_id": "651", + "loc_data": "{3148,3207,0,1,0}-" + }, + { + "npc_id": "652", + "loc_data": "{3147,3209,0,1,0}-" + }, + { + "npc_id": "656", + "loc_data": "{2822,3374,0,0,3}-" + }, + { + "npc_id": "657", + "loc_data": "{3048,3236,0,1,2}-" + }, + { + "npc_id": "658", + "loc_data": "{2832,3336,0,1,2}-" + }, + { + "npc_id": "659", + "loc_data": "{3052,3373,0,0,0}-" + }, + { + "npc_id": "663", + "loc_data": "{3290,3290,0,1,2}-" + }, + { + "npc_id": "665", + "loc_data": "{2986,9811,0,0,0}-" + }, + { + "npc_id": "666", + "loc_data": "{2819,3451,0,0,3}-" + }, + { + "npc_id": "668", + "loc_data": "{3284,3503,1,1,1}-" + }, + { + "npc_id": "669", + "loc_data": "{2678,3086,1,1,1}-" + }, + { + "npc_id": "670", + "loc_data": "{2465,3496,0,1,6}-{2466,9896,0,0,6}-" + }, + { + "npc_id": "671", + "loc_data": "{2479,3463,1,1,6}-" + }, + { + "npc_id": "672", + "loc_data": "{2390,3515,1,1,6}-" + }, + { + "npc_id": "673", + "loc_data": "{2464,3495,3,0,6}-" + }, + { + "npc_id": "674", + "loc_data": "{3001,3041,0,0,0}-{2954,3025,0,0,0}-" + }, + { + "npc_id": "675", + "loc_data": "{2944,3041,0,0,0}-" + }, + { + "npc_id": "677", + "loc_data": "{2708,9486,0,0,5}-" + }, + { + "npc_id": "678", + "loc_data": "{2658,3436,0,1,3}-{2655,3431,0,1,0}-{2656,3424,0,1,1}-{2664,3426,0,1,4}-{2671,3438,0,1,1}-{2669,3441,0,1,7}-{2674,3438,0,1,5}-{2671,3418,0,1,7}-{2666,3417,0,1,3}-{2666,3415,0,1,2}-{2664,3416,0,1,3}-{2680,3431,0,1,6}-{2678,3431,0,1,1}-{2661,3430,0,1,4}-" + }, + { + "npc_id": "679", + "loc_data": "{2658,3439,0,0,0}-" + }, + { + "npc_id": "680", + "loc_data": "{2679,3432,0,1,6}-" + }, + { + "npc_id": "682", + "loc_data": "{2671,3434,0,1,4}-" + }, + { + "npc_id": "683", + "loc_data": "{2673,3434,0,1,3}-" + }, + { + "npc_id": "684", + "loc_data": "{2667,3430,2,0,6}-" + }, + { + "npc_id": "685", + "loc_data": "{2670,3429,2,0,3}-" + }, + { + "npc_id": "686", + "loc_data": "{2669,3426,2,0,1}-" + }, + { + "npc_id": "687", + "loc_data": "{2666,3427,2,0,4}-" + }, + { + "npc_id": "688", + "loc_data": "{2668,3442,2,1,6}-{2667,3444,2,1,6}-{2669,3444,2,1,6}-" + }, + { + "npc_id": "689", + "loc_data": "{2682,3428,2,1,3}-{2684,3427,2,1,3}-{2684,3429,2,1,3}-" + }, + { + "npc_id": "690", + "loc_data": "{2654,3428,2,1,1}-{2652,3427,2,1,1}-{2652,3429,2,1,1}-" + }, + { + "npc_id": "691", + "loc_data": "{2668,3414,2,1,4}-{2667,3412,2,1,4}-{2669,3412,2,1,4}-" + }, + { + "npc_id": "692", + "loc_data": "{2661,3419,0,1,7}-" + }, + { + "npc_id": "693", + "loc_data": "{2673,3416,0,0,0}-" + }, + { + "npc_id": "694", + "loc_data": "{2659,3431,0,0,3}-" + }, + { + "npc_id": "695", + "loc_data": "{2765,3276,0,1,3}-" + }, + { + "npc_id": "696", + "loc_data": "{2716,3303,0,1,1}-" + }, + { + "npc_id": "697", + "loc_data": "{2766,3288,1,0,6}-" + }, + { + "npc_id": "698", + "loc_data": "{2799,3320,0,1,1}-" + }, + { + "npc_id": "700", + "loc_data": "{2716,3303,0,1,1}-" + }, + { + "npc_id": "701", + "loc_data": "{2793,3321,0,1,1}-" + }, + { + "npc_id": "702", + "loc_data": "{2774,3273,0,1,0}-{2775,3284,0,1,0}-{2781,3290,1,1,0}-{2785,3284,1,1,0}-" + }, + { + "npc_id": "703", + "loc_data": "{2770,3284,0,1,0}-{2794,3279,0,1,0}-{2768,3285,1,1,0}-{2784,3277,1,1,0}-" + }, + { + "npc_id": "704", + "loc_data": "{2778,3291,0,1,0}-{2773,3282,1,1,0}-{2787,3280,1,1,0}-" + }, + { + "npc_id": "705", + "loc_data": "{3208,3252,0,0,6}-" + }, + { + "npc_id": "706", + "loc_data": "{3103,3163,2,0,0}-" + }, + { + "npc_id": "707", + "loc_data": "{3113,3160,2,0,0}-" + }, + { + "npc_id": "708", + "loc_data": "{2595,3125,0,1,1}-{2601,3135,0,1,3}-{2598,3120,0,1,6}-{2615,3120,0,1,4}-{2598,3108,0,1,4}-{2865,3175,0,1,0}-{2855,3188,0,1,1}-{2838,3194,0,1,3}-{2827,3170,0,1,5}-{2598,3145,0,1,1}-{2588,3145,0,1,7}-{2603,3221,0,1,7}-{2604,3212,0,1,3}-{2612,3230,0,1,0}-{2611,3204,0,1,7}-{2942,3247,0,1,7}-{2627,3231,0,1,4}-{2932,3322,0,0,5}-{2934,3301,0,1,5}-{2934,3301,0,1,3}-{2935,3343,0,1,1}-{2941,3356,0,0,7}-{3162,3410,0,1,2}-{3189,3503,0,1,6}-{3219,3221,0,1,4}-{2977,3202,0,1,7}-{2997,3300,0,1,3}-{2957,3308,0,1,0}-{2973,3274,0,1,3}-{2994,3270,0,1,0}-{3215,3282,0,1,1}-{3242,3306,0,1,1}-{2980,3337,0,1,6}-{2977,3377,0,0,3}-{3208,3357,0,1,3}-{2962,3424,0,1,6}-{3240,3393,0,1,3}-{2715,3451,0,1,4}-{2715,3436,0,1,3}-{2733,3459,0,1,3}-{2732,3457,0,1,6}-{3239,3523,0,1,4}-{3262,3523,0,1,2}-{2806,3181,0,1,3}-{2788,3179,0,1,3}-{2753,3178,0,1,3}-{2759,3155,0,1,0}-{2544,3184,0,1,3}-{3068,3251,0,1,6}-{3031,3322,0,1,1}-{3010,3271,0,1,6}-{3011,3513,0,1,1}-" + }, + { + "npc_id": "710", + "loc_data": "{2575,3333,0,1,0}-" + }, + { + "npc_id": "711", + "loc_data": "{2536,3314,1,0,0}-" + }, + { + "npc_id": "713", + "loc_data": "{2526,3319,0,0,6}-" + }, + { + "npc_id": "715", + "loc_data": "{2541,9672,0,1,0}-" + }, + { + "npc_id": "716", + "loc_data": "{2542,3286,0,1,3}-" + }, + { + "npc_id": "717", + "loc_data": "{2513,3294,0,1,0}-{2518,3320,0,1,0}-{2530,3274,0,1,0}-{2535,3296,0,1,0}-" + }, + { + "npc_id": "718", + "loc_data": "{2582,3329,0,1,2}-" + }, + { + "npc_id": "719", + "loc_data": "{2560,3288,0,1,2}-" + }, + { + "npc_id": "720", + "loc_data": "{2542,3306,0,1,0}-" + }, + { + "npc_id": "721", + "loc_data": "{2531,3331,0,1,3}-" + }, + { + "npc_id": "722", + "loc_data": "{2532,3331,0,1,3}-" + }, + { + "npc_id": "723", + "loc_data": "{2530,3331,0,1,3}-" + }, + { + "npc_id": "724", + "loc_data": "{2531,3331,1,1,6}-" + }, + { + "npc_id": "725", + "loc_data": "{2540,3305,0,1,0}-" + }, + { + "npc_id": "728", + "loc_data": "{2540,3308,0,1,0}-" + }, + { + "npc_id": "729", + "loc_data": "{2536,3308,0,1,0}-" + }, + { + "npc_id": "731", + "loc_data": "{3278,3487,0,1,1}-" + }, + { + "npc_id": "732", + "loc_data": "{3267,3392,0,0,0}-" + }, + { + "npc_id": "733", + "loc_data": "{3226,3399,0,0,0}-" + }, + { + "npc_id": "734", + "loc_data": "{3045,3256,0,1,3}-" + }, + { + "npc_id": "735", + "loc_data": "{2795,3155,0,1,7}-" + }, + { + "npc_id": "736", + "loc_data": "{2957,3373,0,1,7}-" + }, + { + "npc_id": "737", + "loc_data": "{2572,3319,0,1,3}-" + }, + { + "npc_id": "738", + "loc_data": "{2691,3492,0,0,0}-" + }, + { + "npc_id": "739", + "loc_data": "{2554,3080,0,1,3}-" + }, + { + "npc_id": "740", + "loc_data": "{2808,3086,0,1,0}-" + }, + { + "npc_id": "741", + "loc_data": "{3212,3220,1,1,1}-" + }, + { + "npc_id": "742", + "loc_data": "{2854,9636,0,1,4}-" + }, + { + "npc_id": "743", + "loc_data": "{3098,3257,0,1,4}-" + }, + { + "npc_id": "744", + "loc_data": "{3047,3204,0,0,0}-" + }, + { + "npc_id": "745", + "loc_data": "{3013,3189,0,1,1}-" + }, + { + "npc_id": "746", + "loc_data": "{3010,3502,0,1,3}-" + }, + { + "npc_id": "747", + "loc_data": "{3069,3516,0,1,6}-" + }, + { + "npc_id": "749", + "loc_data": "{1749,5323,0,1,4}-" + }, + { + "npc_id": "750", + "loc_data": "{1765,5321,0,1,2}-{1744,5326,0,1,4}-" + }, + { + "npc_id": "751", + "loc_data": "{1778,5323,0,1,6}-" + }, + { + "npc_id": "753", + "loc_data": "{2929,9649,0,1,4}-" + }, + { + "npc_id": "755", + "loc_data": "{3100,3268,0,0,0}-" + }, + { + "npc_id": "756", + "loc_data": "{3222,3398,0,0,3}-" + }, + { + "npc_id": "758", + "loc_data": "{3189,3273,0,0,4}-" + }, + { + "npc_id": "767", + "loc_data": "{3303,3511,0,0,1}-{3302,3514,0,0,1}-" + }, + { + "npc_id": "780", + "loc_data": "{3151,3410,0,1,6}-" + }, + { + "npc_id": "781", + "loc_data": "{3221,3434,0,1,0}-" + }, + { + "npc_id": "782", + "loc_data": "{3150,3406,0,0,0}-" + }, + { + "npc_id": "783", + "loc_data": "{3221,3435,0,1,0}-" + }, + { + "npc_id": "784", + "loc_data": "{3155,3406,0,0,0}-" + }, + { + "npc_id": "785", + "loc_data": "{2468,3304,0,1,0}-{2476,3317,0,1,0}-{2478,3291,0,1,0}-{2492,3312,0,1,0}-" + }, + { + "npc_id": "786", + "loc_data": "{2469,3309,0,1,0}-{2475,3298,0,1,0}-{2487,3308,0,1,0}-{2490,3289,0,1,0}-" + }, + { + "npc_id": "787", + "loc_data": "{2476,3313,0,1,0}-{2481,3289,0,1,0}-{2490,3313,0,1,0}-{2491,3300,0,1,0}-" + }, + { + "npc_id": "788", + "loc_data": "{2773,3187,0,0,6}-" + }, + { + "npc_id": "789", + "loc_data": "{2811,3167,0,1,0}-" + }, + { + "npc_id": "792", + "loc_data": "{2774,3197,0,1,0}-" + }, + { + "npc_id": "793", + "loc_data": "{2790,3189,0,1,1}-" + }, + { + "npc_id": "794", + "loc_data": "{2793,3191,0,1,3}-" + }, + { + "npc_id": "795", + "loc_data": "{2866,9956,0,1,6}-" + }, + { + "npc_id": "796", + "loc_data": "{2903,3511,0,1,3}-" + }, + { + "npc_id": "797", + "loc_data": "{2900,3511,1,1,6}-" + }, + { + "npc_id": "798", + "loc_data": "{2930,9686,0,1,0}-" + }, + { + "npc_id": "799", + "loc_data": "{2778,3197,0,1,4}-{2777,3194,0,1,4}-{2777,3199,0,1,3}-{2771,3193,0,1,3}-{2761,3192,0,1,5}-{2775,3190,0,1,4}-{2803,3195,0,1,4}-" + }, + { + "npc_id": "800", + "loc_data": "{2884,9765,0,0,0}-{2890,9766,0,0,0}-{2894,9764,0,0,0}-" + }, + { + "npc_id": "801", + "loc_data": "{3058,3485,0,1,6}-" + }, + { + "npc_id": "802", + "loc_data": "{3046,3486,1,0,0}-" + }, + { + "npc_id": "804", + "loc_data": "{2934,3285,1,1,0}-" + }, + { + "npc_id": "805", + "loc_data": "{2936,3288,0,1,4}-" + }, + { + "npc_id": "806", + "loc_data": "{2745,3576,1,0,4}-" + }, + { + "npc_id": "807", + "loc_data": "{2750,3576,0,0,4}-" + }, + { + "npc_id": "808", + "loc_data": "{2736,3575,0,1,1}-" + }, + { + "npc_id": "809", + "loc_data": "{2734,3581,0,1,1}-" + }, + { + "npc_id": "810", + "loc_data": "{2736,3576,1,1,1}-" + }, + { + "npc_id": "811", + "loc_data": "{2731,3579,0,1,4}-" + }, + { + "npc_id": "812", + "loc_data": "{2741,3562,0,1,0}-" + }, + { + "npc_id": "813", + "loc_data": "{2741,3552,0,1,1}-" + }, + { + "npc_id": "820", + "loc_data": "{2697,3496,0,0,0}-" + }, + { + "npc_id": "821", + "loc_data": "{2750,3580,0,1,0}-" + }, + { + "npc_id": "822", + "loc_data": "{3302,9466,0,1,6}-" + }, + { + "npc_id": "827", + "loc_data": "{3273,9417,0,1,6}-{3289,9443,0,1,4}-" + }, + { + "npc_id": "830", + "loc_data": "{3271,3028,0,1,4}-" + }, + { + "npc_id": "831", + "loc_data": "{3291,3032,1,0,1}-" + }, + { + "npc_id": "832", + "loc_data": "{3171,3025,0,1,4}-" + }, + { + "npc_id": "833", + "loc_data": "{3167,3033,0,1,2}-{3170,3027,0,1,5}-{3174,3028,0,1,6}-" + }, + { + "npc_id": "834", + "loc_data": "{3170,3045,0,0,3}-" + }, + { + "npc_id": "836", + "loc_data": "{3304,3124,0,1,6}-" + }, + { + "npc_id": "837", + "loc_data": "{3305,3121,0,1,0}-{3301,3120,0,1,1}-{3302,3123,0,1,6}-{3307,3125,0,1,1}-" + }, + { + "npc_id": "838", + "loc_data": "{3306,3117,0,1,4}-" + }, + { + "npc_id": "839", + "loc_data": "{3150,3044,0,1,0}-{3172,3009,0,1,0}-{3197,3012,0,1,0}-{3198,3040,0,1,0}-{3237,2968,0,1,0}-{3224,3013,0,1,0}-{3225,3034,0,1,0}-{3226,3060,0,1,0}-{3250,3057,0,1,0}-{3217,3092,0,1,0}-{3235,3074,0,1,0}-{3252,3125,0,1,0}-{3258,3078,0,1,0}-{3265,3066,0,1,0}-{3267,3066,0,1,0}-{3310,3068,0,1,0}-{3322,3011,0,1,0}-{3322,3052,0,1,0}-{3324,3030,0,1,0}-{3283,3108,0,1,0}-{3323,3094,0,1,0}-" + }, + { + "npc_id": "840", + "loc_data": "{3190,3054,0,1,0}-{3192,3016,0,1,0}-{3237,3000,0,1,0}-{3245,2960,0,1,0}-{3208,3032,0,1,0}-{3217,3064,0,1,0}-{3238,3015,0,1,0}-{3258,3063,0,1,0}-{3217,3111,0,1,0}-{3222,3086,0,1,0}-{3238,3101,0,1,0}-{3244,3080,0,1,0}-{3253,3116,0,1,0}-{3255,3095,0,1,0}-{3268,3052,0,1,0}-{3281,3056,0,1,0}-{3307,3055,0,1,0}-{3318,3020,0,1,0}-{3318,3040,0,1,0}-{3267,3077,0,1,0}-{3269,3110,0,1,0}-{3275,3094,0,1,0}-{3291,3078,0,1,0}-{3291,3100,0,1,0}-{3305,3089,0,1,0}-{3318,3078,0,1,0}-{3321,3104,0,1,0}-" + }, + { + "npc_id": "841", + "loc_data": "{3288,3022,0,1,1}-" + }, + { + "npc_id": "843", + "loc_data": "{2644,3274,0,1,3}-{2644,3276,0,1,5}-{2647,3271,0,1,6}-" + }, + { + "npc_id": "844", + "loc_data": "{2683,3323,0,0,0}-" + }, + { + "npc_id": "845", + "loc_data": "{2635,3311,0,0,0}-" + }, + { + "npc_id": "846", + "loc_data": "{2787,3184,0,1,4}-" + }, + { + "npc_id": "847", + "loc_data": "{3143,3447,0,1,5}-" + }, + { + "npc_id": "848", + "loc_data": "{2480,3489,1,1,3}-" + }, + { + "npc_id": "849", + "loc_data": "{2417,3498,1,1,0}-{2482,3487,1,1,0}-{2482,3489,1,1,0}-" + }, + { + "npc_id": "850", + "loc_data": "{2439,3502,1,0,4}-" + }, + { + "npc_id": "851", + "loc_data": "{2449,3503,1,1,4}-" + }, + { + "npc_id": "852", + "loc_data": "{2615,2987,0,1,0}-{2568,9432,0,1,0}-{2573,9447,0,1,0}-{2579,9441,0,1,0}-{2580,9431,0,1,0}-{2611,9454,0,1,0}-{2616,9447,0,1,0}-{2505,3032,0,1,0}-{2512,3022,0,1,0}-{2518,3041,0,1,0}-{2528,3044,0,1,0}-" + }, + { + "npc_id": "853", + "loc_data": "{2506,3115,0,1,3}-" + }, + { + "npc_id": "854", + "loc_data": "{2511,3085,0,1,4}-" + }, + { + "npc_id": "855", + "loc_data": "{2576,3027,0,1,3}-" + }, + { + "npc_id": "856", + "loc_data": "{2577,3021,0,1,1}-" + }, + { + "npc_id": "858", + "loc_data": "{2549,3029,0,1,0}-{2551,3031,0,1,0}-" + }, + { + "npc_id": "859", + "loc_data": "{2505,3062,0,1,0}-{2507,3062,0,1,0}-" + }, + { + "npc_id": "860", + "loc_data": "{2501,3012,0,1,0}-{2503,3011,0,1,0}-" + }, + { + "npc_id": "861", + "loc_data": "{2526,3018,0,1,0}-{2529,3019,0,1,0}-" + }, + { + "npc_id": "862", + "loc_data": "{2541,3034,0,1,3}-{2541,3029,0,1,3}-{2543,3031,0,1,3}-" + }, + { + "npc_id": "870", + "loc_data": "{2507,3040,0,1,0}-{2507,3036,0,1,0}-" + }, + { + "npc_id": "872", + "loc_data": "{2547,3117,2,1,3}-{2547,3112,2,1,6}-{2544,3114,2,1,3}-{2547,3118,2,1,3}-" + }, + { + "npc_id": "873", + "loc_data": "{2518,3035,0,1,1}-" + }, + { + "npc_id": "874", + "loc_data": "{2528,3048,0,1,6}-" + }, + { + "npc_id": "875", + "loc_data": "{2513,3034,0,1,1}-" + }, + { + "npc_id": "876", + "loc_data": "{2525,3043,0,1,3}-" + }, + { + "npc_id": "877", + "loc_data": "{2536,3092,0,1,1}-{2534,3091,0,1,1}-{2550,3118,0,1,4}-{2543,3117,0,1,3}-{2546,3113,0,1,4}-" + }, + { + "npc_id": "878", + "loc_data": "{2534,3092,0,1,1}-" + }, + { + "npc_id": "881", + "loc_data": "{3112,3162,1,0,0}-" + }, + { + "npc_id": "882", + "loc_data": "{3203,3424,0,0,6}-" + }, + { + "npc_id": "883", + "loc_data": "{3204,3470,0,1,3}-" + }, + { + "npc_id": "884", + "loc_data": "{3204,3495,2,1,6}-" + }, + { + "npc_id": "885", + "loc_data": "{2565,3271,0,1,0}-{2573,3268,1,1,0}-" + }, + { + "npc_id": "886", + "loc_data": "{2567,9669,0,1,0}-" + }, + { + "npc_id": "887", + "loc_data": "{2570,3275,0,1,3}-" + }, + { + "npc_id": "888", + "loc_data": "{2565,3273,0,1,0}-" + }, + { + "npc_id": "889", + "loc_data": "{2571,3270,0,1,0}-" + }, + { + "npc_id": "890", + "loc_data": "{2569,3272,0,1,0}-{2573,3269,1,1,0}-" + }, + { + "npc_id": "891", + "loc_data": "{2609,9670,0,1,0}-" + }, + { + "npc_id": "893", + "loc_data": "{2567,9683,0,1,0}-" + }, + { + "npc_id": "894", + "loc_data": "{2609,9686,0,1,0}-{2607,9681,0,1,0}-{2609,9674,0,1,0}-{2608,9672,0,1,0}-{2604,9674,0,1,0}-{2605,9671,0,1,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}-{3198,3355,0,1,0}-{2677,3656,0,1,2}-{2672,3654,0,1,2}-{2676,3652,0,1,4}-{2681,3652,0,1,4}-{2675,3652,0,1,3}-{3231,3296,0,1,6}-{3233,3300,0,1,4}-{3235,3293,0,1,3}-{3232,3288,0,1,0}-{3234,3298,0,1,6}-{3234,3297,0,1,3}-{3234,3292,0,1,4}-{3235,3289,0,1,1}-{2693,3271,0,1,3}-{2692,3272,0,1,3}-{2785,3069,0,1,4}-{2800,3072,0,1,6}-" + }, + { + "npc_id": "1019", + "loc_data": "{3187,5555,0,1,4}-{3190,5563,0,1,1}-{3193,5555,0,1,3}-{3213,9377,0,1,3}-{3209,9397,0,1,4}-{3245,9401,0,1,6}-{3237,9402,0,1,2}-{3207,9349,0,1,3}-{3220,9347,0,1,6}-{3233,9359,0,1,4}-{3235,9354,0,0,6}-{3259,9370,0,1,1}-{3258,9387,0,1,6}-{2707,9880,0,1,0}-{2711,9876,0,1,0}-{2712,9871,0,1,0}-{2715,9874,0,1,0}-{2720,9871,0,1,0}-{2717,9880,0,1,0}-{2722,9879,0,1,0}-{2723,9875,0,1,0}-{3278,9368,0,1,5}-{3271,9359,0,1,3}-{3287,9359,0,1,5}-{3301,9394,0,1,5}-{3318,9352,0,1,5}-" + }, + { + "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": "{3508,3494,0,1,1}-" + }, + { + "npc_id": "1039", + "loc_data": "{3500,3505,0,1,5}-" + }, + { + "npc_id": "1040", + "loc_data": "{3475,3494,0,1,6}-" + }, + { + "npc_id": "1041", + "loc_data": "{3490,3500,0,1,4}-" + }, + { + "npc_id": "1042", + "loc_data": "{3495,3469,0,1,3}-" + }, + { + "npc_id": "1043", + "loc_data": "{2852,4561,0,0,0}-{2853,4566,0,0,0}-{2856,4568,0,0,0}-{2864,4561,0,0,0}-{2864,4563,0,0,0}-{3409,3369,0,0,0}-{3420,3349,0,0,0}-{3430,3340,0,0,0}-{3435,3330,0,0,0}-{3435,3359,0,0,0}-{3437,3374,0,0,0}-{3446,3385,0,0,0}-{3447,3388,0,0,0}-{3450,3370,0,0,0}-{3450,3387,0,0,0}-{3451,3336,0,0,0}-{3452,3355,0,0,0}-{3418,3420,0,0,0}-{3424,3452,0,0,0}-{3428,3409,0,0,0}-{3433,3416,0,0,0}-{3434,3394,0,0,0}-{3436,3407,0,0,0}-{3438,3414,0,0,0}-{3439,3449,0,0,0}-{3446,3439,0,0,0}-{3454,3410,0,0,0}-{3454,3423,0,0,0}-{3726,3289,0,0,0}-{3732,3291,0,0,0}-{3737,3280,0,0,0}-{3745,3285,0,0,0}-{3726,3381,0,0,0}-{3745,3359,0,0,0}-{3749,3365,0,0,0}-{3750,3354,0,0,0}-{3757,3363,0,0,0}-{3763,3337,0,0,0}-{3459,3457,0,0,0}-{3460,3462,0,0,0}-{3462,3459,0,0,0}-{3463,3490,0,0,0}-{3464,3511,0,0,0}-{3465,3466,0,0,0}-{3466,3495,0,0,0}-{3466,3497,0,0,0}-{3467,3485,0,0,0}-{3467,3497,0,0,0}-{3467,3509,0,0,0}-{3469,3470,0,0,0}-{3470,3469,0,0,0}-{3471,3477,0,0,0}-{3474,3505,0,0,0}-{3476,3507,0,0,0}-{3479,3511,0,0,0}-{3480,3468,0,0,0}-{3480,3470,0,0,0}-{3482,3469,0,0,0}-{3483,3511,0,0,0}-{3484,3464,0,0,0}-{3485,3509,0,0,0}-{3490,3461,0,0,0}-{3491,3460,0,0,0}-{3494,3461,0,0,0}-{3494,3512,0,0,0}-{3498,3461,0,0,0}-{3499,3513,0,0,0}-{3501,3513,0,0,0}-{3502,3464,0,0,0}-{3504,3463,0,0,0}-{3506,3512,0,0,0}-{3506,3515,0,0,0}-{3507,3515,0,0,0}-{3509,3512,0,0,0}-{3511,3488,0,0,0}-{3513,3467,0,0,0}-{3513,3487,0,0,0}-{3513,3489,0,0,0}-{3513,3503,0,0,0}-{3514,3490,0,0,0}-{3515,3495,0,0,0}-{3515,3499,0,0,0}-{3516,3469,0,0,0}-" + }, + { + "npc_id": "1044", + "loc_data": "{3408,3484,0,1,6}-{3408,3493,0,1,1}-{3410,3489,1,1,3}-{3412,3487,1,1,4}-" + }, + { + "npc_id": "1045", + "loc_data": "{3413,3489,0,1,3}-{3409,3485,1,1,4}-{3413,3489,1,1,1}-{3213,3476,0,1,3}-" + }, + { + "npc_id": "1046", + "loc_data": "{3409,3489,0,1,4}-{3413,3485,0,1,4}-{3414,3491,1,1,6}-" + }, + { + "npc_id": "1048", + "loc_data": "{3437,3486,0,0,0}-" + }, + { + "npc_id": "1051", + "loc_data": "{3440,9738,1,1,0}-" + }, + { + "npc_id": "1052", + "loc_data": "{3451,3380,0,1,0}-{3445,3374,0,1,0}-{3438,3365,0,1,0}-{3429,3367,0,1,0}-{3417,3367,0,1,0}-{3413,3362,0,1,0}-{3417,3358,0,1,0}-{3426,3352,0,1,0}-{3435,3349,0,1,0}-{3442,3353,0,1,0}-{3448,3358,0,1,0}-{3426,3338,0,1,0}-{3423,3335,0,1,0}-{3420,3442,0,1,0}-{3429,3436,0,1,0}-{3439,3437,0,1,0}-{3448,3444,0,1,0}-{3449,3420,0,1,0}-{3441,3421,0,1,0}-{3427,3421,0,1,0}-{3414,3422,0,1,0}-{3411,3423,0,1,0}-{3415,3414,0,1,0}-{3410,3409,0,1,0}-{3422,3405,0,1,0}-{3431,3402,0,1,0}-{3444,3405,0,1,0}-{3453,3397,0,1,0}-{3470,3385,0,1,0}-{3463,3379,0,1,0}-{3457,3355,0,1,0}-{3461,3348,0,1,0}-{3457,3343,0,1,0}-{3471,3344,0,1,0}-{3458,3433,0,1,0}-{3459,3419,0,1,0}-{3467,3402,0,1,0}-{3473,3395,0,1,0}-" + }, + { + "npc_id": "1054", + "loc_data": "{3444,3459,0,0,0}-" + }, + { + "npc_id": "1055", + "loc_data": "{2811,3191,0,0,0}-" + }, + { + "npc_id": "1060", + "loc_data": "{2898,3528,0,0,1}-" + }, + { + "npc_id": "1061", + "loc_data": "{2898,3532,0,0,4}-" + }, + { + "npc_id": "1062", + "loc_data": "{2893,3540,0,0,3}-" + }, + { + "npc_id": "1063", + "loc_data": "{2900,3531,0,0,7}-{2900,3533,0,0,7}-" + }, + { + "npc_id": "1064", + "loc_data": "{2895,3537,0,0,1}-{2894,3537,0,0,1}-{2893,3537,0,0,1}-{2892,3537,0,0,1}-{2891,3537,0,0,1}-{2891,3538,0,0,1}-{2892,3538,0,0,1}-{2893,3538,0,0,1}-{2894,3538,0,0,1}-{2895,3538,0,0,1}-" + }, + { + "npc_id": "1065", + "loc_data": "{2889,3529,0,1,1}-{2904,3538,0,1,6}-{2882,3531,0,1,4}-{2903,3544,0,1,4}-{2911,3542,0,1,4}-{2906,3540,0,1,3}-" + }, + { + "npc_id": "1066", + "loc_data": "{2892,3532,0,0,4}-" + }, + { + "npc_id": "1067", + "loc_data": "{2893,3533,0,0,6}-" + }, + { + "npc_id": "1068", + "loc_data": "{2894,3532,0,0,3}-" + }, + { + "npc_id": "1069", + "loc_data": "{2840,3589,0,0,3}-" + }, + { + "npc_id": "1070", + "loc_data": "{2269,4758,0,1,3}-" + }, + { + "npc_id": "1071", + "loc_data": "{2820,3554,0,1,1}-" + }, + { + "npc_id": "1072", + "loc_data": "{2893,3558,0,1,3}-" + }, + { + "npc_id": "1073", + "loc_data": "{2900,3549,0,0,3}-{2897,3549,0,0,4}-{2902,3568,1,1,3}-{2903,3559,1,1,3}-{2903,3557,1,1,3}-{2903,3569,1,1,3}-{2894,3559,1,1,3}-{2892,3557,1,0,3}-" + }, + { + "npc_id": "1074", + "loc_data": "{2893,3569,1,1,3}-" + }, + { + "npc_id": "1076", + "loc_data": "{2897,3556,0,0,6}-{2896,3566,0,0,6}-" + }, + { + "npc_id": "1077", + "loc_data": "{2900,3556,0,0,6}-" + }, + { + "npc_id": "1078", + "loc_data": "{2905,3540,1,1,0}-" + }, + { + "npc_id": "1079", + "loc_data": "{2906,3537,0,1,5}-" + }, + { + "npc_id": "1080", + "loc_data": "{2900,3567,1,1,0}-" + }, + { + "npc_id": "1081", + "loc_data": "{2897,3565,0,1,4}-" + }, + { + "npc_id": "1082", + "loc_data": "{2919,3576,0,0,0}-" + }, + { + "npc_id": "1083", + "loc_data": "{2931,3546,0,1,3}-" + }, + { + "npc_id": "1084", + "loc_data": "{2915,3549,0,1,0}-" + }, + { + "npc_id": "1085", + "loc_data": "{2927,3558,0,1,6}-" + }, + { + "npc_id": "1086", + "loc_data": "{2965,3262,0,1,4}-" + }, + { + "npc_id": "1087", + "loc_data": "{2911,3540,0,1,7}-" + }, + { + "npc_id": "1088", + "loc_data": "{2918,3545,0,1,0}-" + }, + { + "npc_id": "1089", + "loc_data": "{2919,3529,0,1,0}-" + }, + { + "npc_id": "1090", + "loc_data": "{2931,3566,0,1,1}-" + }, + { + "npc_id": "1093", + "loc_data": "{2817,3560,0,1,3}-" + }, + { + "npc_id": "1094", + "loc_data": "{2802,3841,0,1,0}-{2798,3842,0,1,0}-{2804,3843,0,1,0}-{2807,3843,0,1,0}-" + }, + { + "npc_id": "1096", + "loc_data": "{2852,3591,0,1,7}-" + }, + { + "npc_id": "1097", + "loc_data": "{2858,3596,0,1,4}-" + }, + { + "npc_id": "1098", + "loc_data": "{2868,3591,0,1,6}-" + }, + { + "npc_id": "1099", + "loc_data": "{2857,3589,0,0,0}-" + }, + { + "npc_id": "1100", + "loc_data": "{2859,3589,0,0,0}-" + }, + { + "npc_id": "1101", + "loc_data": "{2854,3600,0,0,1}-" + }, + { + "npc_id": "1102", + "loc_data": "{2859,3600,0,0,1}-" + }, + { + "npc_id": "1103", + "loc_data": "{2863,3600,0,0,1}-" + }, + { + "npc_id": "1104", + "loc_data": "{2867,3600,0,0,1}-" + }, + { + "npc_id": "1105", + "loc_data": "{2870,3600,0,0,1}-{2851,3598,0,0,1}-" + }, + { + "npc_id": "1106", + "loc_data": "{2864,3594,0,1,1}-{2857,3587,0,1,7}-{2855,3596,0,1,4}-{2843,3682,0,1,1}-{2827,10078,1,1,6}-{2881,3592,0,1,5}-{2916,3631,0,1,0}-{2769,10149,0,1,0}-{2777,10142,0,1,0}-" + }, + { + "npc_id": "1107", + "loc_data": "{2874,3595,0,1,3}-{2875,3596,0,1,1}-{2873,3598,0,1,1}-{2845,3674,0,1,4}-{2836,10088,1,1,5}-{2917,3634,0,1,0}-{2781,10143,0,1,0}-" + }, + { + "npc_id": "1108", + "loc_data": "{2878,3592,0,1,6}-{2861,3586,0,1,6}-{2852,3673,0,1,2}-{2855,10058,2,1,4}-{2783,10140,0,1,0}-" + }, + { + "npc_id": "1109", + "loc_data": "{2875,3588,0,1,1}-{2851,3667,0,1,3}-{2825,10087,1,1,3}-{2836,10080,1,1,6}-{2883,3586,0,1,6}-{2909,3641,0,1,0}-{2923,10029,0,1,7}-{2784,10040,2,1,3}-{2784,10134,0,1,0}-" + }, + { + "npc_id": "1110", + "loc_data": "{2869,3594,0,1,3}-{2861,3587,0,1,4}-{2872,3594,0,1,5}-{2829,10080,1,1,6}-{2836,10097,2,1,4}-{2852,10110,2,1,4}-{2883,3591,0,1,4}-{2771,10143,0,1,0}-" + }, + { + "npc_id": "1111", + "loc_data": "{2863,3588,0,1,6}-{2865,3591,0,1,0}-{2866,3598,0,1,1}-{2855,3685,0,1,1}-{2840,10101,1,1,4}-{2857,10055,2,1,3}-{2836,10090,2,1,1}-{2925,10033,0,1,6}-{2788,10131,0,1,0}-" + }, + { + "npc_id": "1112", + "loc_data": "{2867,3595,0,1,6}-{2871,3597,0,1,4}-{2832,10089,1,1,1}-{2836,10077,2,1,1}-{2925,10030,0,1,1}-" + }, + { + "npc_id": "1113", + "loc_data": "{2829,10083,0,1,0}-" + }, + { + "npc_id": "1114", + "loc_data": "{2827,10077,0,1,0}-" + }, + { + "npc_id": "1115", + "loc_data": "{2831,10086,2,0,0}-" + }, + { + "npc_id": "1116", + "loc_data": "{2822,10073,2,0,0}-" + }, + { + "npc_id": "1117", + "loc_data": "{2829,10100,2,0,0}-" + }, + { + "npc_id": "1118", + "loc_data": "{2906,3623,0,0,6}-" + }, + { + "npc_id": "1119", + "loc_data": "{2920,3622,0,0,3}-" + }, + { + "npc_id": "1120", + "loc_data": "{2923,3616,0,0,3}-" + }, + { + "npc_id": "1121", + "loc_data": "{2923,3612,0,0,3}-" + }, + { + "npc_id": "1122", + "loc_data": "{2916,3601,0,0,0}-" + }, + { + "npc_id": "1123", + "loc_data": "{2902,3604,0,0,1}-" + }, + { + "npc_id": "1124", + "loc_data": "{2922,3606,0,0,3}-" + }, + { + "npc_id": "1125", + "loc_data": "{2911,3612,0,0,3}-" + }, + { + "npc_id": "1128", + "loc_data": "{2832,10079,0,0,4}-" + }, + { + "npc_id": "1129", + "loc_data": "{2832,10083,0,0,4}-" + }, + { + "npc_id": "1130", + "loc_data": "{2902,3696,0,0,1}-" + }, + { + "npc_id": "1131", + "loc_data": "{2898,3698,0,0,1}-" + }, + { + "npc_id": "1132", + "loc_data": "{2893,3700,0,0,1}-" + }, + { + "npc_id": "1133", + "loc_data": "{2889,3699,0,0,1}-" + }, + { + "npc_id": "1134", + "loc_data": "{2886,3698,0,0,1}-" + }, + { + "npc_id": "1135", + "loc_data": "{2843,10057,1,0,0}-" + }, + { + "npc_id": "1136", + "loc_data": "{2840,10056,1,0,0}-" + }, + { + "npc_id": "1137", + "loc_data": "{2847,10056,1,0,0}-" + }, + { + "npc_id": "1138", + "loc_data": "{2826,10083,1,1,0}-{2837,10083,2,1,1}-{2904,3642,0,1,3}-{2923,10031,0,1,3}-" + }, + { + "npc_id": "1139", + "loc_data": "{2854,3689,0,0,6}-" + }, + { + "npc_id": "1140", + "loc_data": "{2819,3586,0,1,4}-{2856,10054,1,0,0}-{2860,10057,1,0,0}-{2892,3685,0,1,0}-{2900,3671,0,1,0}-" + }, + { + "npc_id": "1141", + "loc_data": "{2857,10059,1,0,0}-{2857,10057,1,0,0}-{2893,3678,0,1,0}-" + }, + { + "npc_id": "1142", + "loc_data": "{2851,10089,0,1,1}-{2854,10092,0,1,3}-" + }, + { + "npc_id": "1143", + "loc_data": "{2850,10088,0,1,3}-{2854,10086,0,1,6}-" + }, + { + "npc_id": "1144", + "loc_data": "{2856,10089,0,1,4}-{2858,10089,0,1,6}-" + }, + { + "npc_id": "1146", + "loc_data": "{2864,10081,0,1,7}-" + }, + { + "npc_id": "1147", + "loc_data": "{2853,10076,0,1,5}-" + }, + { + "npc_id": "1148", + "loc_data": "{2858,10082,0,1,3}-" + }, + { + "npc_id": "1149", + "loc_data": "{2861,10085,0,1,2}-" + }, + { + "npc_id": "1150", + "loc_data": "{2857,10075,0,1,1}-" + }, + { + "npc_id": "1151", + "loc_data": "{2845,10058,1,0,0}-" + }, + { + "npc_id": "1153", + "loc_data": "{3489,9509,2,1,4}-{3499,9515,2,1,6}-{3505,9527,2,1,5}-{3510,9521,2,1,1}-{3490,9526,2,1,3}-{3475,9524,2,1,5}-{3465,9505,2,1,1}-{3479,9501,2,1,3}-{3487,9491,2,1,3}-" + }, + { + "npc_id": "1154", + "loc_data": "{3472,9501,2,1,3}-{3495,9494,2,1,4}-{3479,9484,2,1,1}-{3500,9477,2,1,6}-{3470,9482,2,1,6}-{3465,9481,2,1,3}-{3465,9491,2,1,6}-{3468,9488,2,1,0}-" + }, + { + "npc_id": "1155", + "loc_data": "{3505,9496,2,1,6}-{3513,9496,2,1,7}-" + }, + { + "npc_id": "1157", + "loc_data": "{3480,9508,0,1,0}-{3495,9505,0,1,2}-{3492,9490,0,1,1}-{3472,9497,0,1,6}-" + }, + { + "npc_id": "1158", + "loc_data": "{3484,9491,0,1,2}-" + }, + { + "npc_id": "1161", + "loc_data": "{3487,9526,2,1,5}-{3474,9519,2,1,1}-{3465,9512,2,1,0}-{3489,9501,2,1,4}-{3494,9501,2,1,3}-{3480,9489,2,1,6}-{3483,9477,2,1,3}-{3494,9476,2,1,3}-{3473,9487,0,1,3}-{3476,9496,0,1,6}-{3473,9501,0,1,7}-{3481,9504,0,1,6}-{3488,9514,0,1,6}-{3497,9501,0,1,3}-{3486,9490,0,1,0}-" + }, + { + "npc_id": "1162", + "loc_data": "{2781,3088,1,1,0}-" + }, + { + "npc_id": "1164", + "loc_data": "{2780,3058,0,1,0}-" + }, + { + "npc_id": "1171", + "loc_data": "{2766,3168,0,1,6}-" + }, + { + "npc_id": "1172", + "loc_data": "{2909,3086,0,0,0}-" + }, + { + "npc_id": "1174", + "loc_data": "{2792,3015,0,0,0}-{2792,3019,0,0,0}-{2801,3010,0,0,0}-{2801,3010,0,0,4}-" + }, + { + "npc_id": "1176", + "loc_data": "{2928,3112,0,0,0}-" + }, + { + "npc_id": "1177", + "loc_data": "{2912,3119,0,0,0}-" + }, + { + "npc_id": "1178", + "loc_data": "{2768,3165,0,0,0}-" + }, + { + "npc_id": "1179", + "loc_data": "{2941,3100,0,1,6}-" + }, + { + "npc_id": "1180", + "loc_data": "{2916,3110,0,1,7}-" + }, + { + "npc_id": "1181", + "loc_data": "{2902,3101,0,1,1}-{2922,3105,0,1,6}-" + }, + { + "npc_id": "1183", + "loc_data": "{2206,3258,0,1,3}-{2200,3261,0,1,3}-{2193,3253,0,1,3}-{2192,3243,0,1,3}-" + }, + { + "npc_id": "1184", + "loc_data": "{2192,3249,0,1,3}-{2200,3249,0,1,3}-{2195,3247,0,1,3}-{2212,3258,0,1,3}-" + }, + { + "npc_id": "1192", + "loc_data": "{2924,3506,0,1,0}-{2927,3498,0,0,0}-{2297,3176,0,1,6}-" + }, + { + "npc_id": "1193", + "loc_data": "{2297,3176,0,1,6}-" + }, + { + "npc_id": "1194", + "loc_data": "{2298,3177,0,1,4}-" + }, + { + "npc_id": "1195", + "loc_data": "{2215,3171,0,1,4}-{2200,3224,0,1,0}-{2293,3156,0,1,3}-" + }, + { + "npc_id": "1196", + "loc_data": "{2217,3171,0,1,0}-{2199,3222,0,1,0}-{2201,3218,0,1,0}-{2291,3156,0,1,0}-" + }, + { + "npc_id": "1197", + "loc_data": "{2197,3218,0,1,0}-{2292,3153,0,1,0}-" + }, + { + "npc_id": "1198", + "loc_data": "{2296,3192,0,1,2}-" + }, + { + "npc_id": "1199", + "loc_data": "{2194,3255,0,0,6}-{2262,3151,0,1,0}-" + }, + { + "npc_id": "1201", + "loc_data": "{2198,3250,0,0,6}-{2203,3250,0,0,4}-{2264,3147,0,1,1}-{2266,3145,0,1,4}-" + }, + { + "npc_id": "1202", + "loc_data": "{2352,3172,0,1,4}-" + }, + { + "npc_id": "1203", + "loc_data": "{2189,3155,0,0,1}-{2183,3156,0,1,4}-{2194,3156,0,1,6}-{2182,3148,0,1,4}-{2179,3137,0,1,3}-{2189,3136,0,1,3}-{2182,3146,0,1,3}-{2187,3172,0,0,1}-{2180,3180,0,1,1}-" + }, + { + "npc_id": "1205", + "loc_data": "{3054,3254,0,0,0}-" + }, + { + "npc_id": "1206", + "loc_data": "{2185,3144,0,0,4}-" + }, + { + "npc_id": "1207", + "loc_data": "{3055,3254,0,0,0}-" + }, + { + "npc_id": "1208", + "loc_data": "{2194,3140,0,1,5}-" + }, + { + "npc_id": "1211", + "loc_data": "{2914,3418,0,0,6}-" + }, + { + "npc_id": "1212", + "loc_data": "{2194,3189,0,0,0}-{2196,3187,0,0,0}-{2196,3189,0,0,0}-{2206,3164,0,0,0}-{2209,3164,0,0,0}-{2216,3142,0,0,0}-{2218,3139,0,0,0}-{2178,3238,0,0,0}-{2208,3225,0,0,0}-{2210,3217,0,0,0}-{2213,3243,0,0,0}-{2218,3245,0,0,0}-{2220,3243,0,0,0}-{2221,3243,0,0,0}-{3202,5990,0,0,0}-{3232,5977,0,0,0}-{3234,5969,0,0,0}-{3237,5995,0,0,0}-{3242,5997,0,0,0}-{3244,5995,0,0,0}-{3245,5995,0,0,0}-{2729,6086,0,0,0}-{2731,6080,0,0,0}-{2731,6089,0,0,0}-{2744,6139,0,0,0}-{2695,6170,0,0,0}-{2697,6171,0,0,0}-{2697,6202,0,0,0}-{2699,6204,0,0,0}-{2707,6155,0,0,0}-{2715,6167,0,0,0}-{2715,6169,0,0,0}-{2746,6206,0,0,0}-{2281,3142,0,0,0}-{2283,3136,0,0,0}-{2283,3145,0,0,0}-{2296,3195,0,0,0}-{2247,3226,0,0,0}-{2249,3227,0,0,0}-{2249,3258,0,0,0}-{2251,3260,0,0,0}-{2259,3211,0,0,0}-{2267,3223,0,0,0}-{2267,3225,0,0,0}-{2298,3262,0,0,0}-{3271,5978,0,0,0}-{3273,5979,0,0,0}-{3273,6010,0,0,0}-{3275,6012,0,0,0}-{3283,5963,0,0,0}-{3291,5975,0,0,0}-{3291,5977,0,0,0}-{3322,6014,0,0,0}-" + }, + { + "npc_id": "1213", + "loc_data": "{2192,3188,0,0,0}-{2192,3191,0,0,0}-{2194,3190,0,0,0}-{2208,3163,0,0,0}-{2209,3163,0,0,0}-{2216,3136,0,0,0}-{2216,3140,0,0,0}-{2180,3236,0,0,0}-{2208,3227,0,0,0}-{2210,3215,0,0,0}-{2210,3224,0,0,0}-{2214,3244,0,0,0}-{2215,3245,0,0,0}-{2219,3244,0,0,0}-{3204,5988,0,0,0}-{3232,5979,0,0,0}-{3234,5967,0,0,0}-{3234,5976,0,0,0}-{3238,5996,0,0,0}-{3239,5997,0,0,0}-{3243,5996,0,0,0}-{2730,6084,0,0,0}-{2696,6169,0,0,0}-{2696,6171,0,0,0}-{2699,6201,0,0,0}-{2701,6203,0,0,0}-{2716,6168,0,0,0}-{2739,6206,0,0,0}-{2282,3140,0,0,0}-{2248,3225,0,0,0}-{2248,3227,0,0,0}-{2251,3257,0,0,0}-{2253,3259,0,0,0}-{2268,3224,0,0,0}-{2291,3262,0,0,0}-{3272,5977,0,0,0}-{3272,5979,0,0,0}-{3275,6009,0,0,0}-{3277,6011,0,0,0}-{3292,5976,0,0,0}-{3315,6014,0,0,0}-" + }, + { + "npc_id": "1214", + "loc_data": "{2891,3676,0,0,1}-" + }, + { + "npc_id": "1215", + "loc_data": "{2612,3287,0,0,0}-{2613,3287,0,0,0}-" + }, + { + "npc_id": "1216", + "loc_data": "{2611,3285,0,0,0}-" + }, + { + "npc_id": "1217", + "loc_data": "{3000,3386,0,1,0}-{3019,3368,0,1,0}-" + }, + { + "npc_id": "1218", + "loc_data": "{3412,3512,0,1,4}-{3412,3515,0,1,1}-{3413,3514,0,1,6}-{3414,3512,0,1,5}-{3415,3518,0,1,5}-{3416,3509,0,1,7}-{3416,3511,0,1,4}-{3417,3518,0,1,4}-{3418,3509,0,1,4}-{3419,3512,0,1,7}-{3420,3517,0,1,7}-{3420,3518,0,1,6}-{3423,3461,0,1,5}-{3426,3465,0,1,4}-{3427,3463,0,1,5}-{3428,3458,0,1,4}-{3428,3465,0,1,2}-{3430,3462,0,1,1}-{3430,3467,0,1,3}-{3433,3458,0,1,1}-{3433,3468,0,1,4}-{3434,3464,0,1,6}-" + }, + { + "npc_id": "1219", + "loc_data": "{3588,3461,0,1,0}-{3589,3493,0,1,0}-{3590,3509,0,1,0}-{3596,3476,0,1,0}-{3599,3483,0,1,0}-{3607,3460,0,1,0}-{3613,3499,0,1,0}-{3614,3515,0,1,0}-{3620,3472,0,1,0}-{3623,3504,0,1,0}-{3626,3475,0,1,0}-{3626,3491,0,1,0}-{3629,3511,0,1,0}-{3633,3463,0,1,0}-{3644,3498,0,1,0}-{3537,3496,0,1,0}-{3545,3502,0,1,0}-{3546,3462,0,1,0}-{3548,3480,0,1,0}-{3551,3489,0,1,0}-{3554,3506,0,1,0}-{3560,3462,0,1,0}-{3566,3483,0,1,0}-{3567,3474,0,1,0}-{3572,3492,0,1,0}-{3574,3507,0,1,0}-" + }, + { + "npc_id": "1221", + "loc_data": "{3597,3489,0,1,0}-{3611,3462,0,1,0}-{3620,3500,0,1,0}-{3636,3484,0,1,0}-{3566,3457,0,1,0}-{3567,3503,0,1,0}-" + }, + { + "npc_id": "1223", + "loc_data": "{3585,3478,0,1,0}-{3586,3480,0,1,0}-{3586,3496,0,1,0}-{3592,3477,0,1,0}-{3593,3458,0,1,0}-{3593,3508,0,1,0}-{3595,3491,0,1,0}-{3597,3479,0,1,0}-{3607,3466,0,1,0}-{3617,3477,0,1,0}-{3618,3514,0,1,0}-{3631,3471,0,1,0}-{3631,3499,0,1,0}-{3632,3459,0,1,0}-{3632,3475,0,1,0}-{3638,3472,0,1,0}-{3646,3467,0,1,0}-" + }, + { + "npc_id": "1227", + "loc_data": "{3424,3318,0,1,0}-{3423,3312,0,1,4}-{3426,3381,0,1,0}-{3418,3375,0,1,0}-{3414,3363,0,1,0}-{3411,3358,0,1,0}-{3441,3356,0,1,0}-{3424,3426,0,1,0}-{3414,3413,0,1,0}-{3421,3408,0,1,0}-{3422,3408,0,1,0}-{3440,3406,0,1,0}-{3412,3392,0,1,0}-{3494,3388,0,1,0}-{3467,3346,0,1,0}-{3465,3347,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}-{3481,3446,0,1,0}-{3480,3445,0,1,0}-{3479,3443,0,1,0}-{3471,3433,0,1,0}-{3459,3432,0,1,0}-{3459,3426,0,1,0}-{3459,3418,0,1,0}-{3456,3415,0,1,0}-{3484,3456,0,1,0}-{3486,3458,0,1,4}-{3563,3441,0,1,0}-{3544,3430,0,1,0}-{3540,3413,0,1,0}-{3529,3441,0,1,0}-" + }, + { + "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}-{3427,3376,0,1,0}-{3420,3407,0,1,0}-{3425,3403,0,1,0}-{3438,3399,0,1,0}-{3446,3406,0,1,0}-{3420,3426,0,1,0}-{3477,3368,0,1,0}-{3479,3367,0,1,0}-{3502,3455,0,1,2}-{3516,3415,0,1,4}-{3511,3401,0,1,1}-{3484,3443,0,1,0}-{3483,3442,0,1,0}-{3457,3416,0,1,0}-{3469,3402,0,1,0}-{3471,3401,0,1,0}-{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}-{3476,3350,0,1,0}-{3496,3447,0,1,7}-{3481,3442,0,1,0}-{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}-{3411,3362,0,1,0}-{3415,3365,0,1,0}-{3415,3371,0,1,0}-{3413,3373,0,1,0}-{3431,3383,0,1,0}-{3508,3420,0,1,1}-{3514,3415,0,1,7}-{3470,3400,0,1,0}-{3464,3405,0,1,0}-{3474,3417,0,1,0}-{3474,3419,0,1,0}-{3477,3419,0,1,0}-{3480,3441,0,1,0}-{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}-{3409,3387,0,1,0}-{3424,3377,0,1,0}-{3438,3351,0,1,0}-{3513,3383,0,1,0}-{3491,3388,0,1,3}-{3461,3355,0,1,0}-{3473,3348,0,1,0}-{3477,3367,0,1,0}-{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}-{3483,3444,0,1,0}-{3479,3423,0,1,0}-{3466,3408,0,1,0}-{3466,3407,0,1,0}-{3520,3412,0,1,0}-" + }, + { + "npc_id": "1235", + "loc_data": "{3425,3306,0,1,1}-{3442,3303,0,1,3}-{3437,3299,0,1,1}-{3422,3321,0,1,1}-{3509,3386,0,1,2}-{3503,3420,0,1,3}-{3514,3398,0,1,4}-{3486,3458,0,1,7}-{3524,3419,0,1,1}-" + }, + { + "npc_id": "1239", + "loc_data": "{3166,3033,0,1,1}-{3174,3043,0,1,6}-{3177,3041,0,1,4}-{3176,3038,0,1,4}-{3177,3035,0,1,7}-{3183,3035,0,1,6}-{3182,3032,0,1,6}-" + }, + { + "npc_id": "1240", + "loc_data": "{3502,3288,0,1,4}-{3505,3284,0,1,4}-{3490,3292,0,1,4}-{3502,3300,0,1,4}-{3492,3303,0,1,4}-{3516,3303,0,1,4}-{3478,3281,0,1,4}-{3473,3273,0,1,4}-{3469,3288,0,1,4}-{3469,3304,0,1,4}-" + }, + { + "npc_id": "1251", + "loc_data": "{3474,3309,0,1,5}-{3486,3277,0,1,4}-{3496,3289,0,0,3}-" + }, + { + "npc_id": "1253", + "loc_data": "{3488,3297,0,0,6}-" + }, + { + "npc_id": "1257", + "loc_data": "{3490,3290,0,0,3}-{3487,3303,0,1,5}-{3496,3296,0,1,4}-{3479,3303,0,1,4}-{3498,3282,0,1,3}-{3473,3281,0,1,3}-{3478,3308,0,1,1}-" + }, + { + "npc_id": "1258", + "loc_data": "{3488,3287,0,1,1}-{3501,3285,0,1,5}-{3478,3274,0,1,4}-{3497,3283,0,1,1}-" + }, + { + "npc_id": "1261", + "loc_data": "{3492,3300,0,1,4}-{3498,3300,0,1,5}-{3487,3302,0,1,6}-{3497,3299,0,1,5}-{3488,3273,0,1,6}-" + }, + { + "npc_id": "1262", + "loc_data": "{3487,3283,0,1,1}-{3477,3300,0,1,4}-{3497,3276,0,1,1}-{3502,3279,0,1,1}-{3481,3296,0,1,2}-" + }, + { + "npc_id": "1263", + "loc_data": "{3108,3160,1,1,1}-" + }, + { + "npc_id": "1266", + "loc_data": "{2680,3719,0,0,7}-{2684,3722,0,0,1}-{2684,3726,0,0,1}-{2678,3728,0,0,2}-{2673,3728,0,0,0}-{2670,3724,0,0,3}-{2671,3726,0,0,2}-{2679,3731,0,0,2}-{2684,3729,0,0,6}-{2706,3711,0,0,1}-{2688,3721,0,0,1}-{2693,3715,0,0,5}-{2699,3715,0,0,2}-{2712,3718,0,0,5}-{2716,3721,0,0,7}-{2722,3726,0,0,1}-{2524,3764,0,0,7}-{2508,3762,0,0,0}-{2510,3755,0,0,2}-{2523,3756,0,0,1}-{2532,3753,0,0,0}-{2529,3755,0,0,1}-{2528,3739,0,0,6}-{2529,3730,0,0,0}-{2518,3724,0,0,1}-{2508,3723,0,0,1}-{2525,3718,0,0,1}-{2549,3733,0,0,3}-" + }, + { + "npc_id": "1268", + "loc_data": "{2681,3716,0,0,2}-{2678,3721,0,0,2}-{2684,3719,0,0,1}-{2699,3719,0,0,4}-{2701,3717,0,0,2}-{2707,3719,0,0,0}-{2700,3726,0,0,2}-{2695,3713,0,0,2}-{2716,3718,0,0,5}-{2706,3713,0,0,2}-{2539,3761,0,0,7}-{2539,3759,0,0,7}-{2528,3767,0,0,3}-{2526,3753,0,0,2}-{2513,3755,0,0,3}-{2505,3753,0,0,3}-{2530,3751,0,0,1}-{2526,3755,0,0,1}-{2532,3758,0,0,6}-{2532,3751,0,0,3}-{2530,3737,0,0,2}-{2531,3728,0,0,0}-{2523,3723,0,0,1}-{2520,3724,0,0,3}-{2515,3718,0,0,3}-{2505,3724,0,0,4}-{2505,3729,0,0,6}-{2535,3718,0,0,4}-{2536,3724,0,0,1}-{2543,3729,0,0,6}-{2550,3734,0,0,1}-{2552,3747,0,0,6}-{2552,3752,0,0,6}-" + }, + { + "npc_id": "1269", + "loc_data": "{2672,3683,0,1,3}-" + }, + { + "npc_id": "1270", + "loc_data": "{2770,3624,0,0,0}-" + }, + { + "npc_id": "1274", + "loc_data": "{2660,3680,0,0,6}-" + }, + { + "npc_id": "1275", + "loc_data": "{2657,3680,0,0,6}-" + }, + { + "npc_id": "1276", + "loc_data": "{2658,3679,0,0,6}-" + }, + { + "npc_id": "1277", + "loc_data": "{2659,3678,0,0,6}-" + }, + { + "npc_id": "1278", + "loc_data": "{2659,3664,0,1,3}-" + }, + { + "npc_id": "1281", + "loc_data": "{2661,3651,0,1,3}-" + }, + { + "npc_id": "1282", + "loc_data": "{2640,3681,0,1,4}-" + }, + { + "npc_id": "1283", + "loc_data": "{2647,3660,0,1,5}-" + }, + { + "npc_id": "1284", + "loc_data": "{2658,3673,0,1,1}-" + }, + { + "npc_id": "1285", + "loc_data": "{2657,3669,0,1,1}-" + }, + { + "npc_id": "1286", + "loc_data": "{2656,3676,0,1,1}-" + }, + { + "npc_id": "1287", + "loc_data": "{2652,3588,0,1,1}-" + }, + { + "npc_id": "1288", + "loc_data": "{2634,3667,0,1,1}-" + }, + { + "npc_id": "1289", + "loc_data": "{2667,3692,0,1,1}-" + }, + { + "npc_id": "1294", + "loc_data": "{2658,3673,0,1,7}-" + }, + { + "npc_id": "1296", + "loc_data": "{2660,3663,0,0,6}-{2657,3663,0,0,6}-" + }, + { + "npc_id": "1298", + "loc_data": "{2663,3646,0,0,6}-" + }, + { + "npc_id": "1299", + "loc_data": "{2660,3646,0,0,6}-" + }, + { + "npc_id": "1300", + "loc_data": "{2662,3674,0,1,1}-" + }, + { + "npc_id": "1301", + "loc_data": "{2623,3675,0,1,6}-" + }, + { + "npc_id": "1302", + "loc_data": "{2641,3699,0,0,4}-" + }, + { + "npc_id": "1303", + "loc_data": "{2664,3692,0,1,3}-" + }, + { + "npc_id": "1304", + "loc_data": "{2582,3845,0,0,3}-" + }, + { + "npc_id": "1305", + "loc_data": "{2643,3676,0,1,7}-" + }, + { + "npc_id": "1306", + "loc_data": "{2674,3677,0,1,6}-" + }, + { + "npc_id": "1307", + "loc_data": "{2658,3673,0,1,7}-" + }, + { + "npc_id": "1308", + "loc_data": "{2672,3662,0,1,3}-" + }, + { + "npc_id": "1309", + "loc_data": "{2667,3653,0,1,3}-" + }, + { + "npc_id": "1310", + "loc_data": "{2667,3701,0,1,4}-" + }, + { + "npc_id": "1311", + "loc_data": "{2655,3651,0,1,3}-" + }, + { + "npc_id": "1312", + "loc_data": "{2639,3651,0,1,6}-" + }, + { + "npc_id": "1313", + "loc_data": "{2626,3653,0,1,5}-" + }, + { + "npc_id": "1314", + "loc_data": "{2675,3677,0,1,3}-" + }, + { + "npc_id": "1315", + "loc_data": "{2646,3675,0,1,3}-" + }, + { + "npc_id": "1317", + "loc_data": "{2632,3677,0,1,2}-{2641,3682,0,1,3}-{2643,3675,0,1,6}-{2643,3674,0,1,4}-{2646,3668,0,1,3}-{2651,3669,0,1,3}-{2648,3688,0,1,4}-{2656,3692,0,1,1}-" + }, + { + "npc_id": "1318", + "loc_data": "{2613,3668,0,1,3}-{2671,3658,0,1,2}-{2638,3648,0,1,3}-{2626,3658,0,1,4}-{2626,3668,0,1,1}-{2630,3673,0,1,7}-{2635,3680,0,1,4}-{2644,3675,0,1,5}-{2642,3681,0,1,3}-{2631,3678,0,1,6}-{2661,3698,0,1,1}-{2663,3687,0,1,3}-{2673,3695,0,1,3}-{2674,3710,0,1,1}-{2676,3693,0,1,3}-{2675,3685,0,1,4}-{2669,3677,0,1,1}-{2667,3670,0,1,2}-{2675,3670,0,1,4}-{2679,3685,0,1,2}-{2667,3671,0,1,3}-{2666,3663,0,1,6}-{2658,3657,0,1,4}-{2684,3649,0,1,4}-{2679,3649,0,1,4}-{2646,3664,0,1,4}-{2646,3655,0,1,1}-{2637,3655,0,1,3}-{2667,3658,0,1,3}-{2688,3659,0,1,2}-" + }, + { + "npc_id": "1320", + "loc_data": "{2738,3636,0,1,7}-{2739,3636,0,1,7}-{2738,3637,0,1,4}-{2735,3637,0,1,3}-" + }, + { + "npc_id": "1321", + "loc_data": "{2736,3642,0,1,4}-{2744,3638,0,1,4}-" + }, + { + "npc_id": "1329", + "loc_data": "{3085,3640,0,1,0}-" + }, + { + "npc_id": "1334", + "loc_data": "{2509,3639,1,1,3}-" + }, + { + "npc_id": "1335", + "loc_data": "{2518,4633,0,1,1}-" + }, + { + "npc_id": "1336", + "loc_data": "{2508,3635,0,1,3}-" + }, + { + "npc_id": "1337", + "loc_data": "{2445,4599,1,1,3}-" + }, + { + "npc_id": "1338", + "loc_data": "{3151,5559,0,1,2}-{3157,5548,0,1,3}-{3155,5553,0,1,4}-{3145,5559,0,1,6}-{3152,5556,0,1,3}-{3149,5544,0,1,0}-{3145,5548,0,1,3}-" + }, + { + "npc_id": "1339", + "loc_data": "{3153,5547,0,1,0}-{3150,5545,0,1,1}-" + }, + { + "npc_id": "1340", + "loc_data": "{3147,5557,0,1,3}-{3154,5559,0,1,4}-" + }, + { + "npc_id": "1343", + "loc_data": "{3145,5548,0,1,6}-" + }, + { + "npc_id": "1357", + "loc_data": "{2201,4942,0,1,4}-" + }, + { + "npc_id": "1358", + "loc_data": "{2208,4959,0,1,6}-" + }, + { + "npc_id": "1359", + "loc_data": "{2612,3875,1,1,6}-" + }, + { + "npc_id": "1360", + "loc_data": "{2620,3896,0,0,3}-" + }, + { + "npc_id": "1369", + "loc_data": "{2527,5876,0,1,0}-" + }, + { + "npc_id": "1373", + "loc_data": "{2500,3860,1,1,4}-" + }, + { + "npc_id": "1374", + "loc_data": "{2505,3854,1,0,4}-{2509,3849,0,0,4}-" + }, + { + "npc_id": "1375", + "loc_data": "{2501,3858,1,0,4}-" + }, + { + "npc_id": "1376", + "loc_data": "{2548,3895,0,1,0}-" + }, + { + "npc_id": "1377", + "loc_data": "{2544,3843,0,1,6}-" + }, + { + "npc_id": "1378", + "loc_data": "{2516,3865,0,1,4}-" + }, + { + "npc_id": "1385", + "loc_data": "{2629,3694,0,0,6}-" + }, + { + "npc_id": "1389", + "loc_data": "{2603,3877,0,1,1}-" + }, + { + "npc_id": "1390", + "loc_data": "{2604,3876,0,1,4}-" + }, + { + "npc_id": "1391", + "loc_data": "{2604,3876,0,1,3}-" + }, + { + "npc_id": "1393", + "loc_data": "{2519,3868,0,0,3}-" + }, + { + "npc_id": "1394", + "loc_data": "{2519,3863,0,0,1}-" + }, + { + "npc_id": "1395", + "loc_data": "{2547,3868,0,0,1}-" + }, + { + "npc_id": "1396", + "loc_data": "{2525,3892,0,0,4}-" + }, + { + "npc_id": "1397", + "loc_data": "{2573,3856,0,0,4}-" + }, + { + "npc_id": "1398", + "loc_data": "{2526,3850,0,0,6}-" + }, + { + "npc_id": "1399", + "loc_data": "{2577,3854,0,0,1}-" + }, + { + "npc_id": "1400", + "loc_data": "{3037,4512,0,1,0}-{3049,4498,0,1,1}-" + }, + { + "npc_id": "1401", + "loc_data": "{2605,3878,0,1,1}-" + }, + { + "npc_id": "1402", + "loc_data": "{2603,3877,0,1,1}-" + }, + { + "npc_id": "1404", + "loc_data": "{2239,3164,0,1,2}-{2241,3166,0,1,3}-{2242,3163,0,1,3}-{3037,4492,0,1,4}-{3036,4502,0,1,4}-{3029,4520,0,1,1}-{3019,4512,0,1,1}-" + }, + { + "npc_id": "1405", + "loc_data": "{2576,3843,0,0,6}-" + }, + { + "npc_id": "1427", + "loc_data": "{2957,3025,0,0,0}-" + }, + { + "npc_id": "1433", + "loc_data": "{2770,2789,0,1,6}-" + }, + { + "npc_id": "1434", + "loc_data": "{2753,2770,0,1,6}-" + }, + { + "npc_id": "1447", + "loc_data": "{2761,2785,0,1,3}-" + }, + { + "npc_id": "1452", + "loc_data": "{2743,2796,0,1,6}-" + }, + { + "npc_id": "1454", + "loc_data": "{2742,2790,0,1,1}-" + }, + { + "npc_id": "1457", + "loc_data": "{2747,2782,0,0,1}-{2754,2778,0,1,1}-{2761,2778,0,1,1}-{2765,2779,0,1,4}-{2767,2784,0,1,6}-{2772,2781,0,1,5}-{2774,2787,0,1,1}-{2777,2790,0,1,4}-{2762,2793,0,1,3}-{2756,2789,0,1,3}-" + }, + { + "npc_id": "1458", + "loc_data": "{2728,2751,0,1,1}-{2715,2749,0,1,2}-{2728,2758,0,1,1}-{2727,2762,0,1,6}-{2728,2755,0,1,6}-{2714,2753,0,1,1}-{2714,2757,0,1,6}-{2714,2761,0,1,6}-" + }, + { + "npc_id": "1459", + "loc_data": "{2790,2791,0,1,4}-{2803,2785,0,1,7}-{2797,2792,0,1,0}-{2800,2779,0,1,5}-{2788,2779,0,1,7}-{2795,2776,0,1,3}-" + }, + { + "npc_id": "1460", + "loc_data": "{2787,2794,0,1,3}-" + }, + { + "npc_id": "1463", + "loc_data": "{2600,3276,0,1,4}-{2601,3276,0,1,6}-{2601,3277,0,1,6}-{2601,3278,0,1,2}-{2602,3279,0,1,5}-{2602,3282,0,1,4}-{2604,3276,0,1,4}-{2605,3280,0,1,4}-{2605,3281,0,1,7}-{2606,3280,0,1,4}-" + }, + { + "npc_id": "1469", + "loc_data": "{2595,3277,0,0,0}-" + }, + { + "npc_id": "1471", + "loc_data": "{2707,9102,0,1,0}-{2704,9107,0,1,0}-{2705,9110,0,1,0}-{2708,9107,0,1,0}-{2743,9105,0,1,0}-{2741,9103,0,1,0}-{2744,9101,0,1,0}-{2743,9099,0,1,0}-{2737,9124,0,1,0}-{2741,9123,0,1,0}-{2740,9120,0,1,0}-{2736,9119,0,1,0}-{2734,9117,0,1,0}-{2712,9135,0,1,0}-{2714,9131,0,1,0}-{2709,9131,0,1,0}-{2708,9135,0,1,0}-" + }, + { + "npc_id": "1514", + "loc_data": "{3432,3487,0,0,0}-" + }, + { + "npc_id": "1515", + "loc_data": "{3438,3487,0,0,0}-" + }, + { + "npc_id": "1516", + "loc_data": "{3437,3487,0,0,0}-" + }, + { + "npc_id": "1517", + "loc_data": "{3434,3483,0,0,0}-" + }, + { + "npc_id": "1518", + "loc_data": "{3435,3483,0,0,0}-" + }, + { + "npc_id": "1519", + "loc_data": "{3433,3486,0,0,0}-" + }, + { + "npc_id": "1520", + "loc_data": "{3434,3486,0,0,0}-" + }, + { + "npc_id": "1526", + "loc_data": "{2443,3089,0,0,0}-" + }, + { + "npc_id": "1544", + "loc_data": "{2911,10175,0,0,4}-{2912,10175,0,0,4}-{2913,10175,0,0,4}-{2914,10175,0,0,4}-{2909,10173,0,0,4}-{2910,10173,0,0,4}-{2911,10173,0,0,4}-{2912,10173,0,0,4}-{2913,10173,0,0,4}-{2909,10171,0,0,4}-{2910,10171,0,0,4}-{2911,10169,0,0,4}-{2912,10169,0,0,4}-{2913,10169,0,0,4}-" + }, + { + "npc_id": "1553", + "loc_data": "{2828,10065,1,0,0}-" + }, + { + "npc_id": "1554", + "loc_data": "{2829,10104,1,0,0}-" + }, + { + "npc_id": "1555", + "loc_data": "{2829,10096,1,0,0}-" + }, + { + "npc_id": "1558", + "loc_data": "{2800,3859,0,1,0}-{2803,3864,0,1,0}-{2805,3859,0,1,0}-" + }, + { + "npc_id": "1560", + "loc_data": "{3150,5454,0,1,4}-{3144,5446,0,1,7}-{3166,5446,0,1,3}-{3158,5451,0,1,7}-{2775,10224,0,1,0}-{2783,10199,0,1,0}-{2784,10233,0,1,0}-" + }, + { + "npc_id": "1561", + "loc_data": "{3144,5450,0,1,1}-{3153,5454,0,1,5}-{3160,5450,0,1,5}-{3167,5450,0,1,3}-{3161,5451,0,1,1}-{2777,10224,0,1,0}-{2784,10231,0,1,0}-{2792,10222,0,1,0}-" + }, + { + "npc_id": "1562", + "loc_data": "{3147,5448,0,1,4}-{2772,10221,0,1,0}-{2785,10201,0,1,0}-{2787,10232,0,1,0}-" + }, + { + "npc_id": "1563", + "loc_data": "{3152,5453,0,1,0}-{2773,10220,0,1,0}-{2788,10198,0,1,0}-{2790,10220,0,1,0}-{2800,10218,0,1,0}-" + }, + { + "npc_id": "1564", + "loc_data": "{2770,10219,0,1,0}-{2788,10230,0,1,0}-{2798,10216,0,1,0}-" + }, + { + "npc_id": "1565", + "loc_data": "{2788,10221,0,1,0}-{2801,10216,0,1,0}-" + }, + { + "npc_id": "1566", + "loc_data": "{2786,10199,0,1,0}-{2790,10222,0,1,0}-{2799,10214,0,1,0}-" + }, + { + "npc_id": "1582", + "loc_data": "{2568,9889,0,1,6}-{2578,9898,0,1,1}-{2657,9500,0,0,1}-{2632,9589,2,0,1}-{2628,9549,2,0,5}-{2629,9543,2,0,2}-{3249,5517,0,1,3}-" + }, + { + "npc_id": "1583", + "loc_data": "{2563,9885,0,1,7}-{2575,9897,0,1,1}-{2655,9481,0,0,3}-{2631,9579,2,0,4}-{3211,5519,0,1,0}-{3254,5513,0,1,7}-" + }, + { + "npc_id": "1584", + "loc_data": "{2661,9481,0,0,4}-{2635,9570,2,0,1}-{2627,9539,2,0,5}-{3231,5494,0,1,7}-{3209,5512,0,1,6}-" + }, + { + "npc_id": "1585", + "loc_data": "{2575,9892,0,1,6}-{2666,9482,0,0,5}-{2641,9563,2,0,5}-{3257,5523,0,1,1}-{3204,5516,0,1,3}-{3244,9369,0,1,3}-{3252,9359,0,1,3}-{3306,9400,0,1,3}-" + }, + { + "npc_id": "1586", + "loc_data": "{2668,9497,0,0,4}-{2635,9565,2,0,4}-{2633,9554,2,0,4}-{3247,5520,0,1,5}-{3205,5513,0,1,6}-{3213,5519,0,1,7}-" + }, + { + "npc_id": "1587", + "loc_data": "{2830,3241,0,1,6}-{3145,3812,0,1,0}-{3141,3826,0,1,0}-{2645,9489,0,0,7}-{2681,9577,0,0,0}-{2673,9591,0,0,5}-{2655,9575,0,0,5}-{2640,9580,0,0,0}-{2646,9553,0,0,1}-{2650,9536,0,0,7}-{2670,9543,0,0,0}-{2663,9555,0,0,7}-{2692,3202,0,1,4}-{2695,3216,0,1,0}-{3237,5557,0,1,3}-" + }, + { + "npc_id": "1588", + "loc_data": "{2826,3245,0,1,3}-{2637,9532,0,0,5}-{2634,9521,0,0,0}-{2642,9493,0,0,0}-{2645,9484,0,0,7}-{2679,9592,0,0,2}-{2654,9587,0,0,3}-{2650,9564,0,0,6}-{2647,9548,0,0,6}-{2637,9545,0,0,5}-{2678,9548,0,0,4}-{2658,9551,0,0,1}-{2691,3215,0,1,3}-{3246,5558,0,1,4}-" + }, + { + "npc_id": "1589", + "loc_data": "{2698,9503,0,0,7}-{2713,9505,0,0,0}-{2717,9502,0,0,7}-{2699,9523,0,0,0}-{2706,9525,0,0,2}-{2719,9517,0,0,5}-{2724,9515,0,0,3}-" + }, + { + "npc_id": "1590", + "loc_data": "{3184,5557,0,1,7}-{3148,5514,0,1,6}-{3144,5514,0,1,3}-{2730,9490,0,1,0}-{2731,9482,0,1,7}-{2742,9491,0,1,0}-" + }, + { + "npc_id": "1591", + "loc_data": "{2703,9456,0,0,0}-{2717,9459,0,0,7}-{2731,9460,0,0,3}-{2737,9454,0,0,3}-{2737,9444,0,0,3}-{2736,9432,0,0,3}-{2738,9421,0,0,5}-{2727,9418,0,0,1}-{2724,9439,0,0,0}-{2715,9443,0,0,4}-{2702,9443,0,0,3}-{2700,9428,0,0,3}-{2705,9419,0,0,7}-" + }, + { + "npc_id": "1592", + "loc_data": "{2728,9426,0,0,4}-{2714,9432,0,0,0}-{2707,9450,0,0,6}-{2724,9451,0,0,6}-" + }, + { + "npc_id": "1593", + "loc_data": "{2652,9526,0,0,7}-{2656,9514,0,0,7}-{2666,9522,0,0,4}-{2665,9513,0,0,3}-{2666,9489,0,0,1}-{2626,9535,2,0,6}-{2628,9507,2,0,5}-{2635,9508,2,0,2}-{2634,9483,2,0,7}-{2681,9563,0,0,5}-{2677,9586,0,0,2}-{2644,9587,0,0,1}-{2638,9592,2,0,5}-{2633,9583,2,0,7}-{2627,9545,2,0,6}-{2744,9503,0,0,2}-{2702,9525,0,0,2}-{2704,9487,0,0,7}-{2711,9485,0,0,2}-{2722,9486,0,0,4}-{2717,9480,0,0,6}-{2733,9494,0,0,1}-{2742,9489,0,0,0}-" + }, + { + "npc_id": "1594", + "loc_data": "{2646,9530,0,0,7}-{2643,9519,0,0,7}-{2660,9521,0,0,1}-{2665,9527,0,0,2}-{2671,9521,0,0,7}-{2670,9516,0,0,6}-{2660,9497,0,0,7}-{2643,9487,0,0,5}-{2653,9488,0,0,2}-{2677,9501,0,0,2}-{2683,9476,0,0,7}-{2630,9515,2,0,7}-{2642,9506,2,0,7}-{2629,9487,2,0,5}-{2648,9476,2,0,7}-{2672,9569,0,0,1}-{2657,9577,0,0,3}-{2643,9543,0,0,7}-{2658,9546,0,0,6}-{2636,9574,2,0,7}-{2643,9566,2,0,7}-{2635,9559,2,0,7}-{2699,9516,0,0,0}-{2709,9524,0,0,7}-{2700,9489,0,0,5}-{2703,9484,0,0,5}-{2710,9481,0,0,2}-{2716,9486,0,0,2}-{2726,9489,0,0,1}-{2735,9481,0,0,4}-{2740,9497,0,0,2}-{2739,9506,0,0,0}-" + }, + { + "npc_id": "1595", + "loc_data": "{2744,3152,0,1,7}-" + }, + { + "npc_id": "1597", + "loc_data": "{3147,9914,0,1,6}-" + }, + { + "npc_id": "1598", + "loc_data": "{2447,4429,0,1,6}-" + }, + { + "npc_id": "1600", + "loc_data": "{3190,9574,0,1,6}-{2791,9997,0,1,3}-" + }, + { + "npc_id": "1601", + "loc_data": "{2797,9997,0,1,6}-{2781,9996,0,1,7}-" + }, + { + "npc_id": "1602", + "loc_data": "{2793,9995,0,1,3}-{2783,9998,0,1,6}-" + }, + { + "npc_id": "1603", + "loc_data": "{2789,9994,0,1,1}-{2786,9998,0,1,5}-" + }, + { + "npc_id": "1604", + "loc_data": "{3434,3550,1,1,3}-{3439,3549,1,1,4}-{3425,3549,1,1,5}-{3423,3553,1,1,4}-{3426,3541,1,1,1}-{3425,3541,1,1,2}-{3426,3539,1,1,0}-{3420,3537,1,1,3}-{3417,3536,1,1,6}-" + }, + { + "npc_id": "1608", + "loc_data": "{2698,9999,0,1,6}-{2695,9997,0,1,7}-{2697,9996,0,1,5}-{2695,9999,0,1,6}-" + }, + { + "npc_id": "1609", + "loc_data": "{2700,9994,0,1,6}-{2698,10001,0,1,4}-{2703,9994,0,1,4}-{2701,10000,0,1,4}-" + }, + { + "npc_id": "1610", + "loc_data": "{3443,3537,2,1,4}-{3434,3536,2,1,4}-{3443,3540,2,1,3}-{3446,3548,2,1,4}-{3439,3549,2,1,3}-{3434,3547,2,1,1}-{3444,3537,2,1,3}-{3174,5474,0,1,4}-{3175,5479,0,1,6}-{3183,5473,0,1,7}-{3178,5468,0,1,4}-" + }, + { + "npc_id": "1612", + "loc_data": "{3438,3562,0,1,1}-{3430,3564,0,1,3}-{3434,3555,0,1,5}-{3443,3551,0,1,4}-{3442,3545,0,1,1}-{3449,3536,0,1,1}-{3440,3539,0,1,4}-" + }, + { + "npc_id": "1613", + "loc_data": "{3439,3565,2,1,6}-{3443,3571,2,1,1}-{3435,3565,2,1,3}-{3443,3564,2,1,2}-{3444,3559,2,1,2}-{3427,3558,2,1,0}-{3425,3557,2,1,4}-{3430,3557,2,1,3}-{3170,5554,0,1,2}-{3166,5556,0,1,5}-{3171,5560,0,1,4}-{3173,5556,0,1,3}-" + }, + { + "npc_id": "1615", + "loc_data": "{3422,3566,2,1,5}-{3426,3568,2,1,6}-{3418,3562,2,1,4}-{3414,3566,2,1,3}-{3412,3570,2,1,4}-{3421,3572,2,1,2}-{3422,3571,2,1,1}-{3033,4873,0,1,7}-{3039,4885,0,1,3}-{3050,4890,0,1,3}-{3062,4892,0,1,5}-{3064,4901,0,1,3}-{3056,4912,0,1,3}-{3048,4916,0,1,5}-{3038,4911,0,1,6}-{3022,4914,0,1,6}-{3018,4902,0,1,6}-{3025,4898,0,1,4}-{3033,4885,0,1,0}-{3021,4877,0,1,3}-{3048,4872,0,1,1}-{3061,4880,0,1,3}-{3061,4891,0,1,1}-{3054,4901,0,1,1}-" + }, + { + "npc_id": "1616", + "loc_data": "{2741,10011,0,1,2}-{2742,10010,0,1,3}-{2739,10014,0,1,1}-{2740,10006,0,1,3}-" + }, + { + "npc_id": "1617", + "loc_data": "{2743,10003,0,1,1}-{2746,10008,0,1,2}-{2743,10015,0,1,3}-{2745,10011,0,1,5}-{2744,10016,0,1,1}-" + }, + { + "npc_id": "1618", + "loc_data": "{3424,3565,1,1,1}-{3416,3562,1,1,6}-{3423,3559,1,1,0}-{3424,3560,1,1,4}-{3412,3558,1,1,1}-{3412,3568,1,1,1}-{3417,3565,1,1,3}-" + }, + { + "npc_id": "1620", + "loc_data": "{2794,10036,0,1,4}-{2788,10037,0,1,0}-{2795,10034,0,1,3}-{2785,10035,0,1,3}-" + }, + { + "npc_id": "1621", + "loc_data": "{2797,10035,0,1,6}-{2787,10032,0,1,3}-{2802,10031,0,1,3}-" + }, + { + "npc_id": "1623", + "loc_data": "{2725,9999,0,1,6}-" + }, + { + "npc_id": "1624", + "loc_data": "{3179,5522,0,1,6}-{3168,5513,0,1,3}-{3174,5514,0,1,6}-{3178,5517,0,1,4}-{3178,5528,0,1,1}-{3169,5516,0,1,3}-{3179,5524,0,1,4}-{3223,9393,0,1,5}-{3225,9401,0,1,0}-{3222,9375,0,1,6}-{3220,9380,0,1,6}-{3215,9364,0,1,3}-{3213,9361,0,1,1}-{3212,9355,0,1,2}-{3230,9367,0,1,3}-{3240,9350,0,1,5}-{3248,9348,0,1,4}-{3255,9350,0,1,4}-{3256,9392,0,1,4}-" + }, + { + "npc_id": "1626", + "loc_data": "{2723,10005,0,1,0}-{2726,9998,0,1,1}-{2723,10009,0,1,3}-" + }, + { + "npc_id": "1627", + "loc_data": "{2724,10008,0,1,4}-{2721,10005,0,1,1}-{2724,10005,0,1,3}-{2729,10004,0,1,3}-{2724,10012,0,1,4}-{2718,10009,0,1,1}-{2721,10001,0,1,7}-{2720,9996,0,1,7}-{2721,10006,0,1,3}-" + }, + { + "npc_id": "1628", + "loc_data": "{3155,5536,0,1,4}-{3156,5540,0,1,3}-{3157,5538,0,1,2}-{3166,5537,0,1,1}-{3165,5541,0,1,3}-{2723,10004,0,1,4}-{2722,9999,0,1,1}-{2729,10002,0,1,1}-" + }, + { + "npc_id": "1629", + "loc_data": "{3162,5538,0,1,2}-{3157,5538,0,1,4}-{3155,5537,0,1,1}-{3164,5538,0,1,2}-" + }, + { + "npc_id": "1631", + "loc_data": "{3160,9589,0,1,3}-{3167,9589,0,1,6}-{3179,9585,0,1,1}-{3188,9572,0,1,7}-{3190,9580,0,1,3}-{3206,9586,0,1,3}-{3221,9586,0,1,7}-{3226,9573,0,1,5}-{3223,9569,0,1,1}-{2800,10017,0,1,1}-{2796,10021,0,1,1}-{2805,10021,0,1,1}-{2803,10013,0,1,1}-" + }, + { + "npc_id": "1632", + "loc_data": "{2798,10019,0,1,1}-{2800,10015,0,1,1}-{2797,10018,0,1,1}-" + }, + { + "npc_id": "1633", + "loc_data": "{1837,3244,0,1,0}-{1836,3250,0,1,0}-{1845,3251,0,1,0}-{1845,3247,0,1,0}-{1849,3241,0,1,0}-{1853,3247,0,1,0}-{1848,3254,0,1,0}-{1858,3251,0,1,0}-{1935,3217,0,1,0}-{1933,3209,0,1,0}-{1930,3208,0,1,0}-{1929,3217,0,1,0}-{1927,3219,0,1,0}-{1924,3211,0,1,0}-{1920,3216,0,1,0}-{1925,3213,0,1,0}-{3263,9399,0,1,3}-{3248,9374,0,1,3}-{3253,9363,0,1,3}-{3249,9355,0,1,3}-{3274,9397,0,1,1}-{3275,9393,0,1,6}-{3271,9384,0,1,0}-{3270,9380,0,1,2}-{3283,9378,0,1,6}-{3285,9386,0,1,5}-{3284,9401,0,1,4}-{3278,9353,0,1,1}-{3299,9380,0,1,1}-{3319,9402,0,1,1}-{3305,9350,0,1,4}-{2761,10007,0,1,0}-{2757,10010,0,1,3}-{2763,10000,0,1,5}-{2760,10011,0,1,0}-{2761,9997,0,1,4}-" + }, + { + "npc_id": "1634", + "loc_data": "{2757,10008,0,1,6}-{2760,10005,0,1,5}-" + }, + { + "npc_id": "1635", + "loc_data": "{2758,10001,0,1,1}-{2763,10006,0,1,1}-{2766,10000,0,1,6}-{2759,9999,0,1,7}-" + }, + { + "npc_id": "1636", + "loc_data": "{2760,10004,0,1,1}-{2760,10002,0,1,6}-" + }, + { + "npc_id": "1637", + "loc_data": "{2700,10027,0,1,6}-{3276,5454,0,1,4}-{3279,5450,0,1,0}-{3271,5446,0,1,3}-{3277,5512,0,1,1}-{3278,5535,0,0,0}-{3277,5520,0,1,1}-" + }, + { + "npc_id": "1638", + "loc_data": "{2704,10022,0,1,4}-{2707,10023,0,1,4}-{3284,5456,0,1,1}-{3278,5453,0,1,1}-{3271,5512,0,1,4}-" + }, + { + "npc_id": "1639", + "loc_data": "{2711,10028,0,1,2}-{2710,10030,0,1,6}-{3283,5461,0,1,4}-{3285,5456,0,1,6}-{3275,5455,0,1,5}-{3272,5446,0,1,4}-{3277,5445,0,1,3}-" + }, + { + "npc_id": "1640", + "loc_data": "{2705,10028,0,1,3}-{2704,10031,0,1,6}-{3272,5445,0,1,4}-" + }, + { + "npc_id": "1641", + "loc_data": "{2708,10025,0,1,6}-" + }, + { + "npc_id": "1642", + "loc_data": "{2703,10020,0,1,0}-" + }, + { + "npc_id": "1643", + "loc_data": "{3434,3562,1,1,6}-{3439,3558,1,1,5}-{3444,3567,1,1,4}-{3439,3571,1,1,3}-{3435,3570,1,1,7}-{3447,3561,1,1,4}-{3442,3573,1,1,6}-{3246,5446,0,1,3}-{3237,5455,0,1,0}-{3246,5450,0,1,4}-" + }, + { + "npc_id": "1644", + "loc_data": "{3240,5448,0,1,7}-" + }, + { + "npc_id": "1648", + "loc_data": "{3426,3537,0,1,4}-{3413,3536,0,1,3}-{3419,3545,0,1,2}-{3426,3544,0,1,6}-{3410,3535,0,1,2}-{3425,3556,0,1,6}-{3418,3557,0,1,1}-{3428,3571,0,1,3}-{3432,3573,0,1,4}-" + }, + { + "npc_id": "1650", + "loc_data": "{3411,3541,0,1,3}-" + }, + { + "npc_id": "1655", + "loc_data": "{3424,3544,0,1,1}-{3423,3546,0,1,1}-{3409,3538,0,1,3}-{3421,3559,0,1,4}-{3413,3559,0,1,6}-{3433,3574,0,1,3}-{3428,3571,0,1,6}-" + }, + { + "npc_id": "1658", + "loc_data": "{2595,3087,1,1,1}-" + }, + { + "npc_id": "1665", + "loc_data": "{3544,3462,0,0,1}-" + }, + { + "npc_id": "1668", + "loc_data": "{3551,3550,0,1,0}-" + }, + { + "npc_id": "1669", + "loc_data": "{3549,3555,2,0,3}-" + }, + { + "npc_id": "1671", + "loc_data": "{3547,3555,2,0,6}-" + }, + { + "npc_id": "1672", + "loc_data": "{3552,3550,0,1,0}-" + }, + { + "npc_id": "1675", + "loc_data": "{3551,3561,0,1,0}-" + }, + { + "npc_id": "1676", + "loc_data": "{3481,9942,0,1,0}-{3493,9925,0,1,0}-{3525,9932,0,1,0}-{3555,9927,0,1,0}-{3554,9947,0,1,0}-{3529,9968,0,1,0}-" + }, + { + "npc_id": "1677", + "loc_data": "{3474,9933,0,1,0}-{3488,9934,0,1,0}-{3547,9932,0,1,0}-{3540,9958,0,1,0}-{3564,9959,0,1,0}-" + }, + { + "npc_id": "1678", + "loc_data": "{3473,9943,0,1,0}-{3488,9944,0,1,0}-{3498,9937,0,1,0}-{3534,9926,0,1,0}-{3553,9941,0,1,0}-{3548,9956,0,1,0}-{3564,9949,0,1,0}-" + }, + { + "npc_id": "1679", + "loc_data": "{2291,3145,0,1,2}-" + }, + { + "npc_id": "1680", + "loc_data": "{2290,3143,0,1,3}-" + }, + { + "npc_id": "1681", + "loc_data": "{3202,5483,0,1,4}-{2544,9843,0,1,3}-" + }, + { + "npc_id": "1683", + "loc_data": "{3678,3511,0,1,4}-" + }, + { + "npc_id": "1684", + "loc_data": "{3660,3517,0,1,7}-" + }, + { + "npc_id": "1685", + "loc_data": "{3659,3497,0,1,0}-" + }, + { + "npc_id": "1686", + "loc_data": "{3657,3516,0,1,3}-{3654,3518,1,1,6}-{3663,3522,0,1,7}-" + }, + { + "npc_id": "1688", + "loc_data": "{3709,3497,0,0,3}-" + }, + { + "npc_id": "1691", + "loc_data": "{3624,3530,0,1,1}-{3623,3524,0,1,2}-{3618,3526,0,1,6}-{3612,3527,0,1,6}-{3609,3527,0,1,4}-" + }, + { + "npc_id": "1692", + "loc_data": "{3632,3528,0,1,5}-{3633,3525,0,1,6}-{3631,3529,0,1,3}-{3631,3529,0,1,2}-{3628,3530,0,1,1}-{3631,3530,0,1,4}-{3629,3529,0,1,4}-" + }, + { + "npc_id": "1694", + "loc_data": "{3673,3491,0,1,3}-" + }, + { + "npc_id": "1695", + "loc_data": "{3461,3558,0,0,3}-" + }, + { + "npc_id": "1697", + "loc_data": "{3662,3502,0,1,4}-{3664,3493,0,1,6}-{3672,3486,0,1,2}-{3685,3485,0,1,3}-{3684,3496,0,1,3}-{3675,3502,0,1,3}-{3667,3484,0,1,0}-{3666,3471,0,1,4}-{3676,3464,0,1,3}-{3683,3467,0,1,6}-{3685,3474,0,1,4}-{3690,3474,0,1,1}-{3695,3473,0,1,3}-{3693,3466,0,1,7}-{3698,3463,0,1,3}-{3688,3467,0,1,3}-{3675,3474,0,1,4}-{3665,3476,0,1,6}-{3662,3491,0,1,1}-{3662,3495,0,1,4}-{3661,3501,0,1,6}-{3682,3486,0,1,4}-{3678,3493,0,1,4}-{3673,3491,0,1,3}-{3658,3502,0,1,4}-{3660,3505,0,1,1}-{3665,3469,0,1,6}-{3659,3465,0,1,6}-{3667,3461,0,1,1}-" + }, + { + "npc_id": "1698", + "loc_data": "{3647,3473,0,1,3}-{3649,3503,0,1,2}-{3665,3529,0,1,4}-{3669,3522,0,1,4}-{3672,3529,0,1,4}-{3668,3534,0,1,4}-{3657,3534,0,1,4}-{3654,3537,0,1,4}-" + }, + { + "npc_id": "1699", + "loc_data": "{3659,3480,0,1,3}-" + }, + { + "npc_id": "1700", + "loc_data": "{3681,3493,0,1,5}-" + }, + { + "npc_id": "1702", + "loc_data": "{3690,3464,0,0,1}-{3691,3464,0,0,1}-{3688,3464,0,0,1}-{3687,3464,0,0,1}-" + }, + { + "npc_id": "1703", + "loc_data": "{3697,3496,0,1,3}-{3699,3497,0,1,2}-{3689,3503,0,1,3}-{3690,3508,0,1,6}-{3690,3512,0,1,6}-{3689,3517,0,1,1}-{3690,3518,0,1,5}-{3689,3503,0,1,3}-{3689,3496,0,1,0}-{3690,3524,0,1,6}-{3689,3528,0,1,5}-" + }, + { + "npc_id": "1704", + "loc_data": "{3703,3487,0,0,3}-" + }, + { + "npc_id": "1706", + "loc_data": "{3661,3508,0,0,1}-{3658,3508,0,0,1}-{3652,3487,0,0,3}-{3652,3484,0,0,3}-" + }, + { + "npc_id": "1710", + "loc_data": "{3165,9631,0,1,6}-{3165,9633,0,1,0}-{3160,9634,0,1,7}-{3156,9630,0,1,6}-{3152,9627,0,1,4}-{3148,9621,0,1,4}-{3175,9630,0,1,1}-{3174,9634,0,1,3}-{3172,9638,0,1,0}-" + }, + { + "npc_id": "1711", + "loc_data": "{2566,5197,0,1,4}-{3160,9638,0,1,1}-{3159,9633,0,1,6}-{3161,9626,0,1,6}-{3169,9626,0,1,4}-{3171,9634,0,1,4}-{3164,9634,0,1,3}-{3165,9631,0,1,7}-{3180,9631,0,1,7}-{3183,9629,0,1,6}-{3182,9633,0,1,1}-{3172,9632,0,1,4}-{3158,9631,0,1,1}-{3160,9630,0,1,1}-{3159,9633,0,1,4}-{3150,9623,0,1,6}-{3152,9620,0,1,6}-{3159,9622,0,1,0}-{3169,9623,0,1,6}-{3169,9623,0,1,4}-" + }, + { + "npc_id": "1712", + "loc_data": "{2573,5201,0,1,4}-{2577,5195,0,1,4}-{2577,5189,0,0,6}-{2571,5195,0,0,6}-{3153,9646,0,1,3}-{3151,9651,0,1,1}-" + }, + { + "npc_id": "1714", + "loc_data": "{3172,9624,0,1,2}-{3161,9623,0,1,1}-{3151,9624,0,1,1}-{3150,9622,0,1,4}-{3153,9627,0,1,3}-{3157,9634,0,1,2}-{3171,9636,0,1,6}-{3174,9639,0,1,3}-{3174,9634,0,1,3}-" + }, + { + "npc_id": "1715", + "loc_data": "{3171,9635,0,1,3}-{3159,9636,0,1,2}-{3169,9630,0,1,3}-{3172,9625,0,1,0}-{3159,9627,0,1,3}-" + }, + { + "npc_id": "1752", + "loc_data": "{2381,3428,0,1,0}-{2392,3405,0,1,0}-{2413,3397,0,1,0}-{2415,3408,0,1,0}-{2432,3387,0,1,0}-{2442,3388,0,1,0}-{2453,3363,0,1,0}-{2463,3375,0,1,0}-{2437,3442,0,1,0}-{2449,3421,0,1,0}-{2457,3394,0,1,0}-{2464,3394,0,1,0}-{2480,3406,0,1,0}-{2463,3456,0,1,0}-{2468,3456,0,1,0}-" + }, + { + "npc_id": "1754", + "loc_data": "{3100,3246,0,1,2}-{3131,3263,0,1,5}-{3122,3278,0,1,2}-{3259,3308,0,1,4}-" + }, + { + "npc_id": "1755", + "loc_data": "{3106,3275,0,1,1}-{3106,3266,0,1,3}-{3114,3273,0,1,4}-{3258,3312,0,1,3}-" + }, + { + "npc_id": "1756", + "loc_data": "{3096,3251,0,1,3}-{3102,3261,0,1,1}-{3117,3278,0,1,0}-{3262,3320,0,1,1}-" + }, + { + "npc_id": "1757", + "loc_data": "{3233,3308,0,1,4}-" + }, + { + "npc_id": "1758", + "loc_data": "{2895,3403,0,0,0}-{3262,3325,0,0,0}-" + }, + { + "npc_id": "1759", + "loc_data": "{3250,3310,0,0,0}-" + }, + { + "npc_id": "1761", + "loc_data": "{3121,3274,0,0,0}-" + }, + { + "npc_id": "1765", + "loc_data": "{3198,3270,0,1,0}-{2923,3320,0,1,7}-{2921,3320,0,1,4}-{2923,3320,0,1,5}-{3206,3265,0,1,4}-{3201,3265,0,1,0}-" + }, + { + "npc_id": "1766", + "loc_data": "{3171,3320,0,1,0}-{3259,3262,0,1,4}-{3262,3274,0,1,3}-{3259,3278,0,1,1}-{3263,3273,0,1,7}-" + }, + { + "npc_id": "1767", + "loc_data": "{2924,3288,0,0,1}-{2921,3290,0,1,1}-{2926,3279,0,1,1}-{2931,3272,0,1,1}-{2936,3273,0,1,6}-{3262,3262,0,1,6}-{3025,3311,0,1,5}-" + }, + { + "npc_id": "1769", + "loc_data": "{2596,9822,0,1,4}-{2979,3203,0,1,4}-{3264,3249,0,1,4}-" + }, + { + "npc_id": "1770", + "loc_data": "{2595,9826,0,1,1}-{3189,3246,0,1,0}-{3143,3231,0,1,0}-{3140,3263,0,1,0}-{3262,3253,0,1,6}-{3259,3254,0,1,3}-{2999,3212,0,1,1}-" + }, + { + "npc_id": "1771", + "loc_data": "{3192,3245,0,1,0}-{3184,3248,0,1,0}-{3140,3230,0,1,0}-{3141,3261,0,1,0}-{3187,3286,0,1,0}-{3174,3294,0,1,0}-{3178,3294,0,1,0}-{3144,3302,0,1,0}-{3142,3303,0,1,0}-{3151,3300,0,1,0}-{3139,3299,0,1,0}-{2993,3199,0,1,6}-{3248,3233,0,1,6}-{3247,3240,0,1,3}-{3209,3277,0,1,0}-" + }, + { + "npc_id": "1772", + "loc_data": "{3194,3255,0,1,0}-{3143,3234,0,1,0}-{3251,3231,0,1,1}-{3260,3241,0,1,0}-{3259,3222,0,0,4}-{2993,3203,0,1,0}-" + }, + { + "npc_id": "1773", + "loc_data": "{3252,3227,0,1,1}-{3263,3219,0,1,0}-{2991,3219,0,1,1}-" + }, + { + "npc_id": "1774", + "loc_data": "{2592,9834,0,1,6}-{3255,3228,0,1,4}-{3265,3219,0,1,2}-{3012,3204,0,1,3}-" + }, + { + "npc_id": "1775", + "loc_data": "{2585,9829,0,1,1}-{3254,3224,0,1,1}-{3010,3211,0,1,6}-" + }, + { + "npc_id": "1776", + "loc_data": "{2999,3216,0,1,6}-{3264,3247,0,1,2}-" + }, + { + "npc_id": "1778", + "loc_data": "{3260,3859,0,0,0}-" + }, + { + "npc_id": "1779", + "loc_data": "{2978,3752,0,1,6}-" + }, + { + "npc_id": "1780", + "loc_data": "{3272,3687,0,1,3}-" + }, + { + "npc_id": "1781", + "loc_data": "{3008,3862,0,0,0}-" + }, + { + "npc_id": "1782", + "loc_data": "{3363,3883,0,1,4}-" + }, + { + "npc_id": "1783", + "loc_data": "{3079,3516,0,1,6}-" + }, + { + "npc_id": "1784", + "loc_data": "{3025,3705,0,1,1}-" + }, + { + "npc_id": "1785", + "loc_data": "{3144,3788,0,1,6}-" + }, + { + "npc_id": "1786", + "loc_data": "{3234,3633,0,1,1}-" + }, + { + "npc_id": "1787", + "loc_data": "{3074,3609,0,1,3}-" + }, + { + "npc_id": "1795", + "loc_data": "{2967,9811,0,0,0}-" + }, + { + "npc_id": "1796", + "loc_data": "{2968,9809,0,0,0}-" + }, + { + "npc_id": "1798", + "loc_data": "{2701,3471,0,0,0}-" + }, + { + "npc_id": "1805", + "loc_data": "{2760,3660,0,1,6}-" + }, + { + "npc_id": "1806", + "loc_data": "{2798,3667,0,1,3}-" + }, + { + "npc_id": "1807", + "loc_data": "{2811,3673,0,1,3}-" + }, + { + "npc_id": "1808", + "loc_data": "{2765,3676,0,1,3}-" + }, + { + "npc_id": "1810", + "loc_data": "{2811,3681,0,1,3}-" + }, + { + "npc_id": "1814", + "loc_data": "{2794,3668,0,1,0}-" + }, + { + "npc_id": "1815", + "loc_data": "{2801,3669,0,1,0}-" + }, + { + "npc_id": "1816", + "loc_data": "{2803,3668,0,1,0}-" + }, + { + "npc_id": "1817", + "loc_data": "{2797,3672,0,1,0}-" + }, + { + "npc_id": "1818", + "loc_data": "{2802,3674,0,1,0}-" + }, + { + "npc_id": "1819", + "loc_data": "{2813,3687,0,1,0}-" + }, + { + "npc_id": "1820", + "loc_data": "{2812,3685,0,1,0}-" + }, + { + "npc_id": "1821", + "loc_data": "{2757,3674,0,1,0}-{2762,3671,0,1,0}-{2785,3658,0,1,0}-" + }, + { + "npc_id": "1828", + "loc_data": "{3231,9545,0,1,6}-" + }, + { + "npc_id": "1829", + "loc_data": "{3153,9548,0,1,1}-{3154,9547,0,1,4}-{3176,9546,0,1,4}-{3221,9558,0,1,4}-{3226,9548,0,1,7}-{3224,9551,0,1,1}-{3219,9558,0,1,7}-" + }, + { + "npc_id": "1831", + "loc_data": "{3165,9542,0,1,3}-{3164,9542,0,1,3}-{3166,9542,0,1,3}-{3208,9555,0,1,1}-{3211,9570,0,1,5}-{3211,9570,0,1,5}-{3208,9555,0,1,1}-{3208,9555,0,1,1}-{3207,9555,0,1,1}-{3213,9570,0,1,5}-" + }, + { + "npc_id": "1832", + "loc_data": "{3150,9573,0,1,1}-{3188,9550,0,1,0}-{3148,9578,0,1,4}-{3152,9556,0,1,3}-{3150,9553,0,1,2}-{3167,9547,0,1,1}-{3168,9546,0,1,3}-{3184,9551,0,1,3}-{3191,9583,0,1,4}-{3187,9583,0,1,3}-{3220,9573,0,1,4}-{3224,9574,0,1,1}-{3208,9588,0,1,6}-{3210,9585,0,1,4}-{3272,5549,0,1,0}-{3275,5546,0,1,0}-{3281,5545,0,1,2}-{3286,5544,0,1,0}-{3285,5539,0,1,3}-{3268,5553,0,1,1}-{3274,5558,0,1,6}-{3281,5557,0,1,4}-{3282,5554,0,1,4}-{3269,5551,0,1,3}-{3287,5549,0,1,3}-{3279,5546,0,1,6}-{3283,5545,0,1,0}-{3269,5548,0,1,1}-" + }, + { + "npc_id": "1833", + "loc_data": "{3151,9575,0,1,3}-{3150,9577,0,1,4}-{3148,9554,0,1,6}-{3165,9547,0,1,1}-{3165,9546,0,1,2}-{3184,9551,0,1,3}-{3189,9581,0,1,4}-{3222,9575,0,1,4}-{3226,9576,0,1,3}-{3210,9587,0,1,2}-" + }, + { + "npc_id": "1834", + "loc_data": "{3169,3174,0,0,6}-" + }, + { + "npc_id": "1839", + "loc_data": "{2825,10167,0,1,0}-" + }, + { + "npc_id": "1841", + "loc_data": "{3020,3452,0,0,0}-" + }, + { + "npc_id": "1843", + "loc_data": "{2839,10128,0,1,0}-" + }, + { + "npc_id": "1844", + "loc_data": "{2855,10144,0,1,0}-" + }, + { + "npc_id": "1846", + "loc_data": "{2873,10166,0,1,3}-" + }, + { + "npc_id": "1847", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1848", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1849", + "loc_data": "{2868,10168,0,1,0}-" + }, + { + "npc_id": "1860", + "loc_data": "{2957,3203,0,1,3}-" + }, + { + "npc_id": "1861", + "loc_data": "{3208,3256,0,0,1}-" + }, + { + "npc_id": "1862", + "loc_data": "{3304,3211,0,1,1}-" + }, + { + "npc_id": "1863", + "loc_data": "{3358,2956,0,0,6}-" + }, + { + "npc_id": "1864", + "loc_data": "{3363,2956,0,0,3}-" + }, + { + "npc_id": "1865", + "loc_data": "{3352,2974,0,1,4}-" + }, + { + "npc_id": "1866", + "loc_data": "{3360,2983,0,1,4}-" + }, + { + "npc_id": "1867", + "loc_data": "{3347,2966,0,0,6}-" + }, + { + "npc_id": "1870", + "loc_data": "{3373,2972,0,1,5}-" + }, + { + "npc_id": "1871", + "loc_data": "{3344,2986,0,1,3}-" + }, + { + "npc_id": "1872", + "loc_data": "{3354,2953,0,0,6}-" + }, + { + "npc_id": "1873", + "loc_data": "{3343,2962,0,1,0}-{3343,2964,0,1,1}-" + }, + { + "npc_id": "1874", + "loc_data": "{3328,2952,0,1,0}-{3328,2957,0,1,0}-{3331,2955,0,1,0}-{3331,2961,0,1,0}-{3371,9301,0,1,0}-{3371,9303,0,1,0}-{3371,9305,0,1,0}-{3371,9307,0,1,0}-{3371,9309,0,1,0}-{3373,9301,0,1,0}-{3373,9303,0,1,0}-{3373,9306,0,1,0}-{3373,9308,0,1,0}-{3375,9301,0,1,0}-{3375,9303,0,1,0}-{3375,9305,0,1,0}-{3375,9307,0,1,0}-{3375,9309,0,1,0}-{3403,2963,0,1,0}-{3429,2976,0,1,0}-{3396,3029,0,1,0}-{3397,3044,0,1,0}-{3398,3038,0,1,0}-{3266,2955,0,1,0}-{3271,2967,0,1,0}-{3279,2957,0,1,0}-{3280,2975,0,1,0}-{3284,2959,0,1,0}-{3294,2964,0,1,0}-{3295,2978,0,1,0}-{3305,2966,0,1,0}-{3307,2959,0,1,0}-{3309,2973,0,1,0}-" + }, + { + "npc_id": "1875", + "loc_data": "{3354,2952,0,0,1}-" + }, + { + "npc_id": "1884", + "loc_data": "{2811,3174,0,1,0}-" + }, + { + "npc_id": "1902", + "loc_data": "{3333,2946,0,0,1}-" + }, + { + "npc_id": "1905", + "loc_data": "{3335,2948,0,1,2}-{3331,2948,0,1,6}-{3333,2950,0,1,0}-" + }, + { + "npc_id": "1907", + "loc_data": "{3488,3091,0,1,0}-" + }, + { + "npc_id": "1911", + "loc_data": "{3416,3155,0,1,0}-" + }, + { + "npc_id": "1912", + "loc_data": "{3376,3429,0,1,6}-" + }, + { + "npc_id": "1916", + "loc_data": "{3112,9690,0,0,7}-" + }, + { + "npc_id": "1917", + "loc_data": "{3177,2987,0,1,6}-" + }, + { + "npc_id": "1918", + "loc_data": "{3180,3043,0,1,2}-" + }, + { + "npc_id": "1920", + "loc_data": "{3497,3478,0,0,0}-" + }, + { + "npc_id": "1921", + "loc_data": "{3159,2980,0,1,1}-" + }, + { + "npc_id": "1923", + "loc_data": "{3185,2983,0,1,4}-" + }, + { + "npc_id": "1924", + "loc_data": "{3213,2956,0,1,1}-" + }, + { + "npc_id": "1926", + "loc_data": "{3179,2967,0,1,3}-{3172,2966,0,1,1}-{3171,2980,0,1,3}-{3166,2972,0,1,0}-{3181,2979,0,1,3}-{3187,2976,0,1,7}-{3174,2984,0,1,3}-{3183,2982,0,1,4}-{3187,2985,0,1,1}-{3182,2988,0,1,5}-{3188,2990,0,1,2}-{3172,2992,0,1,4}-{3171,2987,0,1,4}-{3161,2982,0,1,2}-{3161,2986,0,1,3}-{3159,2987,0,1,3}-{3163,2976,0,1,4}-{3161,2977,0,1,4}-{3161,2979,0,1,6}-{3162,2982,0,1,6}-{3164,2985,0,1,2}-{3158,2970,0,1,3}-{3155,2979,0,1,4}-{3167,2972,0,1,6}-{3172,2973,0,1,5}-{3175,2973,0,1,4}-{3185,2967,0,1,2}-" + }, + { + "npc_id": "1927", + "loc_data": "{3160,2979,0,0,5}-{3162,2989,0,0,1}-" + }, + { + "npc_id": "1928", + "loc_data": "{3161,2977,0,0,4}-{3161,2987,0,0,1}-" + }, + { + "npc_id": "1929", + "loc_data": "{3158,2987,0,0,3}-" + }, + { + "npc_id": "1930", + "loc_data": "{3161,2985,0,0,6}-{3161,2983,0,0,6}-{3161,2982,0,0,6}-{3161,2981,0,0,6}-{3161,2984,0,0,6}-" + }, + { + "npc_id": "1932", + "loc_data": "{2836,3740,0,0,6}-" + }, + { + "npc_id": "1935", + "loc_data": "{2841,3738,0,0,3}-" + }, + { + "npc_id": "1936", + "loc_data": "{2852,3735,0,1,0}-{2855,3729,0,1,0}-{2861,3725,0,1,0}-" + }, + { + "npc_id": "1937", + "loc_data": "{2850,3732,0,1,0}-{2857,3726,0,1,0}-{2865,3729,0,1,0}-" + }, + { + "npc_id": "1938", + "loc_data": "{2849,3735,0,1,0}-{2853,3731,0,1,0}-{2859,3728,0,1,0}-" + }, + { + "npc_id": "1939", + "loc_data": "{2856,3728,0,1,0}-{2862,3722,0,1,0}-{2864,3723,0,1,0}-" + }, + { + "npc_id": "1940", + "loc_data": "{2853,3728,0,1,0}-{2856,3732,0,1,0}-{2863,3720,0,1,0}-" + }, + { + "npc_id": "1941", + "loc_data": "{2854,3725,0,1,0}-{2862,3728,0,1,0}-{2862,3731,0,1,0}-" + }, + { + "npc_id": "1942", + "loc_data": "{2855,3735,0,1,0}-{2858,3730,0,1,0}-{2865,3726,0,1,0}-" + }, + { + "npc_id": "1943", + "loc_data": "{2825,3807,2,0,1}-" + }, + { + "npc_id": "1945", + "loc_data": "{2825,3811,2,0,6}-" + }, + { + "npc_id": "1947", + "loc_data": "{2835,3740,0,0,6}-" + }, + { + "npc_id": "1949", + "loc_data": "{2837,3740,0,0,6}-" + }, + { + "npc_id": "1951", + "loc_data": "{2886,3723,0,1,3}-{2887,3720,0,1,4}-{2890,3721,0,1,7}-{2890,3724,0,1,1}-{2909,3736,0,1,5}-{2910,3736,0,1,2}-{2891,3724,0,1,7}-" + }, + { + "npc_id": "1952", + "loc_data": "{2892,3726,0,1,0}-{2894,3726,0,1,6}-{2895,3728,0,1,0}-{2895,3730,0,1,5}-{2922,3755,0,1,3}-{2893,3725,0,1,2}-{2894,3727,0,1,6}-{2895,3725,0,1,3}-{2898,3735,0,1,6}-" + }, + { + "npc_id": "1953", + "loc_data": "{2897,3733,0,1,5}-{2897,3737,0,1,5}-{2913,3736,0,1,3}-{2897,3737,0,1,6}-{2915,3740,0,1,1}-{2894,3742,0,1,0}-" + }, + { + "npc_id": "1954", + "loc_data": "{2895,3751,0,1,1}-{2895,3755,0,1,6}-{2898,3751,0,1,3}-{2898,3755,0,1,6}-{2930,3765,0,1,5}-{2893,3748,0,1,5}-{2896,3749,0,1,1}-{2929,3762,0,1,3}-" + }, + { + "npc_id": "1955", + "loc_data": "{2895,3740,0,1,1}-{2895,3744,0,1,3}-{2898,3740,0,1,6}-{2898,3744,0,1,1}-{2908,3739,0,1,0}-{2928,3763,0,1,3}-{2899,3737,0,1,2}-{2897,3739,0,1,6}-{2912,3742,0,1,2}-{2898,3750,0,1,3}-{2895,3751,0,1,4}-" + }, + { + "npc_id": "1956", + "loc_data": "{2886,3758,0,1,6}-{2886,3762,0,1,3}-{2890,3756,0,1,4}-{2890,3762,0,1,1}-{2928,3753,0,1,2}-{2926,3753,0,1,6}-{2895,3754,0,1,3}-" + }, + { + "npc_id": "1958", + "loc_data": "{3147,5467,0,1,1}-{3144,5466,0,1,2}-{3147,5479,0,1,2}-{3144,5475,0,1,6}-{3161,5482,0,1,5}-{3147,5475,0,1,6}-{3240,9326,0,1,3}-{3209,9310,0,1,4}-{3245,9333,0,1,4}-{3213,9307,0,1,4}-{3221,9318,0,1,3}-{3229,9333,0,1,3}-{3216,9329,0,1,4}-{3208,9294,0,1,3}-{3201,9286,0,1,1}-{3212,9326,0,1,4}-{3217,9284,0,1,7}-{3226,9284,0,1,4}-{3232,9299,0,1,6}-{3242,9295,0,1,1}-{3220,9302,0,1,0}-{3238,9305,0,1,6}-{3220,9296,0,1,1}-{3246,9291,0,1,3}-{3240,9289,0,1,4}-{3237,9305,0,1,3}-{3257,9292,0,0,5}-{3246,9321,0,1,1}-{3261,9310,0,1,3}-{3258,9309,0,1,4}-{3251,9316,0,1,3}-{3247,9310,0,1,3}-" + }, + { + "npc_id": "1961", + "loc_data": "{2832,4959,2,1,0}-{3156,5477,0,1,5}-{3162,5479,0,1,4}-{3159,5477,0,1,6}-{3150,5474,0,1,3}-{3147,5480,0,1,1}-{3159,5484,0,1,3}-{3201,9293,0,1,0}-{3205,9303,0,1,0}-{3206,9328,0,1,0}-{3207,9310,0,1,0}-{3211,9284,0,1,0}-{3220,9289,0,1,0}-{3224,9334,0,1,0}-{3249,9286,0,1,0}-{3251,9303,0,1,0}-{3252,9327,0,1,0}-{3252,9332,0,1,0}-{3255,9315,0,1,0}-{3261,9296,0,1,0}-{3261,9306,0,1,0}-{2764,4944,1,1,0}-{2796,4976,1,1,0}-{2798,4950,1,1,0}-{2806,4939,1,1,0}-" + }, + { + "npc_id": "1962", + "loc_data": "{2838,4948,2,1,0}-{2900,4948,3,1,0}-{3168,5458,0,1,2}-{3205,9329,0,1,0}-{3210,9292,0,1,0}-{3221,9310,0,1,0}-{3225,9323,0,1,0}-{3243,9310,0,1,0}-{3246,9290,0,1,0}-{3255,9301,0,1,0}-{3255,9321,0,1,0}-{3261,9331,0,1,0}-{2762,4962,1,1,0}-{2763,4973,1,1,0}-{2780,4977,1,1,0}-{2787,4967,1,1,0}-{2796,4959,1,1,0}-" + }, + { + "npc_id": "1963", + "loc_data": "{2858,4964,2,1,0}-{2926,4965,3,1,0}-{3166,5465,0,1,7}-{3168,5462,0,1,0}-{3167,5467,0,1,1}-{3167,5467,0,1,7}-{3204,9309,0,1,0}-{3214,9333,0,1,0}-{3219,9297,0,1,0}-{3239,9300,0,1,0}-{3240,9286,0,1,0}-{3240,9330,0,1,0}-{3245,9306,0,1,0}-{3250,9316,0,1,0}-{3250,9317,0,1,0}-{3257,9290,0,1,0}-{2771,4947,1,1,0}-{2775,4963,1,1,0}-{2781,4948,1,1,0}-{2792,4942,1,1,0}-{2798,4954,1,1,0}-" + }, + { + "npc_id": "1964", + "loc_data": "{2864,4946,2,1,0}-{3202,9283,0,1,0}-{3206,9333,0,1,0}-{3209,9299,0,1,0}-{3219,9301,0,1,0}-{3226,9285,0,1,0}-{3227,9303,0,1,0}-{3228,9293,0,1,0}-{3229,9333,0,1,0}-{3237,9293,0,1,0}-{3246,9321,0,1,0}-{3256,9296,0,1,0}-{3260,9285,0,1,0}-{3262,9317,0,1,0}-{2761,4950,1,1,0}-{2765,4938,1,1,0}-{2770,4955,1,1,0}-{2772,4940,1,1,0}-{2777,4943,1,1,0}-{2782,4967,1,1,0}-{2797,4965,1,1,0}-{2799,4937,1,1,0}-{2799,4941,1,1,0}-{2807,4968,1,1,0}-{2808,4975,1,1,0}-{2809,4953,1,1,0}-" + }, + { + "npc_id": "1970", + "loc_data": "{3233,9317,0,1,1}-" + }, + { + "npc_id": "1972", + "loc_data": "{2536,3426,0,1,0}-" + }, + { + "npc_id": "1973", + "loc_data": "{2626,5065,0,1,0}-{2637,5099,0,1,0}-{2638,5058,0,1,0}-{2644,5090,0,1,0}-{2658,5082,0,1,0}-{2666,5097,0,1,0}-{2680,5074,0,1,0}-{2693,5075,0,1,0}-{2695,5089,0,1,0}-{2697,5096,0,1,0}-{2703,5064,0,1,0}-{2710,5105,0,1,0}-{2719,5078,0,1,0}-{2719,5112,0,1,0}-{2720,5096,0,1,0}-{2726,5086,0,1,0}-{2729,5096,0,1,0}-{2735,5061,0,1,0}-{2740,5069,0,1,0}-{2740,5085,0,1,0}-{2746,5092,0,1,0}-{2746,5114,0,1,0}-" + }, + { + "npc_id": "1976", + "loc_data": "{2694,5067,0,1,0}-{2717,5081,0,1,0}-{2718,5108,0,1,0}-{2721,5104,0,1,0}-{2726,5091,0,1,0}-{2731,5060,0,1,0}-{2732,5073,0,1,0}-{2740,5083,0,1,0}-{2742,5103,0,1,0}-{2747,5094,0,1,0}-{3315,5508,0,1,5}-{3308,5510,0,1,3}-{3304,5507,0,1,4}-{3299,5509,0,1,3}-{3317,5514,0,1,3}-{3319,5518,0,1,4}-{3320,5507,0,1,4}-{3324,5513,0,1,1}-{3283,5510,0,1,5}-{3320,5550,0,1,0}-{3313,5523,0,1,4}-{3310,5541,0,1,4}-{3308,5535,0,1,5}-{3301,5531,0,1,4}-{3315,5530,0,1,1}-{3322,5536,0,1,4}-{3320,5539,0,1,7}-{3311,5519,0,1,3}-{3289,5525,0,1,2}-{3300,5519,0,1,6}-{3289,5515,0,1,7}-{3287,5523,0,1,3}-" + }, + { + "npc_id": "1990", + "loc_data": "{3300,2797,0,0,6}-" + }, + { + "npc_id": "1993", + "loc_data": "{3337,2922,0,1,0}-{3343,2931,0,1,0}-{3349,2922,0,1,0}-{3351,2927,0,1,0}-{3357,2922,0,1,0}-{3359,2927,0,1,0}-{3364,2937,0,1,0}-{3258,2868,0,1,0}-{3259,2823,0,1,0}-{3259,2845,0,1,0}-{3260,2830,0,1,0}-{3260,2859,0,1,0}-{3261,2819,0,1,0}-{3261,2851,0,1,0}-{3268,2854,0,1,0}-{3269,2827,0,1,0}-{3269,2866,0,1,0}-{3269,2875,0,1,0}-{3270,2818,0,1,0}-{3272,2841,0,1,0}-{3276,2842,0,1,0}-{3282,2839,0,1,0}-{3290,2847,0,1,0}-{3264,2886,0,1,0}-{3267,2891,0,1,0}-{3268,2881,0,1,0}-{3269,2885,0,1,0}-{3296,2912,0,1,0}-{3297,2919,0,1,0}-{3301,2920,0,1,0}-{3308,2916,0,1,0}-" + }, + { + "npc_id": "1994", + "loc_data": "{3329,2933,0,1,0}-{3330,2931,0,1,0}-{3331,2933,0,1,0}-{3343,2895,0,1,0}-{3345,2896,0,1,0}-{3346,2894,0,1,0}-{3348,2894,0,1,0}-{3376,2934,0,1,0}-{3378,2933,0,1,0}-{3378,2935,0,1,0}-{3381,2907,0,1,0}-{3383,2905,0,1,0}-{3383,2908,0,1,0}-{3384,2907,0,1,0}-{3400,2997,0,1,0}-{3402,2999,0,1,0}-{3403,2997,0,1,0}-{3445,2993,0,1,0}-{3446,2992,0,1,0}-{3448,2991,0,1,0}-{3448,2994,0,1,0}-{3406,3014,0,1,0}-{3408,3013,0,1,0}-{3408,3016,0,1,0}-{3410,3015,0,1,0}-{3212,2863,0,1,0}-{3213,2866,0,1,0}-{3215,2863,0,1,0}-{3216,2865,0,1,0}-{3216,2868,0,1,0}-{3218,2831,0,1,0}-{3220,2830,0,1,0}-{3220,2833,0,1,0}-{3221,2831,0,1,0}-{3238,2845,0,1,0}-{3240,2845,0,1,0}-{3241,2843,0,1,0}-{3241,2847,0,1,0}-{3306,2817,0,1,0}-{3308,2818,0,1,0}-{3309,2816,0,1,0}-{3312,2817,0,1,0}-{3314,2861,0,1,0}-{3319,2873,0,1,0}-{3321,2871,0,1,0}-{3324,2870,0,1,0}-{3327,2858,0,1,0}-{3265,2931,0,1,0}-{3265,2935,0,1,0}-{3268,2932,0,1,0}-{3268,2935,0,1,0}-{3316,2900,0,1,0}-{3319,2897,0,1,0}-{3320,2901,0,1,0}-{3321,2899,0,1,0}-" + }, + { + "npc_id": "1995", + "loc_data": "{3309,2806,0,1,0}-{3312,2806,0,1,0}-{3314,2752,0,1,0}-{3314,2806,0,1,0}-{3315,2805,0,1,0}-{3316,2780,0,1,0}-{3316,2795,0,1,0}-{3317,2752,0,1,0}-{3317,2762,0,1,0}-{3317,2763,0,1,0}-{3317,2779,0,1,0}-{3317,2794,0,1,0}-{3318,2761,0,1,0}-{3318,2778,0,1,0}-{3318,2781,0,1,0}-{3318,2794,0,1,0}-{3318,2795,0,1,0}-{3319,2760,0,1,0}-{3319,2763,0,1,0}-" + }, + { + "npc_id": "1997", + "loc_data": "{3272,2802,0,1,0}-{3273,2802,0,1,0}-{3273,2805,0,1,0}-{3274,2798,0,1,0}-{3274,2799,0,1,0}-{3274,2802,0,1,0}-{3274,2804,0,1,0}-{3275,2795,0,1,0}-{3275,2796,0,1,0}-{3275,2798,0,1,0}-{3277,2752,0,1,0}-{3277,2753,0,1,0}-{3277,2756,0,1,0}-{3277,2757,0,1,0}-{3278,2752,0,1,0}-{3278,2753,0,1,0}-{3278,2754,0,1,0}-{3278,2755,0,1,0}-" + }, + { + "npc_id": "1998", + "loc_data": "{3275,2803,0,1,0}-" + }, + { + "npc_id": "1999", + "loc_data": "{3278,2804,0,1,0}-" + }, + { + "npc_id": "2000", + "loc_data": "{3277,2802,0,1,0}-" + }, + { + "npc_id": "2002", + "loc_data": "{3315,2849,0,1,0}-" + }, + { + "npc_id": "2003", + "loc_data": "{3304,9196,0,1,0}-" + }, + { + "npc_id": "2004", + "loc_data": "{3304,9195,0,1,0}-" + }, + { + "npc_id": "2014", + "loc_data": "{3293,2788,0,1,0}-" + }, + { + "npc_id": "2020", + "loc_data": "{3503,3477,0,0,0}-" + }, + { + "npc_id": "2021", + "loc_data": "{3224,9516,2,1,0}-{3224,9517,2,1,0}-{3224,9518,2,1,0}-{3225,9516,2,1,0}-{3225,9517,2,1,0}-{3225,9518,2,1,0}-{3226,9516,2,1,0}-{3226,9517,2,1,0}-{3226,9518,2,1,0}-" + }, + { + "npc_id": "2031", + "loc_data": "{3290,5448,0,1,6}-{3291,5452,0,1,1}-{3293,5456,0,1,6}-{3551,9680,0,0,2}-{3553,9679,0,0,1}-{3551,9675,0,0,6}-{3554,9676,0,0,7}-{3548,9676,0,0,5}-{3552,9691,0,0,2}-{3569,9681,0,0,7}-{3577,9711,0,0,5}-{3567,9675,0,0,5}-{3567,9675,0,0,5}-{3570,9678,0,0,2}-{3568,9686,0,0,1}-{3545,9721,0,0,7}-{3560,9712,0,0,6}-{3552,9685,0,0,1}-{3525,9698,0,0,6}-{3538,9668,0,0,4}-" + }, + { + "npc_id": "2032", + "loc_data": "{3187,5513,0,1,3}-{3186,5510,0,1,6}-{3186,5516,0,1,6}-{3194,5511,0,1,1}-{3185,5514,0,1,5}-{3549,9678,0,0,0}-{3569,9676,0,0,5}-{3566,9678,0,0,1}-{3560,9678,0,0,2}-{3526,9678,0,0,2}-{3532,9693,0,0,5}-{3534,9690,0,0,7}-{3536,9695,0,0,2}-{3542,9694,0,0,7}-{3548,9695,0,0,7}-{3556,9695,0,0,5}-{3554,9709,0,0,2}-{3533,9708,0,0,5}-{3534,9703,0,0,5}-{3538,9721,0,0,7}-{3567,9720,0,0,6}-{3561,9711,0,0,7}-{3552,9703,0,0,1}-{3551,9698,0,0,1}-{3532,9678,0,0,5}-{3531,9675,0,0,5}-{3537,9677,0,0,2}-" + }, + { + "npc_id": "2033", + "loc_data": "{3188,5511,0,1,7}-{3190,5511,0,1,6}-{3300,5494,0,1,2}-{3285,5492,0,1,6}-{3285,5495,0,1,0}-{3288,5497,0,1,3}-{3268,5487,0,0,0}-{3533,9691,0,0,4}-{3537,9694,0,0,2}-{3533,9696,0,0,2}-{3533,9693,0,0,5}-" + }, + { + "npc_id": "2034", + "loc_data": "{3288,5484,0,1,4}-{3288,5467,0,1,0}-{3293,5468,0,1,2}-{3296,5475,0,1,7}-{3296,5473,0,1,2}-{3297,5480,0,1,4}-{3565,9695,0,0,1}-{3566,9692,0,0,5}-{3572,9695,0,0,7}-" + }, + { + "npc_id": "2035", + "loc_data": "{3569,9696,0,0,2}-{3569,9693,0,0,6}-{3566,9694,0,0,6}-{3567,9698,0,0,2}-{3572,9692,0,0,5}-" + }, + { + "npc_id": "2036", + "loc_data": "{3569,9674,0,0,0}-{3561,9677,0,0,6}-{3535,9686,0,0,6}-{3535,9698,0,0,1}-{3576,9677,0,0,3}-{3577,9679,0,0,1}-{3568,9691,0,0,2}-{3560,9694,0,0,6}-{3570,9682,0,0,1}-{3569,9687,0,0,2}-{3550,9711,0,0,5}-{3551,9708,0,0,6}-{3548,9713,0,0,6}-{3537,9711,0,0,4}-{3535,9702,0,0,7}-{3553,9722,0,0,4}-{3567,9712,0,0,5}-{3569,9704,0,0,6}-{3579,9693,0,0,7}-{3534,9675,0,0,2}-{3526,9711,0,0,1}-{3566,9668,0,0,4}-" + }, + { + "npc_id": "2037", + "loc_data": "{3543,9677,0,0,3}-{3572,9680,0,0,7}-{3559,9677,0,0,4}-{3535,9685,0,0,1}-{3537,9693,0,0,1}-{3570,9697,0,0,2}-{3552,9713,0,0,6}-{3553,9711,0,0,3}-{3567,9708,0,0,6}-{3535,9679,0,0,1}-{3525,9683,0,0,6}-" + }, + { + "npc_id": "2038", + "loc_data": "{2443,3051,0,0,1}-" + }, + { + "npc_id": "2039", + "loc_data": "{2442,3049,0,0,3}-" + }, + { + "npc_id": "2040", + "loc_data": "{2444,3047,0,0,0}-" + }, + { + "npc_id": "2041", + "loc_data": "{2447,3050,0,0,0}-" + }, + { + "npc_id": "2042", + "loc_data": "{2454,3047,0,0,2}-" + }, + { + "npc_id": "2043", + "loc_data": "{2443,3038,0,1,1}-{2452,3030,0,1,1}-" + }, + { + "npc_id": "2044", + "loc_data": "{2486,3048,0,1,3}-{2484,9392,2,1,0}-{2465,9454,2,1,0}-{2471,9422,2,1,0}-{2448,9442,2,1,0}-" + }, + { + "npc_id": "2045", + "loc_data": "{2482,3046,0,1,3}-{2459,9394,2,1,0}-{2473,9430,2,1,0}-{2478,9442,2,1,0}-{2487,9455,2,1,0}-" + }, + { + "npc_id": "2046", + "loc_data": "{2466,9389,2,1,0}-{2449,9429,0,1,0}-{2436,9452,2,1,0}-{2436,9465,2,1,0}-{2450,9460,2,1,0}-{2455,9440,2,1,0}-{2473,9439,2,1,0}-{2483,9408,2,1,0}-" + }, + { + "npc_id": "2047", + "loc_data": "{2480,3046,0,1,3}-{2477,9384,2,1,0}-{2449,9434,0,1,0}-{2437,9429,2,1,0}-{2440,9438,2,1,0}-{2458,9408,2,1,0}-" + }, + { + "npc_id": "2048", + "loc_data": "{2485,9398,2,1,0}-{2455,9435,0,1,0}-{2457,9420,2,1,0}-{2462,9431,2,1,0}-{2482,9422,2,1,0}-" + }, + { + "npc_id": "2049", + "loc_data": "{2452,9440,0,1,0}-" + }, + { + "npc_id": "2050", + "loc_data": "{2461,3047,0,1,3}-{2466,3052,0,1,6}-" + }, + { + "npc_id": "2051", + "loc_data": "{2464,3049,0,1,2}-" + }, + { + "npc_id": "2052", + "loc_data": "{2464,3045,0,1,2}-" + }, + { + "npc_id": "2053", + "loc_data": "{2467,3049,0,1,2}-" + }, + { + "npc_id": "2054", + "loc_data": "{2468,3046,0,1,2}-" + }, + { + "npc_id": "2055", + "loc_data": "{2470,3055,0,0,5}-" + }, + { + "npc_id": "2056", + "loc_data": "{2470,3040,0,0,0}-" + }, + { + "npc_id": "2057", + "loc_data": "{2480,3046,0,1,0}-{2453,9393,2,1,0}-{2461,9403,2,1,0}-{2464,9380,2,1,0}-{2442,9436,2,1,0}-{2458,9413,2,1,0}-{2470,9435,2,1,0}-{2472,9460,2,1,0}-{2483,9413,2,1,0}-{2485,9447,2,1,0}-" + }, + { + "npc_id": "2058", + "loc_data": "{3161,9547,0,0,7}-{3164,9556,0,0,7}-{3162,9574,0,0,7}-{3198,9554,0,0,7}-{3198,9572,0,0,7}-{3215,9560,0,0,7}-{3216,9588,0,0,7}-" + }, + { + "npc_id": "2059", + "loc_data": "{2588,3088,0,1,3}-" + }, + { + "npc_id": "2067", + "loc_data": "{3154,9544,0,0,0}-{3245,9570,0,0,0}-" + }, + { + "npc_id": "2069", + "loc_data": "{3313,9652,0,1,0}-" + }, + { + "npc_id": "2070", + "loc_data": "{3324,9621,0,1,0}-" + }, + { + "npc_id": "2071", + "loc_data": "{3323,9643,0,1,0}-" + }, + { + "npc_id": "2072", + "loc_data": "{3319,9632,0,1,0}-" + }, + { + "npc_id": "2074", + "loc_data": "{3319,9605,0,0,1}-" + }, + { + "npc_id": "2075", + "loc_data": "{3312,9638,0,0,3}-" + }, + { + "npc_id": "2076", + "loc_data": "{3312,9628,0,0,3}-" + }, + { + "npc_id": "2077", + "loc_data": "{3323,9616,0,0,6}-" + }, + { + "npc_id": "2078", + "loc_data": "{3313,9622,0,0,3}-" + }, + { + "npc_id": "2082", + "loc_data": "{3210,3220,1,1,6}-" + }, + { + "npc_id": "2084", + "loc_data": "{3314,9612,0,1,0}-" + }, + { + "npc_id": "2085", + "loc_data": "{3231,9610,0,1,0}-" + }, + { + "npc_id": "2092", + "loc_data": "{2869,10196,1,1,0}-" + }, + { + "npc_id": "2093", + "loc_data": "{2871,10207,1,1,0}-" + }, + { + "npc_id": "2094", + "loc_data": "{2869,10205,1,0,6}-" + }, + { + "npc_id": "2095", + "loc_data": "{2889,10211,1,0,6}-" + }, + { + "npc_id": "2096", + "loc_data": "{2890,10207,1,0,6}-" + }, + { + "npc_id": "2097", + "loc_data": "{2891,10196,1,0,6}-" + }, + { + "npc_id": "2098", + "loc_data": "{2890,10192,1,0,6}-" + }, + { + "npc_id": "2100", + "loc_data": "{2870,10196,1,1,0}-" + }, + { + "npc_id": "2101", + "loc_data": "{2868,10202,1,1,0}-" + }, + { + "npc_id": "2102", + "loc_data": "{2869,10208,1,1,0}-" + }, + { + "npc_id": "2103", + "loc_data": "{2892,10209,1,1,0}-" + }, + { + "npc_id": "2104", + "loc_data": "{2890,10204,1,1,0}-" + }, + { + "npc_id": "2105", + "loc_data": "{2890,10196,1,1,0}-" + }, + { + "npc_id": "2106", + "loc_data": "{2892,10190,1,1,0}-" + }, + { + "npc_id": "2110", + "loc_data": "{2877,10198,1,1,0}-{2878,10196,1,1,0}-" + }, + { + "npc_id": "2111", + "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}-" + }, + { + "npc_id": "2113", + "loc_data": "{2878,10201,1,1,0}-{2876,10201,1,1,0}-" + }, + { + "npc_id": "2115", + "loc_data": "{2882,10201,1,1,0}-{2883,10200,1,1,0}-" + }, + { + "npc_id": "2117", + "loc_data": "{2883,10198,1,1,0}-{2884,10199,1,1,0}-" + }, + { + "npc_id": "2119", + "loc_data": "{2882,10197,1,1,0}-{2883,10196,1,1,0}-" + }, + { + "npc_id": "2121", + "loc_data": "{2879,10193,1,1,0}-{2880,10195,1,1,0}-" + }, + { + "npc_id": "2127", + "loc_data": "{2880,10199,1,1,0}-" + }, + { + "npc_id": "2130", + "loc_data": "{2889,10207,0,1,0}-" + }, + { + "npc_id": "2131", + "loc_data": "{2822,10212,0,1,0}-{2891,10192,0,1,0}-" + }, + { + "npc_id": "2132", + "loc_data": "{2828,10210,0,1,0}-{2891,10199,0,1,0}-" + }, + { + "npc_id": "2133", + "loc_data": "{2847,10190,0,1,0}-" + }, + { + "npc_id": "2134", + "loc_data": "{2823,10220,0,1,0}-" + }, + { + "npc_id": "2135", + "loc_data": "{2825,10209,0,1,0}-" + }, + { + "npc_id": "2136", + "loc_data": "{2827,10212,1,1,0}-" + }, + { + "npc_id": "2138", + "loc_data": "{2836,10192,1,1,0}-" + }, + { + "npc_id": "2139", + "loc_data": "{2835,10193,1,0,1}-" + }, + { + "npc_id": "2140", + "loc_data": "{2835,10226,0,0,6}-" + }, + { + "npc_id": "2141", + "loc_data": "{2906,10204,0,1,0}-" + }, + { + "npc_id": "2142", + "loc_data": "{2904,10206,0,1,0}-" + }, + { + "npc_id": "2151", + "loc_data": "{2873,10210,0,1,0}-" + }, + { + "npc_id": "2152", + "loc_data": "{2827,10231,0,1,0}-" + }, + { + "npc_id": "2153", + "loc_data": "{2826,10197,0,1,0}-" + }, + { + "npc_id": "2154", + "loc_data": "{2869,10190,0,1,0}-" + }, + { + "npc_id": "2155", + "loc_data": "{2871,10199,0,1,0}-" + }, + { + "npc_id": "2156", + "loc_data": "{2892,10212,0,1,0}-" + }, + { + "npc_id": "2157", + "loc_data": "{2887,10212,0,1,0}-" + }, + { + "npc_id": "2158", + "loc_data": "{2885,10207,0,1,0}-" + }, + { + "npc_id": "2159", + "loc_data": "{2885,10196,0,1,0}-" + }, + { + "npc_id": "2160", + "loc_data": "{2925,10210,0,1,0}-" + }, + { + "npc_id": "2161", + "loc_data": "{2869,10211,0,1,0}-" + }, + { + "npc_id": "2162", + "loc_data": "{2886,10187,0,1,0}-" + }, + { + "npc_id": "2163", + "loc_data": "{2836,10205,0,1,0}-" + }, + { + "npc_id": "2164", + "loc_data": "{2838,10205,0,0,1}-" + }, + { + "npc_id": "2169", + "loc_data": "{2836,10222,0,1,0}-" + }, + { + "npc_id": "2170", + "loc_data": "{2853,10196,0,1,0}-" + }, + { + "npc_id": "2171", + "loc_data": "{2931,10184,0,1,0}-" + }, + { + "npc_id": "2172", + "loc_data": "{2929,10191,0,1,0}-" + }, + { + "npc_id": "2173", + "loc_data": "{2931,10191,0,1,0}-" + }, + { + "npc_id": "2174", + "loc_data": "{2930,10188,0,1,0}-" + }, + { + "npc_id": "2175", + "loc_data": "{2931,10187,0,1,0}-" + }, + { + "npc_id": "2177", + "loc_data": "{2843,10191,1,0,1}-" + }, + { + "npc_id": "2178", + "loc_data": "{2916,10195,0,1,0}-" + }, + { + "npc_id": "2179", + "loc_data": "{2840,10199,0,0,6}-" + }, + { + "npc_id": "2180", + "loc_data": "{2997,9837,0,1,3}-" + }, + { + "npc_id": "2181", + "loc_data": "{2874,9871,0,1,0}-" + }, + { + "npc_id": "2182", + "loc_data": "{2908,10176,0,1,0}-" + }, + { + "npc_id": "2187", + "loc_data": "{2906,10198,0,1,0}-" + }, + { + "npc_id": "2188", + "loc_data": "{2851,10223,0,1,0}-" + }, + { + "npc_id": "2189", + "loc_data": "{2851,10223,0,1,0}-" + }, + { + "npc_id": "2190", + "loc_data": "{2923,10203,0,1,0}-" + }, + { + "npc_id": "2191", + "loc_data": "{2924,10213,0,1,0}-" + }, + { + "npc_id": "2192", + "loc_data": "{2933,10219,0,0,3}-" + }, + { + "npc_id": "2193", + "loc_data": "{2899,10218,0,1,0}-" + }, + { + "npc_id": "2194", + "loc_data": "{2926,10225,0,1,0}-" + }, + { + "npc_id": "2195", + "loc_data": "{2900,10227,0,1,0}-" + }, + { + "npc_id": "2196", + "loc_data": "{2838,10196,0,1,0}-" + }, + { + "npc_id": "2197", + "loc_data": "{2848,10198,0,1,0}-" + }, + { + "npc_id": "2198", + "loc_data": "{2906,10216,0,1,0}-" + }, + { + "npc_id": "2199", + "loc_data": "{2855,10199,0,1,0}-" + }, + { + "npc_id": "2201", + "loc_data": "{2866,10208,0,1,0}-" + }, + { + "npc_id": "2203", + "loc_data": "{2912,10222,0,1,0}-" + }, + { + "npc_id": "2205", + "loc_data": "{2842,10129,0,0,3}-" + }, + { + "npc_id": "2206", + "loc_data": "{2888,10227,0,0,6}-" + }, + { + "npc_id": "2234", + "loc_data": "{3081,3251,0,1,6}-{2636,3365,0,1,6}-" + }, + { + "npc_id": "2235", + "loc_data": "{3241,3345,0,1,4}-" + }, + { + "npc_id": "2236", + "loc_data": "{3078,3250,0,1,1}-{3081,3250,0,1,2}-" + }, + { + "npc_id": "2237", + "loc_data": "{3249,3266,0,1,5}-" + }, + { + "npc_id": "2238", + "loc_data": "{3219,3247,0,1,6}-" + }, + { + "npc_id": "2240", + "loc_data": "{3077,3259,0,1,4}-" + }, + { + "npc_id": "2241", + "loc_data": "{3078,3260,0,1,3}-" + }, + { + "npc_id": "2242", + "loc_data": "{3076,3260,0,1,1}-{3078,3260,0,1,6}-" + }, + { + "npc_id": "2243", + "loc_data": "{3078,3259,0,1,3}-" + }, + { + "npc_id": "2244", + "loc_data": "{3233,3228,0,1,3}-" + }, + { + "npc_id": "2245", + "loc_data": "{2505,3240,0,1,4}-{2512,3250,0,1,1}-{2506,3242,0,1,3}-{2510,3252,0,1,4}-{2516,3248,0,1,1}-{2518,3245,0,1,7}-{2518,3245,0,1,1}-{2517,3242,0,1,6}-{2523,3241,0,1,4}-{2525,3241,0,1,1}-{2515,3256,0,1,6}-{2519,3257,0,1,4}-" + }, + { + "npc_id": "2246", + "loc_data": "{2521,3239,0,1,5}-" + }, + { + "npc_id": "2247", + "loc_data": "{2543,3225,0,1,7}-{2553,3220,0,1,3}-{2545,3220,0,1,4}-{2525,3220,0,1,4}-{2518,3218,0,1,1}-" + }, + { + "npc_id": "2248", + "loc_data": "{2540,3218,0,1,1}-{2515,3216,0,1,1}-{2512,3215,0,1,7}-" + }, + { + "npc_id": "2249", + "loc_data": "{2530,3224,0,1,4}-{2524,3221,0,1,2}-{2543,3217,0,1,4}-{2530,3212,0,1,4}-{2557,3234,0,1,4}-{2540,3226,0,1,5}-{2532,3213,0,1,4}-{2527,3201,0,1,6}-{2544,3216,0,1,6}-{2519,3222,0,1,6}-{2526,3227,0,1,4}-{2550,3234,0,1,3}-{2553,3220,0,1,3}-{2521,3202,0,1,6}-{2525,3220,0,1,4}-{2544,3230,0,1,3}-{2547,3212,0,1,4}-{2545,3212,0,1,0}-{2540,3208,0,1,4}-{2533,3202,0,1,6}-{2536,3236,0,1,6}-{2525,3204,0,1,0}-{2539,3238,0,1,4}-" + }, + { + "npc_id": "2250", + "loc_data": "{2525,3209,0,1,4}-{2558,3228,0,1,4}-{2543,3212,0,1,3}-{2538,3223,0,1,0}-{2527,3201,0,1,7}-{2517,3222,0,1,3}-{2547,3231,0,1,6}-{2550,3220,0,1,4}-{2538,3224,0,1,3}-{2545,3213,0,1,0}-{2542,3208,0,1,5}-" + }, + { + "npc_id": "2251", + "loc_data": "{2520,3217,0,1,3}-{2524,3209,0,1,1}-{2529,3200,0,1,5}-{2530,3209,0,1,1}-{2519,3223,0,1,4}-{2518,3224,0,1,2}-{2527,3201,0,1,7}-{2521,3202,0,1,4}-{2542,3208,0,1,4}-{2539,3233,0,1,6}-" + }, + { + "npc_id": "2252", + "loc_data": "{3079,3257,0,1,1}-{3091,3250,0,1,6}-{3137,3254,0,1,0}-{3199,3302,0,1,3}-" + }, + { + "npc_id": "2256", + "loc_data": "{2571,3305,0,1,7}-{2572,3297,0,1,2}-{2572,3304,0,1,1}-{2577,3308,0,1,6}-{2582,3287,0,1,3}-{2582,3299,0,1,0}-{2583,3292,0,1,4}-{2572,3292,1,1,1}-{2577,3295,1,1,6}-{2576,3286,1,1,1}-{2585,3289,1,1,7}-{2585,3304,1,1,1}-{2582,3306,1,1,5}-{2582,3286,1,1,3}-{2653,3315,0,1,6}-{2658,3306,0,1,4}-" + }, + { + "npc_id": "2257", + "loc_data": "{3105,3554,0,1,1}-" + }, + { + "npc_id": "2260", + "loc_data": "{3259,3385,0,1,6}-" + }, + { + "npc_id": "2262", + "loc_data": "{3039,4834,0,0,7}-" + }, + { + "npc_id": "2263", + "loc_data": "{3021,4812,0,1,5}-{3014,4838,0,1,6}-{3061,4836,0,1,4}-{3017,4812,0,1,6}-{3061,4842,0,1,3}-{3048,4808,0,1,3}-{3021,4847,0,1,6}-{3054,4849,0,1,6}-{3015,4833,0,1,0}-{3035,4854,0,1,6}-{3016,4829,0,1,4}-{3040,4810,0,1,3}-{3059,4815,0,1,1}-{3027,4851,0,1,6}-{3020,4819,0,1,4}-{3020,4845,0,1,2}-{3061,4829,0,1,0}-{3018,4842,0,1,4}-{3037,4906,0,1,2}-{3057,4875,0,0,6}-{3016,4892,0,1,6}-{3058,4877,0,0,6}-{3051,4874,0,1,0}-{3029,4890,0,1,6}-{3032,4901,0,1,2}-{3024,4916,0,1,3}-{3020,4903,0,1,4}-{3061,4883,0,0,3}-{3058,4889,0,0,3}-{3053,4896,0,0,3}-{3038,4871,0,1,6}-{3053,4900,0,0,7}-{3055,4915,0,0,5}-{3037,4906,0,0,5}-{3047,4886,0,1,4}-{3033,4884,0,1,4}-{3030,4877,0,1,2}-{3026,4879,0,1,3}-{3024,4879,0,1,3}-" + }, + { + "npc_id": "2264", + "loc_data": "{3017,4821,0,1,4}-{3059,4813,0,1,0}-{3032,4808,0,1,3}-{3059,4820,0,1,3}-{3062,4828,0,1,6}-{3036,4856,0,1,7}-{3062,4837,0,1,5}-{3025,4850,0,1,1}-{3052,4858,0,1,1}-{3024,4813,0,1,4}-{3048,4854,0,1,3}-{3034,4852,0,1,1}-{3028,4808,0,1,1}-{3018,4843,0,1,2}-{3028,4851,0,1,0}-{3016,4831,0,1,1}-{3043,4810,0,1,3}-{3020,4888,0,1,3}-{3039,4871,0,1,4}-{3049,4921,0,1,7}-{3019,4877,0,1,6}-{3017,4893,0,1,7}-{3019,4898,0,1,4}-{3020,4912,0,1,2}-{3054,4915,0,1,7}-{3052,4898,0,1,1}-{3034,4905,0,1,4}-{3052,4896,0,1,3}-{3046,4901,0,1,2}-{3062,4884,0,1,6}-{3049,4873,0,1,1}-{3041,4871,0,1,1}-" + }, + { + "npc_id": "2265", + "loc_data": "{3038,4809,0,1,6}-{3024,4850,0,1,3}-{3052,4808,0,1,5}-{3021,4847,0,1,3}-{3058,4811,0,1,4}-{3055,4848,0,1,0}-{3062,4822,0,1,6}-{3017,4837,0,1,3}-{3017,4817,0,1,7}-{3025,4812,0,1,1}-{3060,4825,0,1,3}-{3062,4838,0,1,3}-{3054,4850,0,1,6}-{3020,4914,0,1,2}-{3059,4890,0,1,3}-{3031,4901,0,1,7}-{3044,4915,0,1,6}-{3044,4883,0,1,4}-{3039,4885,0,1,1}-{3048,4917,0,1,5}-{3059,4888,0,1,5}-{3047,4874,0,1,2}-{3055,4908,0,1,5}-{3017,4876,0,1,1}-{3029,4890,0,1,5}-{3059,4906,0,1,1}-{3021,4875,0,1,3}-{3032,4903,0,1,3}-{3026,4914,0,1,6}-" + }, + { + "npc_id": "2266", + "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": "2322", + "loc_data": "{3679,9957,0,1,1}-" + }, + { + "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,1,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": "2491", + "loc_data": "{2679,3080,0,1,6}-{2684,3084,0,1,4}-{2682,3093,0,1,4}-{2678,3095,0,1,3}-{2679,3102,0,1,1}-{2681,3110,0,1,4}-{2683,3115,0,1,6}-{2675,3108,0,1,3}-{2671,3109,0,1,3}-{2671,3113,0,1,4}-{2676,3090,0,1,4}-{2670,3088,0,1,6}-{2671,3086,0,1,6}-{2664,3088,0,1,7}-{2660,3086,0,1,6}-{2662,3088,0,1,6}-{2670,3082,0,1,7}-{2674,3081,0,1,3}-{2663,3086,0,1,6}-{2679,3082,0,1,1}-{2682,3093,0,1,3}-" + }, + { + "npc_id": "2504", + "loc_data": "{2796,3077,1,1,3}-" + }, + { + "npc_id": "2507", + "loc_data": "{2794,3077,0,1,6}-" + }, + { + "npc_id": "2510", + "loc_data": "{2786,3075,1,1,4}-" + }, + { + "npc_id": "2513", + "loc_data": "{2796,3088,0,1,7}-" + }, + { + "npc_id": "2516", + "loc_data": "{2791,3102,0,1,3}-" + }, + { + "npc_id": "2519", + "loc_data": "{2794,3064,0,1,5}-" + }, + { + "npc_id": "2522", + "loc_data": "{2758,3099,0,1,1}-" + }, + { + "npc_id": "2525", + "loc_data": "{2800,3048,0,1,2}-" + }, + { + "npc_id": "2528", + "loc_data": "{2816,3085,0,1,3}-" + }, + { + "npc_id": "2531", + "loc_data": "{2784,3089,0,1,1}-" + }, + { + "npc_id": "2547", + "loc_data": "{2615,9525,0,1,4}-{2615,9521,0,1,1}-{3109,9942,0,1,4}-{3111,9938,0,1,4}-{3116,9926,0,1,4}-" + }, + { + "npc_id": "2548", + "loc_data": "{3349,2970,0,0,4}-{3358,2987,0,0,4}-" + }, + { + "npc_id": "2549", + "loc_data": "{3364,3001,0,0,6}-" + }, + { + "npc_id": "2553", + "loc_data": "{1944,4958,0,1,0}-" + }, + { + "npc_id": "2564", + "loc_data": "{1936,4966,0,0,4}-" + }, + { + "npc_id": "2565", + "loc_data": "{1939,4968,0,1,0}-" + }, + { + "npc_id": "2568", + "loc_data": "{2729,3495,0,0,6}-{2724,3495,0,0,6}-{2728,3495,0,0,6}-{2721,3495,0,0,6}-" + }, + { + "npc_id": "2569", + "loc_data": "{2727,3495,0,0,6}-{2722,3495,0,0,6}-" + }, + { + "npc_id": "2572", + "loc_data": "{3079,3250,0,1,2}-" + }, + { + "npc_id": "2574", + "loc_data": "{3087,3247,0,0,7}-" + }, + { + "npc_id": "2580", + "loc_data": "{2918,3535,0,1,0}-" + }, + { + "npc_id": "2591", + "loc_data": "{4786,5106,0,1,6}-{2474,5145,0,1,2}-" + }, + { + "npc_id": "2592", + "loc_data": "{4680,5117,0,1,6}-{2461,5142,0,1,6}-" + }, + { + "npc_id": "2594", + "loc_data": "{4730,5077,0,1,5}-{4703,5123,0,1,5}-{4680,5126,0,1,7}-" + }, + { + "npc_id": "2595", + "loc_data": "{4633,5069,0,1,4}-{4647,5132,0,1,7}-{4699,5100,0,1,6}-{4757,5100,0,1,2}-" + }, + { + "npc_id": "2596", + "loc_data": "{4628,5117,0,1,4}-{4787,5118,0,1,7}-{2483,5155,0,1,4}-" + }, + { + "npc_id": "2597", + "loc_data": "{4621,5094,0,1,5}-{2446,5144,0,1,1}-{2470,5141,0,1,6}-" + }, + { + "npc_id": "2598", + "loc_data": "{2485,5159,0,1,4}-{2489,5155,0,1,1}-{2471,5144,0,1,3}-{2476,5153,0,1,0}-{2477,5150,0,1,1}-" + }, + { + "npc_id": "2600", + "loc_data": "{4656,5125,0,1,5}-{4711,5072,0,1,4}-{4751,5087,0,1,3}-" + }, + { + "npc_id": "2601", + "loc_data": "{4724,5107,0,1,5}-{4676,5123,0,1,5}-{4693,5127,0,1,7}-{4788,5110,0,1,4}-" + }, + { + "npc_id": "2602", + "loc_data": "{4668,5133,0,1,7}-{4627,5122,0,1,5}-{4714,5113,0,1,6}-{4752,5074,0,1,7}-{4789,5122,0,1,2}-" + }, + { + "npc_id": "2603", + "loc_data": "{4711,5086,0,1,2}-{4760,5091,0,1,4}-" + }, + { + "npc_id": "2604", + "loc_data": "{2439,5170,0,1,3}-{2451,5170,0,1,3}-{2453,5161,0,1,6}-{2460,5160,0,1,7}-{2457,5155,0,1,3}-{2480,5168,0,1,6}-{2469,5166,0,1,1}-{2490,5173,0,1,3}-{2488,5171,0,1,4}-{2483,5156,0,1,3}-{2470,5147,0,1,4}-{2468,5139,0,1,7}-{2458,5141,0,1,3}-{2454,5141,0,1,1}-{2452,5124,0,1,3}-{2454,5122,0,1,2}-{2453,5143,0,1,3}-{2443,5144,0,1,6}-" + }, + { + "npc_id": "2605", + "loc_data": "{4631,5088,0,1,7}-{4633,5125,0,1,7}-{4760,5074,0,1,4}-" + }, + { + "npc_id": "2606", + "loc_data": "{4670,5081,0,1,6}-{4642,5115,0,1,7}-{4756,5086,0,1,4}-" + }, + { + "npc_id": "2607", + "loc_data": "{4638,5095,0,1,2}-{4647,5141,0,1,4}-{4699,5141,0,1,3}-{4774,5098,0,1,3}-" + }, + { + "npc_id": "2608", + "loc_data": "{4656,5091,0,1,5}-{4637,5093,0,1,0}-" + }, + { + "npc_id": "2609", + "loc_data": "{4617,5073,0,1,6}-{4691,5090,0,1,7}-{4727,5082,0,1,6}-{4711,5125,0,1,4}-{4748,5070,0,1,4}-{2440,5128,0,1,3}-{2436,5133,0,1,3}-{2450,5168,0,1,2}-{2443,5170,0,1,4}-{2445,5177,0,1,3}-" + }, + { + "npc_id": "2610", + "loc_data": "{2446,5169,0,1,4}-{2458,5159,0,1,3}-{2466,5166,0,1,7}-{2471,5166,0,1,3}-{2478,5164,0,1,4}-{2490,5174,0,1,4}-{2485,5159,0,1,5}-{2442,5173,0,1,0}-{2440,5171,0,1,7}-{2440,5170,0,1,3}-" + }, + { + "npc_id": "2611", + "loc_data": "{4646,5107,0,1,6}-{4683,5098,0,1,4}-{4690,5113,0,1,5}-{4720,5118,0,1,7}-{4716,5097,0,1,5}-{4762,5088,0,1,0}-" + }, + { + "npc_id": "2614", + "loc_data": "{4666,5082,0,1,0}-{4669,5103,0,1,2}-{4639,5074,0,1,3}-{4664,5121,0,1,4}-{4688,5016,0,1,3}-{4704,5077,0,1,5}-" + }, + { + "npc_id": "2615", + "loc_data": "{4625,5084,0,1,7}-{4628,5134,0,1,3}-{4725,5067,0,1,5}-{4695,5128,0,1,7}-{4748,5076,0,1,6}-{4790,5127,0,1,0}-" + }, + { + "npc_id": "2616", + "loc_data": "{4631,5106,0,1,0}-{4673,5114,0,1,1}-{2441,5130,0,1,1}-{2438,5135,0,1,6}-{2443,5138,0,1,2}-" + }, + { + "npc_id": "2617", + "loc_data": "{4611,5132,0,1,5}-{2440,5168,0,0,1}-" + }, + { + "npc_id": "2618", + "loc_data": "{2399,5178,0,1,6}-{4603,5065,0,1,1}-" + }, + { + "npc_id": "2619", + "loc_data": "{4644,5149,0,1,7}-{2418,5203,0,1,6}-{2446,5179,0,0,6}-" + }, + { + "npc_id": "2620", + "loc_data": "{4701,5153,0,1,0}-{2478,5146,0,1,2}-" + }, + { + "npc_id": "2621", + "loc_data": "{2724,3729,0,0,6}-" + }, + { + "npc_id": "2622", + "loc_data": "{4689,5173,0,0,0}-{2464,5149,0,0,7}-" + }, + { + "npc_id": "2623", + "loc_data": "{4655,5172,0,1,0}-{2462,5125,0,1,5}-" + }, + { + "npc_id": "2624", + "loc_data": "{4718,5148,0,1,6}-{4708,5173,0,1,6}-{4716,5178,0,1,7}-{4726,5173,0,1,0}-{4730,5163,0,1,3}-{4726,5153,0,1,5}-" + }, + { + "npc_id": "2625", + "loc_data": "{4668,5177,0,1,4}-{4676,5177,0,1,2}-" + }, + { + "npc_id": "2634", + "loc_data": "{3095,3252,0,0,0}-" + }, + { + "npc_id": "2652", + "loc_data": "{2868,3579,0,0,0}-{2851,3582,0,0,0}-" + }, + { + "npc_id": "2655", + "loc_data": "{2921,3558,0,1,6}-" + }, + { + "npc_id": "2660", + "loc_data": "{3208,3496,0,1,3}-" + }, + { + "npc_id": "2674", + "loc_data": "{3017,3231,0,1,1}-" + }, + { + "npc_id": "2676", + "loc_data": "{2914,3322,0,0,0}-" + }, + { + "npc_id": "2677", + "loc_data": "{2945,3307,0,0,0}-" + }, + { + "npc_id": "2678", + "loc_data": "{2988,3210,0,1,7}-" + }, + { + "npc_id": "2679", + "loc_data": "{3000,3222,0,1,1}-" + }, + { + "npc_id": "2680", + "loc_data": "{2582,9836,0,1,2}-{2985,3193,0,1,1}-" + }, + { + "npc_id": "2681", + "loc_data": "{2991,3199,0,1,3}-" + }, + { + "npc_id": "2682", + "loc_data": "{2904,3208,0,1,0}-{1866,4237,0,1,0}-{1866,4240,0,1,0}-{1867,4243,0,1,0}-{2950,3204,0,1,5}-{2971,3212,0,1,4}-{2960,3201,0,1,1}-{2959,3202,0,1,4}-{2956,3203,0,1,1}-{2969,3204,0,1,4}-" + }, + { + "npc_id": "2683", + "loc_data": "{2967,3212,1,0,7}-" + }, + { + "npc_id": "2684", + "loc_data": "{2965,3213,1,0,0}-" + }, + { + "npc_id": "2685", + "loc_data": "{3124,9873,0,1,3}-{2911,3285,0,1,1}-{2696,9689,0,1,1}-" + }, + { + "npc_id": "2686", + "loc_data": "{2907,3293,0,1,4}-{2700,9696,0,1,1}-" + }, + { + "npc_id": "2687", + "loc_data": "{2907,3293,0,1,6}-{2908,3290,0,1,3}-{2703,9698,0,1,1}-" + }, + { + "npc_id": "2688", + "loc_data": "{3127,9874,0,1,4}-{2906,3288,0,1,1}-{2910,3293,0,1,4}-{2699,9690,0,1,1}-" + }, + { + "npc_id": "2689", + "loc_data": "{2918,3319,0,0,0}-" + }, + { + "npc_id": "2690", + "loc_data": "{3048,3258,0,1,3}-" + }, + { + "npc_id": "2691", + "loc_data": "{3048,3256,0,1,4}-" + }, + { + "npc_id": "2692", + "loc_data": "{3049,3256,0,0,0}-" + }, + { + "npc_id": "2693", + "loc_data": "{3238,3238,0,1,0}-{3238,3246,0,1,0}-{3239,3235,0,1,0}-{3214,3313,0,1,0}-{3214,3320,0,1,0}-{3215,3303,0,1,0}-{3219,3285,0,1,0}-{3219,3288,0,1,0}-{3234,3267,0,1,0}-{3234,3268,0,1,0}-{2976,3359,0,1,0}-{2977,3357,0,1,0}-{2988,3383,0,1,1}-{2992,3385,0,1,3}-{3015,3285,0,1,0}-" + }, + { + "npc_id": "2695", + "loc_data": "{3013,3192,0,1,1}-" + }, + { + "npc_id": "2696", + "loc_data": "{3013,3196,0,1,1}-" + }, + { + "npc_id": "2697", + "loc_data": "{3018,3189,0,1,4}-" + }, + { + "npc_id": "2698", + "loc_data": "{3018,3181,0,1,6}-" + }, + { + "npc_id": "2699", + "loc_data": "{3016,3184,1,1,0}-" + }, + { + "npc_id": "2700", + "loc_data": "{3016,3182,1,1,0}-" + }, + { + "npc_id": "2701", + "loc_data": "{3011,3181,1,1,0}-" + }, + { + "npc_id": "2702", + "loc_data": "{3019,3180,1,1,0}-" + }, + { + "npc_id": "2703", + "loc_data": "{3012,3185,1,1,0}-" + }, + { + "npc_id": "2704", + "loc_data": "{3019,3185,0,0,0}-" + }, + { + "npc_id": "2705", + "loc_data": "{3018,3185,2,1,0}-" + }, + { + "npc_id": "2706", + "loc_data": "{3725,3015,0,1,4}-{3745,3026,0,1,6}-{3741,3033,0,1,6}-{3734,3045,0,1,1}-{3019,3145,0,1,3}-{3015,3142,0,1,4}-{3022,3139,0,1,4}-" + }, + { + "npc_id": "2709", + "loc_data": "{3002,3266,0,1,6}-" + }, + { + "npc_id": "2710", + "loc_data": "{3001,3268,0,1,4}-" + }, + { + "npc_id": "2711", + "loc_data": "{3002,3268,0,1,4}-" + }, + { + "npc_id": "2712", + "loc_data": "{3001,3268,0,1,3}-" + }, + { + "npc_id": "2713", + "loc_data": "{3002,3267,0,1,2}-" + }, + { + "npc_id": "2725", + "loc_data": "{2500,3488,0,1,2}-" + }, + { + "npc_id": "2726", + "loc_data": "{2994,3138,0,1,0}-" + }, + { + "npc_id": "2727", + "loc_data": "{2969,3146,0,1,0}-" + }, + { + "npc_id": "2728", + "loc_data": "{3043,3235,0,1,7}-" + }, + { + "npc_id": "2729", + "loc_data": "{3047,3234,0,1,7}-" + }, + { + "npc_id": "2730", + "loc_data": "{2835,3335,0,0,5}-" + }, + { + "npc_id": "2731", + "loc_data": "{2833,3335,0,0,3}-" + }, + { + "npc_id": "2732", + "loc_data": "{2933,3281,0,1,1}-" + }, + { + "npc_id": "2733", + "loc_data": "{2932,3286,0,1,1}-" + }, + { + "npc_id": "2759", + "loc_data": "{3104,3368,0,0,0}-{3096,3493,0,0,0}-{3097,3494,0,0,0}-{3180,3438,0,0,0}-{3180,3442,0,0,0}-{3191,3441,0,0,0}-{3191,3437,0,0,0}-{3191,3445,0,0,0}-{3254,3418,0,0,0}-{3255,3418,0,0,0}-{3256,3418,0,0,0}-" + }, + { + "npc_id": "2782", + "loc_data": "{1925,4636,0,1,7}-" + }, + { + "npc_id": "2783", + "loc_data": "{1995,4660,0,1,7}-{1989,4660,0,1,0}-{1989,4664,0,1,7}-{1997,4664,0,1,3}-{1989,4658,0,1,0}-{2001,4646,0,1,4}-{2000,4645,0,1,3}-{1997,4644,0,1,1}-{1999,4636,0,1,3}-{2022,4665,0,1,0}-{2024,4662,0,1,0}-{2020,4660,0,1,0}-{2023,4656,0,1,0}-{2029,4660,0,1,0}-{2031,4665,0,1,0}-{2034,4661,0,1,0}-{2032,4658,0,1,0}-{1991,4653,0,1,0}-{1995,4651,0,1,0}-" + }, + { + "npc_id": "2784", + "loc_data": "{2022,4616,0,1,5}-{2024,4620,0,1,5}-" + }, + { + "npc_id": "2785", + "loc_data": "{2000,4613,0,0,0}-{2000,4611,0,0,5}-" + }, + { + "npc_id": "2786", + "loc_data": "{2008,4613,0,1,0}-" + }, + { + "npc_id": "2787", + "loc_data": "{2023,4612,0,1,0}-" + }, + { + "npc_id": "2790", + "loc_data": "{3163,4822,0,0,6}-" + }, + { + "npc_id": "2792", + "loc_data": "{3228,3412,0,0,0}-" + }, + { + "npc_id": "2794", + "loc_data": "{2606,3100,0,0,0}-" + }, + { + "npc_id": "2795", + "loc_data": "{2979,3197,0,1,0}-" + }, + { + "npc_id": "2796", + "loc_data": "{3096,3108,0,0,6}-" + }, + { + "npc_id": "2800", + "loc_data": "{2981,3190,0,0,0}-" + }, + { + "npc_id": "2801", + "loc_data": "{2523,3373,0,1,0}-{2523,3376,0,1,0}-{2526,3373,0,1,0}-{2526,3376,0,1,0}-{2529,3373,0,1,0}-{2529,3376,0,1,0}-{2531,3376,0,1,0}-{2532,3373,0,1,0}-" + }, + { + "npc_id": "2802", + "loc_data": "{2406,3498,0,1,0}-" + }, + { + "npc_id": "2803", + "loc_data": "{3338,2804,0,1,0}-{3339,2815,0,1,0}-{3342,2809,0,1,0}-{3344,2800,0,1,0}-{3348,2809,0,1,0}-{3349,2814,0,1,0}-{3357,2813,0,1,0}-{3357,2806,0,1,0}-{3365,2815,0,1,0}-{3387,3068,0,1,0}-{3387,3017,0,1,0}-{3404,3062,0,1,0}-{3441,3061,0,1,0}-{3445,3034,0,1,0}-" + }, + { + "npc_id": "2804", + "loc_data": "{3388,3067,0,1,0}-{3396,3064,0,1,6}-{3402,3063,0,1,1}-{3423,3019,0,1,3}-{3420,3015,0,1,6}-{3430,3014,0,1,2}-{3426,3020,0,0,0}-{3416,3013,0,1,4}-{3410,3016,0,1,6}-" + }, + { + "npc_id": "2805", + "loc_data": "{3391,3071,0,1,2}-{3385,3064,0,1,6}-{3397,3069,0,1,2}-{3398,3068,0,1,1}-{3406,3064,0,1,6}-{3413,3053,0,1,6}-{3424,3061,0,1,2}-{3423,3020,0,1,0}-{3429,3019,0,0,0}-" + }, + { + "npc_id": "2806", + "loc_data": "{3426,3052,0,1,4}-{3423,3044,0,1,3}-{3415,3018,0,1,5}-{3425,3009,0,1,3}-" + }, + { + "npc_id": "2807", + "loc_data": "{3384,3013,0,1,0}-{3387,3014,0,1,0}-{3441,3059,0,1,0}-{3443,3033,0,1,0}-{3443,3059,0,1,0}-" + }, + { + "npc_id": "2808", + "loc_data": "{3385,3067,0,1,0}-{3389,3065,0,1,0}-{3400,3032,0,1,0}-{3402,3061,0,1,0}-{3404,3060,0,1,0}-{3407,3067,0,1,0}-{3408,3060,0,1,0}-{3411,3065,0,1,0}-{3412,3032,0,1,0}-{3412,3049,0,1,0}-{3414,3029,0,1,0}-{3417,3036,0,1,0}-{3418,3032,0,1,0}-{3419,3034,0,1,0}-{3422,3058,0,1,0}-{3424,3059,0,1,0}-{3435,3057,0,1,0}-{3435,3062,0,1,0}-{3439,3013,0,1,0}-{3439,3065,0,1,0}-{3440,3016,0,1,0}-{3443,3012,0,1,0}-{3443,3017,0,1,0}-{3443,3034,0,1,0}-{3443,3047,0,1,0}-{3446,3014,0,1,0}-" + }, + { + "npc_id": "2809", + "loc_data": "{3274,3168,0,1,0}-" + }, + { + "npc_id": "2810", + "loc_data": "{3311,3208,0,1,0}-" + }, + { + "npc_id": "2811", + "loc_data": "{3289,3209,0,1,0}-" + }, + { + "npc_id": "2812", + "loc_data": "{3296,3227,0,0,0}-" + }, + { + "npc_id": "2813", + "loc_data": "{3285,3201,0,0,0}-" + }, + { + "npc_id": "2816", + "loc_data": "{3301,3190,0,0,0}-" + }, + { + "npc_id": "2817", + "loc_data": "{3306,3197,0,0,0}-" + }, + { + "npc_id": "2818", + "loc_data": "{3303,3204,0,0,0}-" + }, + { + "npc_id": "2819", + "loc_data": "{3295,3193,0,0,0}-" + }, + { + "npc_id": "2820", + "loc_data": "{3302,3185,0,1,4}-" + }, + { + "npc_id": "2821", + "loc_data": "{3306,3196,0,0,4}-" + }, + { + "npc_id": "2823", + "loc_data": "{3302,3197,0,1,4}-" + }, + { + "npc_id": "2824", + "loc_data": "{3275,3193,0,0,0}-" + }, + { + "npc_id": "2860", + "loc_data": "{2345,3165,0,1,4}-" + }, + { + "npc_id": "2861", + "loc_data": "{2347,3165,0,1,3}-" + }, + { + "npc_id": "2881", + "loc_data": "{2907,4440,0,1,6}-" + }, + { + "npc_id": "2882", + "loc_data": "{2910,4457,0,1,6}-" + }, + { + "npc_id": "2883", + "loc_data": "{2923,4443,0,1,4}-" + }, + { + "npc_id": "2890", + "loc_data": "{1889,4411,0,0,4}-{1895,4409,0,0,2}-{1903,4409,0,0,3}-{1906,4407,0,0,5}-{1903,4400,0,0,1}-{1899,4400,0,0,2}-{1893,4398,0,0,2}-{1888,4386,0,0,6}-{1884,4383,0,0,5}-{1879,4376,0,0,5}-{1883,4376,0,0,1}-{1921,4368,0,0,2}-{1927,4362,0,0,7}-{1935,4360,0,0,7}-{1942,4356,0,0,7}-{1948,4355,0,0,7}-{1951,4358,0,0,2}-{1957,4365,0,0,2}-{1958,4368,0,0,2}-" + }, + { + "npc_id": "2894", + "loc_data": "{2908,4435,0,0,1}-{2924,4436,0,0,5}-{2924,4460,0,0,6}-{2908,4463,0,0,3}-{2901,4458,0,0,3}-{2928,4441,0,0,3}-{2929,4449,0,0,6}-{2902,4437,0,0,1}-" + }, + { + "npc_id": "2910", + "loc_data": "{3082,9885,0,1,4}-" + }, + { + "npc_id": "2932", + "loc_data": "{2437,3347,0,1,0}-" + }, + { + "npc_id": "2935", + "loc_data": "{3671,3484,0,1,4}-" + }, + { + "npc_id": "2939", + "loc_data": "{2657,3701,0,1,0}-" + }, + { + "npc_id": "2940", + "loc_data": "{2675,3671,0,1,3}-" + }, + { + "npc_id": "2942", + "loc_data": "{3017,3234,0,1,7}-" + }, + { + "npc_id": "2943", + "loc_data": "{3265,3400,0,0,0}-" + }, + { + "npc_id": "2945", + "loc_data": "{2562,3319,0,0,0}-" + }, + { + "npc_id": "2946", + "loc_data": "{3246,9865,0,1,1}-" + }, + { + "npc_id": "2947", + "loc_data": "{3245,9868,0,1,3}-" + }, + { + "npc_id": "2948", + "loc_data": "{3265,3403,0,0,0}-" + }, + { + "npc_id": "2949", + "loc_data": "{2562,3320,0,0,0}-" + }, + { + "npc_id": "2950", + "loc_data": "{3020,3229,0,0,7}-" + }, + { + "npc_id": "2997", + "loc_data": "{3308,3511,1,1,1}-" + }, + { + "npc_id": "2998", + "loc_data": "{2657,9641,0,1,0}-" + }, + { + "npc_id": "2999", + "loc_data": "{2658,9634,0,1,1}-" + }, + { + "npc_id": "3001", + "loc_data": "{2646,9639,0,1,4}-" + }, + { + "npc_id": "3002", + "loc_data": "{2666,9630,0,1,6}-" + }, + { + "npc_id": "3003", + "loc_data": "{2660,9624,0,1,6}-" + }, + { + "npc_id": "3020", + "loc_data": "{3348,2942,0,1,3}-" + }, + { + "npc_id": "3021", + "loc_data": "{2572,3105,0,1,0}-{2614,3226,0,1,4}-{3088,3357,0,1,0}-{2856,3435,0,1,0}-{3603,3529,0,1,6}-{2611,3859,0,1,6}-{2589,3863,0,1,0}-{3189,3234,0,1,0}-{2940,3225,0,1,6}-{2663,3374,0,1,4}-{3182,3354,0,1,0}-{2933,3434,0,1,4}-{3449,3471,0,1,6}-{2666,3521,0,1,4}-{2491,3177,0,1,1}-{3007,3371,0,1,2}-{2474,3446,0,1,4}-{2434,3415,0,1,3}-{3232,3459,0,1,3}-{2796,3104,0,1,0}-{2764,3210,0,1,1}-{3315,3205,0,1,6}-{3060,3263,0,1,0}-{3053,3304,0,1,1}-{2812,3333,0,1,4}-{2815,3466,0,1,3}-" + }, + { + "npc_id": "3022", + "loc_data": "{3371,9320,0,0,6}-" + }, + { + "npc_id": "3029", + "loc_data": "{3422,2937,0,1,1}-" + }, + { + "npc_id": "3030", + "loc_data": "{3421,2912,0,0,1}-" + }, + { + "npc_id": "3031", + "loc_data": "{3439,2890,0,0,3}-" + }, + { + "npc_id": "3032", + "loc_data": "{3439,2927,0,1,5}-" + }, + { + "npc_id": "3034", + "loc_data": "{3409,2929,1,1,4}-" + }, + { + "npc_id": "3035", + "loc_data": "{3450,2895,0,0,3}-" + }, + { + "npc_id": "3036", + "loc_data": "{3424,2900,0,1,3}-" + }, + { + "npc_id": "3037", + "loc_data": "{3424,2906,0,0,1}-" + }, + { + "npc_id": "3038", + "loc_data": "{3406,2924,0,1,3}-" + }, + { + "npc_id": "3039", + "loc_data": "{3414,2907,0,1,3}-" + }, + { + "npc_id": "3040", + "loc_data": "{3443,2914,0,0,3}-" + }, + { + "npc_id": "3041", + "loc_data": "{3445,2910,0,0,1}-" + }, + { + "npc_id": "3042", + "loc_data": "{3441,2920,0,1,4}-" + }, + { + "npc_id": "3043", + "loc_data": "{3445,2917,0,0,3}-" + }, + { + "npc_id": "3044", + "loc_data": "{3428,2928,0,1,5}-" + }, + { + "npc_id": "3045", + "loc_data": "{3432,2912,0,0,1}-" + }, + { + "npc_id": "3046", + "loc_data": "{3425,2891,0,0,4}-{3425,2894,0,0,4}-{3425,2893,0,0,4}-{3425,2889,0,0,4}-" + }, + { + "npc_id": "3050", + "loc_data": "{3187,9758,0,1,3}-" + }, + { + "npc_id": "3068", + "loc_data": "{3062,9550,0,1,5}-{3050,9554,0,1,7}-{3048,9545,0,1,4}-{3061,9542,0,1,3}-{3065,9548,0,1,1}-{3034,9544,0,1,4}-{3033,9552,0,1,3}-{3029,9545,0,1,7}-{3027,9555,0,1,3}-" + }, + { + "npc_id": "3072", + "loc_data": "{3207,5448,0,1,3}-{3223,5446,0,1,4}-{3210,5562,0,1,3}-" + }, + { + "npc_id": "3073", + "loc_data": "{3217,5442,0,1,4}-{3231,5444,0,1,0}-" + }, + { + "npc_id": "3074", + "loc_data": "{3404,3492,0,0,6}-" + }, + { + "npc_id": "3076", + "loc_data": "{3403,3492,0,0,0}-" + }, + { + "npc_id": "3090", + "loc_data": "{3082,3442,0,0,0}-" + }, + { + "npc_id": "3094", + "loc_data": "{3361,3306,0,1,6}-" + }, + { + "npc_id": "3095", + "loc_data": "{3364,3309,0,1,3}-{3363,3317,1,1,1}-" + }, + { + "npc_id": "3097", + "loc_data": "{3364,3304,0,1,6}-" + }, + { + "npc_id": "3099", + "loc_data": "{3364,9626,2,1,1}-" + }, + { + "npc_id": "3100", + "loc_data": "{3363,9646,0,1,1}-" + }, + { + "npc_id": "3101", + "loc_data": "{3364,9644,1,1,6}-" + }, + { + "npc_id": "3103", + "loc_data": "{3362,3318,1,0,6}-" + }, + { + "npc_id": "3104", + "loc_data": "{3364,3310,0,1,1}-{3379,9627,0,1,0}-{3367,9647,2,1,4}-" + }, + { + "npc_id": "3105", + "loc_data": "{3367,3304,1,1,1}-{3376,9655,0,1,7}-" + }, + { + "npc_id": "3106", + "loc_data": "{3360,3305,1,1,3}-{3348,9654,0,1,4}-{3362,9648,2,1,4}-" + }, + { + "npc_id": "3107", + "loc_data": "{3361,3306,0,1,3}-{3348,9626,0,1,6}-" + }, + { + "npc_id": "3108", + "loc_data": "{2549,3100,0,0,4}-" + }, + { + "npc_id": "3109", + "loc_data": "{2551,3079,0,1,3}-" + }, + { + "npc_id": "3110", + "loc_data": "{2789,3175,0,0,4}-" + }, + { + "npc_id": "3111", + "loc_data": "{2788,3176,0,0,0}-" + }, + { + "npc_id": "3114", + "loc_data": "{2815,3344,0,1,3}-" + }, + { + "npc_id": "3115", + "loc_data": "{2439,4444,0,1,3}-" + }, + { + "npc_id": "3123", + "loc_data": "{3344,2827,0,1,5}-" + }, + { + "npc_id": "3124", + "loc_data": "{3372,2847,1,0,3}-" + }, + { + "npc_id": "3125", + "loc_data": "{3366,2845,3,0,6}-" + }, + { + "npc_id": "3127", + "loc_data": "{3331,2948,0,1,4}-" + }, + { + "npc_id": "3153", + "loc_data": "{2871,3091,0,1,4}-{2862,3111,0,1,1}-{2861,3101,0,1,3}-{2870,3113,0,1,4}-{2870,3110,0,1,6}-{2865,3103,0,1,3}-{2873,3103,0,1,2}-{2863,3118,0,1,4}-{2886,3109,0,1,1}-{2892,3109,0,1,2}-{2883,3105,0,1,4}-{2889,3111,0,1,6}-{2883,3110,0,1,3}-" + }, + { + "npc_id": "3155", + "loc_data": "{3687,2953,0,1,1}-{3679,3494,0,1,3}-" + }, + { + "npc_id": "3162", + "loc_data": "{3691,2954,0,1,3}-" + }, + { + "npc_id": "3164", + "loc_data": "{3667,2990,0,1,3}-" + }, + { + "npc_id": "3166", + "loc_data": "{3693,2979,0,1,3}-" + }, + { + "npc_id": "3167", + "loc_data": "{3662,2978,1,1,4}-" + }, + { + "npc_id": "3168", + "loc_data": "{3663,2981,0,1,3}-" + }, + { + "npc_id": "3170", + "loc_data": "{3674,2970,0,1,0}-" + }, + { + "npc_id": "3188", + "loc_data": "{3664,2971,0,1,6}-" + }, + { + "npc_id": "3189", + "loc_data": "{3690,2976,0,1,1}-{3671,3006,0,1,6}-" + }, + { + "npc_id": "3191", + "loc_data": "{3659,2955,0,1,3}-{3683,2994,0,1,4}-{3662,3005,0,1,4}-" + }, + { + "npc_id": "3192", + "loc_data": "{3700,2994,0,1,4}-{3683,3005,0,1,3}-" + }, + { + "npc_id": "3193", + "loc_data": "{3696,2993,0,1,3}-{3693,3006,0,1,3}-" + }, + { + "npc_id": "3194", + "loc_data": "{3654,2992,0,1,4}-{3682,2962,0,1,3}-{3704,3006,0,1,0}-" + }, + { + "npc_id": "3195", + "loc_data": "{3696,2978,0,1,1}-" + }, + { + "npc_id": "3196", + "loc_data": "{3649,3004,0,1,6}-" + }, + { + "npc_id": "3198", + "loc_data": "{3682,2982,0,0,3}-" + }, + { + "npc_id": "3199", + "loc_data": "{3682,2981,0,0,3}-{3682,2983,0,0,3}-" + }, + { + "npc_id": "3200", + "loc_data": "{3263,3932,0,1,3}-" + }, + { + "npc_id": "3205", + "loc_data": "{3139,3448,0,1,3}-" + }, + { + "npc_id": "3207", + "loc_data": "{1970,5002,0,1,4}-" + }, + { + "npc_id": "3213", + "loc_data": "{2568,3334,0,1,0}-" + }, + { + "npc_id": "3214", + "loc_data": "{2517,9755,0,1,0}-" + }, + { + "npc_id": "3216", + "loc_data": "{2534,3273,0,0,1}-{2539,3273,0,0,1}-" + }, + { + "npc_id": "3217", + "loc_data": "{2956,3370,0,1,3}-" + }, + { + "npc_id": "3219", + "loc_data": "{3027,3344,0,1,4}-" + }, + { + "npc_id": "3220", + "loc_data": "{3013,3338,0,1,4}-" + }, + { + "npc_id": "3221", + "loc_data": "{3016,3334,0,1,3}-{3016,3339,0,1,6}-{3019,3341,0,1,4}-" + }, + { + "npc_id": "3223", + "loc_data": "{2994,3365,0,1,4}-" + }, + { + "npc_id": "3224", + "loc_data": "{3047,3365,0,1,6}-" + }, + { + "npc_id": "3226", + "loc_data": "{2996,3375,0,1,4}-" + }, + { + "npc_id": "3227", + "loc_data": "{2756,3182,0,1,6}-" + }, + { + "npc_id": "3228", + "loc_data": "{2969,3373,0,1,3}-{2948,3353,0,1,3}-{3036,3357,0,1,3}-" + }, + { + "npc_id": "3229", + "loc_data": "{2912,3344,0,1,7}-{3040,3354,0,1,2}-" + }, + { + "npc_id": "3230", + "loc_data": "{2986,3370,0,1,1}-" + }, + { + "npc_id": "3234", + "loc_data": "{3011,3383,0,1,0}-" + }, + { + "npc_id": "3235", + "loc_data": "{2970,3369,1,1,6}-" + }, + { + "npc_id": "3236", + "loc_data": "{2973,3369,1,1,6}-" + }, + { + "npc_id": "3237", + "loc_data": "{2984,3435,0,0,0}-" + }, + { + "npc_id": "3238", + "loc_data": "{2981,3434,0,0,0}-" + }, + { + "npc_id": "3239", + "loc_data": "{2975,3436,0,0,0}-" + }, + { + "npc_id": "3240", + "loc_data": "{2977,3442,0,0,0}-" + }, + { + "npc_id": "3241", + "loc_data": "{2969,3451,0,1,4}-" + }, + { + "npc_id": "3242", + "loc_data": "{2910,3332,0,1,0}-{2910,3337,0,1,4}-{2904,3331,0,1,3}-{2979,3568,0,1,7}-{2983,3566,0,1,3}-{2979,3566,0,1,3}-{2981,3570,0,1,6}-{2984,3561,0,1,6}-" + }, + { + "npc_id": "3243", + "loc_data": "{2906,3331,0,1,6}-{2909,3333,0,1,6}-" + }, + { + "npc_id": "3244", + "loc_data": "{2909,3333,0,1,1}-" + }, + { + "npc_id": "3245", + "loc_data": "{2907,3336,0,1,0}-" + }, + { + "npc_id": "3246", + "loc_data": "{3071,3446,0,1,6}-{2548,3567,0,1,4}-" + }, + { + "npc_id": "3247", + "loc_data": "{2548,3567,0,1,1}-" + }, + { + "npc_id": "3248", + "loc_data": "{3073,3442,0,1,5}-" + }, + { + "npc_id": "3249", + "loc_data": "{3082,3441,0,1,0}-" + }, + { + "npc_id": "3250", + "loc_data": "{3088,3426,0,1,3}-{2547,3571,0,1,7}-" + }, + { + "npc_id": "3251", + "loc_data": "{3073,3422,0,1,6}-" + }, + { + "npc_id": "3252", + "loc_data": "{3077,3424,0,1,6}-" + }, + { + "npc_id": "3253", + "loc_data": "{2542,3578,0,1,3}-" + }, + { + "npc_id": "3255", + "loc_data": "{3073,3423,0,1,3}-" + }, + { + "npc_id": "3256", + "loc_data": "{3080,3415,0,1,3}-{3084,3415,0,1,6}-" + }, + { + "npc_id": "3257", + "loc_data": "{3096,3432,0,1,0}-" + }, + { + "npc_id": "3258", + "loc_data": "{3080,3444,0,1,3}-" + }, + { + "npc_id": "3259", + "loc_data": "{3075,3438,0,1,4}-" + }, + { + "npc_id": "3260", + "loc_data": "{3083,3425,0,1,3}-{2554,3564,0,1,6}-{2547,3558,0,1,6}-" + }, + { + "npc_id": "3261", + "loc_data": "{3086,3425,0,1,3}-" + }, + { + "npc_id": "3262", + "loc_data": "{3080,3423,0,1,1}-" + }, + { + "npc_id": "3263", + "loc_data": "{3084,3419,0,1,4}-" + }, + { + "npc_id": "3264", + "loc_data": "{3126,3450,0,1,6}-{3121,3422,0,1,3}-{2565,9843,0,1,0}-{3259,3338,0,1,7}-{3311,3375,0,1,0}-" + }, + { + "npc_id": "3265", + "loc_data": "{3121,3427,0,1,2}-{2582,9836,0,1,1}-" + }, + { + "npc_id": "3266", + "loc_data": "{3094,3406,0,1,3}-" + }, + { + "npc_id": "3267", + "loc_data": "{3134,3459,0,1,1}-" + }, + { + "npc_id": "3268", + "loc_data": "{3012,3454,0,1,1}-" + }, + { + "npc_id": "3269", + "loc_data": "{3022,3447,0,1,4}-" + }, + { + "npc_id": "3270", + "loc_data": "{3010,3452,0,1,3}-" + }, + { + "npc_id": "3272", + "loc_data": "{3042,3468,0,1,1}-" + }, + { + "npc_id": "3273", + "loc_data": "{3030,3465,0,1,5}-" + }, + { + "npc_id": "3276", + "loc_data": "{3039,3475,0,1,4}-" + }, + { + "npc_id": "3277", + "loc_data": "{3035,3471,0,1,2}-" + }, + { + "npc_id": "3278", + "loc_data": "{3045,3477,0,1,1}-" + }, + { + "npc_id": "3279", + "loc_data": "{3039,3471,0,1,6}-" + }, + { + "npc_id": "3280", + "loc_data": "{3015,3451,0,0,0}-" + }, + { + "npc_id": "3281", + "loc_data": "{3018,3450,0,0,0}-" + }, + { + "npc_id": "3282", + "loc_data": "{3008,3442,0,0,0}-" + }, + { + "npc_id": "3283", + "loc_data": "{2964,3455,0,0,0}-{3039,3424,0,1,5}-{3043,3429,0,1,4}-{3047,3427,0,1,1}-{3048,3441,0,1,3}-" + }, + { + "npc_id": "3284", + "loc_data": "{2969,3437,0,0,0}-{3058,3419,0,0,0}-{3048,3423,0,1,6}-{3054,3432,0,1,5}-" + }, + { + "npc_id": "3285", + "loc_data": "{2952,3376,0,0,0}-{3036,3430,0,1,3}-{3025,3442,0,0,0}-" + }, + { + "npc_id": "3286", + "loc_data": "{2968,3448,0,1,1}-{3034,3450,0,1,3}-{3057,3447,0,1,6}-" + }, + { + "npc_id": "3287", + "loc_data": "{3073,3443,0,1,0}-{2987,3444,0,1,0}-{3046,3453,0,1,6}-" + }, + { + "npc_id": "3288", + "loc_data": "{2987,3446,0,1,7}-{3034,3437,0,1,0}-" + }, + { + "npc_id": "3293", + "loc_data": "{3113,3369,1,0,0}-{3122,3353,0,0,3}-{3124,3360,0,0,2}-{3115,3362,0,0,1}-" + }, + { + "npc_id": "3294", + "loc_data": "{3014,3339,0,1,2}-" + }, + { + "npc_id": "3295", + "loc_data": "{3020,3337,0,1,1}-" + }, + { + "npc_id": "3296", + "loc_data": "{2957,3359,0,1,0}-{2958,3358,0,1,0}-{2967,3360,0,1,0}-{2970,3361,0,1,0}-{2973,3359,0,1,0}-{2977,3357,0,1,0}-{2989,3384,0,1,0}-" + }, + { + "npc_id": "3298", + "loc_data": "{3366,9637,2,1,3}-" + }, + { + "npc_id": "3299", + "loc_data": "{3077,3257,0,1,1}-" + }, + { + "npc_id": "3302", + "loc_data": "{2453,4449,0,1,2}-" + }, + { + "npc_id": "3307", + "loc_data": "{2467,4438,0,1,4}-{2471,4439,0,1,7}-{2465,4436,0,1,3}-{2464,4432,0,1,4}-" + }, + { + "npc_id": "3308", + "loc_data": "{2905,3333,2,1,0}-" + }, + { + "npc_id": "3310", + "loc_data": "{2415,4451,0,1,6}-{2416,4435,0,1,7}-{2423,4432,0,1,4}-" + }, + { + "npc_id": "3318", + "loc_data": "{2401,4378,0,0,0}-" + }, + { + "npc_id": "3322", + "loc_data": "{2384,4439,0,1,0}-" + }, + { + "npc_id": "3324", + "loc_data": "{3159,3433,0,1,0}-" + }, + { + "npc_id": "3325", + "loc_data": "{3161,3432,1,1,4}-" + }, + { + "npc_id": "3328", + "loc_data": "{3203,3344,0,0,0}-" + }, + { + "npc_id": "3329", + "loc_data": "{3112,3414,0,0,0}-" + }, + { + "npc_id": "3330", + "loc_data": "{3132,3508,0,0,0}-" + }, + { + "npc_id": "3331", + "loc_data": "{3242,3240,0,0,4}-" + }, + { + "npc_id": "3340", + "loc_data": "{1759,5182,0,1,0}-" + }, + { + "npc_id": "3341", + "loc_data": "{1739,5154,0,1,0}-{1759,5191,0,1,0}-{1754,5200,0,1,0}-{1748,5223,0,1,0}-{1784,5199,0,1,0}-" + }, + { + "npc_id": "3342", + "loc_data": "{1768,5175,0,1,0}-{1760,5152,0,1,0}-{1745,5169,0,1,0}-{1745,5208,0,1,0}-{1737,5215,0,1,0}-{1758,5210,0,1,0}-" + }, + { + "npc_id": "3343", + "loc_data": "{1773,5149,0,1,0}-{1764,5198,0,1,0}-{1774,5195,0,1,0}-{1742,5292,0,1,0}-" + }, + { + "npc_id": "3344", + "loc_data": "{2412,4470,0,0,0}-{2414,4467,0,0,0}-{2415,4475,0,0,0}-{2419,4467,0,0,0}-{2419,4474,0,0,0}-" + }, + { + "npc_id": "3345", + "loc_data": "{2412,4370,0,0,0}-{2416,4376,0,0,0}-{2419,4374,0,0,0}-{2420,4377,0,0,0}-{2421,4370,0,0,0}-" + }, + { + "npc_id": "3348", + "loc_data": "{2978,3343,0,1,4}-{2978,3337,0,1,1}-{2966,3337,1,1,6}-{2970,3330,2,1,1}-" + }, + { + "npc_id": "3349", + "loc_data": "{2961,3330,2,1,0}-{2966,3352,2,1,1}-{2974,3328,2,1,6}-" + }, + { + "npc_id": "3350", + "loc_data": "{2959,3340,2,1,3}-" + }, + { + "npc_id": "3376", + "loc_data": "{2438,4393,0,1,1}-{3313,5453,0,1,3}-{3312,5452,0,1,3}-{3310,5452,0,1,6}-{3311,5462,0,1,5}-{3313,5554,0,1,6}-{3297,5562,0,1,1}-{3305,5554,0,1,1}-{3300,5540,0,1,1}-" + }, + { + "npc_id": "3382", + "loc_data": "{3080,9885,0,1,4}-{3076,9886,0,1,4}-{3081,9890,0,1,4}-{3081,9895,0,1,4}-" + }, + { + "npc_id": "3384", + "loc_data": "{3211,3225,0,0,0}-" + }, + { + "npc_id": "3402", + "loc_data": "{2560,2842,0,0,1}-" + }, + { + "npc_id": "3407", + "loc_data": "{3114,3515,0,1,4}-" + }, + { + "npc_id": "3408", + "loc_data": "{3113,3510,0,1,7}-" + }, + { + "npc_id": "3419", + "loc_data": "{2568,2991,0,1,0}-{2574,2988,0,1,0}-{2584,2967,0,1,0}-{2585,2977,0,1,0}-{2592,2965,0,1,0}-{2608,2980,0,1,0}-{2609,2990,0,1,0}-{2614,2998,0,1,0}-{2561,3020,0,1,0}-{2567,3046,0,1,0}-{2585,9608,0,1,0}-{2586,9614,0,1,0}-{2588,9606,0,1,0}-{2581,9738,0,1,0}-{2581,9742,0,1,0}-{2584,9734,0,1,0}-{2585,9738,0,1,0}-{2587,9733,0,1,0}-{2587,9740,0,1,0}-{2590,9737,0,1,0}-{2387,9685,0,1,0}-{2389,9683,0,1,0}-{2719,9668,0,1,0}-{2719,9715,0,1,0}-{2720,9702,0,1,0}-{2722,9669,0,1,0}-{2723,9707,0,1,0}-{2724,9713,0,1,0}-{2504,2965,0,1,0}-{2504,2984,0,1,0}-{2520,2973,0,1,0}-{2525,2986,0,1,0}-{2533,2977,0,1,0}-{2536,2975,0,1,0}-{2539,2990,0,1,0}-{2549,2979,0,1,0}-{2551,2955,0,1,0}-{2552,2961,0,1,0}-{2513,3026,0,1,0}-{2522,3055,0,1,0}-{2523,3057,0,1,0}-{2529,3032,0,1,0}-{2539,3018,0,1,0}-{2541,3016,0,1,0}-{2550,3043,0,1,0}-{2550,3046,0,1,0}-{2553,3047,0,1,0}-{2496,3092,0,1,0}-{2497,3089,0,1,0}-{2500,3092,0,1,0}-{2500,3097,0,1,0}-{2503,3095,0,1,0}-{2504,3133,0,1,0}-{2554,3192,0,1,0}-" + }, + { + "npc_id": "3450", + "loc_data": "{2901,2947,0,0,0}-{2925,2959,0,0,0}-{2920,3053,0,0,0}-{2924,3063,0,0,0}-" + }, + { + "npc_id": "3469", + "loc_data": "{2657,2966,0,0,0}-" + }, + { + "npc_id": "3470", + "loc_data": "{2755,3083,0,0,0}-" + }, + { + "npc_id": "3479", + "loc_data": "{2906,3514,1,0,0}-" + }, + { + "npc_id": "3541", + "loc_data": "{3517,3240,0,1,6}-" + }, + { + "npc_id": "3579", + "loc_data": "{3195,3263,0,1,0}-" + }, + { + "npc_id": "3638", + "loc_data": "{3309,3453,0,0,1}-" + }, + { + "npc_id": "3661", + "loc_data": "{3276,9836,0,1,0}-{3287,9840,0,1,0}-" + }, + { + "npc_id": "3662", + "loc_data": "{3280,9849,0,1,0}-{3286,9830,0,1,0}-" + }, + { + "npc_id": "3663", + "loc_data": "{3269,9849,0,1,0}-{3291,9843,0,1,0}-" + }, + { + "npc_id": "3664", + "loc_data": "{3269,9843,0,1,0}-{3275,9833,0,1,0}-" + }, + { + "npc_id": "3665", + "loc_data": "{3305,9832,0,1,0}-{3305,9843,0,1,0}-{3311,9850,0,1,0}-{3312,9845,0,1,0}-{3313,9827,0,1,0}-{3315,9837,0,1,0}-{3319,9832,0,1,0}-{3319,9849,0,1,0}-{3323,9842,0,1,0}-" + }, + { + "npc_id": "3666", + "loc_data": "{3302,9806,0,1,0}-{3302,9816,0,1,0}-{3304,9801,0,1,0}-{3310,9796,0,1,0}-{3312,9811,0,1,0}-{3313,9805,0,1,0}-{3313,9817,0,1,0}-{3314,9797,0,1,0}-{3319,9800,0,1,0}-{3319,9806,0,1,0}-{3321,9811,0,1,0}-" + }, + { + "npc_id": "3667", + "loc_data": "{3269,9798,0,1,0}-{3269,9806,0,1,0}-{3269,9814,0,1,0}-{3275,9817,0,1,0}-{3276,9809,0,1,0}-{3280,9803,0,1,0}-{3282,9797,0,1,0}-{3285,9812,0,1,0}-{3288,9819,0,1,0}-{3289,9809,0,1,0}-{3290,9797,0,1,0}-" + }, + { + "npc_id": "3670", + "loc_data": "{3361,3505,0,1,0}-" + }, + { + "npc_id": "3671", + "loc_data": "{3085,3251,0,0,3}-" + }, + { + "npc_id": "3673", + "loc_data": "{2322,3189,0,1,0}-{3195,3265,0,1,0}-{2924,3318,0,1,0}-{2924,3325,0,1,4}-{2656,3359,0,1,0}-{2656,3375,0,1,0}-{2653,4716,0,1,0}-{3248,3351,0,1,0}-" + }, + { + "npc_id": "3675", + "loc_data": "{3336,2778,0,1,0}-{3343,2793,0,1,0}-{3367,2791,0,1,0}-{3333,2864,0,1,1}-{3335,2860,0,1,2}-{3338,2864,0,1,4}-{3215,2841,0,1,0}-{3217,2845,0,1,0}-{3220,2841,0,1,0}-{3295,2866,0,1,0}-{3297,2863,0,1,0}-{3301,2864,0,1,0}-" + }, + { + "npc_id": "3677", + "loc_data": "{2637,3440,0,0,0}-" + }, + { + "npc_id": "3679", + "loc_data": "{2875,3482,0,0,4}-" + }, + { + "npc_id": "3680", + "loc_data": "{3282,3327,0,1,7}-" + }, + { + "npc_id": "3695", + "loc_data": "{2661,3701,0,1,0}-" + }, + { + "npc_id": "3696", + "loc_data": "{2673,3658,0,1,3}-" + }, + { + "npc_id": "3707", + "loc_data": "{2713,10133,0,1,2}-{2708,10133,0,1,4}-{2706,10135,0,0,3}-{2708,10133,0,1,3}-{2703,10135,0,1,6}-{2702,10131,0,1,4}-{2717,10133,0,1,0}-{2708,10130,0,1,1}-" + }, + { + "npc_id": "3709", + "loc_data": "{2847,5075,0,0,0}-{2974,2906,0,0,0}-" + }, + { + "npc_id": "3711", + "loc_data": "{2575,9746,0,1,0}-{2581,9747,0,1,0}-{2577,9743,0,1,1}-{2579,9740,0,1,0}-{2588,9733,0,1,4}-" + }, + { + "npc_id": "3777", + "loc_data": "{3230,3231,0,1,5}-" + }, + { + "npc_id": "3778", + "loc_data": "{1867,4233,0,0,0}-" + }, + { + "npc_id": "3780", + "loc_data": "{1906,4281,0,0,0}-" + }, + { + "npc_id": "3781", + "loc_data": "{2659,2675,0,1,1}-" + }, + { + "npc_id": "3786", + "loc_data": "{2658,2666,0,1,1}-" + }, + { + "npc_id": "3787", + "loc_data": "{2754,3509,0,0,0}-" + }, + { + "npc_id": "3788", + "loc_data": "{2664,2653,0,1,3}-" + }, + { + "npc_id": "3789", + "loc_data": "{2660,2649,0,1,3}-" + }, + { + "npc_id": "3790", + "loc_data": "{3041,3203,0,0,6}-" + }, + { + "npc_id": "3791", + "loc_data": "{2653,2655,0,1,1}-" + }, + { + "npc_id": "3793", + "loc_data": "{2659,2660,0,1,6}-" + }, + { + "npc_id": "3796", + "loc_data": "{2667,2661,0,0,3}-" + }, + { + "npc_id": "3797", + "loc_data": "{2649,2659,0,0,2}-" + }, + { + "npc_id": "3798", + "loc_data": "{2658,2652,0,1,6}-" + }, + { + "npc_id": "3799", + "loc_data": "{2650,2663,0,0,4}-" + }, + { + "npc_id": "3802", + "loc_data": "{2657,2637,0,0,6}-" + }, + { + "npc_id": "3804", + "loc_data": "{3349,3793,0,0,0}-" + }, + { + "npc_id": "3805", + "loc_data": "{2445,3097,0,1,5}-" + }, + { + "npc_id": "3806", + "loc_data": "{3166,3304,0,1,0}-" + }, + { + "npc_id": "3807", + "loc_data": "{3254,3274,0,0,0}-" + }, + { + "npc_id": "3808", + "loc_data": "{2422,3526,0,1,1}-{2517,3205,0,1,3}-{2513,3204,0,1,3}-" + }, + { + "npc_id": "3809", + "loc_data": "{3285,3213,0,1,1}-" + }, + { + "npc_id": "3810", + "loc_data": "{2847,3499,0,1,7}-{2970,2973,0,0,0}-" + }, + { + "npc_id": "3811", + "loc_data": "{2894,2730,0,1,2}-{2918,3057,0,0,0}-{2465,3501,3,1,3}-" + }, + { + "npc_id": "3813", + "loc_data": "{2541,2969,0,1,4}-" + }, + { + "npc_id": "3817", + "loc_data": "{2521,3208,0,1,1}-" + }, + { + "npc_id": "3819", + "loc_data": "{2416,3525,0,1,3}-{2412,3527,0,1,6}-" + }, + { + "npc_id": "3820", + "loc_data": "{3088,3255,0,0,6}-" + }, + { + "npc_id": "3822", + "loc_data": "{2353,3680,0,1,3}-" + }, + { + "npc_id": "3823", + "loc_data": "{2343,3668,0,1,3}-" + }, + { + "npc_id": "3824", + "loc_data": "{2329,3689,0,1,6}-" + }, + { + "npc_id": "3831", + "loc_data": "{2357,3637,0,0,0}-" + }, + { + "npc_id": "3832", + "loc_data": "{2345,3651,0,0,0}-" + }, + { + "npc_id": "3848", + "loc_data": "{2337,3703,0,0,6}-{2340,3702,0,0,6}-{2343,3702,0,0,3}-{2349,3702,0,0,6}-{2353,3703,0,0,6}-{2310,3704,0,0,6}-{2311,3696,0,0,4}-{2343,3702,0,0,3}-" + }, + { + "npc_id": "3851", + "loc_data": "{2332,3669,0,0,0}-{2325,3671,0,0,4}-{2331,3670,0,0,1}-{2333,3671,0,0,3}-" + }, + { + "npc_id": "3915", + "loc_data": "{2945,3146,0,1,3}-" + }, + { + "npc_id": "3916", + "loc_data": "{2595,3890,0,0,3}-" + }, + { + "npc_id": "3917", + "loc_data": "{2581,3861,0,0,1}-" + }, + { + "npc_id": "3920", + "loc_data": "{2527,10259,0,1,1}-" + }, + { + "npc_id": "3921", + "loc_data": "{2506,10251,0,1,1}-" + }, + { + "npc_id": "3922", + "loc_data": "{2508,10260,0,1,1}-" + }, + { + "npc_id": "3923", + "loc_data": "{2521,10251,0,1,1}-" + }, + { + "npc_id": "3937", + "loc_data": "{2508,3866,0,0,3}-" + }, + { + "npc_id": "4235", + "loc_data": "{2669,3332,0,0,1}-" + }, + { + "npc_id": "4237", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4239", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4241", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4243", + "loc_data": "{2669,3331,0,0,6}-" + }, + { + "npc_id": "4246", + "loc_data": "{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": "4343", + "loc_data": "{3706,3051,0,1,6}-{3703,3040,0,1,3}-{3664,3013,0,1,7}-{3731,2991,0,1,1}-{3723,2998,0,1,2}-{3749,3000,0,1,6}-{3775,3033,0,1,4}-{3766,3010,0,1,4}-{3764,3020,0,1,1}-{3738,3010,0,1,3}-{3720,3040,0,1,6}-{3719,3045,0,1,6}-{3781,3031,0,1,5}-{3780,3022,0,1,1}-{3788,3011,0,1,2}-{3793,3019,0,1,5}-{3779,3045,0,1,1}-{3784,3040,0,1,3}-{3802,3037,0,1,2}-{3808,3026,0,1,3}-{3799,3024,0,1,4}-{3782,3016,0,1,7}-" + }, + { + "npc_id": "4344", + "loc_data": "{3733,2972,0,1,4}-{3747,3013,0,1,4}-{3766,3034,0,1,6}-" + }, + { + "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}-{2440,3213,0,1,0}-" + }, + { + "npc_id": "4690", + "loc_data": "{3104,3875,0,1,2}-{2372,3401,0,1,0}-{2912,9731,0,1,0}-{2439,3211,0,1,0}-{3256,3624,0,1,5}-{3308,3661,0,1,2}-" + }, + { + "npc_id": "4691", + "loc_data": "{3110,3854,0,1,0}-{2371,3398,0,1,0}-{2439,3208,0,1,0}-" + }, + { + "npc_id": "4692", + "loc_data": "{3116,3858,0,1,0}-{2372,3395,0,1,0}-{2442,3210,0,1,0}-" + }, + { + "npc_id": "4693", + "loc_data": "{3094,3849,0,1,0}-{2369,3394,0,1,0}-{2912,9741,0,1,0}-{2906,9736,0,1,0}-{2444,3208,0,1,0}-" + }, + { + "npc_id": "4694", + "loc_data": "{3103,3709,0,1,0}-{3094,3869,0,1,1}-{2843,9558,0,1,1}-{2840,9605,0,1,4}-{3017,3852,0,1,7}-" + }, + { + "npc_id": "4695", + "loc_data": "{2840,3279,0,1,6}-{3098,3699,0,1,4}-{2839,9565,0,1,3}-{2837,9601,0,1,4}-{2631,9880,0,1,1}-{3015,3851,0,1,2}-" + }, + { + "npc_id": "4696", + "loc_data": "{3096,3691,0,1,6}-{3091,3861,0,1,0}-{2839,9560,0,1,4}-{2846,9613,0,1,6}-{2931,9808,0,1,0}-{2930,9799,0,1,0}-{2632,9874,0,1,1}-{3014,3848,0,1,1}-" + }, + { + "npc_id": "4697", + "loc_data": "{2825,3277,0,1,3}-{3105,3695,0,1,5}-{2845,9557,0,1,2}-{2839,9624,0,1,6}-{2630,9867,0,1,1}-{3016,3845,0,1,4}-{3324,3856,0,1,2}-" + }, + { + "npc_id": "4698", + "loc_data": "{2861,9752,0,1,4}-{3139,3712,0,1,5}-{3304,3886,0,1,6}-" + }, + { + "npc_id": "4699", + "loc_data": "{3159,3707,0,1,3}-{2629,9477,2,0,0}-{2640,9502,2,0,1}-{3296,3872,0,1,5}-{3035,10245,0,1,4}-" + }, + { + "npc_id": "4700", + "loc_data": "{2862,9750,0,1,7}-{3144,3701,0,1,4}-{2633,9488,2,0,6}-{3282,3880,0,1,4}-" + }, + { + "npc_id": "4701", + "loc_data": "{3149,3690,0,1,4}-{3287,3894,0,1,3}-{3028,10250,0,1,4}-" + }, + { + "npc_id": "4702", + "loc_data": "{2869,9775,0,1,0}-{2853,9775,0,1,0}-{3084,9957,0,1,6}-{2712,9480,0,0,5}-" + }, + { + "npc_id": "4703", + "loc_data": "{2857,9774,0,1,0}-{2860,9781,0,1,0}-{3092,9958,0,1,2}-" + }, + { + "npc_id": "4704", + "loc_data": "{2863,9767,0,1,0}-{2871,9782,0,1,0}-{3160,5521,0,1,1}-" + }, + { + "npc_id": "4705", + "loc_data": "{3088,9961,0,1,1}-{3169,5528,0,1,0}-" + }, + { + "npc_id": "4706", + "loc_data": "{2195,3808,0,1,0}-{2196,3822,0,1,0}-{2197,3805,0,1,0}-{2197,3813,0,1,0}-{2201,3812,0,1,0}-{2202,3824,0,1,0}-{2203,3804,0,1,0}-{2207,3813,0,1,0}-{2211,3818,0,1,0}-{2214,3819,0,1,0}-{3237,5559,0,1,3}-{3244,5554,0,1,3}-{3239,5553,0,1,5}-{3249,5559,0,1,5}-" + }, + { + "npc_id": "4707", + "loc_data": "{3213,3253,0,0,6}-" + }, + { + "npc_id": "4710", + "loc_data": "{3222,3475,0,0,5}-" + }, + { + "npc_id": "4732", + "loc_data": "{3429,3701,0,0,0}-" + }, + { + "npc_id": "4856", + "loc_data": "{2732,3292,0,1,1}-" + }, + { + "npc_id": "4871", + "loc_data": "{2782,3276,0,1,0}-" + }, + { + "npc_id": "4872", + "loc_data": "{2740,3310,0,0,0}-" + }, + { + "npc_id": "4874", + "loc_data": "{2709,3291,0,0,0}-" + }, + { + "npc_id": "4878", + "loc_data": "{2726,3283,0,1,1}-" + }, + { + "npc_id": "4883", + "loc_data": "{2713,3278,0,1,1}-" + }, + { + "npc_id": "4885", + "loc_data": "{2705,3287,0,1,1}-{2716,3287,0,1,1}-{2732,3282,0,1,1}-" + }, + { + "npc_id": "4887", + "loc_data": "{2738,3303,0,1,1}-{2723,3274,0,1,1}-" + }, + { + "npc_id": "4891", + "loc_data": "{2768,3286,0,1,0}-{2777,3278,0,1,0}-{2783,3289,0,1,0}-{2784,3277,0,1,0}-{2768,3289,1,1,0}-{2780,3288,1,1,0}-" + }, + { + "npc_id": "4895", + "loc_data": "{2718,3302,0,1,1}-" + }, + { + "npc_id": "4898", + "loc_data": "{3124,9874,0,1,3}-{3128,9875,0,1,6}-" + }, + { + "npc_id": "4899", + "loc_data": "{3231,3197,0,1,6}-" + }, + { + "npc_id": "4900", + "loc_data": "{3210,3213,1,1,1}-" + }, + { + "npc_id": "4901", + "loc_data": "{3245,3157,0,0,7}-" + }, + { + "npc_id": "4902", + "loc_data": "{3227,3147,0,0,1}-" + }, + { + "npc_id": "4903", + "loc_data": "{3244,3214,0,1,1}-" + }, + { + "npc_id": "4904", + "loc_data": "{3228,3254,0,1,3}-" + }, + { + "npc_id": "4906", + "loc_data": "{3230,3244,0,1,4}-" + }, + { + "npc_id": "4907", + "loc_data": "{3208,3222,2,0,6}-" + }, + { + "npc_id": "4909", + "loc_data": "{2831,3352,0,0,7}-" + }, + { + "npc_id": "4911", + "loc_data": "{2703,9894,0,0,0}-{2705,9897,0,0,0}-{2706,9904,0,0,0}-{2707,9906,0,0,0}-{2704,9906,0,0,0}-{2706,9910,0,0,0}-{2703,9911,0,0,0}-{2699,9915,0,0,0}-{2697,9915,0,0,0}-{2694,9915,0,0,0}-{2692,9914,0,0,0}-{2696,9910,0,0,0}-{2694,9908,0,0,0}-{2693,9906,0,0,0}-{2696,9906,0,0,0}-{2698,9901,0,0,0}-{2698,9900,0,0,0}-{2696,9900,0,0,0}-{2690,9903,0,0,0}-{2692,9902,0,0,0}-{2693,9900,0,0,0}-{2692,9900,0,0,0}-{2691,9899,0,0,0}-{2692,9895,0,0,0}-{2693,9891,0,0,0}-{2695,9889,0,0,0}-{2692,9885,0,0,0}-{2699,9882,0,0,0}-{2695,9881,0,0,0}-{2693,9881,0,0,0}-{2690,9880,0,0,0}-{2700,9879,0,0,0}-{2696,9880,0,0,0}-{2694,9878,0,0,0}-{2695,9877,0,0,0}-{2699,9876,0,0,0}-{2692,9876,0,0,0}-{2692,9874,0,0,0}-{2699,9872,0,0,0}-{2698,9871,0,0,0}-{2701,9870,0,0,0}-{2695,9870,0,0,0}-{2695,9869,0,0,0}-{2702,9868,0,0,0}-{2701,9867,0,0,0}-{2697,9866,0,0,0}-{2699,9865,0,0,0}-" + }, + { + "npc_id": "4920", + "loc_data": "{3188,5510,0,1,6}-{3194,5510,0,1,2}-{3289,5496,0,0,5}-{3272,5490,0,0,0}-" + }, + { + "npc_id": "4921", + "loc_data": "{3304,5493,0,1,3}-{3303,5496,0,1,0}-{3271,5490,0,0,5}-" + }, + { + "npc_id": "4926", + "loc_data": "{3227,3547,0,1,1}-{3251,3528,0,1,1}-{2969,3686,0,1,1}-" + }, + { + "npc_id": "4927", + "loc_data": "{3227,3533,0,1,1}-{3236,3549,0,1,1}-{2962,3689,0,1,1}-{2971,3690,0,1,1}-{3049,3695,0,1,1}-" + }, + { + "npc_id": "4930", + "loc_data": "{3412,2889,0,1,3}-{3414,2892,0,1,1}-{3414,2890,0,1,1}-{3416,2886,0,1,4}-{3416,2893,0,1,6}-" + }, + { + "npc_id": "4932", + "loc_data": "{3414,2891,0,1,1}-{3410,2888,0,1,7}-" + }, + { + "npc_id": "4942", + "loc_data": "{3191,3210,0,1,0}-" + }, + { + "npc_id": "4944", + "loc_data": "{2042,5213,0,1,5}-{2020,5236,0,1,2}-{1993,5188,0,1,2}-{1994,5191,0,1,2}-" + }, + { + "npc_id": "4945", + "loc_data": "{1994,5192,0,1,4}-{1997,5190,0,1,1}-" + }, + { + "npc_id": "4946", + "loc_data": "{2721,3432,0,1,4}-" + }, + { + "npc_id": "4947", + "loc_data": "{2825,3696,0,1,3}-" + }, + { + "npc_id": "4950", + "loc_data": "{2855,10051,1,0,0}-" + }, + { + "npc_id": "4953", + "loc_data": "{2684,3274,1,0,0}-" + }, + { + "npc_id": "4955", + "loc_data": "{2772,3223,0,0,0}-" + }, + { + "npc_id": "4956", + "loc_data": "{2783,3119,0,0,0}-" + }, + { + "npc_id": "4961", + "loc_data": "{2683,3275,1,0,0}-" + }, + { + "npc_id": "4965", + "loc_data": "{2826,3685,0,0,1}-" + }, + { + "npc_id": "4975", + "loc_data": "{3034,2971,0,0,5}-{3030,2971,0,0,1}-{3303,3024,0,0,7}-{3299,3016,0,0,2}-{3280,9438,0,0,6}-{3275,9460,0,0,3}-{3267,9458,0,0,3}-{3270,9415,0,0,6}-{3272,9429,0,0,4}-{3272,9421,0,0,4}-{3271,9450,0,0,1}-{3288,9413,0,0,6}-{3297,9414,0,0,6}-{3300,9414,0,0,6}-{3304,9424,0,0,1}-{3292,9426,0,0,4}-{3295,9421,0,0,3}-{3297,9412,0,0,1}-{3292,9413,0,0,3}-{3324,9429,0,0,3}-{3311,9440,0,0,6}-{3294,9459,0,0,1}-{3291,9463,0,0,6}-{3296,9468,0,0,1}-{3288,9468,0,0,1}-{3313,9449,0,0,1}-" + }, + { + "npc_id": "4976", + "loc_data": "{3036,2972,0,0,1}-{3039,2968,0,0,1}-{3046,2982,0,0,6}-{3045,2974,0,0,6}-{3042,2972,0,0,6}-{3301,3017,0,0,7}-{3281,9446,0,0,1}-{3270,9451,0,0,4}-{3269,9418,0,0,1}-{3267,9441,0,0,1}-{3280,9436,0,0,1}-{3291,9414,0,0,6}-{3305,9413,0,0,6}-{3308,9415,0,0,1}-{3309,9420,0,0,1}-{3296,9425,0,0,1}-{3294,9420,0,0,1}-{3295,9412,0,0,3}-{3323,9439,0,0,6}-{3295,9460,0,0,3}-{3294,9464,0,0,6}-{3294,9467,0,0,1}-{3311,9454,0,0,6}-" + }, + { + "npc_id": "4977", + "loc_data": "{3038,2973,0,0,1}-{3027,2974,0,0,1}-{3023,2977,0,0,1}-{3035,2989,0,0,4}-{3039,2985,0,0,4}-{3294,3016,0,0,6}-{3287,3016,0,0,3}-{3277,9452,0,0,1}-{3267,9443,0,0,6}-{3267,9431,0,0,6}-{3269,9420,0,0,6}-{3273,9460,0,0,4}-{3277,9454,0,0,6}-{3281,9448,0,0,6}-{3294,9413,0,0,6}-{3306,9423,0,0,1}-{3298,9436,0,0,6}-{3292,9466,0,0,6}-{3296,9462,0,0,3}-{3290,9467,0,0,1}-{3315,9451,0,0,1}-{3316,9454,0,0,3}-" + }, + { + "npc_id": "4978", + "loc_data": "{3042,2969,0,0,1}-{3038,2976,0,0,6}-{3041,2983,0,0,6}-{3043,2983,0,0,6}-{3044,2981,0,0,6}-{3043,2979,0,0,1}-{3038,2989,0,0,1}-{3025,2984,0,0,1}-{3026,2988,0,0,1}-{3030,2990,0,0,1}-{3024,2979,0,0,3}-{3291,3017,0,0,5}-{3275,9463,0,0,1}-{3273,9430,0,0,6}-{3273,9422,0,0,6}-{3267,9428,0,0,1}-{3294,9411,0,0,6}-{3300,9424,0,0,1}-{3289,9412,0,0,3}-" + }, + { + "npc_id": "4985", + "loc_data": "{3304,3027,0,1,2}-" + }, + { + "npc_id": "4986", + "loc_data": "{3308,3112,0,1,1}-" + }, + { + "npc_id": "4989", + "loc_data": "{3272,3029,0,1,4}-" + }, + { + "npc_id": "4990", + "loc_data": "{3269,3029,0,1,4}-" + }, + { + "npc_id": "4991", + "loc_data": "{3270,3030,0,1,6}-" + }, + { + "npc_id": "4992", + "loc_data": "{3267,3029,0,1,0}-" + }, + { + "npc_id": "4993", + "loc_data": "{3277,3028,0,1,1}-{3279,9441,0,1,4}-" + }, + { + "npc_id": "4994", + "loc_data": "{3285,3027,0,1,7}-" + }, + { + "npc_id": "4995", + "loc_data": "{3279,3024,0,1,3}-{3286,3036,1,1,4}-" + }, + { + "npc_id": "4996", + "loc_data": "{3284,3022,0,1,2}-" + }, + { + "npc_id": "4997", + "loc_data": "{3289,3026,0,1,3}-{3317,9438,0,0,3}-" + }, + { + "npc_id": "4998", + "loc_data": "{3293,3027,0,1,5}-{3286,9419,0,1,3}-{3296,9433,0,1,7}-{3310,9457,0,1,1}-" + }, + { + "npc_id": "4999", + "loc_data": "{3280,3020,0,1,6}-{3319,9439,0,1,0}-{3299,9467,0,1,3}-" + }, + { + "npc_id": "5000", + "loc_data": "{3285,3022,0,1,6}-{3295,9434,0,1,3}-" + }, + { + "npc_id": "5001", + "loc_data": "{3297,3020,0,1,6}-{3280,9414,0,1,1}-{3279,9416,0,1,1}-" + }, + { + "npc_id": "5002", + "loc_data": "{3295,3028,0,1,3}-{3291,9423,0,1,1}-" + }, + { + "npc_id": "5020", + "loc_data": "{2905,3180,0,0,0}-" + }, + { + "npc_id": "5021", + "loc_data": "{2906,3173,0,0,0}-" + }, + { + "npc_id": "5022", + "loc_data": "{2907,3169,0,0,0}-" + }, + { + "npc_id": "5029", + "loc_data": "{2534,3575,0,1,0}-" + }, + { + "npc_id": "5030", + "loc_data": "{2533,3570,0,1,4}-" + }, + { + "npc_id": "5031", + "loc_data": "{2525,3575,0,1,3}-" + }, + { + "npc_id": "5032", + "loc_data": "{2525,3566,0,1,3}-" + }, + { + "npc_id": "5033", + "loc_data": "{2520,3569,0,1,3}-" + }, + { + "npc_id": "5049", + "loc_data": "{2808,3355,0,1,6}-" + }, + { + "npc_id": "5062", + "loc_data": "{2927,3410,0,0,0}-" + }, + { + "npc_id": "5063", + "loc_data": "{2460,3108,0,1,3}-" + }, + { + "npc_id": "5065", + "loc_data": "{2925,3303,0,0,0}-" + }, + { + "npc_id": "5070", + "loc_data": "{2912,3480,0,0,0}-{2911,3480,0,0,0}-" + }, + { + "npc_id": "5071", + "loc_data": "{2609,2922,0,0,0}-" + }, + { + "npc_id": "5072", + "loc_data": "{2464,2832,0,1,2}-{2510,2876,0,1,3}-{2517,2878,0,1,6}-{2527,2877,0,1,2}-{2507,2877,0,1,1}-{2536,2877,0,1,3}-{2530,2872,0,1,3}-{2517,2870,0,1,4}-{2526,2928,0,1,6}-{2532,2938,0,1,4}-{2527,2933,0,1,4}-{2521,2938,0,1,6}-{2507,2914,0,1,0}-{2512,2910,0,1,4}-{2517,2912,0,1,1}-{2517,2921,0,1,6}-{2511,2911,0,1,3}-{2521,2917,0,1,7}-{2522,2929,0,1,4}-{2510,2888,0,1,1}-{2502,2890,0,1,3}-{2520,2882,0,1,7}-{2500,2893,0,1,4}-{2548,2887,0,1,1}-{2544,2889,0,1,7}-{2520,2885,0,1,0}-{2498,2883,0,1,6}-{2501,2886,0,1,7}-{2508,2916,0,1,4}-{2502,2888,0,1,7}-{2514,2915,0,1,4}-{2520,2908,0,1,6}-{2526,2943,0,1,4}-{2525,2893,0,1,4}-{2521,2896,0,1,1}-{2515,2944,0,1,4}-" + }, + { + "npc_id": "5073", + "loc_data": "{2588,2879,0,1,6}-{2603,2892,0,1,1}-{2604,2892,0,1,6}-{2611,2917,0,1,6}-{2611,2916,0,1,3}-{2613,2894,0,1,1}-{2614,2892,0,1,6}-{2608,2927,0,1,2}-{2612,2934,0,1,4}-{2609,2930,0,1,0}-{2594,2929,0,1,2}-{2616,2922,0,1,4}-{2593,2911,0,1,1}-{2617,2911,0,1,1}-{2602,2900,0,1,3}-{2601,2886,0,1,6}-{2587,2897,0,1,4}-{2612,2936,0,1,3}-{2606,2929,0,1,5}-{2615,2930,0,1,3}-{2576,2896,0,1,1}-{2593,2880,0,1,7}-{2583,2886,0,1,5}-{2609,2897,0,1,3}-{2608,2905,0,1,2}-{2606,2917,0,1,4}-{2604,2918,0,1,4}-{2613,2890,0,1,5}-{2608,2921,0,1,6}-{2604,2886,0,1,6}-{2602,2920,0,1,1}-{2607,2932,0,1,6}-{2578,2885,0,1,1}-" + }, + { + "npc_id": "5074", + "loc_data": "{2727,3763,0,1,1}-{2724,3765,0,1,2}-{2725,3768,0,1,2}-{2730,3768,0,1,2}-{2731,3764,0,1,2}-{2731,3763,0,1,2}-{2731,3765,0,1,2}-{2733,3765,0,1,2}-{2709,3823,1,1,2}-{2713,3823,1,1,2}-{2709,3825,1,1,2}-{2710,3826,1,1,2}-{2709,3825,1,1,2}-" + }, + { + "npc_id": "5075", + "loc_data": "{3409,3078,0,1,5}-{3410,3075,0,1,5}-{3411,3079,0,1,5}-{3408,3076,0,1,5}-{3414,3077,0,1,5}-{3402,3140,0,1,2}-{3402,3138,0,1,3}-{3404,3139,0,1,5}-{3400,3142,0,1,6}-{3405,3143,0,1,5}-" + }, + { + "npc_id": "5076", + "loc_data": "{2310,3581,0,1,2}-{2314,3580,0,1,2}-{2311,3583,0,1,2}-{2304,3588,0,1,6}-{2305,3601,0,1,7}-{2341,3610,0,1,7}-{2344,3606,0,1,3}-{2340,3603,0,1,4}-{2348,3591,0,1,1}-{2356,3595,0,1,6}-{2361,3595,0,1,1}-{2359,3589,0,1,4}-{2346,3585,0,1,6}-{2360,3584,0,1,7}-" + }, + { + "npc_id": "5077", + "loc_data": "{2560,2911,0,0,0}-{2561,2914,0,0,0}-{2560,2909,0,0,0}-{2561,2916,0,0,0}-{2467,2879,0,0,0}-{2484,2877,0,0,0}-{2485,2876,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2456,2832,0,0,0}-{2494,2940,0,0,0}-{2495,2940,0,0,0}-{2512,2864,0,0,0}-{2558,2911,0,0,0}-{2558,2913,0,0,0}-{2556,2913,0,0,0}-{2557,2912,0,0,0}-{2559,2910,0,0,0}-{2559,2909,0,0,0}-{2559,2917,0,0,0}-{2558,2914,0,0,0}-{2559,2915,0,0,0}-{2558,2916,0,0,0}-{2559,2932,0,0,0}-{2559,2928,0,0,0}-{2558,2928,0,0,0}-{2558,2931,0,0,0}-{2558,2929,0,0,0}-{2556,2911,0,0,0}-{2557,2911,0,0,0}-{2556,2910,0,0,0}-{2556,2912,0,0,0}-{2557,2919,0,0,0}-{2556,2914,0,0,0}-{2556,2913,0,0,0}-{2556,2918,0,0,0}-{2556,2929,0,0,0}-{2557,2933,0,0,0}-{2556,2932,0,0,0}-{2557,2932,0,0,0}-{2557,2928,0,0,0}-{2559,2934,0,0,0}-{2554,2911,0,0,0}-{2555,2911,0,0,0}-{2554,2912,0,0,0}-{2555,2915,0,0,0}-{2555,2919,0,0,0}-{2554,2919,0,0,0}-{2554,2913,0,0,0}-{2556,2915,0,0,0}-{2555,2931,0,0,0}-{2554,2933,0,0,0}-{2555,2934,0,0,0}-{2555,2933,0,0,0}-{2555,2932,0,0,0}-{2554,2935,0,0,0}-{2554,2934,0,0,0}-{2556,2936,0,0,0}-{2558,2936,0,0,0}-{2554,2936,0,0,0}-{2553,2935,0,0,0}-{2556,2933,0,0,0}-{2558,2938,0,0,0}-{2556,2938,0,0,0}-{2557,2937,0,0,0}-{2557,2917,0,0,0}-{2555,2917,0,0,0}-{2555,2912,0,0,0}-{2555,2935,0,0,0}-{2547,2940,0,0,0}-{2546,2939,0,0,0}-{2548,2939,0,0,0}-{2557,2930,0,0,0}-{2556,2929,0,0,0}-{2546,2941,0,0,0}-{2548,2941,0,0,0}-{2557,2931,0,0,0}-{2556,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2935,0,0,0}-{2559,2930,0,0,0}-{2547,2940,0,0,0}-{2509,2910,0,0,0}-{2507,2910,0,0,0}-{2509,2908,0,0,0}-{2508,2908,0,0,0}-{2508,2909,0,0,0}-{2510,2908,0,0,0}-{2506,2906,0,0,0}-{2508,2906,0,0,0}-{2507,2906,0,0,0}-{2507,2905,0,0,0}-{2509,2905,0,0,0}-{2505,2907,0,0,0}-{2504,2908,0,0,0}-{2511,2908,0,0,0}-{2504,2906,0,0,0}-{2508,2907,0,0,0}-{2509,2906,0,0,0}-{2508,2909,0,0,0}-{2506,2906,0,0,0}-{2510,2907,0,0,0}-{2503,2907,0,0,0}-{2509,2908,0,0,0}-{2507,2908,0,0,0}-{2508,2906,0,0,0}-{2505,2907,0,0,0}-{2507,2905,0,0,0}-{2506,2904,0,0,0}-{2505,2903,0,0,0}-{2508,2902,0,0,0}-{2504,2901,0,0,0}-{2506,2907,0,0,0}-{2507,2906,0,0,0}-{2524,2898,0,0,0}-{2524,2896,0,0,0}-{2525,2897,0,0,0}-{2521,2895,0,0,0}-{2525,2895,0,0,0}-{2507,2903,0,0,0}-{2526,2898,0,0,0}-{2507,2889,0,0,0}-{2506,2903,0,0,0}-{2503,2905,0,0,0}-{2507,2905,0,0,0}-{2511,2908,0,0,0}-{2507,2887,0,0,0}-{2554,2937,0,0,0}-{2558,2938,0,0,0}-{2557,2937,0,0,0}-{2556,2938,0,0,0}-{2553,2937,0,0,0}-{2556,2937,0,0,0}-{2553,2935,0,0,0}-{2556,2935,0,0,0}-{2558,2936,0,0,0}-{2555,2936,0,0,0}-{2552,2936,0,0,0}-{2554,2936,0,0,0}-{2552,2934,0,0,0}-{2553,2934,0,0,0}-{2557,2931,0,0,0}-{2559,2932,0,0,0}-{2558,2932,0,0,0}-{2556,2936,0,0,0}-{2553,2936,0,0,0}-{2554,2935,0,0,0}-{2559,2917,0,0,0}-{2556,2916,0,0,0}-{2555,2917,0,0,0}-{2559,2914,0,0,0}-{2557,2915,0,0,0}-{2557,2912,0,0,0}-{2559,2912,0,0,0}-{2558,2913,0,0,0}-{2555,2911,0,0,0}-{2554,2910,0,0,0}-{2559,2916,0,0,0}-{2557,2932,0,0,0}-{2557,2909,0,0,0}-{2559,2913,0,0,0}-{2552,2935,0,0,0}-{2559,2909,0,0,0}-{2557,2914,0,0,0}-{2555,2915,0,0,0}-{2527,2896,0,0,0}-{2524,2895,0,0,0}-{2524,2894,0,0,0}-{2525,2896,0,0,0}-{2524,2896,0,0,0}-{2523,2893,0,0,0}-{2523,2896,0,0,0}-{2523,2897,0,0,0}-{2521,2895,0,0,0}-{2521,2893,0,0,0}-{2526,2895,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2506,2886,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2504,2903,0,0,0}-{2505,2907,0,0,0}-{2504,2906,0,0,0}-{2505,2905,0,0,0}-{2506,2907,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2903,0,0,0}-{2507,2906,0,0,0}-{2507,2907,0,0,0}-{2505,2893,0,0,0}-{2504,2892,0,0,0}-{2508,2909,0,0,0}-{2502,2891,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2508,2891,0,0,0}-{2507,2891,0,0,0}-{2507,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2505,2888,0,0,0}-{2506,2909,0,0,0}-{2507,2887,0,0,0}-{2505,2887,0,0,0}-{2506,2887,0,0,0}-{2508,2890,0,0,0}-{2504,2894,0,0,0}-{2502,2891,0,0,0}-{2524,2899,0,0,0}-{2524,2898,0,0,0}-{2526,2899,0,0,0}-{2526,2895,0,0,0}-{2526,2900,0,0,0}-{2524,2900,0,0,0}-{2525,2900,0,0,0}-{2525,2899,0,0,0}-{2524,2893,0,0,0}-{2522,2894,0,0,0}-{2521,2895,0,0,0}-{2523,2895,0,0,0}-{2524,2895,0,0,0}-{2506,2886,0,0,0}-{2521,2893,0,0,0}-{2526,2898,0,0,0}-{2524,2899,0,0,0}-{2523,2896,0,0,0}-{2522,2894,0,0,0}-{2524,2901,0,0,0}-{2524,2900,0,0,0}-{2523,2895,0,0,0}-{2526,2901,0,0,0}-{2525,2896,0,0,0}-{2526,2900,0,0,0}-{2523,2893,0,0,0}-{2525,2900,0,0,0}-{2524,2898,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2524,2899,0,0,0}-{2508,2904,0,0,0}-{2507,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2907,0,0,0}-{2506,2906,0,0,0}-{2503,2907,0,0,0}-{2503,2905,0,0,0}-{2505,2893,0,0,0}-{2503,2891,0,0,0}-{2506,2891,0,0,0}-{2508,2891,0,0,0}-{2506,2892,0,0,0}-{2506,2909,0,0,0}-{2503,2890,0,0,0}-{2503,2889,0,0,0}-{2504,2889,0,0,0}-{2507,2890,0,0,0}-{2505,2887,0,0,0}-{2505,2888,0,0,0}-{2508,2890,0,0,0}-{2507,2905,0,0,0}-{2503,2890,0,0,0}-{2503,2891,0,0,0}-{2504,2892,0,0,0}-{2504,2894,0,0,0}-{2506,2887,0,0,0}-{2507,2887,0,0,0}-{2507,2891,0,0,0}-{2504,2889,0,0,0}-{2508,2891,0,0,0}-{2505,2888,0,0,0}-{2506,2894,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2506,2886,0,0,0}-{2507,2903,0,0,0}-{2505,2893,0,0,0}-{2506,2891,0,0,0}-{2503,2889,0,0,0}-{2506,2887,0,0,0}-{2506,2892,0,0,0}-{2507,2891,0,0,0}-{2507,2887,0,0,0}-{2507,2890,0,0,0}-{2503,2905,0,0,0}-{2505,2906,0,0,0}-{2506,2905,0,0,0}-{2506,2906,0,0,0}-{2507,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2503,2907,0,0,0}-{2506,2894,0,0,0}-{2507,2908,0,0,0}-{2507,2907,0,0,0}-{2508,2909,0,0,0}-{2505,2888,0,0,0}-{2505,2907,0,0,0}-{2496,2939,0,0,0}-{2497,2938,0,0,0}-{2498,2942,0,0,0}-{2499,2943,0,0,0}-{2500,2942,0,0,0}-{2503,2940,0,0,0}-{2496,2939,0,0,0}-{2502,2939,0,0,0}-{2498,2944,0,0,0}-{2500,2944,0,0,0}-" + }, + { + "npc_id": "5078", + "loc_data": "{2560,2909,0,0,0}-{2560,2933,0,0,0}-{2560,2918,0,0,0}-{2561,2916,0,0,0}-{2560,2915,0,0,0}-{2467,2878,0,0,0}-{2486,2877,0,0,0}-{2495,2938,0,0,0}-{2556,2911,0,0,0}-{2558,2910,0,0,0}-{2558,2909,0,0,0}-{2558,2918,0,0,0}-{2559,2930,0,0,0}-{2557,2915,0,0,0}-{2557,2931,0,0,0}-{2558,2935,0,0,0}-{2555,2936,0,0,0}-{2553,2936,0,0,0}-{2553,2933,0,0,0}-{2553,2934,0,0,0}-{2506,2908,0,0,0}-{2507,2908,0,0,0}-{2510,2906,0,0,0}-{2509,2907,0,0,0}-{2508,2907,0,0,0}-{2506,2904,0,0,0}-{2508,2904,0,0,0}-{2504,2906,0,0,0}-{2506,2909,0,0,0}-{2508,2904,0,0,0}-{2506,2902,0,0,0}-{2504,2903,0,0,0}-{2504,2902,0,0,0}-{2525,2899,0,0,0}-{2523,2897,0,0,0}-{2522,2897,0,0,0}-{2523,2895,0,0,0}-{2521,2893,0,0,0}-{2523,2893,0,0,0}-{2506,2888,0,0,0}-{2509,2889,0,0,0}-{2505,2889,0,0,0}-{2508,2888,0,0,0}-{2506,2887,0,0,0}-{2505,2887,0,0,0}-{2554,2935,0,0,0}-{2552,2935,0,0,0}-{2557,2934,0,0,0}-{2554,2934,0,0,0}-{2558,2933,0,0,0}-{2559,2931,0,0,0}-{2558,2916,0,0,0}-{2557,2917,0,0,0}-{2555,2915,0,0,0}-{2557,2914,0,0,0}-{2554,2912,0,0,0}-{2556,2912,0,0,0}-{2559,2909,0,0,0}-{2558,2909,0,0,0}-{2527,2894,0,0,0}-{2525,2894,0,0,0}-{2523,2895,0,0,0}-{2522,2894,0,0,0}-{2508,2890,0,0,0}-{2508,2891,0,0,0}-{2503,2905,0,0,0}-{2503,2907,0,0,0}-{2506,2902,0,0,0}-{2506,2904,0,0,0}-{2504,2894,0,0,0}-{2506,2894,0,0,0}-{2506,2892,0,0,0}-{2508,2904,0,0,0}-{2506,2891,0,0,0}-{2524,2901,0,0,0}-{2526,2901,0,0,0}-{2524,2894,0,0,0}-{2525,2894,0,0,0}-{2527,2894,0,0,0}-{2523,2896,0,0,0}-{2525,2896,0,0,0}-{2507,2903,0,0,0}-{2507,2905,0,0,0}-{2505,2902,0,0,0}-{2506,2904,0,0,0}-{2505,2907,0,0,0}-{2506,2905,0,0,0}-{2505,2905,0,0,0}-{2504,2906,0,0,0}-{2506,2894,0,0,0}-{2504,2894,0,0,0}-{2507,2891,0,0,0}-{2507,2887,0,0,0}-{2506,2891,0,0,0}-{2506,2907,0,0,0}-{2501,2938,0,0,0}-{2501,2940,0,0,0}-{2503,2938,0,0,0}-" + }, + { + "npc_id": "5079", + "loc_data": "{2323,3538,0,1,6}-{2319,3539,0,1,6}-{2321,3545,0,1,5}-{2324,3556,0,1,1}-{2358,3573,0,1,3}-{2360,3571,0,1,4}-{2342,3549,0,1,0}-{2357,3539,0,1,1}-{2357,3537,0,1,4}-{2357,3549,0,1,6}-{2322,3594,0,1,5}-{2323,3594,0,1,1}-{2323,3602,0,1,1}-{2326,3602,0,1,4}-{2324,3622,0,1,1}-{2326,3630,0,1,6}-{2347,3617,0,1,7}-{2341,3615,0,1,3}-{2339,3596,0,1,3}-{2329,3584,0,1,6}-{2337,3589,0,1,5}-{2371,3560,0,1,3}-" + }, + { + "npc_id": "5080", + "loc_data": "{2562,2927,0,1,4}-{2466,2878,0,1,6}-{2464,2832,0,1,2}-{2490,2877,0,1,1}-{2467,2881,0,1,7}-{2557,2915,0,1,3}-{2557,2921,0,1,1}-{2556,2938,0,1,7}-{2551,2930,0,1,6}-{2558,2912,0,1,4}-{2558,2909,0,1,3}-{2557,2932,0,1,6}-{2556,2935,0,1,1}-{2555,2915,0,1,2}-{2553,2935,0,1,4}-{2559,2918,0,1,4}-{2508,2909,0,1,3}-{2501,2912,0,1,6}-{2507,2907,0,1,1}-{2505,2902,0,1,2}-{2504,2896,0,1,2}-{2522,2895,0,1,3}-{2526,2897,0,1,1}-{2527,2904,0,1,0}-{2507,2889,0,1,4}-{2553,2935,0,1,1}-{2556,2931,0,1,5}-{2555,2915,0,1,6}-{2559,2911,0,1,4}-{2559,2918,0,1,3}-{2527,2896,0,1,4}-{2533,2896,0,1,5}-{2508,2890,0,1,4}-{2522,2895,0,1,2}-{2496,2897,0,1,1}-{2506,2906,0,1,0}-{2506,2894,0,1,3}-{2506,2902,0,1,6}-{2499,2942,0,1,7}-{2496,2936,0,1,4}-{2498,2939,0,1,3}-{2545,2948,0,1,2}-" + }, + { + "npc_id": "5081", + "loc_data": "{2318,3501,0,1,5}-{2316,3502,0,1,6}-{2317,3512,0,1,6}-{2315,3512,0,1,5}-{2312,3504,0,1,1}-{2307,3513,0,1,6}-{2311,3517,0,1,0}-{2320,3517,0,1,1}-{2309,3520,0,1,6}-" + }, + { + "npc_id": "5082", + "loc_data": "{2561,2913,0,1,1}-{2561,2918,0,1,7}-{2560,2915,0,1,1}-{2566,2881,0,1,7}-{2568,2917,0,1,6}-{2568,2918,0,1,4}-{2568,2885,0,1,4}-{2487,2878,0,1,4}-{2554,2894,0,1,4}-{2538,2897,0,1,6}-{2545,2899,0,1,3}-{2559,2908,0,1,6}-{2557,2920,0,1,4}-{2553,2913,0,1,4}-{2544,2910,0,1,2}-{2547,2909,0,1,4}-{2537,2906,0,1,5}-{2525,2914,0,1,3}-{2555,2920,0,1,6}-{2557,2908,0,1,6}-{2539,2920,0,1,1}-{2528,2915,0,1,4}-{2527,2904,0,1,0}-{2518,2901,0,1,5}-{2528,2902,0,1,1}-{2529,2918,0,1,6}-{2557,2915,0,1,7}-{2546,2912,0,1,3}-{2542,2911,0,1,5}-{2548,2910,0,1,6}-{2554,2907,0,1,1}-{2548,2896,0,1,5}-{2545,2895,0,1,6}-{2544,2910,0,1,7}-{2536,2895,0,1,3}-{2538,2902,0,1,6}-{2523,2910,0,1,3}-{2533,2910,0,1,2}-" + }, + { + "npc_id": "5083", + "loc_data": "{2621,3835,1,1,4}-{2708,3772,0,1,0}-{2714,3765,0,1,0}-{2699,3780,0,1,4}-{2694,3785,0,1,4}-{2717,3776,0,1,0}-{2718,3784,0,1,0}-{2723,3786,0,1,0}-{2725,3833,1,1,0}-{2728,3776,0,1,0}-" + }, + { + "npc_id": "5084", + "loc_data": "{2724,3771,0,1,0}-{2738,3769,0,1,0}-{2703,3829,1,1,0}-{2708,3824,1,1,0}-{2714,3826,1,1,0}-{2722,3833,1,1,0}-{2737,3776,0,1,0}-" + }, + { + "npc_id": "5085", + "loc_data": "{2328,3526,0,0,0}-{2313,3528,0,0,0}-{2312,3542,0,0,0}-{2312,3544,0,0,0}-{2323,3543,0,0,0}-{2325,3544,0,0,0}-{2328,3539,0,0,0}-{2311,3564,0,0,0}-{2332,3548,0,0,0}-{2335,3545,0,0,0}-{2320,3580,0,0,0}-{2325,3582,0,0,0}-{2330,3583,0,0,0}-{2355,3573,0,0,0}-{2359,3571,0,0,0}-{2352,3567,0,0,0}-{2361,3554,0,0,0}-{2329,3561,0,0,0}-{2341,3560,0,0,0}-{2344,3543,0,0,0}-{2342,3539,0,0,0}-{2349,3547,0,0,0}-{2352,3545,0,0,0}-{2334,3522,0,0,0}-{2310,3585,0,0,0}-{2324,3584,0,0,0}-{2312,3587,0,0,0}-{2316,3599,0,0,0}-{2324,3599,0,0,0}-{2323,3600,0,0,0}-{2315,3602,0,0,0}-{2309,3605,0,0,0}-{2316,3608,0,0,0}-{2315,3608,0,0,0}-{2325,3614,0,0,0}-{2313,3620,0,0,0}-{2333,3612,0,0,0}-{2331,3608,0,0,0}-{2331,3604,0,0,0}-{2346,3591,0,0,0}-{2369,3573,0,0,0}-{2372,3561,0,0,0}-" + }, + { + "npc_id": "5086", + "loc_data": "{2321,3593,0,1,1}-{2322,3597,0,1,0}-{2309,3603,0,1,5}-{2306,3607,0,1,0}-{2305,3619,0,1,1}-{2309,3618,0,1,6}-{2322,3627,0,1,3}-{2324,3614,0,1,1}-{2323,3614,0,1,1}-{2323,3629,0,1,6}-{2329,3633,0,1,4}-{2328,3634,0,1,3}-{2311,3642,0,1,1}-{2307,3644,0,1,6}-{2318,3645,0,1,1}-{2323,3645,0,1,3}-{2333,3629,0,1,3}-{2340,3641,0,1,7}-{2343,3640,0,1,6}-{2335,3633,0,1,6}-" + }, + { + "npc_id": "5087", + "loc_data": "{2712,3774,0,1,3}-{2715,3772,0,1,3}-{2720,3767,0,1,4}-{2712,3766,0,1,0}-{2718,3767,0,1,7}-{2719,3771,0,1,3}-{2706,3776,0,1,4}-{2716,3778,0,1,4}-" + }, + { + "npc_id": "5088", + "loc_data": "{2586,2883,0,1,6}-{2589,2909,0,1,1}-{2574,2915,0,1,3}-{2572,2928,0,1,3}-{2567,2932,0,1,6}-{2568,2933,0,1,0}-{2585,2884,0,1,5}-{2577,2890,0,1,0}-{2578,2918,0,1,1}-{2568,2934,0,1,1}-{2574,2930,0,1,1}-{2576,2928,0,1,4}-{2580,2889,0,1,1}-{2580,2886,0,1,1}-{2580,2886,0,1,0}-{2572,2900,0,1,4}-{2572,2896,0,1,3}-{2568,2905,0,1,4}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,4}-{2536,2872,0,1,6}-{2536,2872,0,1,3}-{2536,2864,0,1,7}-{2512,2864,0,1,6}-{2512,2864,0,1,1}-{2538,2889,0,1,3}-{2538,2902,0,1,4}-" + }, + { + "npc_id": "5089", + "loc_data": "{2315,3523,0,1,1}-{2315,3530,0,1,6}-{2308,3536,0,1,4}-{2307,3538,0,1,1}-{2325,3551,0,1,3}-{2316,3559,0,1,1}-{2312,3559,0,1,1}-{2310,3572,0,1,3}-{2334,3552,0,1,4}-{2308,3582,0,1,2}-{2363,3579,0,1,1}-{2363,3577,0,1,1}-{2350,3568,0,1,1}-{2352,3561,0,1,1}-{2346,3547,0,1,7}-{2347,3546,0,1,3}-{2363,3541,0,1,1}-{2362,3536,0,1,2}-{2350,3526,0,1,7}-{2350,3521,0,1,4}-{2351,3562,0,1,0}-{2310,3574,0,1,6}-" + }, + { + "npc_id": "5093", + "loc_data": "{2375,3607,0,0,0}-" + }, + { + "npc_id": "5098", + "loc_data": "{2376,3582,0,1,6}-{2378,3582,0,1,1}-{2383,3582,0,1,5}-{2372,3592,0,1,5}-{2370,3587,0,1,3}-{2371,3586,0,1,6}-{2393,3590,0,1,6}-" + }, + { + "npc_id": "5099", + "loc_data": "{2366,3577,0,1,4}-{2365,3597,0,1,5}-{2372,3573,0,1,1}-{2369,3588,0,1,4}-{2377,3584,0,1,1}-{2385,3590,0,1,3}-{2388,3598,0,1,3}-" + }, + { + "npc_id": "5100", + "loc_data": "{2369,3579,0,1,5}-{2373,3575,0,1,1}-{2390,3588,0,1,4}-" + }, + { + "npc_id": "5103", + "loc_data": "{2733,3777,0,1,6}-{2731,3784,0,1,4}-{2735,3790,0,1,6}-{2741,3783,0,1,2}-{2738,3790,0,1,1}-{2731,3783,0,1,1}-{2724,3791,0,1,4}-" + }, + { + "npc_id": "5104", + "loc_data": "{2564,2896,0,0,4}-{2571,2882,0,1,1}-{2573,2900,0,1,3}-{2551,2890,0,0,1}-{2541,2899,0,0,0}-{2552,2910,0,0,0}-{2558,2881,0,0,0}-{2543,2899,0,0,0}-{2545,2925,0,0,0}-{2549,2905,0,1,3}-" + }, + { + "npc_id": "5105", + "loc_data": "{2785,3006,0,1,0}-{2775,2996,0,1,0}-{2780,3017,0,1,0}-{2766,3013,0,1,0}-" + }, + { + "npc_id": "5109", + "loc_data": "{3443,2902,0,1,3}-" + }, + { + "npc_id": "5110", + "loc_data": "{2566,3084,0,1,6}-" + }, + { + "npc_id": "5111", + "loc_data": "{2569,3083,0,1,1}-" + }, + { + "npc_id": "5112", + "loc_data": "{2712,3833,1,1,1}-" + }, + { + "npc_id": "5113", + "loc_data": "{2525,2916,0,1,5}-" + }, + { + "npc_id": "5114", + "loc_data": "{3411,3076,0,1,6}-{3415,3075,0,1,1}-{3406,3093,0,1,3}-{3413,3078,0,1,1}-{3403,3093,0,1,1}-{3412,3081,0,1,3}-{3405,3099,0,1,3}-{3405,3102,0,1,0}-{3406,3098,0,1,4}-{3401,3099,0,1,7}-{3405,3093,0,1,4}-{3404,3085,0,1,5}-{3402,3094,0,1,6}-{3399,3096,0,1,1}-{3405,3088,0,1,3}-{3411,3088,0,1,2}-" + }, + { + "npc_id": "5115", + "loc_data": "{2451,3219,0,1,3}-{2448,3221,0,1,3}-{2476,3236,0,1,3}-{2450,3226,0,1,6}-{2474,3239,0,1,0}-{2470,3240,0,1,2}-{2468,3243,0,1,0}-{2455,3219,0,1,6}-{2449,3225,0,1,7}-{2445,3226,0,1,2}-{2467,3241,0,1,1}-{2452,3221,0,1,3}-{2473,3235,0,1,7}-{2472,3241,0,1,4}-{2475,3237,0,1,3}-" + }, + { + "npc_id": "5116", + "loc_data": "{3291,3675,0,1,6}-{3293,3678,0,1,1}-{3302,3671,0,1,0}-{3299,3666,0,1,5}-{3319,3662,0,1,2}-{3309,3661,0,1,5}-{3321,3661,0,1,1}-{3298,3672,0,1,0}-{3316,3657,0,1,4}-{3302,3667,0,1,1}-{3302,3666,0,1,3}-{3315,3663,0,1,1}-" + }, + { + "npc_id": "5117", + "loc_data": "{3537,3449,0,1,2}-{3535,3447,0,1,6}-{3539,3447,0,1,2}-{3533,3446,0,1,1}-{3549,3439,0,1,4}-{3547,3439,0,1,7}-{3549,3439,0,1,4}-" + }, + { + "npc_id": "5120", + "loc_data": "{2326,3488,0,1,2}-{2325,3483,0,1,2}-{2315,3482,0,1,2}-{2336,3491,0,1,2}-" + }, + { + "npc_id": "5125", + "loc_data": "{2316,3501,0,0,0}-" + }, + { + "npc_id": "5138", + "loc_data": "{2607,3264,0,0,0}-" + }, + { + "npc_id": "5139", + "loc_data": "{2607,3260,0,0,0}-" + }, + { + "npc_id": "5146", + "loc_data": "{2925,3324,0,1,0}-{3205,3263,0,0,0}-" + }, + { + "npc_id": "5147", + "loc_data": "{3207,3270,0,1,0}-" + }, + { + "npc_id": "5148", + "loc_data": "{3229,3347,0,1,1}-{3238,3347,0,1,1}-" + }, + { + "npc_id": "5149", + "loc_data": "{3233,3350,0,1,0}-" + }, + { + "npc_id": "5156", + "loc_data": "{3195,3265,0,1,0}-{3199,3267,0,1,0}-{3203,3262,0,1,5}-" + }, + { + "npc_id": "5157", + "loc_data": "{3194,3265,0,1,0}-{3205,3262,0,1,1}-{3201,3274,0,1,0}-" + }, + { + "npc_id": "5160", + "loc_data": "{3195,3272,0,1,0}-{3195,3268,0,1,0}-{3205,3270,0,1,4}-{3249,3345,0,1,5}-" + }, + { + "npc_id": "5161", + "loc_data": "{2926,3326,0,1,6}-{2924,3324,0,1,1}-{3210,3273,0,1,6}-{3233,3340,0,1,5}-{3241,3344,0,1,3}-" + }, + { + "npc_id": "5164", + "loc_data": "{2410,4456,0,1,1}-{2415,4429,0,1,2}-" + }, + { + "npc_id": "5166", + "loc_data": "{2811,3687,0,1,0}-" + }, + { + "npc_id": "5167", + "loc_data": "{2810,3684,0,1,0}-" + }, + { + "npc_id": "5168", + "loc_data": "{3199,3270,0,1,1}-{2658,4713,0,1,0}-{3209,3259,0,1,3}-{3260,3349,0,1,0}-" + }, + { + "npc_id": "5169", + "loc_data": "{3196,3275,0,1,0}-{2657,4705,0,1,0}-{3234,3345,0,1,0}-" + }, + { + "npc_id": "5170", + "loc_data": "{3052,3516,0,1,0}-" + }, + { + "npc_id": "5171", + "loc_data": "{2860,10054,1,0,0}-" + }, + { + "npc_id": "5173", + "loc_data": "{2769,3611,0,1,7}-{2768,3607,0,1,6}-{2764,3612,0,1,2}-{2763,3608,0,1,6}-{2762,3611,0,1,4}-{2765,3606,0,1,0}-" + }, + { + "npc_id": "5199", + "loc_data": "{3093,3357,0,0,0}-" + }, + { + "npc_id": "5200", + "loc_data": "{3099,3369,0,1,1}-" + }, + { + "npc_id": "5202", + "loc_data": "{3621,3528,0,1,4}-" + }, + { + "npc_id": "5249", + "loc_data": "{3321,3431,0,0,1}-" + }, + { + "npc_id": "5258", + "loc_data": "{2666,2651,0,0,1}-{2668,2651,0,0,1}-" + }, + { + "npc_id": "5260", + "loc_data": "{2665,2651,0,0,1}-{2667,2651,0,0,1}-" + }, + { + "npc_id": "5277", + "loc_data": "{3264,2784,0,0,4}-{3264,2785,0,0,4}-" + }, + { + "npc_id": "5291", + "loc_data": "{3300,2792,0,0,6}-{3282,2807,0,0,6}-{3285,2811,0,0,1}-{3283,2772,0,1,5}-{3281,2771,0,1,7}-{3283,2776,0,1,6}-{3278,2770,0,1,6}-" + }, + { + "npc_id": "5298", + "loc_data": "{2499,3286,0,1,6}-" + }, + { + "npc_id": "5300", + "loc_data": "{2500,3283,0,1,0}-" + }, + { + "npc_id": "5303", + "loc_data": "{2506,3284,0,1,6}-" + }, + { + "npc_id": "5310", + "loc_data": "{3212,3682,0,1,0}-{3228,3680,0,1,6}-{3230,3683,0,1,1}-{3234,3692,0,1,5}-" + }, + { + "npc_id": "5311", + "loc_data": "{3231,3673,0,1,0}-{3225,3677,0,1,3}-{3229,3689,0,1,4}-{3239,3681,0,1,4}-{3236,3694,0,1,6}-" + }, + { + "npc_id": "5312", + "loc_data": "{3210,3667,0,1,4}-{3217,3688,0,1,4}-{3241,3694,0,1,3}-" + }, + { + "npc_id": "5313", + "loc_data": "{3224,3686,0,1,5}-{3243,3688,0,1,1}-{3242,3687,0,1,0}-" + }, + { + "npc_id": "5314", + "loc_data": "{2588,9491,0,1,3}-{2585,9492,0,1,3}-{2586,9491,0,1,5}-{2591,9492,0,1,3}-" + }, + { + "npc_id": "5316", + "loc_data": "{2497,3292,0,1,3}-" + }, + { + "npc_id": "5318", + "loc_data": "{2511,3287,0,1,3}-" + }, + { + "npc_id": "5323", + "loc_data": "{3221,3693,0,1,6}-" + }, + { + "npc_id": "5324", + "loc_data": "{3221,3682,0,1,4}-" + }, + { + "npc_id": "5325", + "loc_data": "{3223,3675,0,1,6}-{3237,3692,0,1,7}-" + }, + { + "npc_id": "5326", + "loc_data": "{3232,3685,0,1,0}-" + }, + { + "npc_id": "5327", + "loc_data": "{3229,3678,0,1,6}-" + }, + { + "npc_id": "5332", + "loc_data": "{3102,3571,0,1,3}-{2855,9571,0,1,0}-{3368,9748,0,1,0}-{3378,9757,0,1,0}-{3383,9746,0,1,0}-{3368,9812,0,1,0}-{3378,9821,0,1,0}-{3383,9810,0,1,0}-{2887,9822,0,1,0}-{2884,9824,0,1,0}-{3008,3595,0,1,0}-{3018,3584,0,1,0}-{3022,3595,0,1,0}-" + }, + { + "npc_id": "5333", + "loc_data": "{3116,3524,0,1,4}-{3106,3548,0,1,2}-{2860,9571,0,1,7}-{3376,9751,0,1,0}-{3380,9756,0,1,0}-{3376,9815,0,1,0}-{3380,9820,0,1,0}-{2886,9816,0,1,0}-{3013,3586,0,1,0}-{3013,3602,0,1,0}-{3025,3597,0,1,0}-" + }, + { + "npc_id": "5334", + "loc_data": "{3107,3528,0,1,1}-{2859,9572,0,1,6}-{3377,9743,0,1,0}-{3377,9754,0,1,0}-{3377,9807,0,1,0}-{3377,9818,0,1,0}-{2885,9819,0,1,0}-{3012,3597,0,1,0}-{3018,3592,0,1,0}-{3018,3599,0,1,0}-{3025,3590,0,1,0}-" + }, + { + "npc_id": "5337", + "loc_data": "{2846,3250,0,1,7}-{2834,3233,0,1,5}-{3100,3563,0,1,1}-{2832,9656,0,1,6}-{3179,3786,0,1,0}-{2885,9825,0,1,0}-{3003,10347,0,1,7}-{3003,10350,0,1,6}-{3003,10357,0,1,4}-{2996,10343,0,1,3}-{2993,10346,0,1,4}-" + }, + { + "npc_id": "5338", + "loc_data": "{2843,3248,0,1,1}-{2840,3240,0,1,3}-{3119,3565,0,1,1}-{2838,9648,0,1,5}-{2831,9654,0,1,1}-{2989,3944,0,1,4}-{2994,10349,0,1,5}-{2998,10350,0,1,0}-{3003,10350,0,1,4}-" + }, + { + "npc_id": "5339", + "loc_data": "{2844,3239,0,1,0}-{3095,3554,0,1,4}-{2831,9655,0,1,7}-" + }, + { + "npc_id": "5340", + "loc_data": "{2848,3246,0,1,3}-{3112,3573,0,1,6}-{2833,9655,0,1,6}-{2982,3947,0,1,0}-" + }, + { + "npc_id": "5341", + "loc_data": "{2840,9633,0,1,1}-{2844,9634,0,1,6}-{2842,9637,0,1,3}-{2641,9889,0,1,0}-{2648,9890,0,1,0}-{2657,9894,0,1,0}-{2658,9888,0,1,0}-" + }, + { + "npc_id": "5342", + "loc_data": "{3276,3659,0,1,0}-" + }, + { + "npc_id": "5343", + "loc_data": "{3282,3662,0,1,0}-" + }, + { + "npc_id": "5345", + "loc_data": "{2501,3294,0,1,3}-" + }, + { + "npc_id": "5346", + "loc_data": "{3279,3654,0,1,0}-" + }, + { + "npc_id": "5347", + "loc_data": "{3288,3650,0,1,0}-" + }, + { + "npc_id": "5348", + "loc_data": "{3290,3653,0,1,0}-" + }, + { + "npc_id": "5349", + "loc_data": "{3099,3354,1,1,3}-" + }, + { + "npc_id": "5350", + "loc_data": "{3116,3367,1,1,3}-" + }, + { + "npc_id": "5351", + "loc_data": "{3110,3358,1,1,1}-" + }, + { + "npc_id": "5352", + "loc_data": "{3121,3359,1,1,4}-" + }, + { + "npc_id": "5355", + "loc_data": "{2873,2950,0,0,0}-{2876,2951,0,0,0}-" + }, + { + "npc_id": "5356", + "loc_data": "{2877,2958,0,0,0}-" + }, + { + "npc_id": "5357", + "loc_data": "{2876,2958,0,0,0}-" + }, + { + "npc_id": "5358", + "loc_data": "{2874,2954,0,0,6}-" + }, + { + "npc_id": "5359", + "loc_data": "{2627,5093,0,1,0}-{2629,5061,0,1,0}-{2638,5090,0,1,0}-{2651,5105,0,1,0}-{2654,5079,0,1,0}-{2663,5116,0,1,0}-{2683,5059,0,1,0}-{2685,5111,0,1,0}-{2693,5065,0,1,0}-{2693,5089,0,1,0}-{2704,5091,0,1,0}-{2710,5095,0,1,0}-{2712,5076,0,1,0}-{2714,5108,0,1,0}-{2716,5092,0,1,0}-{2719,5071,0,1,0}-{2722,5061,0,1,0}-{2732,5087,0,1,0}-{2739,5078,0,1,0}-{2740,5104,0,1,0}-{2747,5083,0,1,0}-" + }, + { + "npc_id": "5361", + "loc_data": "{3176,5543,0,1,3}-{3191,5542,0,1,4}-{3175,5543,0,1,2}-{3178,5541,0,1,2}-{3187,5542,0,1,3}-{3192,5545,0,1,2}-{3192,5542,0,1,3}-{3179,5549,0,1,6}-{1757,5358,0,1,5}-{1760,5357,0,1,5}-{1755,5352,0,1,5}-{1751,5342,0,1,3}-{1745,5342,0,1,6}-{1739,5342,0,1,3}-{1737,5344,0,1,7}-{1739,5347,0,1,6}-{1740,5350,0,1,5}-{1739,5355,0,1,3}-{1747,5363,0,1,4}-{1749,5363,0,1,4}-{1743,5360,0,1,3}-{1739,5356,0,1,0}-" + }, + { + "npc_id": "5362", + "loc_data": "{1774,5361,0,1,3}-{1776,5350,0,1,3}-{1772,5342,0,1,1}-{1772,5336,0,1,4}-{1778,5328,0,1,7}-{1761,5334,0,1,4}-{1754,5337,0,1,7}-{1749,5331,0,0,1}-" + }, + { + "npc_id": "5363", + "loc_data": "{1781,5340,1,1,5}-{1782,5356,1,1,1}-{1767,5343,1,1,6}-{1761,5338,1,1,3}-{1767,5332,1,1,6}-{1757,5326,1,1,7}-{1784,5329,1,1,1}-" + }, + { + "npc_id": "5364", + "loc_data": "{1765,5344,0,1,3}-" + }, + { + "npc_id": "5365", + "loc_data": "{2354,5191,0,1,4}-{2351,5191,0,1,5}-" + }, + { + "npc_id": "5366", + "loc_data": "{2357,5196,0,1,2}-{2356,5188,0,1,4}-{2343,5195,0,1,6}-{2335,5202,0,1,3}-{2323,5211,0,1,1}-" + }, + { + "npc_id": "5367", + "loc_data": "{3092,3717,0,1,0}-{3105,3721,0,1,0}-{3113,3740,0,1,0}-{2312,5189,0,1,7}-" + }, + { + "npc_id": "5368", + "loc_data": "{3096,3738,0,1,0}-{3102,3716,0,1,0}-{3118,3733,0,1,0}-{3128,3741,0,1,0}-" + }, + { + "npc_id": "5369", + "loc_data": "{2345,5195,0,1,3}-{2323,5227,0,1,5}-{2319,5223,0,1,6}-{2264,5136,0,1,1}-" + }, + { + "npc_id": "5370", + "loc_data": "{2347,5195,0,1,3}-{2323,5225,0,1,3}-{2319,5227,0,1,0}-{2323,5227,0,1,3}-{2318,5227,0,1,0}-{2326,5227,0,1,6}-" + }, + { + "npc_id": "5371", + "loc_data": "{2356,5204,0,1,1}-{2324,5223,0,1,1}-" + }, + { + "npc_id": "5375", + "loc_data": "{2029,5236,0,1,6}-{2031,5235,0,1,1}-{2029,5230,0,1,3}-" + }, + { + "npc_id": "5376", + "loc_data": "{2031,5233,0,1,1}-" + }, + { + "npc_id": "5377", + "loc_data": "{2004,5189,0,1,3}-{2002,5207,0,1,3}-{2009,5199,0,1,5}-{2004,5203,0,1,6}-{1989,5192,0,1,6}-{1988,5187,0,1,6}-" + }, + { + "npc_id": "5378", + "loc_data": "{1993,5192,0,1,5}-{1999,5190,0,1,6}-{1989,5187,0,1,4}-{2008,5205,0,1,4}-" + }, + { + "npc_id": "5379", + "loc_data": "{1952,5192,0,1,5}-{1995,5240,0,1,5}-{2010,5186,0,1,4}-{2023,5190,0,1,1}-" + }, + { + "npc_id": "5380", + "loc_data": "{1952,5192,0,1,2}-{2028,5192,0,1,0}-{2031,5186,0,1,0}-{2027,5194,0,1,0}-{2021,5188,0,1,0}-" + }, + { + "npc_id": "5383", + "loc_data": "{3194,4569,0,0,0}-" + }, + { + "npc_id": "5384", + "loc_data": "{3165,4598,0,0,0}-{3167,4577,0,0,0}-" + }, + { + "npc_id": "5385", + "loc_data": "{3191,4595,1,1,1}-{3191,4601,1,1,2}-" + }, + { + "npc_id": "5386", + "loc_data": "{3143,4595,0,1,1}-{3145,4598,0,1,3}-{3182,4577,0,1,1}-{3187,4578,0,1,0}-{3158,4589,1,1,7}-{3194,4599,1,1,4}-" + }, + { + "npc_id": "5387", + "loc_data": "{3158,4584,0,1,4}-{3159,4577,0,1,6}-{3169,4587,0,1,1}-{3152,4569,1,1,1}-" + }, + { + "npc_id": "5388", + "loc_data": "{3156,4567,2,1,0}-" + }, + { + "npc_id": "5389", + "loc_data": "{3157,4568,0,1,6}-{3185,4562,0,1,3}-" + }, + { + "npc_id": "5390", + "loc_data": "{3182,4562,0,1,3}-{3185,4560,0,1,3}-" + }, + { + "npc_id": "5391", + "loc_data": "{3158,4552,0,1,4}-{3163,4569,0,1,1}-{3177,4549,0,1,3}-{3184,4550,0,1,3}-{3158,4560,1,1,4}-{3174,4566,1,1,1}-{3195,4577,1,1,6}-" + }, + { + "npc_id": "5393", + "loc_data": "{3146,4596,0,1,1}-{3163,4598,0,1,3}-{3155,4591,1,1,3}-" + }, + { + "npc_id": "5394", + "loc_data": "{3167,4589,0,1,6}-{3172,4598,0,1,1}-{2677,9688,0,1,1}-{2676,9679,0,1,4}-{2669,9681,0,1,1}-" + }, + { + "npc_id": "5395", + "loc_data": "{3141,4598,0,1,3}-{3170,4592,0,1,6}-{3144,4589,1,1,3}-" + }, + { + "npc_id": "5396", + "loc_data": "{3145,4583,1,1,0}-" + }, + { + "npc_id": "5397", + "loc_data": "{3155,4583,0,1,3}-{3176,4576,1,1,3}-" + }, + { + "npc_id": "5398", + "loc_data": "{3145,4574,1,1,3}-{3182,4586,1,1,3}-" + }, + { + "npc_id": "5399", + "loc_data": "{3140,4557,2,1,4}-{3188,4578,2,1,1}-" + }, + { + "npc_id": "5400", + "loc_data": "{3175,4579,1,1,6}-{3142,4560,2,1,3}-{3159,4569,2,1,1}-" + }, + { + "npc_id": "5401", + "loc_data": "{3151,4573,1,1,4}-" + }, + { + "npc_id": "5402", + "loc_data": "{3154,4559,0,1,6}-{3171,4571,1,1,6}-{3179,4586,1,1,1}-" + }, + { + "npc_id": "5403", + "loc_data": "{3141,4557,0,1,4}-{3182,4577,2,1,6}-" + }, + { + "npc_id": "5404", + "loc_data": "{3143,4555,0,1,4}-{3144,4549,1,1,4}-{3190,4587,1,1,1}-{3186,4576,2,1,3}-" + }, + { + "npc_id": "5405", + "loc_data": "{3163,4559,0,1,4}-{3146,4565,1,1,3}-{3195,4582,1,1,6}-" + }, + { + "npc_id": "5406", + "loc_data": "{3159,4603,1,1,1}-" + }, + { + "npc_id": "5407", + "loc_data": "{3141,4547,1,1,3}-" + }, + { + "npc_id": "5408", + "loc_data": "{3170,4559,1,1,4}-" + }, + { + "npc_id": "5409", + "loc_data": "{3173,4550,0,1,6}-{3190,4551,0,1,2}-" + }, + { + "npc_id": "5410", + "loc_data": "{3190,4561,0,1,5}-" + }, + { + "npc_id": "5413", + "loc_data": "{3187,5450,0,1,6}-{3177,5453,0,1,6}-" + }, + { + "npc_id": "5414", + "loc_data": "{3176,5454,0,1,3}-{3182,5447,0,1,3}-" + }, + { + "npc_id": "5423", + "loc_data": "{3212,3263,0,0,0}-" + }, + { + "npc_id": "5424", + "loc_data": "{2594,3269,0,0,0}-" + }, + { + "npc_id": "5439", + "loc_data": "{3017,3277,0,0,0}-" + }, + { + "npc_id": "5445", + "loc_data": "{2596,3269,0,1,4}-{2571,3907,0,1,7}-{2571,3907,0,1,3}-{2569,3908,0,1,3}-{2571,3908,0,1,3}-{2572,3906,0,1,6}-" + }, + { + "npc_id": "5463", + "loc_data": "{2324,3809,0,1,0}-{2317,3802,0,1,0}-" + }, + { + "npc_id": "5473", + "loc_data": "{2377,10212,0,1,0}-{2392,10226,0,1,0}-{2407,10228,0,1,0}-{2406,10214,0,1,0}-{2420,10214,0,1,0}-" + }, + { + "npc_id": "5474", + "loc_data": "{2390,10216,0,1,0}-{2417,10228,0,1,0}-{2385,10227,0,1,0}-" + }, + { + "npc_id": "5475", + "loc_data": "{2396,10232,0,1,0}-{2402,10225,0,1,0}-{2420,10221,0,1,0}-" + }, + { + "npc_id": "5478", + "loc_data": "{2407,3803,0,1,3}-" + }, + { + "npc_id": "5479", + "loc_data": "{2407,3803,0,1,3}-" + }, + { + "npc_id": "5480", + "loc_data": "{2406,3803,0,1,3}-" + }, + { + "npc_id": "5481", + "loc_data": "{2644,3709,0,1,0}-" + }, + { + "npc_id": "5482", + "loc_data": "{2422,3781,0,1,0}-" + }, + { + "npc_id": "5483", + "loc_data": "{2395,3796,0,1,2}-" + }, + { + "npc_id": "5484", + "loc_data": "{2418,3816,0,1,3}-" + }, + { + "npc_id": "5485", + "loc_data": "{2393,3796,0,1,2}-" + }, + { + "npc_id": "5486", + "loc_data": "{2396,3805,0,1,3}-" + }, + { + "npc_id": "5487", + "loc_data": "{2417,3815,0,1,3}-" + }, + { + "npc_id": "5488", + "loc_data": "{2416,3799,0,0,1}-" + }, + { + "npc_id": "5489", + "loc_data": "{2372,3801,2,0,3}-" + }, + { + "npc_id": "5490", + "loc_data": "{2364,3800,2,0,4}-" + }, + { + "npc_id": "5491", + "loc_data": "{2387,3800,0,0,3}-{2411,3795,0,0,6}-{2417,3825,0,0,1}-" + }, + { + "npc_id": "5492", + "loc_data": "{2387,3797,0,0,3}-{2414,3825,0,0,1}-{2414,3795,0,0,6}-" + }, + { + "npc_id": "5493", + "loc_data": "{2391,3800,0,1,5}-" + }, + { + "npc_id": "5494", + "loc_data": "{2416,3808,0,1,5}-" + }, + { + "npc_id": "5495", + "loc_data": "{2406,3813,0,1,3}-" + }, + { + "npc_id": "5496", + "loc_data": "{2402,3800,0,1,4}-" + }, + { + "npc_id": "5497", + "loc_data": "{2376,10198,0,1,0}-{2384,10202,0,1,0}-{2390,10207,0,1,0}-{2385,10193,0,1,0}-{2419,10192,0,1,0}-{2412,10200,0,1,0}-" + }, + { + "npc_id": "5498", + "loc_data": "{2412,3818,0,1,0}-{2412,3801,0,1,0}-{2392,3802,0,1,0}-" + }, + { + "npc_id": "5499", + "loc_data": "{2403,3807,0,1,4}-" + }, + { + "npc_id": "5500", + "loc_data": "{2395,3812,0,0,2}-" + }, + { + "npc_id": "5501", + "loc_data": "{2408,3816,0,1,3}-" + }, + { + "npc_id": "5502", + "loc_data": "{2409,3814,0,1,3}-" + }, + { + "npc_id": "5504", + "loc_data": "{2336,3799,0,0,4}-" + }, + { + "npc_id": "5505", + "loc_data": "{2334,3800,0,0,4}-" + }, + { + "npc_id": "5506", + "loc_data": "{2334,3798,0,0,4}-" + }, + { + "npc_id": "5507", + "loc_data": "{2311,3781,0,0,1}-" + }, + { + "npc_id": "5508", + "loc_data": "{2646,3710,0,0,4}-" + }, + { + "npc_id": "5509", + "loc_data": "{2336,3808,0,0,6}-" + }, + { + "npc_id": "5510", + "loc_data": "{2333,3804,0,1,1}-" + }, + { + "npc_id": "5511", + "loc_data": "{2327,3794,0,1,6}-" + }, + { + "npc_id": "5512", + "loc_data": "{2319,3795,0,1,3}-" + }, + { + "npc_id": "5513", + "loc_data": "{2352,3797,0,1,2}-" + }, + { + "npc_id": "5514", + "loc_data": "{2333,3795,1,1,4}-{2337,3831,0,1,0}-{2321,3833,0,1,0}-{2399,3852,0,1,0}-" + }, + { + "npc_id": "5515", + "loc_data": "{2330,3799,1,1,3}-{2327,3858,0,1,0}-{2321,3860,0,1,0}-{2343,3862,0,1,0}-" + }, + { + "npc_id": "5516", + "loc_data": "{2329,3808,1,1,4}-{2347,3890,0,1,0}-{2347,3890,0,1,0}-{2393,3861,0,1,0}-" + }, + { + "npc_id": "5517", + "loc_data": "{2328,3830,0,1,0}-{2335,3894,0,1,0}-{2403,3853,0,1,0}-{2405,3860,0,1,0}-" + }, + { + "npc_id": "5518", + "loc_data": "{2338,3798,0,0,1}-" + }, + { + "npc_id": "5519", + "loc_data": "{2338,3800,0,0,6}-" + }, + { + "npc_id": "5520", + "loc_data": "{2338,3810,0,0,3}-" + }, + { + "npc_id": "5521", + "loc_data": "{2324,3832,0,1,0}-{2350,3859,0,1,0}-{2350,3862,0,1,0}-{2352,3857,0,1,0}-{2375,3892,0,1,0}-{2388,3867,0,1,0}-{2384,3863,0,1,0}-{2387,3859,0,1,0}-" + }, + { + "npc_id": "5522", + "loc_data": "{2362,3893,0,1,0}-{2323,3857,0,1,0}-{2331,3860,0,1,0}-{2390,3861,0,1,0}-{2395,3859,0,1,0}-" + }, + { + "npc_id": "5523", + "loc_data": "{2367,3832,0,1,0}-{2319,3854,0,1,0}-{2324,3891,0,1,0}-{2406,3854,0,1,0}-{2405,3863,0,1,0}-{2400,3856,0,1,0}-" + }, + { + "npc_id": "5524", + "loc_data": "{2340,3830,0,1,0}-{2338,3860,0,1,0}-{2339,3895,0,1,0}-{2411,3886,0,1,0}-{2396,3854,0,1,0}-" + }, + { + "npc_id": "5529", + "loc_data": "{2323,3790,0,1,1}-{2323,3795,0,1,0}-{2322,3795,0,1,0}-{2317,3794,0,1,6}-{2317,3790,0,1,3}-{2321,3792,0,1,6}-" + }, + { + "npc_id": "5531", + "loc_data": "{3323,3138,1,1,6}-" + }, + { + "npc_id": "5532", + "loc_data": "{3320,3138,0,1,3}-" + }, + { + "npc_id": "5533", + "loc_data": "{2904,5460,0,1,4}-" + }, + { + "npc_id": "5534", + "loc_data": "{2900,5455,0,1,1}-" + }, + { + "npc_id": "5535", + "loc_data": "{2903,5449,0,1,4}-" + }, + { + "npc_id": "5536", + "loc_data": "{2905,5451,0,1,1}-" + }, + { + "npc_id": "5537", + "loc_data": "{2905,5457,0,1,4}-" + }, + { + "npc_id": "5538", + "loc_data": "{2912,5455,0,1,3}-" + }, + { + "npc_id": "5539", + "loc_data": "{2922,5464,0,1,1}-" + }, + { + "npc_id": "5540", + "loc_data": "{2924,5462,0,1,4}-" + }, + { + "npc_id": "5541", + "loc_data": "{2924,5458,0,1,4}-" + }, + { + "npc_id": "5542", + "loc_data": "{2930,5458,0,1,1}-" + }, + { + "npc_id": "5544", + "loc_data": "{2931,5469,0,1,3}-" + }, + { + "npc_id": "5545", + "loc_data": "{2925,5471,0,1,1}-" + }, + { + "npc_id": "5546", + "loc_data": "{2931,5475,0,1,1}-" + }, + { + "npc_id": "5547", + "loc_data": "{2907,5486,0,1,6}-" + }, + { + "npc_id": "5548", + "loc_data": "{2907,5492,0,1,6}-" + }, + { + "npc_id": "5549", + "loc_data": "{2910,5491,0,1,6}-" + }, + { + "npc_id": "5550", + "loc_data": "{2915,5483,0,1,1}-" + }, + { + "npc_id": "5551", + "loc_data": "{2922,5486,0,1,4}-" + }, + { + "npc_id": "5552", + "loc_data": "{2921,5493,0,1,4}-" + }, + { + "npc_id": "5553", + "loc_data": "{2899,5468,0,1,4}-" + }, + { + "npc_id": "5554", + "loc_data": "{2894,5470,0,1,3}-" + }, + { + "npc_id": "5555", + "loc_data": "{2898,5478,0,1,4}-" + }, + { + "npc_id": "5556", + "loc_data": "{2896,5482,0,1,3}-" + }, + { + "npc_id": "5557", + "loc_data": "{2894,5483,0,1,4}-" + }, + { + "npc_id": "5558", + "loc_data": "{2894,5485,0,1,3}-" + }, + { + "npc_id": "5562", + "loc_data": "{2913,5462,0,0,0}-{2900,5458,0,0,0}-{2908,5455,0,0,0}-{2910,5455,0,0,0}-{2913,5452,0,0,0}-{2914,5453,0,0,0}-" + }, + { + "npc_id": "5563", + "loc_data": "{2911,5474,0,0,0}-" + }, + { + "npc_id": "5574", + "loc_data": "{2836,3066,0,0,0}-" + }, + { + "npc_id": "5576", + "loc_data": "{3072,3336,0,0,0}-{3063,3348,0,0,0}-" + }, + { + "npc_id": "5608", + "loc_data": "{3786,2827,2,0,0}-" + }, + { + "npc_id": "5609", + "loc_data": "{3786,2824,1,0,0}-" + }, + { + "npc_id": "5610", + "loc_data": "{3785,2827,0,0,0}-" + }, + { + "npc_id": "5611", + "loc_data": "{3784,2824,0,0,0}-" + }, + { + "npc_id": "5614", + "loc_data": "{3779,2826,0,0,0}-{3789,2817,0,0,0}-{3794,2821,0,0,0}-{3808,2823,0,0,0}-{3812,2861,0,0,0}-{3813,2822,0,0,0}-{3818,2862,0,0,0}-{3819,2870,0,0,0}-{3821,2824,0,0,0}-{3831,2828,0,0,0}-" + }, + { + "npc_id": "5627", + "loc_data": "{3808,2845,0,0,0}-{3812,2846,0,0,0}-{3815,2844,0,0,3}-{3819,2839,0,0,0}-{3819,2851,0,0,0}-{3819,2838,1,0,0}-{3823,2836,1,0,0}-{3823,2846,1,0,0}-" + }, + { + "npc_id": "5628", + "loc_data": "{3810,2843,0,0,0}-{3813,2844,0,0,0}-{3821,2837,0,0,0}-{3820,2840,1,0,0}-{3820,2851,1,0,0}-{3823,2839,1,0,0}-" + }, + { + "npc_id": "5629", + "loc_data": "{3811,2826,0,0,0}-" + }, + { + "npc_id": "5631", + "loc_data": "{3827,2833,0,0,0}-" + }, + { + "npc_id": "5633", + "loc_data": "{3815,2859,0,0,0}-" + }, + { + "npc_id": "5634", + "loc_data": "{3793,2851,0,0,0}-" + }, + { + "npc_id": "5635", + "loc_data": "{3784,2843,0,0,0}-" + }, + { + "npc_id": "5637", + "loc_data": "{3803,2843,0,0,0}-" + }, + { + "npc_id": "5638", + "loc_data": "{3810,2833,0,0,0}-" + }, + { + "npc_id": "5639", + "loc_data": "{3824,2857,0,0,0}-" + }, + { + "npc_id": "5640", + "loc_data": "{3817,2824,0,0,0}-" + }, + { + "npc_id": "5641", + "loc_data": "{3799,2855,0,0,0}-" + }, + { + "npc_id": "5642", + "loc_data": "{3796,2858,0,0,0}-" + }, + { + "npc_id": "5644", + "loc_data": "{3795,2845,0,0,3}-" + }, + { + "npc_id": "5646", + "loc_data": "{3792,2830,0,0,0}-" + }, + { + "npc_id": "5647", + "loc_data": "{3803,2872,2,0,0}-" + }, + { + "npc_id": "5650", + "loc_data": "{3800,2835,0,0,0}-" + }, + { + "npc_id": "5656", + "loc_data": "{3794,2873,1,0,0}-" + }, + { + "npc_id": "5658", + "loc_data": "{3804,2873,1,0,0}-" + }, + { + "npc_id": "5659", + "loc_data": "{3805,2874,2,0,0}-" + }, + { + "npc_id": "5661", + "loc_data": "{3794,2874,3,0,0}-" + }, + { + "npc_id": "5665", + "loc_data": "{3803,2848,0,0,0}-" + }, + { + "npc_id": "5668", + "loc_data": "{3787,2824,0,0,0}-" + }, + { + "npc_id": "5669", + "loc_data": "{3787,2827,0,0,0}-" + }, + { + "npc_id": "5670", + "loc_data": "{3786,2827,1,0,0}-" + }, + { + "npc_id": "5671", + "loc_data": "{3788,2826,1,0,0}-" + }, + { + "npc_id": "5672", + "loc_data": "{3788,2825,2,0,0}-" + }, + { + "npc_id": "5748", + "loc_data": "{3429,3276,0,0,0}-{3440,3281,0,0,0}-{3437,3272,0,0,0}-{3440,3271,0,0,0}-{3443,3272,0,0,0}-{3445,3279,0,0,0}-{3445,3274,0,0,0}-{3434,3417,0,0,0}-{3431,3415,0,0,0}-{3440,3410,0,0,0}-{3424,3409,0,0,0}-{3425,3410,0,0,0}-{3425,3407,0,0,0}-{3483,3449,0,0,0}-{3490,3444,0,0,0}-{3479,3434,0,0,0}-{3479,3430,0,0,0}-" + }, + { + "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": "6029", + "loc_data": "{3474,3475,0,1,0}-" + }, + { + "npc_id": "6030", + "loc_data": "{3496,3476,0,1,2}-" + }, + { + "npc_id": "6031", + "loc_data": "{3511,3482,0,1,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,1,0}-" + }, + { + "npc_id": "6037", + "loc_data": "{3499,3477,0,1,3}-" + }, + { + "npc_id": "6038", + "loc_data": "{3490,3472,1,1,0}-" + }, + { + "npc_id": "6039", + "loc_data": "{3483,3495,0,1,4}-" + }, + { + "npc_id": "6040", + "loc_data": "{3498,3472,1,1,0}-" + }, + { + "npc_id": "6041", + "loc_data": "{3479,3492,0,1,4}-" + }, + { + "npc_id": "6042", + "loc_data": "{3480,3499,0,1,4}-" + }, + { + "npc_id": "6043", + "loc_data": "{3490,3473,0,1,0}-" + }, + { + "npc_id": "6044", + "loc_data": "{3505,3491,0,1,0}-" + }, + { + "npc_id": "6045", + "loc_data": "{3479,3498,0,1,0}-" + }, + { + "npc_id": "6046", + "loc_data": "{2835,3509,0,1,1}-{3306,5510,0,1,5}-{3309,5543,0,1,6}-" + }, + { + "npc_id": "6047", + "loc_data": "{2836,3495,0,1,0}-{2837,3499,0,1,0}-{2839,3502,0,1,0}-{2839,3506,0,1,0}-{2842,3504,0,1,0}-{2844,3507,0,1,0}-" + }, + { + "npc_id": "6050", + "loc_data": "{3174,3010,0,1,0}-{3198,3061,0,1,0}-{3199,3016,0,1,0}-{3223,3011,0,1,0}-{3228,3060,0,1,0}-{3251,3059,0,1,0}-{3219,3125,0,1,0}-{3233,3075,0,1,0}-{3250,3126,0,1,0}-{3264,3008,0,1,0}-{3266,3008,0,1,0}-{3285,3066,0,1,0}-{3322,3032,0,1,0}-{3323,3055,0,1,0}-{3325,3010,0,1,0}-{3310,3076,0,1,0}-" + }, + { + "npc_id": "6051", + "loc_data": "{3153,3047,0,1,0}-{3196,3063,0,1,0}-{3199,3036,0,1,0}-{3223,3035,0,1,0}-{3225,3011,0,1,0}-{3215,3092,0,1,0}-{3217,3124,0,1,0}-{3260,3077,0,1,0}-{3266,3010,0,1,0}-{3267,3068,0,1,0}-{3287,3065,0,1,0}-{3287,3067,0,1,0}-{3310,3070,0,1,0}-{3312,3068,0,1,0}-{3320,3054,0,1,0}-{3324,3013,0,1,0}-{3325,3033,0,1,0}-{3283,3084,0,1,0}-" + }, + { + "npc_id": "6052", + "loc_data": "{2761,3863,0,1,0}-{2764,3858,0,1,0}-{2769,3854,0,1,0}-{2769,3861,0,1,0}-" + }, + { + "npc_id": "6055", + "loc_data": "{2563,4348,0,1,0}-{2620,4348,0,1,0}-{2620,4291,0,1,0}-{2563,4291,0,1,0}-{2572,4339,0,1,0}-{2565,4327,0,1,0}-{2575,4315,0,1,0}-{2586,4306,0,1,0}-{2593,4304,0,1,0}-{2605,4312,0,1,0}-{2614,4330,0,1,0}-" + }, + { + "npc_id": "6056", + "loc_data": "{2565,4320,0,1,0}-{2592,4348,0,1,0}-{2596,4346,0,1,0}-{2576,4333,0,1,0}-{2576,4324,0,1,0}-{2612,4333,0,1,0}-{2617,4329,0,1,0}-{2608,4316,0,1,0}-{2593,4295,0,1,0}-{2587,4307,0,1,0}-" + }, + { + "npc_id": "6057", + "loc_data": "{2604,4348,0,1,0}-{2613,4346,0,1,0}-{2582,4347,0,1,0}-{2568,4333,0,1,0}-{2576,4321,0,1,0}-{2571,4302,0,1,0}-{2587,4296,0,1,0}-{2598,4301,0,1,0}-{2612,4303,0,1,0}-{2607,4321,0,1,0}-{2617,4325,0,1,0}-{2617,4346,0,1,0}-" + }, + { + "npc_id": "6058", + "loc_data": "{2585,4343,0,1,0}-{2599,4338,0,1,0}-{2612,4338,0,1,0}-{2573,4329,0,1,0}-{2572,4320,0,1,0}-{2575,4309,0,1,0}-{2587,4302,0,1,0}-{2606,4319,0,1,0}-{2608,4336,0,1,0}-" + }, + { + "npc_id": "6059", + "loc_data": "{2579,4340,0,1,0}-{2578,4323,0,1,0}-{2580,4302,0,1,0}-{2611,4324,0,1,0}-{2594,4338,0,1,0}-" + }, + { + "npc_id": "6060", + "loc_data": "{2589,4341,0,1,0}-{2569,4315,0,1,0}-{2591,4299,0,1,0}-{2611,4330,0,1,0}-" + }, + { + "npc_id": "6065", + "loc_data": "{2612,4348,0,1,0}-{2606,4340,0,1,0}-{2592,4333,0,1,0}-{2579,4336,0,1,0}-{2565,4340,0,1,0}-{2569,4329,0,1,0}-{2574,4311,0,1,0}-{2570,4304,0,1,0}-{2567,4294,0,1,0}-{2604,4305,0,1,0}-{2610,4293,0,1,0}-{2612,4320,0,1,0}-" + }, + { + "npc_id": "6066", + "loc_data": "{2601,4334,0,1,0}-{2586,4335,0,1,0}-{2572,4334,0,1,0}-{2574,4323,0,1,0}-{2579,4295,0,1,0}-{2588,4298,0,1,0}-{2600,4304,0,1,0}-{2612,4312,0,1,0}-{2607,4329,0,1,0}-{2616,4336,0,1,0}-" + }, + { + "npc_id": "6067", + "loc_data": "{2570,4341,0,0,0}-{2613,4298,0,0,0}-" + }, + { + "npc_id": "6070", + "loc_data": "{2589,4313,0,1,6}-" + }, + { + "npc_id": "6072", + "loc_data": "{2426,4442,0,0,0}-" + }, + { + "npc_id": "6073", + "loc_data": "{2594,4319,0,1,6}-{2424,4436,0,0,0}-" + }, + { + "npc_id": "6074", + "loc_data": "{2603,4306,0,1,4}-{2607,4312,0,1,7}-{2610,4319,0,1,6}-{2613,4326,0,1,5}-{2616,4333,0,1,0}-{2619,4340,0,1,6}-{2614,4344,0,1,3}-{2609,4347,0,1,4}-{2605,4347,0,1,4}-{2599,4345,0,1,3}-{2598,4342,0,1,3}-{2597,4339,0,1,3}-{2596,4336,0,1,3}-{2595,4333,0,1,3}-{2594,4330,0,1,4}-{2587,4330,0,1,3}-{2583,4332,0,1,4}-{2579,4335,0,1,3}-{2576,4339,0,1,4}-{2571,4342,0,1,1}-{2569,4332,0,1,4}-{2572,4326,0,1,6}-{2572,4297,0,1,1}-{2585,4291,0,1,3}-{2599,4297,0,1,4}-{2607,4300,0,1,1}-{2614,4301,0,1,6}-{2619,4305,0,1,0}-{2616,4320,0,1,6}-{2613,4327,0,1,0}-{2611,4335,0,1,6}-{2607,4336,0,1,6}-{2581,4322,0,1,1}-{2578,4315,0,1,6}-{2575,4308,0,1,6}-{2572,4301,0,1,4}-{2567,4295,0,1,3}-{2577,4291,0,1,3}-{2587,4298,0,1,3}-{2595,4307,0,1,3}-{2603,4314,0,1,3}-" + }, + { + "npc_id": "6085", + "loc_data": "{3046,3207,1,0,2}-" + }, + { + "npc_id": "6088", + "loc_data": "{2932,3253,0,1,4}-{2936,3249,0,1,2}-{2933,3244,0,1,4}-{2927,3247,0,1,5}-{2925,3256,0,1,0}-" + }, + { + "npc_id": "6089", + "loc_data": "{2932,3249,0,1,0}-{2929,3253,0,1,3}-{2938,3248,0,1,5}-" + }, + { + "npc_id": "6090", + "loc_data": "{2932,3245,0,1,0}-{2934,3246,0,1,3}-" + }, + { + "npc_id": "6101", + "loc_data": "{2932,9811,0,1,0}-" + }, + { + "npc_id": "6102", + "loc_data": "{1762,5329,0,1,1}-" + }, + { + "npc_id": "6104", + "loc_data": "{1751,5326,0,1,1}-" + }, + { + "npc_id": "6105", + "loc_data": "{1767,5355,0,0,6}-" + }, + { + "npc_id": "6108", + "loc_data": "{2854,3385,0,1,0}-" + }, + { + "npc_id": "6109", + "loc_data": "{2526,3215,0,1,6}-{2545,3231,0,1,4}-{2537,3236,0,1,4}-" + }, + { + "npc_id": "6110", + "loc_data": "{2532,3222,0,1,3}-{2533,3209,0,1,4}-" + }, + { + "npc_id": "6112", + "loc_data": "{3238,3247,0,1,0}-" + }, + { + "npc_id": "6113", + "loc_data": "{3232,3280,0,1,4}-{3222,3289,0,1,1}-" + }, + { + "npc_id": "6114", + "loc_data": "{3232,3283,0,1,1}-" + }, + { + "npc_id": "6115", + "loc_data": "{3033,3236,0,1,3}-{3041,3237,0,1,6}-{3035,3234,0,1,7}-{3028,3205,0,1,4}-{3019,3202,0,1,6}-{3026,3203,0,1,1}-" + }, + { + "npc_id": "6116", + "loc_data": "{3030,3244,0,1,0}-{3025,3203,0,1,3}-{3034,3204,0,1,4}-" + }, + { + "npc_id": "6117", + "loc_data": "{1884,5020,0,0,6}-" + }, + { + "npc_id": "6118", + "loc_data": "{2443,3190,0,0,6}-" + }, + { + "npc_id": "6119", + "loc_data": "{2439,3186,0,1,4}-" + }, + { + "npc_id": "6120", + "loc_data": "{2437,3160,1,1,4}-" + }, + { + "npc_id": "6122", + "loc_data": "{2327,9394,0,0,1}-" + }, + { + "npc_id": "6123", + "loc_data": "{2364,9399,0,0,1}-" + }, + { + "npc_id": "6124", + "loc_data": "{2364,9398,0,0,1}-" + }, + { + "npc_id": "6125", + "loc_data": "{2323,9377,0,1,0}-" + }, + { + "npc_id": "6126", + "loc_data": "{2351,9358,0,1,0}-" + }, + { + "npc_id": "6127", + "loc_data": "{2467,3183,0,0,3}-" + }, + { + "npc_id": "6128", + "loc_data": "{2465,3183,0,0,4}-" + }, + { + "npc_id": "6129", + "loc_data": "{2470,3185,0,0,6}-" + }, + { + "npc_id": "6131", + "loc_data": "{1885,5026,0,0,6}-" + }, + { + "npc_id": "6132", + "loc_data": "{1883,5029,0,0,6}-" + }, + { + "npc_id": "6133", + "loc_data": "{1883,5026,0,0,6}-" + }, + { + "npc_id": "6134", + "loc_data": "{1890,5018,0,0,0}-" + }, + { + "npc_id": "6135", + "loc_data": "{3254,3427,0,1,1}-" + }, + { + "npc_id": "6136", + "loc_data": "{3077,3250,0,1,0}-" + }, + { + "npc_id": "6137", + "loc_data": "{2952,3381,0,1,0}-" + }, + { + "npc_id": "6138", + "loc_data": "{2665,3311,0,0,0}-" + }, + { + "npc_id": "6139", + "loc_data": "{2734,3484,0,1,1}-" + }, + { + "npc_id": "6140", + "loc_data": "{2644,2641,0,0,6}-" + }, + { + "npc_id": "6141", + "loc_data": "{2638,2656,0,0,0}-" + }, + { + "npc_id": "6166", + "loc_data": "{1904,4279,0,0,0}-" + }, + { + "npc_id": "6167", + "loc_data": "{1906,4277,0,0,0}-" + }, + { + "npc_id": "6168", + "loc_data": "{1904,4274,0,0,0}-" + }, + { + "npc_id": "6188", + "loc_data": "{1901,4271,0,0,0}-{1909,4271,0,0,0}-" + }, + { + "npc_id": "6189", + "loc_data": "{3016,3516,0,1,7}-" + }, + { + "npc_id": "6190", + "loc_data": "{1906,4270,0,1,0}-" + }, + { + "npc_id": "6191", + "loc_data": "{2739,3577,0,1,0}-{2739,3579,1,1,0}-" + }, + { + "npc_id": "6192", + "loc_data": "{2734,3575,0,1,0}-" + }, + { + "npc_id": "6193", + "loc_data": "{2748,3559,0,1,0}-" + }, + { + "npc_id": "6194", + "loc_data": "{2734,3581,1,1,0}-" + }, + { + "npc_id": "6195", + "loc_data": "{2739,3581,0,1,0}-" + }, + { + "npc_id": "6196", + "loc_data": "{2746,3581,1,1,0}-" + }, + { + "npc_id": "6197", + "loc_data": "{2742,3577,0,1,0}-" + }, + { + "npc_id": "6199", + "loc_data": "{2737,3466,0,1,0}-" + }, + { + "npc_id": "6200", + "loc_data": "{2947,3366,0,0,1}-{2949,3366,0,0,1}-{2946,3366,0,0,1}-{2948,3366,0,0,1}-{2945,3366,0,0,1}-{3010,3353,0,0,1}-{3011,3353,0,0,1}-{3012,3353,0,0,1}-{3013,3353,0,0,1}-{3014,3353,0,0,1}-{3015,3353,0,0,1}-" + }, + { + "npc_id": "6202", + "loc_data": "{2912,3749,0,0,0}-" + }, + { + "npc_id": "6203", + "loc_data": "{2924,5322,2,1,4}-" + }, + { + "npc_id": "6205", + "loc_data": "{2933,5329,2,1,1}-" + }, + { + "npc_id": "6210", + "loc_data": "{2837,5336,2,1,4}-{2876,5326,2,1,4}-{2838,5336,2,1,4}-{2898,5306,2,1,5}-{2927,5340,2,1,3}-{2884,5325,2,1,3}-{2886,5325,2,1,1}-{2919,5345,2,1,5}-" + }, + { + "npc_id": "6211", + "loc_data": "{2920,5353,2,1,4}-{2902,5357,2,1,4}-{2885,5315,2,1,4}-{2898,5313,2,1,2}-{2908,5354,2,1,2}-{2924,5354,2,1,6}-{2925,5352,2,1,3}-{2928,5355,2,1,6}-" + }, + { + "npc_id": "6212", + "loc_data": "{2836,5289,2,1,0}-{2869,5287,2,1,1}-{2858,5267,2,1,6}-{2867,5333,2,1,0}-{2877,5321,2,1,1}-{2872,5318,2,1,2}-{2836,5349,2,1,2}-{2921,5293,1,1,6}-{2894,5286,2,1,7}-{2920,5339,2,1,1}-{2888,5326,2,1,3}-{2896,5312,2,1,4}-{2894,5322,2,1,3}-" + }, + { + "npc_id": "6213", + "loc_data": "{2862,5290,2,1,1}-{2838,5347,2,1,6}-{2872,5318,2,1,4}-{2875,5313,2,1,4}-{2865,5327,2,1,4}-{2843,5352,2,1,4}-{2886,5293,2,1,5}-{2909,5297,2,1,0}-{2919,5293,1,1,4}-{2922,5295,1,1,0}-{2919,5293,1,1,5}-{2894,5353,2,1,0}-{2908,5346,2,1,6}-{2913,5355,2,1,3}-{2916,5345,2,1,1}-{2919,5349,2,1,2}-{2922,5356,2,1,1}-{2927,5354,2,1,6}-{2928,5350,2,1,4}-{2931,5357,2,1,3}-{2911,5338,2,1,6}-" + }, + { + "npc_id": "6214", + "loc_data": "{2862,5292,2,1,7}-{2861,5292,2,1,4}-{2864,5319,2,1,7}-{2828,5337,2,1,6}-{2878,5327,2,1,4}-{2853,5327,2,1,4}-{2854,5345,2,1,2}-{2828,5331,2,1,1}-{2919,5284,1,1,0}-{2895,5309,2,1,6}-{2881,5291,2,1,1}-{2924,5292,1,1,6}-{2918,5281,1,1,1}-{2918,5280,1,1,1}-{2884,5312,2,1,2}-{2883,5320,2,1,0}-{2892,5328,2,1,1}-" + }, + { + "npc_id": "6215", + "loc_data": "{2839,5275,2,1,5}-{2845,5286,2,1,0}-{2832,5288,2,1,6}-{2856,5349,2,1,1}-{2862,5334,2,1,0}-{2862,5337,2,1,4}-{2855,5352,2,1,1}-{2880,5293,2,1,4}-{2926,5345,2,1,7}-{2888,5361,2,1,5}-{2886,5313,2,1,3}-{2881,5320,2,1,3}-{2886,5325,2,1,2}-{2889,5322,2,1,4}-{2919,5359,2,1,0}-{2923,5357,2,1,6}-{2891,5353,2,1,1}-" + }, + { + "npc_id": "6216", + "loc_data": "{2865,5306,2,1,3}-{2867,5304,2,1,6}-{2871,5281,2,1,1}-{2864,5279,2,1,4}-{2871,5312,2,1,3}-{2888,5290,2,1,1}-" + }, + { + "npc_id": "6217", + "loc_data": "{2884,5307,2,1,0}-{2899,5304,2,1,3}-{2915,5270,0,1,5}-" + }, + { + "npc_id": "6218", + "loc_data": "{2856,5267,2,1,7}-{2861,5294,2,1,0}-{2836,5290,2,1,6}-{2858,5266,2,1,1}-{2859,5328,2,1,6}-{2911,5268,0,1,4}-{2916,5269,0,1,1}-{2906,5347,2,1,6}-{2913,5358,2,1,6}-{2887,5318,2,1,4}-{2888,5314,2,1,6}-{2931,5345,2,1,2}-{2903,5353,2,1,4}-{2907,5346,2,1,6}-{2921,5351,2,1,2}-{3044,5347,0,1,2}-{3041,5347,0,1,0}-{3031,5346,0,1,2}-{3023,5346,0,1,6}-{3038,5333,0,1,3}-{3048,5333,0,1,1}-{3054,5345,0,1,3}-{3043,5360,0,1,3}-{3030,5362,0,1,3}-{3040,5341,0,1,4}-" + }, + { + "npc_id": "6219", + "loc_data": "{2915,5340,2,1,6}-" + }, + { + "npc_id": "6220", + "loc_data": "{2846,5286,2,1,5}-{2848,5268,2,1,4}-{2931,5356,2,1,3}-{2937,5351,2,1,3}-" + }, + { + "npc_id": "6221", + "loc_data": "{2906,5354,2,1,7}-{2886,5354,2,1,5}-{2885,5352,2,1,2}-{2901,5362,2,1,1}-{2914,5344,2,1,3}-{2918,5343,2,1,2}-{2918,5361,2,1,0}-{2923,5345,2,1,3}-{2924,5352,2,1,4}-" + }, + { + "npc_id": "6222", + "loc_data": "{2831,5301,2,1,4}-" + }, + { + "npc_id": "6229", + "loc_data": "{2841,5285,2,1,3}-{2841,5289,2,1,1}-{2856,5264,2,1,5}-{2842,5282,2,1,6}-" + }, + { + "npc_id": "6230", + "loc_data": "{2843,5265,2,1,4}-{2851,5273,2,1,3}-{2846,5262,2,1,4}-" + }, + { + "npc_id": "6231", + "loc_data": "{2870,5265,2,1,0}-{2869,5266,2,1,7}-{2855,5273,2,1,2}-{2840,5263,2,1,3}-{2831,5285,2,1,4}-" + }, + { + "npc_id": "6232", + "loc_data": "{2872,5261,2,1,4}-{2863,5308,2,1,1}-{2872,5288,2,1,4}-" + }, + { + "npc_id": "6233", + "loc_data": "{2861,5261,2,1,0}-{2852,5264,2,1,0}-{2865,5263,2,1,6}-" + }, + { + "npc_id": "6234", + "loc_data": "{2839,5293,2,1,0}-{2842,5264,2,1,4}-{2862,5313,2,1,3}-" + }, + { + "npc_id": "6235", + "loc_data": "{2864,5270,2,1,3}-{2859,5303,2,1,4}-{2863,5293,2,1,7}-{2862,5286,2,1,1}-{2869,5314,2,1,3}-" + }, + { + "npc_id": "6236", + "loc_data": "{2854,5271,2,1,6}-{2872,5267,2,1,0}-{2858,5269,2,1,5}-" + }, + { + "npc_id": "6237", + "loc_data": "{2840,5290,2,1,6}-{2841,5269,2,1,7}-{2833,5292,2,1,3}-" + }, + { + "npc_id": "6238", + "loc_data": "{2874,5308,2,1,3}-{2851,5276,2,1,2}-{2864,5268,2,1,2}-" + }, + { + "npc_id": "6239", + "loc_data": "{2824,5216,2,1,7}-{2875,5298,2,1,4}-{2846,5288,2,1,0}-{2845,5281,2,1,1}-" + }, + { + "npc_id": "6240", + "loc_data": "{2837,5269,2,1,2}-{2860,5300,2,1,1}-{2852,5311,2,1,4}-{2879,5286,2,1,5}-" + }, + { + "npc_id": "6241", + "loc_data": "{2863,5272,2,1,6}-{2850,5269,2,1,7}-" + }, + { + "npc_id": "6242", + "loc_data": "{2836,5272,2,1,1}-" + }, + { + "npc_id": "6243", + "loc_data": "{2848,5275,2,1,0}-{2869,5304,2,1,4}-{2850,5292,2,1,1}-{2857,5300,2,1,3}-{2874,5287,2,1,4}-" + }, + { + "npc_id": "6244", + "loc_data": "{2847,5299,2,1,6}-{2844,5283,2,1,6}-" + }, + { + "npc_id": "6245", + "loc_data": "{2861,5299,2,1,4}-{2833,5290,2,1,1}-{2851,5262,2,1,0}-" + }, + { + "npc_id": "6246", + "loc_data": "{2855,5311,2,1,1}-{2872,5299,2,1,0}-{2856,5284,2,1,4}-{2872,5315,2,1,1}-" + }, + { + "npc_id": "6247", + "loc_data": "{2894,5265,0,1,2}-" + }, + { + "npc_id": "6254", + "loc_data": "{2917,5272,0,1,7}-{2898,5295,2,1,4}-{2881,5283,2,1,1}-{2883,5311,2,1,2}-{2894,5301,2,1,3}-{2882,5284,2,1,5}-{2890,5292,2,1,4}-{2896,5296,2,1,2}-{2905,5299,2,1,3}-{2905,5301,2,1,5}-{2921,5283,1,1,1}-{2923,5292,1,1,7}-{2906,5299,0,1,6}-{2910,5297,0,1,6}-{2915,5270,0,1,2}-{2910,5269,0,1,5}-" + }, + { + "npc_id": "6255", + "loc_data": "{2922,5290,1,1,5}-{2880,5304,2,1,1}-{2884,5311,2,1,2}-{2885,5311,2,1,2}-{2895,5310,2,1,1}-{2884,5280,2,1,3}-{2885,5293,2,1,6}-{2886,5291,2,1,6}-{2894,5285,2,1,3}-{2893,5286,2,1,7}-{2905,5292,2,1,4}-{2908,5297,2,1,4}-{2910,5297,2,1,6}-{2897,5308,2,1,4}-{2919,5299,1,1,1}-{2921,5292,1,1,5}-{2924,5288,1,1,0}-{2923,5287,1,1,1}-{2908,5300,0,1,6}-{2907,5294,0,1,6}-{2923,5293,1,1,5}-{2919,5298,1,1,1}-{2922,5291,1,1,6}-{2917,5285,1,1,0}-{2905,5295,0,1,6}-{2887,5312,2,1,1}-{2886,5313,2,1,0}-" + }, + { + "npc_id": "6256", + "loc_data": "{2922,5285,1,1,0}-{2886,5295,2,1,1}-{2891,5300,2,1,1}-{2889,5310,2,1,2}-{2887,5299,2,1,6}-{2891,5297,2,1,3}-{2884,5283,2,1,3}-{2885,5286,2,1,3}-{2886,5294,2,1,1}-{2896,5286,2,1,4}-{2908,5302,2,1,1}-{2919,5290,1,1,1}-{2919,5298,1,1,4}-{2918,5298,1,1,4}-{2911,5298,0,1,6}-{2919,5293,1,1,1}-{2917,5294,1,1,2}-{2923,5284,1,1,1}-{2916,5267,0,1,2}-" + }, + { + "npc_id": "6257", + "loc_data": "{2877,5293,2,1,5}-{2879,5329,2,1,5}-{2913,5267,0,1,6}-{2892,5291,2,1,4}-{2886,5311,2,1,0}-{2888,5305,2,1,3}-{2894,5285,2,1,0}-{2900,5287,2,1,0}-{2905,5302,2,1,1}-{2898,5307,2,1,6}-{2918,5292,1,1,3}-{2924,5286,1,1,0}-{2924,5292,1,1,1}-{2920,5297,1,1,3}-{2924,5285,1,1,1}-{2921,5289,1,1,6}-{2923,5294,1,1,0}-{2922,5285,1,1,3}-{2915,5273,0,1,7}-{2915,5271,0,1,4}-{2895,5313,2,1,6}-" + }, + { + "npc_id": "6258", + "loc_data": "{2918,5289,1,1,6}-{2884,5287,2,1,0}-{2895,5294,2,1,4}-{2897,5307,2,1,7}-{2918,5298,1,1,5}-{2917,5291,1,1,4}-{2916,5269,0,1,2}-" + }, + { + "npc_id": "6259", + "loc_data": "{2890,5304,2,1,7}-{2886,5299,2,1,3}-{2891,5297,2,1,5}-{2890,5291,2,1,3}-{2900,5286,2,1,7}-{2905,5303,2,1,1}-{2918,5282,1,1,6}-{2922,5295,1,1,2}-{2905,5297,0,1,6}-{2920,5291,1,1,5}-{2920,5290,1,1,6}-" + }, + { + "npc_id": "6260", + "loc_data": "{2870,5359,2,1,4}-" + }, + { + "npc_id": "6262", + "loc_data": "{2873,5356,0,1,1}-{2866,5358,2,1,6}-" + }, + { + "npc_id": "6264", + "loc_data": "{2870,5354,2,1,4}-" + }, + { + "npc_id": "6266", + "loc_data": "{2868,5362,2,1,3}-" + }, + { + "npc_id": "6268", + "loc_data": "{2856,5355,2,1,7}-{2838,5358,2,1,2}-{2841,5319,2,1,7}-{2856,5331,2,1,2}-{2854,5325,2,1,2}-{2873,5323,2,1,6}-{2860,5321,2,1,7}-{2868,5334,2,1,0}-{2856,5328,2,1,6}-{2855,5332,2,1,1}-{2845,5339,2,1,5}-{2831,5320,2,1,3}-{2847,5334,2,1,1}-{2858,5349,2,1,2}-{2829,5339,2,1,6}-{2856,5361,2,1,5}-{2839,5360,2,1,6}-" + }, + { + "npc_id": "6269", + "loc_data": "{2846,5351,2,1,3}-{2846,5339,2,1,2}-{2840,5327,2,1,1}-{2836,5345,2,1,7}-{2834,5328,2,1,2}-{2857,5359,2,1,4}-{2846,5357,2,1,3}-" + }, + { + "npc_id": "6270", + "loc_data": "{2845,5336,2,1,2}-{2848,5344,2,1,6}-{2842,5328,2,1,6}-{2844,5349,2,1,2}-{2836,5323,2,1,4}-{2832,5353,2,1,6}-{2848,5358,2,1,7}-" + }, + { + "npc_id": "6271", + "loc_data": "{2840,5330,2,1,0}-{2859,5328,2,1,6}-{2873,5317,2,1,3}-{2862,5332,2,1,2}-{2869,5332,2,1,6}-" + }, + { + "npc_id": "6272", + "loc_data": "{2838,5326,2,1,4}-{2870,5313,2,1,1}-{2865,5332,2,1,0}-{2838,5334,2,1,6}-{2837,5353,2,1,7}-" + }, + { + "npc_id": "6273", + "loc_data": "{2832,5324,2,1,7}-{2864,5325,2,1,2}-{2873,5325,2,1,7}-{2873,5324,2,1,7}-{2865,5336,2,1,6}-{2840,5333,2,1,6}-{2836,5336,2,1,4}-{2838,5354,2,1,6}-" + }, + { + "npc_id": "6274", + "loc_data": "{2872,5307,2,1,5}-{2858,5325,2,1,4}-{2873,5319,2,1,6}-{2867,5333,2,1,6}-{2866,5333,2,1,0}-{2839,5331,2,1,5}-{2837,5355,2,1,6}-" + }, + { + "npc_id": "6275", + "loc_data": "{2865,5340,2,1,1}-{2869,5326,2,1,7}-{2875,5318,2,1,1}-{2875,5314,2,1,1}-{2878,5313,2,1,3}-{2879,5322,2,1,3}-{2859,5317,2,1,6}-{2858,5319,2,1,4}-{2878,5320,2,1,0}-{2863,5337,2,1,7}-{2862,5338,2,1,7}-{2867,5341,2,1,6}-{2852,5312,2,1,2}-" + }, + { + "npc_id": "6276", + "loc_data": "{2856,5360,2,1,7}-{2854,5346,2,1,3}-{2845,5332,2,1,0}-{2845,5344,2,1,3}-{2848,5349,2,1,6}-{2837,5347,2,1,3}-{2835,5324,2,1,2}-{2835,5321,2,1,5}-{2858,5349,2,1,4}-{2830,5339,2,1,3}-{2828,5332,2,1,3}-{2846,5358,2,1,4}-{2855,5356,2,1,7}-" + }, + { + "npc_id": "6277", + "loc_data": "{2843,5341,2,1,3}-{2843,5331,2,1,3}-{2847,5341,2,1,5}-{2852,5350,2,1,1}-{2844,5341,2,1,6}-{2843,5347,2,1,1}-{2836,5325,2,1,6}-{2826,5335,2,1,2}-{2837,5351,2,1,4}-{2845,5353,2,1,3}-{2840,5361,2,1,3}-" + }, + { + "npc_id": "6278", + "loc_data": "{2845,5359,2,1,4}-{2830,5318,2,1,3}-{2848,5342,2,1,0}-{2846,5332,2,1,1}-{2843,5320,2,1,3}-{2838,5329,2,1,1}-{2855,5349,2,1,0}-{2837,5342,2,1,1}-{2827,5331,2,1,6}-{2830,5315,2,1,6}-{2845,5352,2,1,6}-{2839,5360,2,1,6}-{2851,5359,2,1,7}-{2860,5353,2,1,6}-{2792,5280,2,1,6}-" + }, + { + "npc_id": "6279", + "loc_data": "{2857,5315,2,1,6}-{2859,5318,2,1,5}-{2858,5329,2,1,3}-{2879,5326,2,1,4}-" + }, + { + "npc_id": "6280", + "loc_data": "{2869,5332,2,1,6}-{2871,5313,2,1,2}-{2879,5330,2,1,2}-{2877,5327,2,1,0}-" + }, + { + "npc_id": "6281", + "loc_data": "{2865,5316,2,1,2}-{2861,5333,2,1,2}-" + }, + { + "npc_id": "6282", + "loc_data": "{2874,5318,2,1,0}-{2859,5316,2,1,3}-{2878,5329,2,1,2}-{2858,5330,2,1,2}-" + }, + { + "npc_id": "6283", + "loc_data": "{2877,5326,2,1,2}-{2857,5328,2,1,3}-{2861,5315,2,1,7}-" + }, + { + "npc_id": "6346", + "loc_data": "{2799,3186,0,1,7}-{2797,3169,0,0,0}-{2793,3161,0,1,3}-" + }, + { + "npc_id": "6347", + "loc_data": "{2799,3167,0,0,0}-{2787,3188,0,1,1}-" + }, + { + "npc_id": "6348", + "loc_data": "{2793,3158,0,1,3}-{2798,3154,0,1,4}-{2796,3189,0,1,0}-" + }, + { + "npc_id": "6349", + "loc_data": "{2797,3186,0,1,6}-{2793,3161,0,1,0}-" + }, + { + "npc_id": "6350", + "loc_data": "{2798,3182,0,1,7}-{2793,3161,0,1,1}-" + }, + { + "npc_id": "6351", + "loc_data": "{3041,3951,0,1,0}-{3043,3958,0,1,0}-" + }, + { + "npc_id": "6352", + "loc_data": "{3039,3955,0,1,0}-{3043,3951,0,1,0}-" + }, + { + "npc_id": "6353", + "loc_data": "{3040,3955,0,1,0}-{3039,3950,0,1,0}-" + }, + { + "npc_id": "6354", + "loc_data": "{3041,3950,0,1,0}-{3041,3956,0,1,0}-" + }, + { + "npc_id": "6355", + "loc_data": "{3043,3950,0,1,0}-{3043,3956,0,1,0}-" + }, + { + "npc_id": "6356", + "loc_data": "{3041,3953,0,1,0}-" + }, + { + "npc_id": "6357", + "loc_data": "{3359,2969,0,1,3}-" + }, + { + "npc_id": "6362", + "loc_data": "{3270,4856,0,1,3}-" + }, + { + "npc_id": "6363", + "loc_data": "{3190,5523,0,1,3}-{3194,5530,0,1,3}-{3251,5455,0,1,4}-{3258,5456,0,1,6}-{3257,5451,0,1,6}-{3257,5464,0,1,2}-{3256,5473,0,1,2}-{3244,5469,0,1,1}-{3225,5485,0,1,5}-{3218,5482,0,1,7}-{3219,5486,0,1,4}-{3207,5461,0,1,6}-{3208,5458,0,1,3}-{3219,5528,0,1,2}-{3223,5520,0,1,3}-{3225,5511,0,1,0}-{3236,5511,0,1,6}-{3232,5523,0,1,0}-{3239,5530,0,1,4}-{3233,5546,0,1,3}-{3222,5539,0,1,5}-{3269,4829,0,1,3}-{3271,4823,0,1,2}-{3283,4809,0,1,3}-" + }, + { + "npc_id": "6364", + "loc_data": "{3188,5529,0,1,3}-{3194,5531,0,1,1}-{3188,5527,0,1,3}-{3222,5486,0,1,3}-{3221,5482,0,1,4}-{3212,5459,0,1,6}-{3210,5462,0,1,2}-{3234,5510,0,1,6}-{3271,4809,0,1,3}-" + }, + { + "npc_id": "6365", + "loc_data": "{3267,4817,0,1,4}-{3290,4803,0,1,3}-" + }, + { + "npc_id": "6367", + "loc_data": "{3207,5466,0,1,6}-{3223,5462,0,1,1}-{3237,5524,0,1,6}-{3229,5510,0,1,4}-{3276,4808,0,1,3}-{3291,4812,0,1,6}-" + }, + { + "npc_id": "6368", + "loc_data": "{3226,5458,0,1,3}-{3222,5470,0,1,6}-{3238,5541,0,1,1}-{3226,5540,0,1,7}-{3222,5522,0,1,7}-" + }, + { + "npc_id": "6369", + "loc_data": "{3268,4825,0,1,6}-{3268,4809,0,1,4}-{3288,4803,0,1,6}-{3298,4810,0,1,4}-" + }, + { + "npc_id": "6376", + "loc_data": "{2925,2976,0,1,1}-{2909,2978,0,1,5}-" + }, + { + "npc_id": "6377", + "loc_data": "{2920,2973,0,1,6}-{2919,2976,0,1,6}-{2910,2978,0,1,7}-" + }, + { + "npc_id": "6379", + "loc_data": "{2894,2995,0,0,0}-{2909,2970,0,0,0}-{3303,5485,0,1,1}-{3320,5494,0,1,4}-{3313,5490,0,1,4}-{3315,5484,0,1,3}-{3323,5471,0,1,6}-{3319,5471,0,1,4}-{3323,5477,0,1,0}-{3310,5484,0,1,4}-{3313,5488,0,1,6}-{3309,5481,0,1,1}-{3320,5495,0,1,0}-" + }, + { + "npc_id": "6380", + "loc_data": "{2883,2994,0,0,0}-{2882,2994,0,0,0}-{3319,5490,0,1,5}-{3298,5486,0,1,3}-{3312,5480,0,1,4}-{3315,5480,0,1,1}-{3308,5482,0,1,2}-{3321,5477,0,1,0}-{3317,5470,0,1,6}-{3324,5472,0,1,6}-{3311,5482,0,1,0}-" + }, + { + "npc_id": "6383", + "loc_data": "{2888,3001,0,0,0}-{2891,3002,0,0,0}-{2890,2998,0,0,0}-{2887,2994,0,0,0}-{2901,2991,0,0,0}-{2905,2990,0,0,0}-{2886,2989,0,0,0}-{2927,2979,0,0,0}-{2926,2973,0,0,0}-{2919,2973,0,0,0}-{2919,2976,0,0,0}-{2920,2979,0,0,0}-" + }, + { + "npc_id": "6386", + "loc_data": "{2683,3326,0,0,0}-" + }, + { + "npc_id": "6387", + "loc_data": "{3275,4808,0,1,3}-{3276,4808,0,1,4}-" + }, + { + "npc_id": "6388", + "loc_data": "{3369,2999,0,1,7}-{3368,2995,0,1,6}-{3362,2997,0,1,3}-{3361,2988,0,1,2}-{3367,2988,0,1,7}-{3379,2983,0,1,1}-" + }, + { + "npc_id": "6390", + "loc_data": "{1707,4814,0,0,6}-" + }, + { + "npc_id": "6394", + "loc_data": "{2703,5366,0,0,0}-" + }, + { + "npc_id": "6396", + "loc_data": "{2791,4279,0,0,0}-" + }, + { + "npc_id": "6405", + "loc_data": "{2799,4260,0,1,0}-" + }, + { + "npc_id": "6406", + "loc_data": "{2765,4267,0,1,0}-" + }, + { + "npc_id": "6407", + "loc_data": "{2802,4246,0,1,0}-" + }, + { + "npc_id": "6408", + "loc_data": "{2770,4238,0,1,0}-" + }, + { + "npc_id": "6409", + "loc_data": "{2787,4262,0,1,0}-{2792,4275,0,1,0}-" + }, + { + "npc_id": "6410", + "loc_data": "{2766,4257,0,1,0}-" + }, + { + "npc_id": "6416", + "loc_data": "{2801,4258,0,1,0}-" + }, + { + "npc_id": "6417", + "loc_data": "{2765,4264,0,1,0}-{2777,4260,0,1,0}-" + }, + { + "npc_id": "6418", + "loc_data": "{2799,4243,0,1,0}-" + }, + { + "npc_id": "6419", + "loc_data": "{2771,4237,0,1,0}-" + }, + { + "npc_id": "6420", + "loc_data": "{2796,4277,0,1,0}-" + }, + { + "npc_id": "6421", + "loc_data": "{2768,4254,0,1,0}-" + }, + { + "npc_id": "6427", + "loc_data": "{2802,4259,0,1,0}-" + }, + { + "npc_id": "6428", + "loc_data": "{2768,4268,0,1,0}-" + }, + { + "npc_id": "6429", + "loc_data": "{2776,4252,0,1,0}-{2805,4242,0,1,0}-" + }, + { + "npc_id": "6430", + "loc_data": "{2769,4236,0,1,0}-" + }, + { + "npc_id": "6431", + "loc_data": "{2792,4260,0,1,0}-{2796,4275,0,1,0}-" + }, + { + "npc_id": "6432", + "loc_data": "{2766,4255,0,1,0}-" + }, + { + "npc_id": "6438", + "loc_data": "{2790,4254,0,1,0}-{2800,4256,0,1,0}-" + }, + { + "npc_id": "6439", + "loc_data": "{2766,4265,0,1,0}-" + }, + { + "npc_id": "6440", + "loc_data": "{2789,4248,0,1,0}-{2802,4244,0,1,0}-" + }, + { + "npc_id": "6441", + "loc_data": "{2771,4241,0,1,0}-{2777,4252,0,1,0}-" + }, + { + "npc_id": "6442", + "loc_data": "{2795,4273,0,1,0}-" + }, + { + "npc_id": "6443", + "loc_data": "{2767,4252,0,1,0}-{2776,4250,0,1,0}-" + }, + { + "npc_id": "6449", + "loc_data": "{2792,4252,0,1,0}-{2800,4254,0,1,0}-" + }, + { + "npc_id": "6450", + "loc_data": "{2764,4266,0,1,0}-{2781,4261,0,1,0}-" + }, + { + "npc_id": "6451", + "loc_data": "{2802,4243,0,1,0}-" + }, + { + "npc_id": "6452", + "loc_data": "{2772,4235,0,1,0}-" + }, + { + "npc_id": "6453", + "loc_data": "{2797,4272,0,1,0}-" + }, + { + "npc_id": "6454", + "loc_data": "{2765,4253,0,1,0}-{2779,4256,0,1,0}-" + }, + { + "npc_id": "6458", + "loc_data": "{2784,4249,0,1,0}-" + }, + { + "npc_id": "6459", + "loc_data": "{2778,4264,0,1,0}-{2784,4252,0,1,0}-" + }, + { + "npc_id": "6460", + "loc_data": "{2783,4251,0,1,0}-{2789,4264,0,1,0}-{2801,4252,0,1,0}-" + }, + { + "npc_id": "6461", + "loc_data": "{2787,4250,0,1,0}-{2791,4249,0,1,0}-" + }, + { + "npc_id": "6462", + "loc_data": "{2785,4248,0,1,0}-{2786,4251,0,1,0}-{2801,4242,0,1,0}-" + }, + { + "npc_id": "6463", + "loc_data": "{2773,4238,0,1,0}-{2779,4248,0,1,0}-{2785,4250,0,1,0}-" + }, + { + "npc_id": "6464", + "loc_data": "{2796,4267,0,1,0}-" + }, + { + "npc_id": "6465", + "loc_data": "{2764,4251,0,1,0}-" + }, + { + "npc_id": "6482", + "loc_data": "{2801,4260,0,0,0}-" + }, + { + "npc_id": "6483", + "loc_data": "{2804,4246,0,0,0}-" + }, + { + "npc_id": "6484", + "loc_data": "{2795,4276,0,0,0}-" + }, + { + "npc_id": "6485", + "loc_data": "{2766,4250,0,0,0}-" + }, + { + "npc_id": "6486", + "loc_data": "{2774,4236,0,0,0}-" + }, + { + "npc_id": "6487", + "loc_data": "{2765,4268,0,0,0}-" + }, + { + "npc_id": "6488", + "loc_data": "{2784,4255,1,0,0}-" + }, + { + "npc_id": "6489", + "loc_data": "{2787,4266,0,0,0}-" + }, + { + "npc_id": "6498", + "loc_data": "{2796,4256,0,1,0}-" + }, + { + "npc_id": "6499", + "loc_data": "{2793,4247,0,1,0}-" + }, + { + "npc_id": "6500", + "loc_data": "{2793,4265,0,1,0}-" + }, + { + "npc_id": "6501", + "loc_data": "{2772,4256,0,1,0}-" + }, + { + "npc_id": "6502", + "loc_data": "{2775,4247,0,1,0}-" + }, + { + "npc_id": "6503", + "loc_data": "{2775,4265,0,1,0}-" + }, + { + "npc_id": "6521", + "loc_data": "{3161,3475,0,0,6}-" + }, + { + "npc_id": "6522", + "loc_data": "{3168,3475,0,0,6}-" + }, + { + "npc_id": "6523", + "loc_data": "{3173,3498,0,0,1}-" + }, + { + "npc_id": "6524", + "loc_data": "{3156,3481,0,0,1}-" + }, + { + "npc_id": "6525", + "loc_data": "{3175,3481,0,0,1}-" + }, + { + "npc_id": "6526", + "loc_data": "{3152,3490,0,0,1}-" + }, + { + "npc_id": "6527", + "loc_data": "{3154,3498,0,0,1}-" + }, + { + "npc_id": "6528", + "loc_data": "{3165,3491,0,0,1}-" + }, + { + "npc_id": "6529", + "loc_data": "{3164,3488,0,0,6}-" + }, + { + "npc_id": "6530", + "loc_data": "{3164,3491,0,0,1}-" + }, + { + "npc_id": "6531", + "loc_data": "{3165,3488,0,0,6}-" + }, + { + "npc_id": "6532", + "loc_data": "{3163,3489,0,0,3}-" + }, + { + "npc_id": "6533", + "loc_data": "{3166,3489,0,0,4}-" + }, + { + "npc_id": "6534", + "loc_data": "{3166,3490,0,0,4}-" + }, + { + "npc_id": "6535", + "loc_data": "{3163,3490,0,0,3}-" + }, + { + "npc_id": "6538", + "loc_data": "{3189,3690,0,0,3}-" + }, + { + "npc_id": "6731", + "loc_data": "{2655,5615,0,1,4}-" + }, + { + "npc_id": "6750", + "loc_data": "{2577,3078,0,1,3}-" + }, + { + "npc_id": "6753", + "loc_data": "{3381,2816,1,1,0}-{3061,4672,1,1,0}-" + }, + { + "npc_id": "6754", + "loc_data": "{3386,2824,1,1,0}-{3066,4680,1,1,0}-" + }, + { + "npc_id": "6755", + "loc_data": "{3382,2820,1,1,0}-{3062,4676,1,1,0}-" + }, + { + "npc_id": "6756", + "loc_data": "{3384,2822,1,1,0}-{3064,4678,1,1,0}-" + }, + { + "npc_id": "6761", + "loc_data": "{3424,2828,1,1,3}-{3430,2835,1,1,3}-{3436,2830,1,1,3}-" + }, + { + "npc_id": "6762", + "loc_data": "{3420,2829,1,1,3}-{3429,2827,1,1,3}-{3432,2832,1,1,3}-" + }, + { + "npc_id": "6763", + "loc_data": "{3427,2832,1,1,3}-{3432,2828,1,1,3}-{3435,2835,1,1,3}-" + }, + { + "npc_id": "6764", + "loc_data": "{3415,2840,1,1,0}-{3426,2852,1,1,0}-{3439,2857,1,1,0}-" + }, + { + "npc_id": "6765", + "loc_data": "{3418,2851,1,1,0}-{3432,2854,1,1,0}-" + }, + { + "npc_id": "6766", + "loc_data": "{3406,2837,1,1,0}-{3434,2848,1,1,0}-" + }, + { + "npc_id": "6767", + "loc_data": "{3409,2844,1,1,0}-{3421,2855,1,1,0}-" + }, + { + "npc_id": "6768", + "loc_data": "{3414,2844,1,1,0}-{3439,2851,1,1,0}-" + }, + { + "npc_id": "6773", + "loc_data": "{3358,9233,1,1,0}-{3363,9246,1,1,0}-{3369,9233,1,1,0}-{3373,9246,1,1,0}-{3377,9244,1,1,0}-{3381,9225,1,1,0}-{3389,9241,1,1,0}-{3391,9237,1,1,0}-{3391,9230,1,1,0}-{3439,2808,0,1,0}-" + }, + { + "npc_id": "6774", + "loc_data": "{3361,9238,1,1,0}-{3363,9241,1,1,0}-{3373,9241,1,1,0}-{3374,9225,1,1,0}-{3379,9230,1,1,0}-{3383,9232,1,1,0}-{3387,9229,1,1,0}-{3389,9236,1,1,0}-{3435,2808,0,1,2}-" + }, + { + "npc_id": "6776", + "loc_data": "{3441,2810,0,1,0}-{3445,2813,0,1,0}-" + }, + { + "npc_id": "6777", + "loc_data": "{3432,2810,0,1,4}-" + }, + { + "npc_id": "6778", + "loc_data": "{3440,2806,0,1,0}-" + }, + { + "npc_id": "6779", + "loc_data": "{3337,2762,0,1,0}-{3340,2767,0,1,0}-{3345,2763,0,1,0}-{3350,2766,0,1,0}-{3352,2761,0,1,0}-{3356,2769,0,1,0}-{3358,2758,0,1,0}-{3359,2763,0,1,0}-{3365,2769,0,1,0}-{3368,2764,0,1,0}-{3375,2762,0,1,0}-{3392,2759,0,1,2}-{3408,2763,0,1,7}-{3410,2786,0,1,2}-{3420,2778,0,1,6}-{3427,2792,0,1,6}-{3429,2802,0,1,4}-" + }, + { + "npc_id": "6780", + "loc_data": "{3366,9254,1,1,6}-{3398,9232,1,1,0}-{3409,9230,1,1,0}-" + }, + { + "npc_id": "6781", + "loc_data": "{3363,9231,1,1,0}-{3369,9254,1,1,6}-{3370,9226,1,1,0}-{3397,9225,1,1,0}-{3408,9226,1,1,0}-" + }, + { + "npc_id": "6785", + "loc_data": "{3442,9245,1,0,1}-" + }, + { + "npc_id": "6786", + "loc_data": "{3409,2812,1,0,0}-" + }, + { + "npc_id": "6787", + "loc_data": "{3408,2813,1,0,0}-" + }, + { + "npc_id": "6788", + "loc_data": "{3368,9263,1,0,6}-" + }, + { + "npc_id": "6891", + "loc_data": "{2598,3272,0,1,0}-" + }, + { + "npc_id": "6893", + "loc_data": "{2926,3435,0,1,2}-" + }, + { + "npc_id": "6894", + "loc_data": "{2928,3432,0,1,0}-" + }, + { + "npc_id": "6895", + "loc_data": "{2927,3431,0,1,0}-" + }, + { + "npc_id": "6896", + "loc_data": "{2573,3082,0,1,2}-" + }, + { + "npc_id": "6897", + "loc_data": "{2574,3083,0,1,0}-" + }, + { + "npc_id": "6917", + "loc_data": "{2916,3082,0,0,0}-{2917,3089,0,0,0}-{2912,3090,0,0,0}-" + }, + { + "npc_id": "6918", + "loc_data": "{2917,3088,0,1,4}-{2917,3092,0,1,4}-" + }, + { + "npc_id": "6921", + "loc_data": "{2954,3273,0,1,6}-{2974,3448,0,1,4}-{2971,3442,0,1,6}-{2968,3451,0,1,1}-{2967,3439,0,1,4}-{2965,3451,0,1,5}-" + }, + { + "npc_id": "6942", + "loc_data": "{2890,3100,0,1,4}-" + }, + { + "npc_id": "6944", + "loc_data": "{2855,3028,0,0,0}-{2841,3039,0,0,0}-{2838,3031,0,0,0}-{2836,3014,0,0,0}-{2839,3022,0,0,0}-" + }, + { + "npc_id": "6962", + "loc_data": "{2842,5222,0,1,6}-" + }, + { + "npc_id": "6971", + "loc_data": "{2926,3443,0,0,5}-" + }, + { + "npc_id": "6972", + "loc_data": "{2922,3482,0,1,0}-" + }, + { + "npc_id": "6973", + "loc_data": "{2893,3419,0,1,0}-" + }, + { + "npc_id": "6974", + "loc_data": "{2897,3419,0,1,0}-" + }, + { + "npc_id": "6975", + "loc_data": "{2928,3480,0,1,0}-{2932,3485,0,1,0}-" + }, + { + "npc_id": "6976", + "loc_data": "{2897,3432,1,1,0}-{2884,3418,0,1,0}-{2884,3434,0,1,0}-{2902,3449,0,1,0}-" + }, + { + "npc_id": "6977", + "loc_data": "{2890,3435,0,1,0}-" + }, + { + "npc_id": "6978", + "loc_data": "{2896,3437,0,1,0}-{2917,3446,0,1,0}-" + }, + { + "npc_id": "6979", + "loc_data": "{2888,3448,0,1,0}-" + }, + { + "npc_id": "6981", + "loc_data": "{2883,3431,0,1,0}-{2900,3449,0,1,0}-" + }, + { + "npc_id": "6982", + "loc_data": "{2896,3429,0,1,0}-{2918,3485,0,1,0}-" + }, + { + "npc_id": "6983", + "loc_data": "{2908,3441,0,1,0}-{2926,3488,0,1,0}-" + }, + { + "npc_id": "6985", + "loc_data": "{2894,3438,0,1,0}-{2913,3451,0,1,0}-" + }, + { + "npc_id": "6986", + "loc_data": "{2887,3441,0,1,0}-" + }, + { + "npc_id": "6987", + "loc_data": "{2894,3445,0,1,0}-" + }, + { + "npc_id": "6996", + "loc_data": "{3169,3266,0,0,3}-{3173,3274,0,0,3}-{3259,3206,0,0,3}-{3259,3204,0,0,3}-{2689,3598,0,0,4}-" + }, + { + "npc_id": "6997", + "loc_data": "{3002,3257,0,0,0}-{3007,3261,0,0,0}-{3058,3429,0,0,0}-{3059,3420,0,0,0}-{3059,3425,0,0,0}-{3049,3429,0,0,0}-" + }, + { + "npc_id": "7003", + "loc_data": "{2582,9895,0,1,7}-{3230,5493,0,1,1}-{3224,5497,0,1,5}-{3253,5535,0,1,7}-" + }, + { + "npc_id": "7004", + "loc_data": "{2581,9891,0,1,2}-{3225,5495,0,1,4}-{3256,5540,0,1,4}-{3247,5536,0,1,6}-" + }, + { + "npc_id": "7005", + "loc_data": "{2487,2924,0,1,0}-" + }, + { + "npc_id": "7009", + "loc_data": "{3282,3467,0,0,0}-" + }, + { + "npc_id": "7010", + "loc_data": "{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2204,3229,0,1,3}-{2243,3200,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-" + }, + { + "npc_id": "7011", + "loc_data": "{2243,3200,0,1,3}-{2268,3239,0,1,3}-{2261,3260,0,1,3}-{2261,3260,0,1,3}-" + }, + { + "npc_id": "7012", + "loc_data": "{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2226,3143,0,1,3}-{2223,3146,0,1,3}-{2223,3146,0,1,3}-{2180,3201,0,1,3}-{2180,3201,0,1,3}-{2182,3204,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}-{2247,3187,0,1,3}-" + }, + { + "npc_id": "7014", + "loc_data": "{2226,3143,0,1,3}-{2180,3201,0,1,3}-{2288,3147,0,1,3}-{2247,3187,0,1,3}-" + }, + { + "npc_id": "7021", + "loc_data": "{2510,2822,0,1,3}-{2511,2823,0,1,4}-{2509,2825,0,1,6}-" + }, + { + "npc_id": "7022", + "loc_data": "{2503,2821,0,1,1}-{2500,2818,0,1,4}-{2503,2819,0,1,3}-" + }, + { + "npc_id": "7023", + "loc_data": "{2552,2821,0,1,4}-" + }, + { + "npc_id": "7024", + "loc_data": "{2523,2823,0,1,1}-" + }, + { + "npc_id": "7025", + "loc_data": "{2537,2820,0,1,1}-" + }, + { + "npc_id": "7026", + "loc_data": "{2543,2822,0,1,1}-" + }, + { + "npc_id": "7027", + "loc_data": "{2609,2863,0,1,0}-" + }, + { + "npc_id": "7028", + "loc_data": "{2610,2865,0,1,0}-" + }, + { + "npc_id": "7029", + "loc_data": "{2611,2862,0,1,0}-" + }, + { + "npc_id": "7030", + "loc_data": "{2611,2864,0,1,0}-" + }, + { + "npc_id": "7031", + "loc_data": "{2588,2801,0,1,1}-{2566,2820,0,1,5}-{2575,2818,0,1,4}-{2578,2819,0,1,4}-{2587,2819,0,1,7}-{2595,2821,0,1,4}-{2595,2826,0,1,0}-{2562,2819,0,1,6}-{2565,2828,0,1,0}-{2573,2825,0,1,1}-{2578,2820,0,1,6}-{2584,2822,0,1,4}-{2590,2831,0,1,4}-{2594,2823,0,1,4}-{2488,2768,0,1,1}-{2499,2820,0,1,4}-{2511,2819,0,1,3}-{2525,2820,0,1,0}-{2527,2818,0,1,3}-{2542,2819,0,1,4}-{2551,2817,0,1,3}-{2554,2819,0,1,3}-{2559,2819,0,1,4}-{2503,2824,0,1,3}-{2509,2822,0,1,4}-{2518,2825,0,1,7}-{2526,2822,0,1,1}-{2547,2822,0,1,4}-{2557,2826,0,1,4}-{2502,2823,0,1,4}-{2513,2820,0,1,7}-{2527,2819,0,1,4}-{2529,2818,0,1,6}-{2541,2822,0,1,3}-{2549,2817,0,1,2}-{2557,2818,0,1,2}-" + }, + { + "npc_id": "7032", + "loc_data": "{2539,2838,0,1,1}-" + }, + { + "npc_id": "7033", + "loc_data": "{2541,2837,0,1,1}-" + }, + { + "npc_id": "7034", + "loc_data": "{2536,2839,0,1,1}-" + }, + { + "npc_id": "7035", + "loc_data": "{2542,2839,0,1,1}-" + }, + { + "npc_id": "7036", + "loc_data": "{2538,2841,0,1,1}-" + }, + { + "npc_id": "7039", + "loc_data": "{2560,2822,0,1,1}-{2570,2823,0,1,1}-{2582,2824,0,1,6}-{2588,2828,0,1,4}-{2503,2824,0,1,4}-{2518,2824,0,1,4}-{2534,2821,0,1,1}-{2548,2823,0,1,4}-" + }, + { + "npc_id": "7040", + "loc_data": "{2562,2863,0,1,4}-{2617,5539,0,1,4}-" + }, + { + "npc_id": "7041", + "loc_data": "{2562,2863,0,1,4}-{2616,5540,0,1,4}-" + }, + { + "npc_id": "7042", + "loc_data": "{2605,5540,0,1,6}-{2551,2863,0,1,6}-" + }, + { + "npc_id": "7043", + "loc_data": "{2607,5539,0,1,6}-{2553,2862,0,1,6}-" + }, + { + "npc_id": "7044", + "loc_data": "{2467,2933,0,0,4}-{2470,2937,0,0,4}-{2445,2897,0,0,4}-{2444,2894,0,0,4}-" + }, + { + "npc_id": "7045", + "loc_data": "{2460,2922,0,0,4}-{2463,2928,0,0,4}-" + }, + { + "npc_id": "7046", + "loc_data": "{3364,3800,0,0,4}-{3368,3811,0,0,4}-{3347,3814,0,0,4}-{3363,3816,0,0,4}-{2460,2925,0,0,4}-" + }, + { + "npc_id": "7047", + "loc_data": "{2611,5513,0,0,1}-{2557,2836,0,1,1}-" + }, + { + "npc_id": "7048", + "loc_data": "{2599,5513,0,0,0}-{2545,2836,0,0,0}-" + }, + { + "npc_id": "7049", + "loc_data": "{2553,2839,0,0,4}-{2553,2836,0,0,4}-" + }, + { + "npc_id": "7051", + "loc_data": "{2547,2852,0,1,0}-" + }, + { + "npc_id": "7052", + "loc_data": "{2571,2855,0,1,3}-" + }, + { + "npc_id": "7054", + "loc_data": "{2560,2851,0,0,1}-" + }, + { + "npc_id": "7055", + "loc_data": "{2559,2851,0,0,1}-" + }, + { + "npc_id": "7056", + "loc_data": "{2560,2851,0,1,6}-{2613,5528,0,0,6}-" + }, + { + "npc_id": "7057", + "loc_data": "{2596,2845,0,1,0}-" + }, + { + "npc_id": "7062", + "loc_data": "{2571,2840,0,1,4}-" + }, + { + "npc_id": "7065", + "loc_data": "{2621,2857,0,1,3}-" + }, + { + "npc_id": "7066", + "loc_data": "{2619,2856,0,1,3}-" + }, + { + "npc_id": "7067", + "loc_data": "{2572,5519,0,0,6}-{2518,2842,0,1,0}-" + }, + { + "npc_id": "7068", + "loc_data": "{2585,5527,0,0,6}-{2531,2850,0,1,6}-" + }, + { + "npc_id": "7069", + "loc_data": "{2613,5535,0,0,6}-{2559,2858,0,1,0}-" + }, + { + "npc_id": "7070", + "loc_data": "{2581,2866,0,1,6}-" + }, + { + "npc_id": "7071", + "loc_data": "{2598,2864,0,1,6}-" + }, + { + "npc_id": "7072", + "loc_data": "{2517,2840,0,1,6}-" + }, + { + "npc_id": "7073", + "loc_data": "{2533,2848,0,1,6}-" + }, + { + "npc_id": "7074", + "loc_data": "{2556,2858,0,1,6}-" + }, + { + "npc_id": "7075", + "loc_data": "{2583,2865,0,1,6}-" + }, + { + "npc_id": "7076", + "loc_data": "{2596,2865,0,1,6}-" + }, + { + "npc_id": "7077", + "loc_data": "{2617,2856,0,1,3}-" + }, + { + "npc_id": "7078", + "loc_data": "{2451,2857,0,1,0}-{2453,2874,0,1,0}-{2475,2868,0,1,0}-{2477,2832,0,1,0}-{2475,2896,0,1,0}-{2476,2883,0,1,0}-{2502,2838,0,1,0}-" + }, + { + "npc_id": "7079", + "loc_data": "{2451,2849,0,1,0}-{2463,2872,0,1,0}-{2470,2853,0,1,0}-{2487,2837,0,1,0}-{2477,2903,0,1,0}-{2478,2907,0,1,0}-{2501,2851,0,1,0}-" + }, + { + "npc_id": "7080", + "loc_data": "{2562,5530,0,0,6}-{2456,2867,0,1,0}-{2457,2836,0,1,0}-{2484,2873,0,1,0}-{2487,2856,0,1,0}-{2480,2916,0,1,0}-{2492,2888,0,1,0}-{2508,2853,0,1,0}-" + }, + { + "npc_id": "7081", + "loc_data": "{2446,2869,0,1,0}-{2451,2828,0,1,0}-{2467,2860,0,1,0}-{2487,2844,0,1,0}-{2480,2892,0,1,0}-{2484,2917,0,1,0}-{2499,2860,0,1,0}-" + }, + { + "npc_id": "7082", + "loc_data": "{2569,5541,0,0,6}-{2440,2858,0,1,0}-{2459,2859,0,1,0}-{2471,2839,0,1,0}-{2482,2863,0,1,0}-{2483,2883,0,1,0}-{2491,2901,0,1,0}-{2516,2858,0,1,0}-" + }, + { + "npc_id": "7091", + "loc_data": "{2609,2863,0,0,0}-" + }, + { + "npc_id": "7092", + "loc_data": "{2610,2865,0,0,0}-" + }, + { + "npc_id": "7093", + "loc_data": "{2611,2862,0,0,0}-" + }, + { + "npc_id": "7094", + "loc_data": "{2611,2864,0,0,0}-" + }, + { + "npc_id": "7095", + "loc_data": "{2609,2863,0,0,0}-{2610,2862,0,0,0}-" + }, + { + "npc_id": "7096", + "loc_data": "{2610,2865,0,0,0}-{2605,2866,0,0,0}-" + }, + { + "npc_id": "7097", + "loc_data": "{2611,2862,0,0,0}-{2607,2867,0,0,0}-" + }, + { + "npc_id": "7098", + "loc_data": "{2611,2864,0,0,0}-{2608,2863,0,0,0}-" + }, + { + "npc_id": "7099", + "loc_data": "{2585,2844,0,1,3}-" + }, + { + "npc_id": "7100", + "loc_data": "{2586,2840,0,0,4}-" + }, + { + "npc_id": "7101", + "loc_data": "{2585,2842,0,1,1}-" + }, + { + "npc_id": "7102", + "loc_data": "{2576,2840,0,0,0}-{2576,2840,0,0,0}-" + }, + { + "npc_id": "7103", + "loc_data": "{2579,2843,0,0,0}-{2582,2843,0,0,0}-{2579,2843,0,0,0}-{2582,2843,0,0,0}-" + }, + { + "npc_id": "7104", + "loc_data": "{2603,5529,0,0,4}-{2549,2852,0,1,4}-" + }, + { + "npc_id": "7105", + "loc_data": "{2612,9484,0,1,1}-{2611,9484,0,1,0}-{2614,9484,0,1,6}-{2611,9486,0,1,1}-{3111,9929,0,1,7}-{3106,9941,0,1,1}-{3111,9935,0,1,4}-{3114,9931,0,1,1}-" + }, + { + "npc_id": "7106", + "loc_data": "{3131,9930,0,1,4}-" + }, + { + "npc_id": "7107", + "loc_data": "{3130,9928,0,1,0}-{3131,9931,0,1,4}-" + }, + { + "npc_id": "7109", + "loc_data": "{3127,9932,0,1,1}-" + }, + { + "npc_id": "7110", + "loc_data": "{3121,9929,0,1,4}-" + }, + { + "npc_id": "7112", + "loc_data": "{3130,9934,0,1,3}-" + }, + { + "npc_id": "7113", + "loc_data": "{3127,9932,0,1,4}-" + }, + { + "npc_id": "7114", + "loc_data": "{3132,9921,0,0,0}-{3132,9935,0,1,1}-" + }, + { + "npc_id": "7115", + "loc_data": "{3050,3259,0,0,0}-" + }, + { + "npc_id": "7121", + "loc_data": "{2189,3148,0,1,4}-" + }, + { + "npc_id": "7125", + "loc_data": "{1605,4702,0,1,0}-{1649,4730,0,1,0}-" + }, + { + "npc_id": "7126", + "loc_data": "{1605,4720,0,1,0}-{1658,4705,0,1,0}-{1633,4678,0,1,0}-" + }, + { + "npc_id": "7127", + "loc_data": "{1631,4730,0,1,0}-{1658,4687,0,1,0}-{1615,4678,0,1,0}-" + }, + { + "npc_id": "7128", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7129", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7130", + "loc_data": "{1625,4689,0,1,0}-{1614,4704,0,1,0}-{1631,4719,0,1,0}-{1651,4705,0,1,0}-" + }, + { + "npc_id": "7131", + "loc_data": "{1634,4699,0,1,0}-" + }, + { + "npc_id": "7132", + "loc_data": "{1630,4705,0,0,0}-" + }, + { + "npc_id": "7138", + "loc_data": "{3190,5523,0,1,2}-{3195,5527,0,1,6}-{3245,5462,0,1,7}-{3257,5454,0,1,3}-{3255,5461,0,1,1}-{3251,5473,0,1,3}-{3213,5472,0,1,3}-{3210,5469,0,1,5}-{3221,5468,0,1,0}-{3223,5465,0,1,0}-{3224,5540,0,1,0}-{3240,5541,0,1,6}-{3239,5529,0,1,6}-{3233,5524,0,1,4}-{3237,5512,0,1,6}-{3225,5510,0,1,1}-{3222,5524,0,1,1}-{3225,5540,0,1,7}-{3218,5537,0,1,3}-" + }, + { + "npc_id": "7139", + "loc_data": "{3220,5463,0,1,7}-" + }, + { + "npc_id": "7142", + "loc_data": "{3082,4246,0,1,4}-" + }, + { + "npc_id": "7143", + "loc_data": "{3082,3459,0,0,0}-" + }, + { + "npc_id": "7144", + "loc_data": "{3085,4230,0,1,3}-" + }, + { + "npc_id": "7145", + "loc_data": "{3081,4252,0,1,7}-" + }, + { + "npc_id": "7146", + "loc_data": "{3078,4240,0,1,4}-" + }, + { + "npc_id": "7147", + "loc_data": "{3079,4245,0,1,4}-" + }, + { + "npc_id": "7148", + "loc_data": "{3077,4229,0,1,4}-" + }, + { + "npc_id": "7149", + "loc_data": "{3085,4235,0,1,4}-" + }, + { + "npc_id": "7150", + "loc_data": "{3085,4240,0,1,3}-" + }, + { + "npc_id": "7151", + "loc_data": "{3081,3457,0,0,4}-" + }, + { + "npc_id": "7152", + "loc_data": "{3081,3455,0,0,4}-" + }, + { + "npc_id": "7153", + "loc_data": "{3081,3453,0,0,4}-" + }, + { + "npc_id": "7154", + "loc_data": "{3079,3457,0,0,4}-" + }, + { + "npc_id": "7155", + "loc_data": "{3079,3455,0,0,4}-" + }, + { + "npc_id": "7156", + "loc_data": "{3077,3457,0,0,4}-" + }, + { + "npc_id": "7157", + "loc_data": "{3077,3453,0,0,4}-" + }, + { + "npc_id": "7158", + "loc_data": "{3080,3463,0,1,0}-{3079,3463,0,1,0}-{3144,4250,2,1,4}-{3143,4255,2,1,2}-{3140,4253,2,1,3}-{3145,4255,2,1,6}-{3149,4257,2,1,5}-{3148,4262,2,1,5}-{3142,4260,2,1,3}-{3144,4259,2,1,4}-{3145,4261,2,1,4}-{3146,4265,2,1,5}-{3143,4264,2,1,3}-{3144,4266,2,1,5}-{3141,4265,2,1,1}-{3144,4270,2,1,1}-{3142,4269,2,1,6}-{3157,4262,2,1,1}-{3164,4265,2,1,6}-{3162,4266,2,1,6}-{3165,4266,2,1,3}-{3164,4261,2,1,2}-{3162,4259,2,1,5}-{3162,4255,2,1,7}-{3165,4256,2,1,4}-{3163,4251,2,1,3}-{3165,4249,2,1,6}-{3162,4250,2,1,4}-{3163,4241,2,1,4}-{3165,4239,2,1,4}-{3165,4241,2,1,0}-{3160,4235,2,1,2}-{3165,4235,2,1,4}-{3165,4237,2,1,4}-{3162,4234,2,1,3}-{3164,4231,2,1,4}-{3166,4229,2,1,6}-{3162,4230,2,1,1}-{3167,4229,2,1,3}-{3184,4236,2,1,1}-{3153,4246,1,1,3}-{3159,4236,1,1,6}-{3163,4236,1,1,4}-{3163,4234,1,1,7}-{3146,4235,1,1,5}-{3144,4236,1,1,1}-{3143,4234,1,1,2}-{3145,4240,1,1,4}-{3148,4242,1,1,1}-{3145,4241,1,1,7}-{3142,4242,1,1,3}-{3146,4230,1,1,4}-" + }, + { + "npc_id": "7159", + "loc_data": "{3174,4238,2,1,6}-{3175,4243,2,1,6}-{3179,4236,2,1,4}-{3145,4272,1,1,3}-{3145,4276,1,1,3}-{3152,4271,1,1,6}-{3144,4264,1,1,4}-{3153,4266,1,1,1}-{3152,4261,1,1,4}-{3145,4259,1,1,6}-{3161,4259,1,1,0}-{3160,4265,1,1,0}-{3153,4246,1,1,3}-{3157,4248,1,1,6}-{3161,4247,1,1,4}-{3146,4247,1,1,6}-{3153,4233,1,1,4}-{3153,4240,1,1,6}-{3155,4238,1,1,5}-{3169,4267,3,1,3}-{3160,4265,3,1,6}-{3168,4250,3,1,4}-{3168,4244,3,1,6}-{3160,4239,3,1,1}-{3160,4228,3,1,3}-{3147,4225,3,1,3}-{3145,4242,3,1,5}-{3139,4249,3,1,6}-{3144,4253,3,1,3}-{3145,4266,3,1,0}-{3138,4266,3,1,3}-{3151,4272,3,1,0}-{3139,4272,3,1,0}-{3146,4231,3,1,0}-{3169,4270,3,1,0}-" + }, + { + "npc_id": "7160", + "loc_data": "{3175,4254,2,1,7}-{3176,4247,2,1,5}-{3174,4269,2,1,1}-{3190,4253,2,1,3}-{3192,4247,2,1,3}-{3189,4235,2,1,0}-{3152,4269,0,1,4}-{3160,4278,0,1,1}-{3160,4262,0,1,3}-{3149,4250,0,1,0}-{3158,4250,0,1,5}-{3161,4236,0,1,6}-{3172,4250,0,1,2}-{3171,4269,3,1,1}-{3157,4277,3,1,3}-{3150,4278,3,1,3}-{3160,4271,1,1,0}-{3163,4274,1,1,0}-{3167,4277,1,1,0}-{3173,4277,1,1,0}-{3175,4275,1,1,0}-{3160,4269,0,1,0}-{3173,4264,0,1,0}-{3190,4237,2,1,0}-{3154,4279,3,1,0}-" + }, + { + "npc_id": "7161", + "loc_data": "{3142,4230,2,1,6}-{3143,4231,2,1,3}-{3141,4235,2,1,4}-{3144,4241,2,1,4}-" + }, + { + "npc_id": "7162", + "loc_data": "{3144,4229,2,1,1}-{3143,4236,2,1,1}-{3145,4235,2,1,1}-{3148,4244,2,1,6}-" + }, + { + "npc_id": "7168", + "loc_data": "{2714,3309,0,1,1}-" + }, + { + "npc_id": "7228", + "loc_data": "{2862,3101,0,0,0}-{2879,3109,0,0,0}-{2874,3098,0,0,0}-{2882,3104,0,0,0}-{2882,3098,0,0,0}-" + }, + { + "npc_id": "7229", + "loc_data": "{2833,3088,0,0,0}-{2830,3085,0,0,0}-{2844,3089,0,0,0}-{2837,3084,0,0,0}-{2840,3076,0,0,0}-" + }, + { + "npc_id": "7230", + "loc_data": "{2840,3008,0,0,0}-{2887,3051,0,0,0}-{2890,3052,0,0,0}-{2894,3058,0,0,0}-{2897,3035,0,0,0}-" + }, + { + "npc_id": "7231", + "loc_data": "{2930,2961,0,0,0}-{2942,2964,0,0,0}-{2933,2964,0,0,0}-{2932,2967,0,0,0}-{2936,2973,0,0,0}-" + }, + { + "npc_id": "7232", + "loc_data": "{2802,3106,0,0,0}-{2807,3101,0,0,0}-{2806,3108,0,0,0}-{2808,3105,0,0,0}-{2814,3100,0,0,0}-" + }, + { + "npc_id": "7233", + "loc_data": "{2922,3070,0,0,0}-{2929,3075,0,0,0}-{2919,3080,0,0,0}-{2924,3080,0,0,0}-{2923,3081,0,0,0}-" + }, + { + "npc_id": "7234", + "loc_data": "{2900,3107,0,0,0}-{2903,3098,0,0,0}-{2908,3104,0,0,0}-{2905,3107,0,0,0}-{2904,3107,0,0,0}-" + }, + { + "npc_id": "7235", + "loc_data": "{2820,3065,0,0,0}-{2830,3059,0,0,0}-{2825,3057,0,0,0}-{2827,3050,0,0,0}-{2822,3053,0,0,0}-" + }, + { + "npc_id": "7236", + "loc_data": "{2799,3005,0,0,0}-{2801,2995,0,0,0}-{2804,3001,0,0,0}-{2803,2986,0,0,0}-{2811,2999,0,0,0}-" + }, + { + "npc_id": "7275", + "loc_data": "{2995,3252,0,0,0}-{2996,3259,0,0,0}-{2993,3250,0,0,0}-{3000,3258,0,0,0}-" + }, + { + "npc_id": "7276", + "loc_data": "{2994,3256,0,0,0}-{3002,3250,0,0,0}-{2992,3252,0,0,0}-{3000,3250,0,0,0}-{2728,3422,0,1,1}-{2718,3413,0,1,4}-{2729,3420,0,1,4}-{2727,3414,0,1,6}-{2714,3419,0,1,3}-{2719,3427,0,1,7}-" + }, + { + "npc_id": "7285", + "loc_data": "{2835,3107,0,0,0}-{2836,3101,0,0,0}-{2836,3110,0,0,0}-{2837,3112,0,0,0}-{2841,3107,0,0,0}-{2885,3018,0,0,0}-{2885,3019,0,0,0}-{2891,3018,0,0,0}-" + }, + { + "npc_id": "7286", + "loc_data": "{2931,3090,0,0,0}-{2925,3097,0,0,0}-{2936,3096,0,0,0}-{2924,3102,0,0,0}-{2933,3104,0,0,0}-" + }, + { + "npc_id": "7288", + "loc_data": "{2834,3045,0,0,0}-{2826,3048,0,0,0}-{2824,3043,0,0,0}-{2822,3045,0,0,0}-{2818,3049,0,0,0}-" + }, + { + "npc_id": "7289", + "loc_data": "{2882,3018,0,1,7}-{2887,3024,0,1,2}-" + }, + { + "npc_id": "7290", + "loc_data": "{2845,3088,0,1,6}-{2854,3085,0,1,1}-" + }, + { + "npc_id": "7292", + "loc_data": "{2863,3048,0,1,5}-{2868,3037,0,1,4}-" + }, + { + "npc_id": "7309", + "loc_data": "{2946,3280,0,0,0}-{3023,3410,0,0,0}-" + }, + { + "npc_id": "7310", + "loc_data": "{2946,3272,0,0,0}-{3015,3409,0,0,0}-" + }, + { + "npc_id": "7311", + "loc_data": "{2956,3273,0,0,0}-" + }, + { + "npc_id": "7312", + "loc_data": "{2950,3268,0,0,0}-" + }, + { + "npc_id": "7420", + "loc_data": "{3168,3334,0,0,3}-" + }, + { + "npc_id": "7421", + "loc_data": "{3163,3336,0,0,1}-" + }, + { + "npc_id": "7451", + "loc_data": "{2328,3170,0,1,6}-{2341,3179,0,1,4}-{2338,3164,0,1,4}-{2189,3181,0,1,2}-{2178,3186,0,1,7}-{2199,3182,0,1,0}-{2288,3149,0,1,2}-{2282,3144,0,1,3}-{2285,3144,0,1,2}-{2281,3139,0,1,1}-{2289,3152,0,1,6}-{2287,3155,0,1,1}-{2286,3146,0,1,1}-{2289,3156,0,0,5}-{2285,3136,0,1,3}-" + }, + { + "npc_id": "7600", + "loc_data": "{1714,5602,0,0,6}-" + }, + { + "npc_id": "7601", + "loc_data": "{1697,5605,0,0,6}-" + }, + { + "npc_id": "7602", + "loc_data": "{1693,5598,0,1,0}-" + }, + { + "npc_id": "7603", + "loc_data": "{1696,5600,0,1,0}-" + }, + { + "npc_id": "7604", + "loc_data": "{1699,5599,0,1,0}-" + }, + { + "npc_id": "7605", + "loc_data": "{1705,5599,0,0,1}-" + }, + { + "npc_id": "7636", + "loc_data": "{3618,9736,0,0,0}-{3620,9732,0,0,0}-{3624,9736,0,0,0}-{3625,9738,0,0,0}-{3618,9736,0,0,4}-{3620,9732,0,0,4}-{3624,9736,0,0,4}-{3625,9738,0,0,4}-" + }, + { + "npc_id": "7637", + "loc_data": "{3588,9768,0,0,0}-{3600,9768,0,0,0}-{3606,9780,0,0,0}-{3612,9729,0,0,0}-{3613,9764,0,0,0}-{3614,9740,0,0,0}-{3614,9750,0,0,0}-{3621,9748,0,0,0}-{3629,9737,0,0,0}-" + }, + { + "npc_id": "7639", + "loc_data": "{3592,9766,0,0,0}-{3603,9770,0,0,0}-{3605,9757,0,0,0}-{3611,9749,0,0,0}-{3611,9766,0,0,0}-{3616,9737,0,0,0}-{3616,9756,0,0,0}-{3616,9762,0,0,0}-{3617,9779,0,0,0}-{3621,9754,0,0,0}-" + }, + { + "npc_id": "7640", + "loc_data": "{3608,9780,0,1,3}-{3610,9777,0,1,1}-{3613,9772,0,1,4}-{3617,9748,0,1,7}-{3617,9758,0,1,1}-{3621,9751,0,1,1}-" + }, + { + "npc_id": "7641", + "loc_data": "{3607,9776,0,1,1}-{3610,9772,0,1,0}-{3614,9778,0,1,7}-{3615,9746,0,1,7}-{3618,9753,0,1,1}-{3619,9748,0,1,6}-" + }, + { + "npc_id": "7642", + "loc_data": "{3595,9742,0,0,0}-{3597,9751,0,0,0}-{3598,9736,0,0,0}-{3604,9746,0,0,0}-" + }, + { + "npc_id": "7643", + "loc_data": "{3587,9737,0,0,0}-{3592,9746,0,0,0}-{3600,9740,0,0,0}-{3605,9738,0,0,0}-" + }, + { + "npc_id": "7690", + "loc_data": "{3415,3489,2,0,0}-" + }, + { + "npc_id": "7707", + "loc_data": "{3440,9895,0,0,0}-" + }, + { + "npc_id": "7711", + "loc_data": "{3405,9902,0,0,0}-" + }, + { + "npc_id": "7716", + "loc_data": "{3005,3475,0,1,7}-{3003,3473,0,1,4}-{2997,3471,0,0,0}-{3004,3494,0,0,0}-{3005,3505,0,0,0}-" + }, + { + "npc_id": "7724", + "loc_data": "{3054,3503,0,1,0}-" + }, + { + "npc_id": "7726", + "loc_data": "{2996,3451,0,0,0}-" + }, + { + "npc_id": "7727", + "loc_data": "{3045,3483,0,0,0}-{3053,3492,0,0,4}-{3055,3491,0,0,0}-{3045,3496,0,0,0}-{3046,3490,1,0,0}-{3057,3497,1,0,3}-" + }, + { + "npc_id": "7728", + "loc_data": "{3009,3433,0,0,0}-" + }, + { + "npc_id": "7731", + "loc_data": "{3008,3466,0,0,0}-" + }, + { + "npc_id": "7732", + "loc_data": "{3009,3466,0,0,0}-" + }, + { + "npc_id": "7733", + "loc_data": "{3008,3479,0,0,0}-" + }, + { + "npc_id": "7734", + "loc_data": "{3009,3487,0,0,0}-" + }, + { + "npc_id": "7735", + "loc_data": "{2999,3469,0,0,0}-" + }, + { + "npc_id": "7744", + "loc_data": "{3149,3411,0,0,0}-" + }, + { + "npc_id": "7746", + "loc_data": "{4755,5116,0,1,7}-" + }, + { + "npc_id": "7747", + "loc_data": "{4751,5116,0,1,5}-" + }, + { + "npc_id": "7748", + "loc_data": "{4752,5114,0,1,5}-" + }, + { + "npc_id": "7749", + "loc_data": "{4739,5070,0,1,0}-" + }, + { + "npc_id": "7750", + "loc_data": "{4765,5064,0,1,7}-" + }, + { + "npc_id": "7751", + "loc_data": "{4778,5070,0,1,2}-" + }, + { + "npc_id": "7752", + "loc_data": "{4432,5096,0,1,0}-" + }, + { + "npc_id": "7753", + "loc_data": "{4438,5089,0,1,0}-{4428,5083,0,1,0}-{4437,5075,0,1,0}-" + }, + { + "npc_id": "7763", + "loc_data": "{4777,5080,0,1,2}-" + }, + { + "npc_id": "7765", + "loc_data": "{4776,5076,0,1,6}-" + }, + { + "npc_id": "7767", + "loc_data": "{2457,5139,0,1,6}-{2460,5140,0,1,0}-{2443,5147,0,1,3}-{2454,5155,0,1,5}-{2461,5125,0,1,3}-{2456,5124,0,1,4}-{2458,5129,0,1,4}-{2443,5145,0,1,1}-{2451,5148,0,1,1}-{2447,5143,0,1,4}-{2443,5138,0,1,4}-{4762,5145,0,1,2}-" + }, + { + "npc_id": "7768", + "loc_data": "{4762,5156,0,1,6}-" + }, + { + "npc_id": "7780", + "loc_data": "{3358,2993,0,1,0}-" + }, + { + "npc_id": "7786", + "loc_data": "{3348,9388,0,1,0}-{3350,9393,0,1,0}-{3350,9400,0,1,0}-{3350,9406,0,1,0}-{3351,9380,0,1,0}-{3352,9374,0,1,0}-{3354,9387,0,1,0}-{3358,9378,0,1,0}-{3358,9396,0,1,0}-{3358,9406,0,1,0}-{3365,9381,0,1,0}-{3365,9387,0,1,0}-{3365,9402,0,1,0}-{3369,9390,0,1,0}-{3350,9412,0,1,0}-{3351,9420,0,1,0}-{3356,9414,0,1,0}-{3358,9421,0,1,0}-{3364,9424,0,1,0}-{3365,9416,0,1,0}-" + }, + { + "npc_id": "7787", + "loc_data": "{3301,4413,0,1,0}-{3302,4394,0,1,0}-{3304,4397,0,1,0}-{3306,4410,0,1,0}-{3307,4383,0,1,0}-{3307,4397,0,1,0}-{3308,4407,0,1,0}-{3310,4383,0,1,0}-{3310,4386,0,1,0}-{3310,4398,0,1,0}-{3311,4407,0,1,0}-{3312,4380,0,1,0}-{3313,4401,0,1,0}-{3314,4411,0,1,0}-{3316,4376,0,1,0}-{3316,4382,0,1,0}-{3316,4387,0,1,0}-{3316,4397,0,1,0}-{3316,4401,0,1,0}-{3316,4405,0,1,0}-{3317,4391,0,1,0}-{3318,4412,0,1,0}-{3319,4373,0,1,0}-{3319,4407,0,1,0}-{3321,4385,0,1,0}-{3321,4398,0,1,0}-{3322,4412,0,1,0}-{3323,4402,0,1,0}-{3324,4394,0,1,0}-{3324,4397,0,1,0}-{3325,4410,0,1,0}-" + }, + { + "npc_id": "7801", + "loc_data": "{3283,4346,0,1,0}-{3308,4349,0,1,0}-{3293,4375,0,1,0}-{3303,4363,0,1,0}-" + }, + { + "npc_id": "7802", + "loc_data": "{3288,4350,0,1,0}-{3296,4340,0,1,0}-{3288,4361,0,1,0}-{3310,4355,0,1,0}-" + }, + { + "npc_id": "7803", + "loc_data": "{3297,4347,0,1,0}-{3315,4346,0,1,0}-{3282,4357,0,1,0}-{3303,4369,0,1,0}-" + }, + { + "npc_id": "7804", + "loc_data": "{3279,4350,0,1,0}-{3294,4353,0,1,0}-{3294,4366,0,1,0}-" + }, + { + "npc_id": "7891", + "loc_data": "{3207,3250,0,0,0}-{3208,3250,0,0,0}-{3209,3250,0,0,0}-" + }, + { + "npc_id": "7942", + "loc_data": "{3294,4937,0,0,6}-" + }, + { + "npc_id": "7959", + "loc_data": "{3188,3425,0,1,0}-" + }, + { + "npc_id": "7969", + "loc_data": "{3205,3240,0,0,0}-" + }, + { + "npc_id": "8000", + "loc_data": "{2987,3691,0,1,3}-" + }, + { + "npc_id": "8041", + "loc_data": "{2794,3100,0,1,0}-" + }, + { + "npc_id": "8065", + "loc_data": "{3399,3464,0,0,1}-" + }, + { + "npc_id": "8066", + "loc_data": "{3346,3513,0,0,6}-" + }, + { + "npc_id": "8067", + "loc_data": "{3280,3524,0,0,0}-" + }, + { + "npc_id": "8068", + "loc_data": "{3237,3525,0,0,0}-" + }, + { + "npc_id": "8069", + "loc_data": "{3171,3535,0,0,0}-" + }, + { + "npc_id": "8070", + "loc_data": "{3088,3515,0,0,0}-" + }, + { + "npc_id": "8071", + "loc_data": "{3035,3517,0,0,0}-" + }, + { + "npc_id": "8072", + "loc_data": "{2972,3515,0,0,0}-" + }, + { + "npc_id": "8073", + "loc_data": "{2941,3564,0,0,0}-" + }, + { + "npc_id": "8074", + "loc_data": "{2946,3621,0,0,0}-" + }, + { + "npc_id": "8075", + "loc_data": "{2938,3679,0,0,0}-" + }, + { + "npc_id": "8076", + "loc_data": "{2939,3772,0,0,0}-" + }, + { + "npc_id": "8082", + "loc_data": "{3552,5590,2,1,2}-" + }, + { + "npc_id": "8083", + "loc_data": "{3560,5602,0,1,3}-" + }, + { + "npc_id": "8084", + "loc_data": "{3541,5597,0,1,4}-" + }, + { + "npc_id": "8085", + "loc_data": "{3550,5598,0,1,3}-" + }, + { + "npc_id": "8111", + "loc_data": "{3273,3681,0,1,0}-" + }, + { + "npc_id": "8133", + "loc_data": "{2993,4380,2,1,0}-" + }, + { + "npc_id": "8149", + "loc_data": "{3244,9996,0,1,6}-{3229,10008,0,1,6}-{3222,10012,0,1,6}-{3205,10010,0,1,6}-{3212,10012,0,1,6}-{3205,10023,0,1,6}-{3210,10030,0,1,6}-{3212,10039,0,1,6}-{3213,10009,0,1,6}-{3243,9993,0,1,6}-{3218,10036,0,1,6}-{3218,10010,0,1,6}-" + }, + { + "npc_id": "8150", + "loc_data": "{3241,9997,0,1,6}-{3230,10011,0,1,6}-{3221,10012,0,1,6}-{3207,10006,0,1,6}-{3207,10017,0,1,6}-{3209,10031,0,1,6}-{3209,10019,0,1,6}-{3217,10012,0,1,6}-{3224,10012,0,1,6}-" + }, + { + "npc_id": "8151", + "loc_data": "{3241,9998,0,1,6}-{3223,10010,0,1,6}-{3208,10012,0,1,6}-{3209,10026,0,1,6}-{3214,10038,0,1,6}-{3211,10035,0,1,6}-{3214,10037,0,1,6}-{3240,9991,0,1,6}-{3231,10010,0,1,6}-{3210,10007,0,1,6}-{3222,10010,0,1,6}-" + }, + { + "npc_id": "8171", + "loc_data": "{3281,3403,0,1,6}-" + }, + { + "npc_id": "8227", + "loc_data": "{3135,3631,0,1,6}-" + }, + { + "npc_id": "8228", + "loc_data": "{3142,3635,0,1,0}-" + }, + { + "npc_id": "8229", + "loc_data": "{3128,3632,0,1,0}-{3135,3616,0,1,6}-" + }, + { + "npc_id": "8231", + "loc_data": "{3135,3627,0,1,0}-" + }, + { + "npc_id": "8233", + "loc_data": "{3133,3628,0,1,0}-" + }, + { + "npc_id": "8267", + "loc_data": "{2866,3546,0,1,1}-" + }, + { + "npc_id": "8273", + "loc_data": "{2931,3536,0,1,5}-" + }, + { + "npc_id": "8274", + "loc_data": "{3514,3513,0,1,6}-" + }, + { + "npc_id": "8275", + "loc_data": "{2869,2982,1,1,5}-" + }, + { + "npc_id": "8312", + "loc_data": "{3016,9974,1,1,0}-{3021,9940,1,1,0}-{3023,9940,1,1,0}-{3024,9959,1,1,0}-{3027,9960,1,1,0}-{3043,9967,1,1,0}-{3023,9992,1,1,0}-{3016,10021,2,1,0}-{3028,10013,2,1,0}-{3029,10028,2,1,0}-{3038,10005,2,1,0}-{3044,10001,2,1,0}-{3045,9993,2,1,0}-{3045,9999,2,1,0}-{3047,10000,2,1,0}-{3052,10009,2,1,0}-{3054,10001,2,1,0}-{3056,10006,2,1,0}-{3032,10090,1,1,0}-{3040,10096,1,1,0}-{3048,10095,1,1,0}-" + }, + { + "npc_id": "8316", + "loc_data": "{3017,9972,1,1,0}-{3025,9962,1,1,0}-{3030,9943,1,1,0}-{3033,9941,1,1,0}-{3034,9950,1,1,0}-{3035,9950,1,1,0}-{3036,9940,1,1,0}-{3038,9939,1,1,0}-{3045,9968,1,1,0}-{3058,9952,1,1,0}-{3063,9952,1,1,0}-{3025,9995,1,1,0}-{3027,10029,2,1,0}-{3051,10006,2,1,0}-{3052,10002,2,1,0}-{3054,10005,2,1,0}-{3027,10101,1,1,0}-{3039,10100,1,1,0}-{3054,10097,1,1,0}-" + }, + { + "npc_id": "8320", + "loc_data": "{3014,9971,1,1,0}-{3016,9969,1,1,0}-{3028,9942,1,1,0}-{3029,9943,1,1,0}-{3030,9951,1,1,0}-{3033,9950,1,1,0}-{3045,9965,1,1,0}-{3061,9953,1,1,0}-{3062,9953,1,1,0}-{3030,9995,1,1,0}-{3016,10023,2,1,0}-{3029,10014,2,1,0}-{3029,10030,2,1,0}-{3036,10005,2,1,0}-{3042,9994,2,1,0}-{3047,10005,2,1,0}-{3049,10002,2,1,0}-{3053,9998,2,1,0}-{3031,10100,1,1,0}-{3044,10098,1,1,0}-{3049,10102,1,1,0}-" + }, + { + "npc_id": "8324", + "loc_data": "{2911,3811,0,1,0}-{2911,3812,0,1,0}-{2911,3813,0,1,0}-{2925,3821,0,1,0}-{2925,3822,0,1,0}-{2925,3823,0,1,0}-{2929,3798,0,1,0}-{2936,3790,0,1,0}-{2936,3810,0,1,0}-{2939,3823,0,1,0}-{3427,5102,0,1,0}-{3428,5099,0,1,0}-{3428,5102,0,1,0}-{3430,5099,0,1,0}-{2949,3819,1,1,0}-{2949,3822,1,1,0}-{2955,3822,1,1,0}-{2957,3822,1,1,0}-{3016,9977,1,1,0}-{3021,9939,1,1,0}-{3024,9954,1,1,0}-{3025,9943,1,1,0}-{3025,9954,1,1,0}-{3026,9966,1,1,0}-{3032,9952,1,1,0}-{3039,9954,1,1,0}-{3044,9967,1,1,0}-{3044,9971,1,1,0}-{3057,9936,1,1,0}-{3059,9953,1,1,0}-{3041,9975,2,1,0}-{3043,9975,2,1,0}-{3045,9975,2,1,0}-{3016,10022,2,1,0}-{3017,10039,2,1,0}-{3027,10028,2,1,0}-{3029,10013,2,1,0}-{3036,10038,2,1,0}-{3037,10006,2,1,0}-{3041,10024,2,1,0}-{3043,10032,2,1,0}-{3057,10002,2,1,0}-{3058,10021,2,1,0}-{3065,10006,2,1,0}-{3027,10092,1,1,0}-{3035,10097,1,1,0}-{3051,10099,1,1,0}-" + }, + { + "npc_id": "8328", + "loc_data": "{3055,10103,1,1,0}-" + }, + { + "npc_id": "8349", + "loc_data": "{2589,5735,0,1,0}-{2589,5713,0,1,0}-{2610,5709,0,1,0}-{2613,5732,0,1,0}-" + }, + { + "npc_id": "8358", + "loc_data": "{2601,5710,0,1,0}-{2603,5737,0,1,0}-" + }, + { + "npc_id": "8380", + "loc_data": "{2907,3806,0,1,0}-" + }, + { + "npc_id": "8381", + "loc_data": "{2910,3805,0,1,0}-" + }, + { + "npc_id": "8382", + "loc_data": "{2912,3812,0,1,0}-{2923,3823,0,1,0}-" + }, + { + "npc_id": "8383", + "loc_data": "{2908,3809,0,1,0}-{2919,3823,0,1,0}-" + }, + { + "npc_id": "8384", + "loc_data": "{2922,3827,0,1,0}-" + }, + { + "npc_id": "8385", + "loc_data": "{2924,3825,0,1,0}-" + }, + { + "npc_id": "8386", + "loc_data": "{2921,3824,0,1,0}-" + }, + { + "npc_id": "8387", + "loc_data": "{2934,3786,0,1,0}-" + }, + { + "npc_id": "8388", + "loc_data": "{2935,3784,0,1,0}-{2940,3827,0,1,0}-" + }, + { + "npc_id": "8389", + "loc_data": "{2937,3785,0,1,0}-" + }, + { + "npc_id": "8390", + "loc_data": "{2935,3781,0,1,0}-{2939,3829,0,1,0}-" + }, + { + "npc_id": "8391", + "loc_data": "{2937,3780,0,1,0}-{2940,3833,0,1,0}-" + }, + { + "npc_id": "8392", + "loc_data": "{2938,3782,0,1,0}-{2938,3831,0,1,0}-" + }, + { + "npc_id": "8536", + "loc_data": "{2654,5600,0,1,3}-{2650,5600,0,0,3}-{2662,5593,0,0,3}-{2653,5590,0,0,3}-{2644,5592,0,0,3}-{2644,5601,0,0,3}-{2654,5604,0,0,3}-{2663,5606,0,0,3}-{2670,5597,0,0,3}-{2657,5589,0,0,3}-" + }, + { + "npc_id": "8545", + "loc_data": "{3122,3626,0,1,0}-" + }, + { + "npc_id": "8590", + "loc_data": "{2743,3444,0,1,0}-" + } +] \ No newline at end of file diff --git a/Server/data/configs/object_configs.json b/Server/data/configs/object_configs.json index 354bfc6de..e4f2c26a3 100644 --- a/Server/data/configs/object_configs.json +++ b/Server/data/configs/object_configs.json @@ -3,14030 +3,14 @@ "examine": "I wonder what's inside.", "ids": "0,1" }, - { - "examine": "Looks and smells like a place where goblins live.", - "ids": "2" - }, - { - "examine": "The door is closed.", - "ids": "3,4" - }, - { - "examine": "A powerful ranging device, unfortunately not working.", - "ids": "5" - }, - { - "examine": "A powerful ranging device that fires metal balls.", - "ids": "6" - }, - { - "examine": "The cannon is built on here.", - "ids": "7" - }, - { - "examine": "The mounting for the multicannon.", - "ids": "8" - }, - { - "examine": "The barrels of the multicannon.", - "ids": "9" - }, { "examine": "I can climb down this.", "ids": "10" }, - { - "examine": "I can climb up this.", - "ids": "11" - }, - { - "examine": "A pile of rocks is blocking my path.", - "ids": "12" - }, - { - "examine": "Mud caved in from above.", - "ids": "13" - }, - { - "examine": "Intended to keep the goblins away from the mines.", - "ids": "14,15,16,17,18,19,20" - }, - { - "examine": "Its eyes stare off into the distance...", - "ids": "21" - }, - { - "examine": "The door is shut.", - "ids": "22" - }, - { - "examine": "Unusually lumpy looking.", - "ids": "23" - }, - { - "examine": "The door is shut.", - "ids": "24" - }, - { - "examine": "Seems to be some kind of clock part.", - "ids": "25,26,27,28,29,30,31,32" - }, - { - "examine": "Yup, definitely a lever.", - "ids": "33,34,35,36" - }, - { - "examine": "Stops people walking past.", - "ids": "37,38,39" - }, - { - "examine": "Animals have no table manners.", - "ids": "40" - }, - { - "examine": "Looks like an outlet pipe to the lake.", - "ids": "41" - }, - { - "examine": "I can see fish swimming in the water.", - "ids": "42,326" - }, - { - "examine": "It seems to sparkle.", - "ids": "43" - }, - { - "examine": "I can see really big fish swimming in the water.", - "ids": "44" - }, - { - "examine": "Big Dave is fishing here.", - "ids": "45" - }, - { - "examine": "Joshua is fishing here.", - "ids": "46" - }, - { - "examine": "A wooden gate.", - "ids": "47,48,49,50" - }, - { - "examine": "Hmmm... I wonder if it's loose enough to get through.", - "ids": "51" - }, - { - "examine": "It's locked.", - "ids": "52,53" - }, - { - "examine": "It's a flight of stairs.", - "ids": "54,55,56,57" - }, - { - "examine": "Known commonly as Red worm vine.", - "ids": "58" - }, - { - "examine": "A fancy hole in the wall.", - "ids": "59" - }, - { - "examine": "A fancy hole in the ceiling.", - "ids": "60" - }, - { - "examine": "A shrine to evil.", - "ids": "61" - }, - { - "examine": "Is there something in there?", - "ids": "62" - }, - { - "examine": "Used for storage.", - "ids": "63,64" - }, - { - "examine": "Surprisingly sturdy looking.", - "ids": "65,66" - }, - { - "examine": "Used for storage.", - "ids": "67" - }, - { - "examine": "Where bees live.", - "ids": "68" - }, - { - "examine": "Handy for boarding boats.", - "ids": "69,70" - }, - { - "examine": "Sturdy looking door.", - "ids": "71,72" - }, - { - "examine": "Securely fastened shut.", - "ids": "73,74" - }, - { - "examine": "I wonder what's inside?", - "ids": "75" - }, - { - "examine": "It's open.", - "ids": "76" - }, - { - "examine": "Securely fastened shut.", - "ids": "77,78" - }, - { - "examine": "Looks secure.", - "ids": "79,80" - }, - { - "examine": "The door is closed.", - "ids": "81,82" - }, - { - "examine": "Seems very old...", - "ids": "86" - }, - { - "examine": "I guess you pull it...", - "ids": "87" - }, - { - "examine": "A wrought iron gate.", - "ids": "89,90" - }, - { - "examine": "I don't like the look of this!", - "ids": "91" - }, - { - "examine": "The door is closed.", - "ids": "92,93" - }, - { - "examine": "A wrought iron gate.", - "ids": "94,95" - }, - { - "examine": "A dark flight of stairs.", - "ids": "96,98" - }, - { - "examine": "The door is closed.", - "ids": "99" - }, { "examine": "Don't you open that trapdoor!", "ids": "100" }, - { - "examine": "I can climb this.", - "ids": "101" - }, - { - "examine": "A sturdy door.", - "ids": "102" - }, - { - "examine": "I wonder what's inside.", - "ids": "103" - }, - { - "examine": "Perhaps I should search it.", - "ids": "104" - }, - { - "examine": "I wonder what that's there for...", - "ids": "105,106" - }, - { - "examine": "An appliance for cooking with.", - "ids": "114" - }, - { - "examine": "A party balloon.", - "ids": "115,116,117,118,119,120" - }, - { - "examine": "Aren't they pretty?", - "ids": "121" - }, - { - "examine": "Paaaarty!", - "ids": "122" - }, - { - "examine": "A party balloon.", - "ids": "123,124,125,126,127,128,129,130" - }, - { - "examine": "The door is closed.", - "ids": "131" - }, - { - "examine": "An escape route.", - "ids": "132" - }, - { - "examine": "Looks spooky down there.", - "ids": "133" - }, - { - "examine": "A large double door.", - "ids": "134,135,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,4423,4424,4425,4426,4427,4428,4429,4430,4629,4630,4631,4632,4633,4634,4635,4963,4964,12349,12350,12446,12447,12448,12449,13094,13095,13096,13097,13098,13099,31814,31815,31816" - }, - { - "examine": "The door is closed.", - "ids": "136" - }, - { - "examine": "Smelly.", - "ids": "152" - }, - { - "examine": "An ornate fountain.", - "ids": "153,36781" - }, - { - "examine": "A pipe I can squeeze through.", - "ids": "154,2287,2288,2290,2291,2292,2293,3236,3237,4058,5099,5100,5101,5102,5152,5153,5154,5155,9293,9295,10063,10064,10065,10066,10067,10068,10069,10070,10071,10072,10073,10074,10075,29370" - }, - { - "examine": "Lots of books.", - "ids": "155,156,157" - }, - { - "examine": "This wall looks odd...", - "ids": "158,159" - }, - { - "examine": "I wonder what this does...", - "ids": "160" - }, - { - "examine": "I'm not touching that.", - "ids": "162,163,164" - }, - { - "examine": "A special furnace for destroying contaminated items.", - "ids": "165" - }, - { - "examine": "A wooden gate.", - "ids": "166,167" - }, - { - "examine": "I wonder what's inside.", - "ids": "170" - }, - { - "examine": "Perhaps I should search it.", - "ids": "171" - }, - { - "examine": "I wonder what's inside.", - "ids": "172" - }, - { - "examine": "Perhaps I should search it.", - "ids": "173" - }, - { - "examine": "Fly Gnome Air.", - "ids": "187" - }, - { - "examine": "Probably pilot error.", - "ids": "188" - }, - { - "examine": "The Blurberry bar", - "ids": "189" - }, - { - "examine": "A large gate in the gnome style.", - "ids": "190" - }, - { - "examine": "The entrance to the cave.", - "ids": "194" - }, - { - "examine": "I can climb this.", - "ids": "195" - }, - { - "examine": "A quirky gnome lamp.", - "ids": "200" - }, - { - "examine": "These insects love this light.", - "ids": "201" - }, - { - "examine": "It's amazing what bees produce!", - "ids": "202" - }, - { - "examine": "Posh candlesticks.", - "ids": "203,204,32287,40074" - }, - { - "examine": "Scary lighting apparatus.", - "ids": "208" - }, - { - "examine": "An ornamental lighting fixture.", - "ids": "209" - }, - { - "examine": "A mysterious glowing ice crystal.", - "ids": "210" - }, - { - "examine": "It's amazing what bees produce!", - "ids": "211" - }, - { - "examine": "Useful for making ships move.", - "ids": "212,218" - }, - { - "examine": "This figure brings luck to those who sail.", - "ids": "221,222" - }, - { - "examine": "Useful for making ships move.", - "ids": "225,226" - }, - { - "examine": "Barnacle infested rope.", - "ids": "227" - }, - { - "examine": "Allows access to other parts of the ship.", - "ids": "245,246" - }, - { - "examine": "Without this I'm going around in circles.", - "ids": "252,253" - }, - { - "examine": "This figure brings luck to those who sail.", - "ids": "254" - }, - { - "examine": "Used for pulling things up.", - "ids": "255" - }, - { - "examine": "This figure brings luck to those who sail.", - "ids": "260" - }, - { - "examine": "Useful if there is any wind.", - "ids": "265" - }, - { - "examine": "Useful for making ships move.", - "ids": "266,267" - }, - { - "examine": "Useful for making ships move", - "ids": "268" - }, - { - "examine": "How much does this weigh?", - "ids": "271" - }, - { - "examine": "I can climb up here.", - "ids": "272" - }, - { - "examine": "I can go below decks with this ladder.", - "ids": "273" - }, - { - "examine": "A conveniently rolled sail.", - "ids": "274,275,276,277,278,279" - }, - { - "examine": "I'm not allowed to climb this ladder.", - "ids": "287" - }, - { - "examine": "It's like a land rudder.", - "ids": "296" - }, - { - "examine": "Disturbingly man-like.", - "ids": "297" - }, - { - "examine": "I bet there's a needle in it somewhere.", - "ids": "298,299,300" - }, - { - "examine": "Animals have no table manners.", - "ids": "301" - }, - { - "examine": "A home for baby creatures.", - "ids": "302" - }, - { - "examine": "In the city we would call this mouldy rubbish.", - "ids": "303" - }, - { - "examine": "I bet there's a needle in it somewhere.", - "ids": "304" - }, - { - "examine": "Where bees live.", - "ids": "305" - }, - { - "examine": "A used cart seller will probably try and sell it later.", - "ids": "306" - }, - { - "examine": "One horsepower; wooden suspension: a beauty.", - "ids": "307,308" - }, - { - "examine": "Dead tree parts piled together neatly.", - "ids": "309" - }, - { - "examine": "A patch of soft dark brown matter. Probably mud.", - "ids": "310" - }, - { - "examine": "A pile of something I hope is mud.", - "ids": "311" - }, - { - "examine": "Potato-licious!", - "ids": "312" - }, - { - "examine": "Baby bread.", - "ids": "313" - }, - { - "examine": "The remains of a bad driver.", - "ids": "327" - }, - { - "examine": "The perfect place to store things.", - "ids": "346,347" - }, - { - "examine": "These open and close!", - "ids": "348" - }, - { - "examine": "This may be worth searching.", - "ids": "349" - }, - { - "examine": "These open and close!", - "ids": "350" - }, - { - "examine": "This may be worth searching.", - "ids": "351" - }, - { - "examine": "These open and close!", - "ids": "352" - }, - { - "examine": "This may be worth searching.", - "ids": "353" - }, - { - "examine": "A wooden crate for storage.", - "ids": "354,355" - }, - { - "examine": "An old crate for storage.", - "ids": "356,357,358" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "359,360,361" - }, - { - "examine": "A wooden barrel for storage.", - "ids": "362" - }, - { - "examine": "A wooden barrel containing lots of fish.", - "ids": "363" - }, - { - "examine": "It's got ale in it.", - "ids": "364,32063" - }, - { - "examine": "These may have something in them.", - "ids": "365" - }, - { - "examine": "Useful for transportation of delicate items.", - "ids": "366" - }, - { - "examine": "This may be worth opening.", - "ids": "367,368,369" - }, - { - "examine": "This may be worth searching.", - "ids": "370,371,372" - }, - { - "examine": "The perfect accompaniment to a bedroom.", - "ids": "373" - }, - { - "examine": "A stand for hats!", - "ids": "374" - }, - { - "examine": "I wonder what's inside.", - "ids": "375,376,377" - }, - { - "examine": "Perhaps I should search it.", - "ids": "378,379" - }, - { - "examine": "A good source of books!", - "ids": "380,381" - }, - { - "examine": "A woven storage basket.", - "ids": "382,383,384,385" - }, - { - "examine": "A gigantic pottery urn.", - "ids": "386" - }, - { - "examine": "This no doubt contains arcane knowledge.", - "ids": "387" - }, - { - "examine": "I wonder what this spooky item contains.", - "ids": "388" - }, - { - "examine": "It smells funny in there.", - "ids": "389,390" - }, - { - "examine": "A fishing net full of fish.", - "ids": "392" - }, - { - "examine": "A good source of books!", - "ids": "393" - }, - { - "examine": "Bamboo storage!", - "ids": "394,395,396" - }, - { - "examine": "The perfect place to store things.", - "ids": "397" - }, - { - "examine": "I hope no-one's home...", - "ids": "398,26931,26935,26937,26939,32290" - }, - { - "examine": "I see dead people.", - "ids": "399,36737,32291" - }, - { - "examine": "A small simple gravestone.", - "ids": "400" - }, - { - "examine": "A simple marker for a forgotten person.", - "ids": "401" - }, - { - "examine": "The remains of someone lie inside.", - "ids": "402" - }, - { - "examine": "A monument to a special person.", - "ids": "403" - }, - { - "examine": "The inscription is worn away and unreadable.", - "ids": "404" - }, - { - "examine": "'Here lies...' is all I can read.", - "ids": "405" - }, - { - "examine": "Whoever bought this must have really cared for the inhabitant.", - "ids": "406" - }, - { - "examine": "Looks like a stone doorway to me.", - "ids": "407" - }, - { - "examine": "Looks like a stone doorway to my untrained eye.", - "ids": "408" - }, - { - "examine": "Shrine to the glory of Saradomin.", - "ids": "409" - }, - { - "examine": "An ancient altar to the glory of Guthix.", - "ids": "410" - }, - { - "examine": "Shrine to the glory of Zamorak.", - "ids": "411,412" - }, - { - "examine": "Looks suspiciously like a big stone table.", - "ids": "413" - }, - { - "examine": "A really posh upright coffin.", - "ids": "414" - }, - { - "examine": "Whoever's inside must have been rich.", - "ids": "415" - }, - { - "examine": "With skill I can play this.", - "ids": "416" - }, - { - "examine": "Great for sleeping in.", - "ids": "417" - }, - { - "examine": "A stylish-looking bed.", - "ids": "418" - }, - { - "examine": "A drab-looking bed.", - "ids": "419" - }, - { - "examine": "A stylish-looking bed.", - "ids": "420,421" - }, - { - "examine": "Technically a bed.", - "ids": "422" - }, - { - "examine": "I guess I could sleep in it if I were really tired.", - "ids": "423" - }, - { - "examine": "Lovely comfy-looking big bed.", - "ids": "424,425" - }, - { - "examine": "Posh-looking bed.", - "ids": "426" - }, - { - "examine": "A bed fit for a king.", - "ids": "427,428" - }, - { - "examine": "One of the nicest beds I have ever seen.", - "ids": "429" - }, - { - "examine": "Perfect for snoozing in the sun.", - "ids": "430" - }, - { - "examine": "A comfortable seat to recline and recuperate.", - "ids": "431" - }, - { - "examine": "I don't think anyone's slept here for a long time.", - "ids": "432" - }, - { - "examine": "Bamboo's a versatile material.", - "ids": "433,434,435" - }, - { - "examine": "A lump of rock.", - "ids": "436,437,438,439" - }, - { - "examine": "A big slimy lump of rock.", - "ids": "440" - }, - { - "examine": "A slimy lump of rock.", - "ids": "441" - }, - { - "examine": "A slippery looking rock.", - "ids": "442,443" - }, - { - "examine": "A huge lump of rock.", - "ids": "444,445" - }, - { - "examine": "Some small stones.", - "ids": "446,447,448" - }, - { - "examine": "A rocky ledge.", - "ids": "449" - }, - { - "examine": "Stoney!", - "ids": "450,451,452,453" - }, - { - "examine": "Solid rock from the cave floor.", - "ids": "454,455" - }, - { - "examine": "Looks slippery!", - "ids": "462,463" - }, - { - "examine": "Formed over many years of dripping limestone.", - "ids": "464,465,466" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "467" - }, - { - "examine": "Limestone floor growth.", - "ids": "468" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "469,470" - }, - { - "examine": "A deposit of rocks.", - "ids": "471,472,473,474" - }, - { - "examine": "Looks slippery!", - "ids": "477,478" - }, - { - "examine": "A deposit of rocks.", - "ids": "479,480,481,482,483,484,485,486" - }, - { - "examine": "A collection of rocks over what looks like a depression.", - "ids": "492" - }, - { - "examine": "A deposit of rocks.", - "ids": "507,508,509,510" - }, - { - "examine": "A rock with a pickaxe and mining tools.", - "ids": "511" - }, - { - "examine": "A very tall column of ice.", - "ids": "516" - }, - { - "examine": "A deposit of rocks.", - "ids": "517,518,519,520" - }, - { - "examine": "Formed over many years of dripping limestone.", - "ids": "523,524,525,526,527,528" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "529" - }, - { - "examine": "Formed over many years of dripping limestone.", - "ids": "530" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "531" - }, - { - "examine": "Limestone floor growth.", - "ids": "532" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "533" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "534" - }, - { - "examine": "A tooth shaped rock formation protruding from the ceiling.", - "ids": "539,540" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "541,542" - }, - { - "examine": "A tooth shaped rock formation protruding from the ceiling.", - "ids": "543" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "544" - }, - { - "examine": "A huge lump of rock.", - "ids": "545" - }, - { - "examine": "Intimidating!", - "ids": "546,547" - }, - { - "examine": "A deposit of rocks.", - "ids": "548,549,550,551,552,553" - }, - { - "examine": "Frozen water has formed an icicle here.", - "ids": "554,555,556,557" - }, - { - "examine": "It looks cold in there.", - "ids": "558,559,560,561" - }, - { - "examine": "This person was of great importance.", - "ids": "562" - }, - { - "examine": "A monarch chiselled in stone.", - "ids": "563,564" - }, - { - "examine": "A sculpture of a monarch.", - "ids": "565" - }, - { - "examine": "This person was of great importance.", - "ids": "566" - }, - { - "examine": "A huge carving of an ancient race.", - "ids": "567" - }, - { - "examine": "A depiction of a great dwarven miner.", - "ids": "568" - }, - { - "examine": "A depiction of a famous gnome warrior.", - "ids": "569,570" - }, - { - "examine": "The head and shoulders of a famous gnome.", - "ids": "571,572" - }, - { - "examine": "A depiction of a famous gnomeballer.", - "ids": "573" - }, - { - "examine": "A carving of a figure from the history of RuneScape.", - "ids": "574,575,576,577,578" - }, - { - "examine": "An expertly chiselled statue.", - "ids": "579,580,581" - }, - { - "examine": "An unusual symbol of ancient times.", - "ids": "582" - }, - { - "examine": "An expertly crafted vase.", - "ids": "583" - }, - { - "examine": "An expertly chiselled statue of a bird.", - "ids": "584" - }, - { - "examine": "What a good likeness!", - "ids": "585,586" - }, - { - "examine": "A depiction of the evil god Zamorak.", - "ids": "587" - }, - { - "examine": "A base for the statue to sit on.", - "ids": "588" - }, - { - "examine": "The scrying glass of a seer.", - "ids": "589" - }, - { - "examine": "Banking transactions are processed here.", - "ids": "590" - }, - { - "examine": "Banking transactions are recorded here.", - "ids": "591" - }, - { - "examine": "Contains washing items.", - "ids": "592" - }, - { - "examine": "A nice sturdy looking table.", - "ids": "593,594,595" - }, - { - "examine": "A banquet could be eaten from this.", - "ids": "596,597" - }, - { - "examine": "Useful for putting things on.", - "ids": "598" - }, - { - "examine": "At one time it was for putting things on.", - "ids": "599" - }, - { - "examine": "The napkins are made from real silk.", - "ids": "600" - }, - { - "examine": "Useful for putting things on.", - "ids": "601,602,603,604,605,606" - }, - { - "examine": "Items for making clothes are kept on here.", - "ids": "607" - }, - { - "examine": "The ideal place to study.", - "ids": "608" - }, - { - "examine": "A handy workbench for a handy person.", - "ids": "609,610" - }, - { - "examine": "Sit back and enjoy the view.", - "ids": "611" - }, - { - "examine": "Items are for sale here.", - "ids": "612" - }, - { - "examine": "Useful for putting things on.", - "ids": "613,614,615" - }, - { - "examine": "A creepy looking table.", - "ids": "616" - }, - { - "examine": "Items are for sale here.", - "ids": "617" - }, - { - "examine": "There are some strange chemicals here.", - "ids": "618,619" - }, - { - "examine": "Bamboo's a versatile material.", - "ids": "620" - }, - { - "examine": "It's a banquet table.", - "ids": "621,622" - }, - { - "examine": "A nice sturdy looking table.", - "ids": "623,624,625,626,627" - }, - { - "examine": "Finely wrought wares of silver.", - "ids": "628" - }, - { - "examine": "Garments for the discerning.", - "ids": "629" - }, - { - "examine": "Bread cakes and pastries.", - "ids": "630" - }, - { - "examine": "Finest precious stones.", - "ids": "631" - }, - { - "examine": "These will keep you warm.", - "ids": "632" - }, - { - "examine": "The spice is right.", - "ids": "633" - }, - { - "examine": "Always a source of good bargains.", - "ids": "634" - }, - { - "examine": "Fine brews from exotic regions.", - "ids": "635" - }, - { - "examine": "Keeps mine carts from rolling away.", - "ids": "636" - }, - { - "examine": "You can 'cart' things around on this.", - "ids": "637" - }, - { - "examine": "Makes you taller.", - "ids": "647,29314" - }, - { - "examine": "A bit large for a bracelet!", - "ids": "648" - }, - { - "examine": "A macabre variation on the neck tie.", - "ids": "649,650" - }, - { - "examine": "Dead and half buried.", - "ids": "651,652,653,654,655,656,657" - }, - { - "examine": "Disturbing but tidy.", - "ids": "658" - }, - { - "examine": "I don't even want to think about what did that...", - "ids": "659" - }, - { - "examine": "Now that's what I call slimline!", - "ids": "660,664,3797,3971,5122,7206,29340" - }, - { - "examine": "He looks very relaxed.", - "ids": "661,665,3798,3972,5123,7207,29341" - }, - { - "examine": "Now he's just too thin.", - "ids": "662,666,3799,3973,5124,7208,7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,29342" - }, - { - "examine": "He hasn't eaten in a long time.", - "ids": "663,667,3974,5125,29343" - }, - { - "examine": "Disturbing but tidy.", - "ids": "658,668,669,670,3969,29347" - }, - { - "examine": "No cats were harmed in the making of this device.", - "ids": "671" - }, - { - "examine": "Shattered.", - "ids": "672,673,674,675,676,677" - }, - { - "examine": "I see fish.", - "ids": "678" - }, - { - "examine": "I can see fish swimming in the water.", - "ids": "679" - }, - { - "examine": "I was always forced to play this as a child.", - "ids": "680" - }, - { - "examine": "I never learnt to play.", - "ids": "681" - }, - { - "examine": "Time for a recital?", - "ids": "682" - }, - { - "examine": "Tick-tock, it's a clock.", - "ids": "683" - }, - { - "examine": "The noxious liquid bubbles horribly.", - "ids": "684" - }, - { - "examine": "Shows which way the wind blows.", - "ids": "685,686,687,688" - }, - { - "examine": "Looking good!", - "ids": "689" - }, - { - "examine": "A privacy aid!", - "ids": "690" - }, - { - "examine": "Use this to get changed behind and retain modesty.", - "ids": "691" - }, - { - "examine": "I don't even want to think about what's cooking in that.", - "ids": "692" - }, - { - "examine": "Good for sweeping.", - "ids": "693" - }, - { - "examine": "I'm guessing it's for making women's clothes.", - "ids": "694" - }, - { - "examine": "Helps make human clothing.", - "ids": "695" - }, - { - "examine": "A hole.", - "ids": "696" - }, - { - "examine": "Dead animal head. Lovely.", - "ids": "697,736,29344" - }, - { - "examine": "Must have been laid by a huge bird.", - "ids": "698" - }, - { - "examine": "Whatever it was I'm glad it's not alive.", - "ids": "699" - }, - { - "examine": "Spooky!", - "ids": "700,701,2614,4048,4049,4050,29345" - }, - { - "examine": "A dog's idea of heaven.", - "ids": "702,703,704,705,706,29346" - }, - { - "examine": "Ding-Dong!", - "ids": "707" - }, - { - "examine": "Some kind of pulley system.", - "ids": "708" - }, - { - "examine": "Surprisingly rope-shaped.", - "ids": "709" - }, - { - "examine": "Suspiciously hole-shaped.", - "ids": "710" - }, - { - "examine": "A huge chunk of shining natural crystal.", - "ids": "711,712,713" - }, - { - "examine": "A recently extinguished fire.", - "ids": "714,715,716,717" - }, - { - "examine": "A barrel full of swords...", - "ids": "718,719,720,721,722" - }, - { - "examine": "I'm glad this isn't around anymore!", - "ids": "723" - }, - { - "examine": "A crude torch stuck in the ground.", - "ids": "724,725,726,25200" - }, - { - "examine": "A fissure in the cave wall.", - "ids": "727,728,729,730,731,732" - }, - { - "examine": "This huge web blocks your path.", - "ids": "733" - }, - { - "examine": "A huge web, cruelly slashed in half.", - "ids": "734" - }, - { - "examine": "The noxious liquid bubbles horribly.", - "ids": "735" - }, - { - "examine": "Whatever it was I'm glad it's not alive.", - "ids": "737,738,739,740" - }, - { - "examine": "Looks a little too broken to climb.", - "ids": "741,742" - }, - { - "examine": "An old-looking ladder.", - "ids": "743" - }, - { - "examine": "An old-looking bit of scaffolding.", - "ids": "744,745,746,747" - }, - { - "examine": "The entrance to the cave.", - "ids": "748" - }, - { - "examine": "A map icon..", - "ids": "771" - }, - { - "examine": "It's a door made from bamboo.", - "ids": "779" - }, - { - "examine": "A loom.", - "ids": "787" - }, - { - "examine": "You'd need a big siege engine to force that.", - "ids": "788,789" - }, - { - "examine": "I always wonder if someone's hiding inside when I see these.", - "ids": "817" - }, - { - "examine": "Looks kind of like a man made of metal.", - "ids": "818,819,820" - }, - { - "examine": "Not suitable for children. Aim away from face.", - "ids": "821,36780" - }, - { - "examine": "Big heavy metal balls.", - "ids": "822,36790" - }, - { - "examine": "The easiest opponent I'll ever fight.", - "ids": "823" - }, - { - "examine": "A wooden defensive structure.", - "ids": "824,825,826,827,828,36928" - }, - { - "examine": "For private use only!", - "ids": "829,830,831,832,833" - }, - { - "examine": "Break glass in case of emergency.", - "ids": "834,835" - }, - { - "examine": "An attractively laid out collection of ranging weapons.", - "ids": "836" - }, - { - "examine": "Break glass in case of emergency.", - "ids": "837,838" - }, - { - "examine": "An attractively laid out collection of ranging weapons.", - "ids": "839" - }, - { - "examine": "A row of sharp pointy spears.", - "ids": "848" - }, - { - "examine": "Rows of sharp pointy spears.", - "ids": "849,850" - }, - { - "examine": "A tatty old standard.", - "ids": "851,852" - }, - { - "examine": "A standard.", - "ids": "853" - }, - { - "examine": "A blue standard.", - "ids": "854,855,856" - }, - { - "examine": "A standard of the gnome race.", - "ids": "857,858" - }, - { - "examine": "The standard of the ogre race.", - "ids": "859" - }, - { - "examine": "The flag of Asgarnia.", - "ids": "860" - }, - { - "examine": "The flag of Kandarin.", - "ids": "861" - }, - { - "examine": "The flag of Ardougne.", - "ids": "862" - }, - { - "examine": "A standard of Asgarnia.", - "ids": "863" - }, - { - "examine": "A standard of Kandarin.", - "ids": "864" - }, - { - "examine": "A standard of Asgarnia.", - "ids": "865" - }, - { - "examine": "A standard of Kandarin.", - "ids": "866" - }, - { - "examine": "A standard of Misthalin.", - "ids": "867" - }, - { - "examine": "A standard of Varrock.", - "ids": "868" - }, - { - "examine": "A flag flies here.", - "ids": "869" - }, - { - "examine": "A sculpted trunk of wood.", - "ids": "870" - }, - { - "examine": "Takes my dirty bath water away.", - "ids": "871" - }, - { - "examine": "It smells horrible in there.", - "ids": "872" - }, - { - "examine": "Ideal for washing things in.", - "ids": "873,874,4063,36971" - }, - { - "examine": "Wash here.", - "ids": "875,876,877" - }, - { - "examine": "No this is not a mirage!", - "ids": "878" - }, - { - "examine": "Everyone needs a water feature.", - "ids": "879,28682" - }, - { - "examine": "A beautiful water feature.", - "ids": "880" - }, - { - "examine": "There's a cover over this manhole.", - "ids": "881" - }, - { - "examine": "How dangerous, someone has left this manhole open.", - "ids": "882" - }, - { - "examine": "This is supposed to stop people falling down the manhole.", - "ids": "883" - }, - { - "examine": "Best used with a bucket.", - "ids": "884" - }, - { - "examine": "If only I had one of those at home...", - "ids": "885,886" - }, - { - "examine": "A painting of the King looking royal.", - "ids": "887" - }, - { - "examine": "A beautiful landscape.", - "ids": "888" - }, - { - "examine": "A mysterious figure stands alone in this haunting image.", - "ids": "889" - }, - { - "examine": "Hail to the King!", - "ids": "890" - }, - { - "examine": "I don't know much about art, but I like this.", - "ids": "891" - }, - { - "examine": "A painting of some guy standing around somewhere.", - "ids": "892" - }, - { - "examine": "A really bad portrait of the King.", - "ids": "893" - }, - { - "examine": "Fine brushwork adds to the realism of this serene landscape.", - "ids": "894" - }, - { - "examine": "Pretty good painting.", - "ids": "895" - }, - { - "examine": "I can see the stars on this.", - "ids": "896" - }, - { - "examine": "A picture of the stars with funny diagrams and things on.", - "ids": "897" - }, - { - "examine": "Cross shaped.", - "ids": "898" - }, - { - "examine": "Mmmm decorative!", - "ids": "899" - }, - { - "examine": "Brightens up the room a bit.", - "ids": "900" - }, - { - "examine": "I don't know art, but I like it!", - "ids": "901" - }, - { - "examine": "Not bad at all.", - "ids": "902" - }, - { - "examine": "Alas poor unicorn, I knew him well.", - "ids": "903" - }, - { - "examine": "The scary-looking head of a scary-looking dragon.", - "ids": "904" - }, - { - "examine": "Not what I would pick as wall decoration.", - "ids": "905" - }, - { - "examine": "Looks like the bull lost.", - "ids": "906" - }, - { - "examine": "Trinkets and stuff.", - "ids": "907" - }, - { - "examine": "Not everyone's idea of a nice wall decoration.", - "ids": "908" - }, - { - "examine": "Something is written on it. I can't read what.", - "ids": "909" - }, - { - "examine": "Looks like an eye with huge lashes.", - "ids": "910" - }, - { - "examine": "A tablet covered in weird looking little squiggles and pictures.", - "ids": "911" - }, - { - "examine": "A posh water bowl.", - "ids": "915" - }, - { - "examine": "For wiping muddy feet on.", - "ids": "954" - }, - { - "examine": "Disturbingly its eyes look everywhere in the room except at you.", - "ids": "955,956,957,958" - }, - { - "examine": "What has a face and hands but is not a man?", - "ids": "959" - }, - { - "examine": "It's a time machine....sort of!", - "ids": "960" - }, - { - "examine": "'Ask about our low, low interest rates!'", - "ids": "961" - }, - { - "examine": "Some helpful people have placed notes on here, how nice.", - "ids": "962" - }, - { - "examine": "Essentials for a seamstress.", - "ids": "963" - }, - { - "examine": "It really was this big!", - "ids": "964,965" - }, - { - "examine": "He doesn't look too jolly to me!", - "ids": "966" - }, - { - "examine": "Looks like this ship was way off course!", - "ids": "967" - }, - { - "examine": "X marks the spot.", - "ids": "968" - }, - { - "examine": "Complete with authentic seaweed!", - "ids": "969" - }, - { - "examine": "This mentions the monk trapped here...", - "ids": "970" - }, - { - "examine": "A useful ranging device.", - "ids": "971" - }, - { - "examine": "Use these with bows.", - "ids": "972" - }, - { - "examine": "A powerful throwing weapon.", - "ids": "973" - }, - { - "examine": "Improved Leather armour for archers.", - "ids": "974" - }, - { - "examine": "Leather made from the skin of a Dragon.", - "ids": "975" - }, - { - "examine": "I can climb over the fence with this..", - "ids": "993" - }, - { - "examine": "This section of railing has been broken", - "ids": "1004,1005" - }, - { - "examine": "There is a hole in this section of the railing.", - "ids": "1006,1007,1008,1009" - }, - { - "examine": "Someone was thirsty...", - "ids": "1010" - }, - { - "examine": "Full of lovely drinks!", - "ids": "1011,1012" - }, - { - "examine": "Storage for cookery items.", - "ids": "1013,32283,40055" - }, - { - "examine": "Storage for all needs.", - "ids": "1014,1015,1016,1017,1018,10495,10496,10497,10498,11768,11769,11770,11771,11772,11773,11774,11775,11776,11777,11778,11779,11780,11781,12980,30648,30649,30650,32284,40045,40046,40047" - }, - { - "examine": "What kind of sick person keeps a skull on their wall?", - "ids": "1019" - }, - { - "examine": "Esoteric artefacts.", - "ids": "1020" - }, - { - "examine": "I don't want to know what's in those little urns.", - "ids": "1021,1022,1023" - }, - { - "examine": "Used for storing things on.", - "ids": "1024" - }, - { - "examine": "Tailor made for needlework supplies", - "ids": "1025,1026" - }, - { - "examine": "Prepared hides hang here drying.", - "ids": "1027" - }, - { - "examine": "This contains dye for leather.", - "ids": "1028,1029" - }, - { - "examine": "Used to squeeze moisture from the hide.", - "ids": "1030,1031" - }, - { - "examine": "Danger!", - "ids": "1032,5127,29373" - }, - { - "examine": "This tells you which way is which.", - "ids": "1033,1034,1035" - }, - { - "examine": "Ye olde Shrimp and Parrot.", - "ids": "1057" - }, - { - "examine": "The Blue Moon Inn", - "ids": "1068" - }, - { - "examine": "The Jolly Boar", - "ids": "1069" - }, - { - "examine": "The Rusty Anchor", - "ids": "1070" - }, - { - "examine": "The Shrimp and Parrot", - "ids": "1071" - }, - { - "examine": "The Dead Man's Chest", - "ids": "1072" - }, - { - "examine": "The Rising Sun", - "ids": "1073" - }, - { - "examine": "The Forester's Arms", - "ids": "1074" - }, - { - "examine": "The Flying Horse", - "ids": "1075" - }, - { - "examine": "The Dancing Donkey", - "ids": "1076" - }, - { - "examine": "The Dragon Inn", - "ids": "1077" - }, - { - "examine": "Karamja Spirits bar", - "ids": "1078" - }, - { - "examine": "North to Varrock :: East to Al-Kharid.", - "ids": "1079" - }, - { - "examine": "North road to Draynor Village :: East road to Varrock.", - "ids": "1080" - }, - { - "examine": "North to Draynor Village.", - "ids": "1081" - }, - { - "examine": "North to Draynor Manor :: West to Asgarnia.", - "ids": "1082" - }, - { - "examine": "South to the Wizards' Tower :: East to Lumbridge.", - "ids": "1083" - }, - { - "examine": "South to Lumbridge :: West to Varrock :: East to the Digsite.", - "ids": "1084" - }, - { - "examine": "South to Al-Kharid :: West to Lumbridge :: East to the Digsite.", - "ids": "1085" - }, - { - "examine": "North to Varrock :: East to the Digsite.", - "ids": "1086" - }, - { - "examine": "South to the Wizards' Tower :: North to Draynor Village :: East to Lumbridge.", - "ids": "1087" - }, - { - "examine": "The ideal thing to sit on.", - "ids": "1088,1115,3801,5345,5614,7385,7386,7387,10491,11489,12885,12886,13582,32032,36830,40039" - }, - { - "examine": "Not so good for sitting on.", - "ids": "1089" - }, - { - "examine": "A comfortable seat.", - "ids": "1090,1091,1092,11760,13583,13584,13585,13586,13587,32286" - }, - { - "examine": "Good for sitting on.", - "ids": "1093,1094,1095,1102,5880,10492,12440,12441,12973,32033" - }, - { - "examine": "Good for rocking in!", - "ids": "1096" - }, - { - "examine": "A kingly seat for a royal behind.", - "ids": "1097,1098,1099,1105,1108,1109,32285" - }, - { - "examine": "The perfect way to sit at the bar.", - "ids": "1100,1101" - }, - { - "examine": "Gnomes sit on these.", - "ids": "1103" - }, - { - "examine": "Gnomes are found sitting here.", - "ids": "1104" - }, - { - "examine": "Sit back and relax...", - "ids": "1106,1107" - }, - { - "examine": "Used for sitting on.", - "ids": "1110" - }, - { - "examine": "Do I dare sit on this?", - "ids": "1111" - }, - { - "examine": "Church seating.", - "ids": "1112" - }, - { - "examine": "Park it here...", - "ids": "1113" - }, - { - "examine": "Wouldn't like to sit on that for too long...", - "ids": "1114" - }, - { - "examine": "Keeps the neighbours out!", - "ids": "1116,1117" - }, - { - "examine": "Found in wild areas.", - "ids": "1118" - }, - { - "examine": "These are commonly found.", - "ids": "1119" - }, - { - "examine": "An unusual bush.", - "ids": "1120" - }, - { - "examine": "A wild bush.", - "ids": "1121" - }, - { - "examine": "All the leaves have fallen off.", - "ids": "1122,1123" - }, - { - "examine": "A commonly found bush.", - "ids": "1124,36782" - }, - { - "examine": "Bushy!", - "ids": "1125" - }, - { - "examine": "A rose by any other name would still have prickly bits.", - "ids": "1126" - }, - { - "examine": "A leafy bush.", - "ids": "1144,1145,1146" - }, - { - "examine": "The abode of a fairy.", - "ids": "1147,1148,1149" - }, - { - "examine": "Fairies need money too!", - "ids": "1150" - }, - { - "examine": "A fairy is selling items here.", - "ids": "1151" - }, - { - "examine": "A small potted plant.", - "ids": "1152" - }, - { - "examine": "Someone has planted this.", - "ids": "1153" - }, - { - "examine": "A nicely potted fern.", - "ids": "1154" - }, - { - "examine": "Better than weeding.", - "ids": "1155" - }, - { - "examine": "I bet bees like this.", - "ids": "1156,1157" - }, - { - "examine": "A large potted plant.", - "ids": "1158,1159" - }, - { - "examine": "A plant in a bamboo pot.", - "ids": "1160" - }, - { - "examine": "Cabbage... yuck!", - "ids": "1161" - }, - { - "examine": "Found near the water's edge.", - "ids": "1162" - }, - { - "examine": "Not good for eating.", - "ids": "1163,1164" - }, - { - "examine": "Fungal growth.", - "ids": "1165" - }, - { - "examine": "Poisonous no doubt.", - "ids": "1166" - }, - { - "examine": "Fungal growth.", - "ids": "1167" - }, - { - "examine": "Poisonous no doubt.", - "ids": "1168,1169" - }, - { - "examine": "I doubt that's edible.", - "ids": "1170,1171,1172" - }, - { - "examine": "A commonly found fern.", - "ids": "1173" - }, - { - "examine": "Spiky!", - "ids": "1174" - }, - { - "examine": "A home for frogs.", - "ids": "1175" - }, - { - "examine": "Otherwise known as a lilypad.", - "ids": "1176" - }, - { - "examine": "A home for frogs.", - "ids": "1177" - }, - { - "examine": "Smells lovely!", - "ids": "1178" - }, - { - "examine": "Who is this Heather girl?", - "ids": "1179" - }, - { - "examine": "I wonder why it's purple?", - "ids": "1180" - }, - { - "examine": "I'd better not get stung.", - "ids": "1181,1182,1183" - }, - { - "examine": "The tendrils of a creeping plant.", - "ids": "1184" - }, - { - "examine": "Nicely planted.", - "ids": "1185" - }, - { - "examine": "A large curling plant.", - "ids": "1186" - }, - { - "examine": "That's pretty.", - "ids": "1187" - }, - { - "examine": "Blooming!", - "ids": "1188" - }, - { - "examine": "Commonly found in grassy areas.", - "ids": "1189" - }, - { - "examine": "Is this the biggest flower around?", - "ids": "1190" - }, - { - "examine": "These are huge!", - "ids": "1191" - }, - { - "examine": "A rare flower.", - "ids": "1192" - }, - { - "examine": "Don't flowers make you feel better?", - "ids": "1193" - }, - { - "examine": "Can this talk?", - "ids": "1194" - }, - { - "examine": "You don't see that many of these.", - "ids": "1195" - }, - { - "examine": "I wonder what these are?", - "ids": "1196" - }, - { - "examine": "A rarely found flower.", - "ids": "1197" - }, - { - "examine": "Very rare flowers.", - "ids": "1198" - }, - { - "examine": "These smell nice.", - "ids": "1199" - }, - { - "examine": "A bed of red flowers.", - "ids": "1200" - }, - { - "examine": "These are pretty.", - "ids": "1201" - }, - { - "examine": "Found only in jungle areas.", - "ids": "1202" - }, - { - "examine": "A very rare exotic flower.", - "ids": "1203" - }, - { - "examine": "A small fernlike plant.", - "ids": "1204" - }, - { - "examine": "The leaves of a nasty creeping plant.", - "ids": "1205" - }, - { - "examine": "The flower of a nasty creeping plant.", - "ids": "1206,1207,1208" - }, - { - "examine": "Nasty curling tendrils of a creeping plant.", - "ids": "1231" - }, - { - "examine": "One of the most common trees in RuneScape.", - "ids": "1276" - }, - { - "examine": "A healthy young tree.", - "ids": "1277" - }, - { - "examine": "A commonly found tree.", - "ids": "1278,1279" - }, - { - "examine": "A healthy young tree.", - "ids": "1280" - }, - { - "examine": "A beautiful old oak.", - "ids": "1281" - }, - { - "examine": "This tree has long been dead.", - "ids": "1282,1283,1284" - }, - { - "examine": "An old weather beaten tree.", - "ids": "1285" - }, - { - "examine": "It's only useful for firewood now.", - "ids": "1286,1287,1288,1289,1290,1291" - }, - { - "examine": "An ancient magical tree.", - "ids": "1292" - }, - { - "examine": "An ancient sentient tree.", - "ids": "1293,1294,1295" - }, - { - "examine": "This tree has fallen to the ground.", - "ids": "1296" - }, - { - "examine": "This tree was either the victim of weather or mankind.", - "ids": "1297" - }, - { - "examine": "A commonly found fern.", - "ids": "1298" - }, - { - "examine": "Is this a weed?", - "ids": "1299" - }, - { - "examine": "A fern is growing here.", - "ids": "1300" - }, - { - "examine": "Home to many unusual creatures.", - "ids": "1301" - }, - { - "examine": "A leafy tree.", - "ids": "1302" - }, - { - "examine": "A tree found in tropical areas.", - "ids": "1303,1304,1305" - }, - { - "examine": "The tree shimmers with a magical force.", - "ids": "1306" - }, - { - "examine": "I bet this makes good syrup!", - "ids": "1307" - }, - { - "examine": "These trees are found near water.", - "ids": "1308" - }, - { - "examine": "A splendid tree.", - "ids": "1309" - }, - { - "examine": "This tree has vines hanging from it.", - "ids": "1310" - }, - { - "examine": "This would be good to sleep under.", - "ids": "1311" - }, - { - "examine": "An essential for tropical islands.", - "ids": "1312" - }, - { - "examine": "An unusual tree grows here.", - "ids": "1313,1314" - }, - { - "examine": "A hardy evergreen tree.", - "ids": "1315,1316" - }, - { - "examine": "A young sentient tree.", - "ids": "1317" - }, - { - "examine": "This would make good firewood.", - "ids": "1318,1319" - }, - { - "examine": "The branch is infested with moss.", - "ids": "1320,1321" - }, - { - "examine": "The trunk of this tree is hollow.", - "ids": "1322" - }, - { - "examine": "A gnarly old tree root.", - "ids": "1323" - }, - { - "examine": "The roots of a tree are exposed.", - "ids": "1324" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "1325" - }, - { - "examine": "A terribly tall tropical tree.", - "ids": "1326,1327,1328" - }, - { - "examine": "Leaves of a bamboo tree.", - "ids": "1329" - }, - { - "examine": "It's thick with snow.", - "ids": "1330,1331,1332" - }, - { - "examine": "This old tree has been taken over by parasitic plants.", - "ids": "1333" - }, - { - "examine": "This old tree is rotting away.", - "ids": "1334,1335,1336,1337,1338" - }, - { - "examine": "They're just waiting to trip someone up.", - "ids": "1339,1340" - }, - { - "examine": "This tree has been cut down.", - "ids": "1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359" - }, - { - "examine": "This tree contains dangerous insects no doubt.", - "ids": "1360,1361,1362" - }, - { - "examine": "They're just waiting to trip someone up.", - "ids": "1363,1364" - }, - { - "examine": "It's only useful for firewood now.", - "ids": "1365" - }, - { - "examine": "A gnarly old tree root.", - "ids": "1366" - }, - { - "examine": "The roots of this tree are exposed.", - "ids": "1367" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "1368" - }, - { - "examine": "An unusual tree grows here.", - "ids": "1369" - }, - { - "examine": "This old tree is rotting away.", - "ids": "1370" - }, - { - "examine": "This tree contains dangerous insects no doubt.", - "ids": "1371,1372,1373,1374,1375,1376,1377" - }, - { - "examine": "This old tree is rotting away.", - "ids": "1378,1379" - }, - { - "examine": "A gnarly old tree root.", - "ids": "1380" - }, - { - "examine": "The roots of this tree are exposed.", - "ids": "1381" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "1382" - }, - { - "examine": "This tree has long been dead.", - "ids": "1383" - }, - { - "examine": "It's only useful for firewood now.", - "ids": "1384" - }, - { - "examine": "A gnarly old tree root.", - "ids": "1385" - }, - { - "examine": "The roots of this tree are exposed.", - "ids": "1386" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "1387" - }, - { - "examine": "They're just waiting to trip someone up.", - "ids": "1388,1389" - }, - { - "examine": "Commonly found in these parts.", - "ids": "1390" - }, - { - "examine": "A leafy plant.", - "ids": "1391" - }, - { - "examine": "A leafy fern.", - "ids": "1392" - }, - { - "examine": "A small bushy plant.", - "ids": "1393" - }, - { - "examine": "A leafy shrub.", - "ids": "1394" - }, - { - "examine": "No flies on me.", - "ids": "1395" - }, - { - "examine": "How do these things manage to grow?", - "ids": "1396" - }, - { - "examine": "This cactus has recently had a chunk taken off.", - "ids": "1397" - }, - { - "examine": "A leafy fern.", - "ids": "1398" - }, - { - "examine": "This has broad leaves.", - "ids": "1399" - }, - { - "examine": "A large waxy jungle plant.", - "ids": "1400" - }, - { - "examine": "A type of jungle fern.", - "ids": "1401" - }, - { - "examine": "A large waxy jungle plant.", - "ids": "1402" - }, - { - "examine": "How do these things manage to grow?", - "ids": "1403,1404,1405,1406" - }, - { - "examine": "Leaves a nasty mark.", - "ids": "1407" - }, - { - "examine": "Looks spiky.", - "ids": "1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503" - }, - { - "examine": "The door is open.", - "ids": "1504" - }, - { - "examine": "The door is closed.", - "ids": "1505" - }, - { - "examine": "A sturdy wooden door.", - "ids": "1506,1507,1508,1509,1510,1511,1512,1513,1514,1515" - }, - { - "examine": "The curtain is closed.", - "ids": "1526,1527,1528" - }, - { - "examine": "The curtain is open.", - "ids": "1529" - }, - { - "examine": "The door is closed.", - "ids": "1530" - }, - { - "examine": "The door is open.", - "ids": "1531,1532" - }, - { - "examine": "A nicely fitted door.", - "ids": "1533,1534,1535" - }, - { - "examine": "An ornately-fashioned door.", - "ids": "1536" - }, - { - "examine": "An elf-fashioned door.", - "ids": "1537,1538" - }, - { - "examine": "Solid bars of iron.", - "ids": "1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550" - }, - { - "examine": "A wooden gate.", - "ids": "1551,1552,1553,1554,1555,1556" - }, - { - "examine": "A wrought iron gate.", - "ids": "1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567" - }, - { - "examine": "I wonder what's under it?", - "ids": "1568,1569" - }, - { - "examine": "I wonder what's down there?", - "ids": "1570,1571" - }, - { - "examine": "This door has been boarded up.", - "ids": "1572" - }, - { - "examine": "A door to a grand place.", - "ids": "1573,1574,1575,1576" - }, - { - "examine": "The top of the archway.", - "ids": "1577,1578,1579" - }, - { - "examine": "The base of the archway.", - "ids": "1580,1581,1582" - }, - { - "examine": "There is something strange about this wall...", - "ids": "1583,1584,1585,1586,1587,1588" - }, - { - "examine": "A wrought iron gate.", - "ids": "1589,1590" - }, - { - "examine": "The door is closed.", - "ids": "1591" - }, - { - "examine": "Something is strange about this panel.", - "ids": "1592,1593" - }, - { - "examine": "An entranceway into the dungeon.", - "ids": "1594" - }, - { - "examine": "The way in.", - "ids": "1595" - }, - { - "examine": "A wrought iron gate.", - "ids": "1596,1597" - }, - { - "examine": "A wooden gate.", - "ids": "1598,1599" - }, - { - "examine": "The doors to the Magic Guild.", - "ids": "1600" - }, - { - "examine": "The doors to the Magic guild.", - "ids": "1601,1602,1603,1604,1605,1606,1607,1608" - }, - { - "examine": "A wooden structure used in the process of building.", - "ids": "1609,1610,1611,1612" - }, - { - "examine": "This timber is broken", - "ids": "1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660" - }, - { - "examine": "A pile of bricks.", - "ids": "1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721" - }, - { - "examine": "I can climb these stairs.", - "ids": "1722,36791" - }, - { - "examine": "They go down.", - "ids": "1723" - }, - { - "examine": "I can climb down these stairs.", - "ids": "1724" - }, - { - "examine": "I can climb up this.", - "ids": "1725,36768,36771" - }, - { - "examine": "I can climb down this.", - "ids": "1726,1727,1728" - }, - { - "examine": "I can climb this.", - "ids": "1729" - }, - { - "examine": "Dare I go up?", - "ids": "1730" - }, - { - "examine": "These stairs look spooky!", - "ids": "1731,1732" - }, - { - "examine": "I can use these stairs to climb down.", - "ids": "1733" - }, - { - "examine": "I can climb these stairs.", - "ids": "1734" - }, - { - "examine": "A rickety old staircase.", - "ids": "1735" - }, - { - "examine": "I can climb down these stairs.", - "ids": "1736" - }, - { - "examine": "I can climb up these stairs.", - "ids": "1737,1738" - }, - { - "examine": "I can climb up or go down these stairs.", - "ids": "1739" - }, - { - "examine": "I can climb down these stairs.", - "ids": "1740,1741" - }, - { - "examine": "I can climb up these stairs.", - "ids": "1742" - }, - { - "examine": "I can climb up or go down these stairs.", - "ids": "1743" - }, - { - "examine": "I can climb down these stairs.", - "ids": "1744,1745" - }, - { - "examine": "I can climb down this.", - "ids": "1746" - }, - { - "examine": "I can climb this.", - "ids": "1747,1748" - }, - { - "examine": "I can climb down this.", - "ids": "1749" - }, - { - "examine": "I can climb this.", - "ids": "1750,1751" - }, - { - "examine": "I can't climb this.", - "ids": "1752" - }, - { - "examine": "It's useless.", - "ids": "1753" - }, - { - "examine": "I can climb down this.", - "ids": "1754" - }, - { - "examine": "I can climb this.", - "ids": "1755" - }, - { - "examine": "I can climb down this.", - "ids": "1756" - }, - { - "examine": "I can climb this.", - "ids": "1757" - }, - { - "examine": "Not much use really.", - "ids": "1758" - }, - { - "examine": "This leads downwards.", - "ids": "1759" - }, - { - "examine": "These can be useful.", - "ids": "1760" - }, - { - "examine": "Steps made from natural rock.", - "ids": "1761" - }, - { - "examine": "A rope hangs here.", - "ids": "1762,1763,1764" - }, - { - "examine": "I can climb down this.", - "ids": "1765" - }, - { - "examine": "I can climb this.", - "ids": "1766" - }, - { - "examine": "I can climb down this.", - "ids": "1767" - }, - { - "examine": "I can climb this.", - "ids": "1768,1769,1770,1771,1772,1773,1774,1775,1776,1777" - }, - { - "examine": "These huge stone discs grind grain to make flour.", - "ids": "1778" - }, - { - "examine": "These sails use the wind to drive the windmill.", - "ids": "1779,1780,1781" - }, - { - "examine": "I'll need an empty pot so I can collect my flour.", - "ids": "1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796" - }, - { - "examine": "Some very hard rocks, I wouldn't want to fall on those.", - "ids": "1797,1798,1799,1800,1801,1802,1803" - }, - { - "examine": "This door requires a key.", - "ids": "1804" - }, - { - "examine": "The door to the Champions' Guild.", - "ids": "1805,1806,1807,1808,1809" - }, - { - "examine": "Here be spiders!", - "ids": "1810" - }, - { - "examine": "I can walk through this tattered web.", - "ids": "1811" - }, - { - "examine": "A magical border of teleportation.", - "ids": "1812" - }, - { - "examine": "For putting things on.", - "ids": "1813" - }, - { - "examine": "I wonder what this does...", - "ids": "1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851" - }, - { - "examine": "This lets in more light.", - "ids": "1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863" - }, - { - "examine": "These obviously mean keep out!", - "ids": "1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875" - }, - { - "examine": "I shudder to think what has taken place here.", - "ids": "1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,32289" - }, - { - "examine": "This hasn't seen a duster for a while.", - "ids": "1896" - }, - { - "examine": "Not as nice as some.", - "ids": "1897" - }, - { - "examine": "This needs dusting before I'll sit on it.", - "ids": "1898,1899" - }, - { - "examine": "This could do with a bit of a spit and polish.", - "ids": "1900" - }, - { - "examine": "This clearly isn't used for dining very often.", - "ids": "1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938" - }, - { - "examine": "A tower.", - "ids": "1939,1940,1941,1942,1943,1944,1945,1946" - }, - { - "examine": "It looks like I might be able to climb over this piece of the wall...", - "ids": "1947,1948" - }, - { - "examine": "A pile of bricks.", - "ids": "1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966" - }, - { - "examine": "A grand door for a grand tree.", - "ids": "1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984" - }, - { - "examine": "A large root.", - "ids": "1985,1986" - }, - { - "examine": "A primitive looking boat.", - "ids": "1987" - }, - { - "examine": "Oops!", - "ids": "1988" - }, - { - "examine": "A good source of books!", - "ids": "1989" - }, - { - "examine": "A wooden crate for storage.", - "ids": "1990" - }, - { - "examine": "Solid bars of iron.", - "ids": "1991" - }, - { - "examine": "There is an indent the size of a pebble in the stone's centre.", - "ids": "1992" - }, - { - "examine": "A stone tomb surrounded by flowers.", - "ids": "1993" - }, - { - "examine": "I wonder what's inside.", - "ids": "1994,37016,37015" - }, - { - "examine": "Perhaps I should search it.", - "ids": "1995" - }, - { - "examine": "I should be safe if I can reach this.", - "ids": "1996,1997" - }, - { - "examine": "Wet rope.", - "ids": "1998" - }, - { - "examine": "A wooden crate for storage.", - "ids": "1999" - }, - { - "examine": "An elf-fashioned door.", - "ids": "2000,2001,2002,2003" - }, - { - "examine": "The pillar has a rune shaped dent in it.", - "ids": "2004" - }, - { - "examine": "A statue of Baxtorian chiselled in stone.", - "ids": "2005" - }, - { - "examine": "A statue of Glarial chiselled in stone.", - "ids": "2006,2007,2008,2009" - }, - { - "examine": "A ledge.", - "ids": "2010,2011,2012" - }, - { - "examine": "Known commonly as Red worm vine.", - "ids": "2013" - }, - { - "examine": "A magically elevated chalice full of tears.", - "ids": "2014" - }, - { - "examine": "A magically elevated chalice full of ash.", - "ids": "2015,2016,2017,2018" - }, - { - "examine": "A whirlpool.", - "ids": "2019" - }, - { - "examine": "It's too wet for firewood.", - "ids": "2020,2021" - }, - { - "examine": "A wooden barrel, maybe a way off this rock.", - "ids": "2022" - }, - { - "examine": "An interesting tree with long straight branches.", - "ids": "2023" - }, - { - "examine": "A large bubbling cauldron, smells... Ugh!", - "ids": "2024" - }, - { - "examine": "The door is closed.", - "ids": "2025" - }, - { - "examine": "I can see fish swimming in the water.", - "ids": "2026,2027,2028,2029,2030,2031" - }, - { - "examine": "It's closed.", - "ids": "2032" - }, - { - "examine": "It's open.", - "ids": "2033" - }, - { - "examine": "It's closed.", - "ids": "2034" - }, - { - "examine": "It's open.", - "ids": "2035" - }, - { - "examine": "It's closed.", - "ids": "2036" - }, - { - "examine": "It's open.", - "ids": "2037" - }, - { - "examine": "I've met less intelligent conversationalists.", - "ids": "2038" - }, - { - "examine": "It's closed.", - "ids": "2039,2040,2041,2042" - }, - { - "examine": "Something's bubbling away in it.", - "ids": "2043" - }, - { - "examine": "Like an animal dinner table.", - "ids": "2044,2045,2046,2047" - }, - { - "examine": "It's a door.", - "ids": "2048,2049" - }, - { - "examine": "A wooden gate.", - "ids": "2050,2051,2052,2053" - }, - { - "examine": "It's a door.", - "ids": "2054,2055" - }, - { - "examine": "It's closed.", - "ids": "2056" - }, - { - "examine": "It's open.", - "ids": "2057" - }, - { - "examine": "It's closed.", - "ids": "2058,2059,2060,2061,2062" - }, - { - "examine": "It's open.", - "ids": "2063" - }, - { - "examine": "For storage.", - "ids": "2064" - }, - { - "examine": "Used for climbing.", - "ids": "2065" - }, - { - "examine": "A wall.", - "ids": "2066" - }, - { - "examine": "A watchtower.", - "ids": "2067" - }, - { - "examine": "A fence.", - "ids": "2068" - }, - { - "examine": "A closed door.", - "ids": "2069,2070" - }, - { - "examine": "Used for storage.", - "ids": "2071,2072" - }, - { - "examine": "Grows weird yellow fruit.", - "ids": "2073,2074,2075,2076,2077,2078" - }, - { - "examine": "I wonder what's inside?", - "ids": "2079,2080" - }, - { - "examine": "Handy for boarding boats.", - "ids": "2081,2082,2083,2084,2085,2086,2087,2088" - }, - { - "examine": "Ornamental.", - "ids": "2089" - }, - { - "examine": "A rocky outcrop.", - "ids": "2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111" - }, - { - "examine": "The door is closed.", - "ids": "2112" - }, - { - "examine": "I can climb down this.", - "ids": "2113" - }, - { - "examine": "This transports coal!", - "ids": "2114" - }, - { - "examine": "The left hand side of the gate.", - "ids": "2115" - }, - { - "examine": "The right hand side of the gate.", - "ids": "2116" - }, - { - "examine": "It's an old wall.", - "ids": "2117" - }, - { - "examine": "Lots of water swirling around", - "ids": "2118" - }, - { - "examine": "A rocky outcrop.", - "ids": "2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140" - }, - { - "examine": "An open chest.", - "ids": "2141" - }, - { - "examine": "An eerie glow permeates the cauldron.", - "ids": "2142" - }, - { - "examine": "Looks secure.", - "ids": "2143,2144" - }, - { - "examine": "Ooooh! Spooky!", - "ids": "2145,2146" - }, - { - "examine": "Going down?", - "ids": "2147" - }, - { - "examine": "Going up?", - "ids": "2148" - }, - { - "examine": "An imposing stone structure.", - "ids": "2149" - }, - { - "examine": "A column of elemental power.", - "ids": "2150,2151,2152,2153,29415" - }, - { - "examine": "A wrought iron gate.", - "ids": "2154,2155" - }, - { - "examine": "A magical portal of teleportation.", - "ids": "2156,2157,2158" - }, - { - "examine": "It's a floating barrel.", - "ids": "2159,2160,2161" - }, - { - "examine": "Smells like fish.", - "ids": "2162,2163,2164,2165,2166" - }, - { - "examine": "Water is pouring in through the hole.", - "ids": "2167" - }, - { - "examine": "A patch of swamp tar has bunged up the hole.", - "ids": "2168" - }, - { - "examine": "There's a strong wind whipping up the sea.", - "ids": "2169,2170,2171" - }, - { - "examine": "A huge net to catch little fish.", - "ids": "2172,2173" - }, - { - "examine": "The upper deck can be accessed with this ladder.", - "ids": "2174" - }, - { - "examine": "Back down to the main deck.", - "ids": "2175" - }, - { - "examine": "This old trawler has seen better days.", - "ids": "2176,2177" - }, - { - "examine": "Handy for boarding boats.", - "ids": "2178,2179" - }, - { - "examine": "It's a war machine.", - "ids": "2180,2181" - }, - { - "examine": "It can't hurt to just have a look, can it?", - "ids": "2182" - }, - { - "examine": "I wonder what is inside...", - "ids": "2183" - }, - { - "examine": "The door is closed.", - "ids": "2184" - }, - { - "examine": "This wall has seen better days.", - "ids": "2185" - }, - { - "examine": "Hmmm... I wonder if it's loose enough to get through.", - "ids": "2186" - }, - { - "examine": "I can climb down this ladder.", - "ids": "2187" - }, - { - "examine": "I can climb up this ladder.", - "ids": "2188,2189" - }, - { - "examine": "I can climb down this ladder.", - "ids": "2190" - }, - { - "examine": "I wonder what's inside?", - "ids": "2191,2192,2193" - }, - { - "examine": "Perhaps I should search it.", - "ids": "2194,2195,2196" - }, - { - "examine": "I wonder what's inside?", - "ids": "2197" - }, - { - "examine": "Perhaps I should search it.", - "ids": "2198" - }, - { - "examine": "An old gate for locking up undesirables.", - "ids": "2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209" - }, - { - "examine": "Looking through it makes far off things look closer!", - "ids": "2210" - }, - { - "examine": "A weathered old gravestone.", - "ids": "2211" - }, - { - "examine": "Looks like fun.", - "ids": "2212" - }, - { - "examine": "The bank teller will serve you from here.", - "ids": "2213" - }, - { - "examine": "This booth is for private customers only.", - "ids": "2214,34205" - }, - { - "examine": "This booth is closed.", - "ids": "2215" - }, - { - "examine": "This looks like it's being used to block entrance to the village.", - "ids": "2216" - }, - { - "examine": "Something seems to be buried under this.", - "ids": "2217" - }, - { - "examine": "A narrow fissure in the ground.", - "ids": "2218,2219" - }, - { - "examine": "A pile of rubble.", - "ids": "2220" - }, - { - "examine": "A stone with some carvings on it.", - "ids": "2221" - }, - { - "examine": "A pile of loose rocks.", - "ids": "2222" - }, - { - "examine": "Probably nothing useful in these.", - "ids": "2223" - }, - { - "examine": "A rotten and barely standing set of gallows.", - "ids": "2224" - }, - { - "examine": "Wet rocks, it looks like you might be able to climb them.", - "ids": "2225" - }, - { - "examine": "This table has seen better days.", - "ids": "2226" - }, - { - "examine": "A crudely constructed raft.", - "ids": "2227,2228" - }, - { - "examine": "A statue to mark Tai Bwo Wannai sacred grounds.", - "ids": "2229" - }, - { - "examine": "A sturdy cart for travelling in.", - "ids": "2230" - }, - { - "examine": "A rocky outcrop.", - "ids": "2231,2232,2233" - }, - { - "examine": "Rocks that have been stacked at regular intervals.", - "ids": "2234" - }, - { - "examine": "An ancient construct for supporting the bones of the deceased.", - "ids": "2235" - }, - { - "examine": "I can climb the rocky outcrop.", - "ids": "2236" - }, - { - "examine": "An exotic looking tree.", - "ids": "2237" - }, - { - "examine": "Large doors set into the hillside.", - "ids": "2238,2239" - }, - { - "examine": "These doors must have been hidden here for years.", - "ids": "2240,2241" - }, - { - "examine": "Perhaps you should give them a push.", - "ids": "2242,2243" - }, - { - "examine": "Commonly found in these parts.", - "ids": "2244" - }, - { - "examine": "A leafy fern.", - "ids": "2245" - }, - { - "examine": "Large carved tomb doors, they look terrifying.", - "ids": "2246,2247,2248,2249,2250,2251,2252" - }, - { - "examine": "Perhaps you should give them a push.", - "ids": "2253,2254" - }, - { - "examine": "An ancient metal gate, but still pretty secure.", - "ids": "2255,2256" - }, - { - "examine": "A rocky outcrop.", - "ids": "2257" - }, - { - "examine": "An ancient construct for supporting the bones of the deceased.", - "ids": "2258" - }, - { - "examine": "The gate is closed.", - "ids": "2259,2260,2261,2262,2263,2264" - }, - { - "examine": "A sturdy cart for travelling in.", - "ids": "2265" - }, - { - "examine": "The door is closed.", - "ids": "2266,2267" - }, - { - "examine": "This leads up to the sleeping area.", - "ids": "2268" - }, - { - "examine": "This leads downwards.", - "ids": "2269" - }, - { - "examine": "A jungle plant.", - "ids": "2270" - }, - { - "examine": "I wonder what's inside?", - "ids": "2271" - }, - { - "examine": "It's open.", - "ids": "2272" - }, - { - "examine": "An odd-looking rock with a rope tied to it.", - "ids": "2273,2274" - }, - { - "examine": "An odd-looking rock formation.", - "ids": "2275,2276,2277,2278" - }, - { - "examine": "Holds the rope up. Hopefuly.", - "ids": "2279,2280,2281" - }, - { - "examine": "Use this to swing over crevices.", - "ids": "2282,2283" - }, - { - "examine": "This must be climbed over.", - "ids": "2284,2285,2286" - }, - { - "examine": "It's hollow...", - "ids": "2289" - }, - { - "examine": "A slippery log I can walk across.", - "ids": "2294,2295,2296,2297" - }, - { - "examine": "The irregular surface can be climbed.", - "ids": "2298,2299,2300" - }, - { - "examine": "Tread carefully!", - "ids": "2301,2302" - }, - { - "examine": "I can just about edge across here.", - "ids": "2303" - }, - { - "examine": "Pointy!", - "ids": "2304,2305,2306" - }, - { - "examine": "A wrought iron gate.", - "ids": "2307,2308" - }, - { - "examine": "Solid bars of iron.", - "ids": "2309" - }, - { - "examine": "This tree has been cut down.", - "ids": "2310" - }, - { - "examine": "I can jump from this stepping stone.", - "ids": "2311" - }, - { - "examine": "I can balance on this rope.", - "ids": "2312" - }, - { - "examine": "I can climb up this.", - "ids": "2313" - }, - { - "examine": "I can climb down here.", - "ids": "2314,2315" - }, - { - "examine": "I can climb up these stairs.", - "ids": "2316,36776" - }, - { - "examine": "I can climb up these rocks to another cave.", - "ids": "2317" - }, - { - "examine": "I can climb down these rocks to another cave.", - "ids": "2318" - }, - { - "examine": "I can traverse these.", - "ids": "2319,2320,2321,12573,12574,12575,29375" - }, - { - "examine": "Use this to swing across gaps.", - "ids": "2322,2323,2324" - }, - { - "examine": "I can swing on this tree.", - "ids": "2325" - }, - { - "examine": "A branch protrudes here.", - "ids": "2326" - }, - { - "examine": "This tree has an unusually long branch on it.", - "ids": "2327" - }, - { - "examine": "A rocky outcrop.", - "ids": "2328,2329,2330,2331" - }, - { - "examine": "A nearly rotten wooden log that crosses the river.", - "ids": "2332" - }, - { - "examine": "A rocky outcrop from the running water.", - "ids": "2333,2334,2335" - }, - { - "examine": "You can see a cauldron directly below.", - "ids": "2336" - }, - { - "examine": "This door is very sturdy looking.", - "ids": "2337" - }, - { - "examine": "This door is closed.", - "ids": "2338" - }, - { - "examine": "This door is very sturdy looking.", - "ids": "2339,2340" - }, - { - "examine": "There is something strange about this wall...", - "ids": "2341" - }, - { - "examine": "Looks like a ventilation grill.", - "ids": "2342" - }, - { - "examine": "A large stone block.", - "ids": "2343,2344,2345,2346,2347,2348,2349" - }, - { - "examine": "Used to move earth to, and from, the dig shaft.", - "ids": "2350,2351" - }, - { - "examine": "This leads back up the dig shaft.", - "ids": "2352,2353" - }, - { - "examine": "Yep, they're sacks!", - "ids": "2354,2355,2356" - }, - { - "examine": "A leafy bush.", - "ids": "2357,2358" - }, - { - "examine": "A sealed barrel with a warning sign on it.", - "ids": "2359" - }, - { - "examine": "I wonder what's inside...", - "ids": "2360" - }, - { - "examine": "A sturdy chest to keep things in.", - "ids": "2361" - }, - { - "examine": "A large stone block.", - "ids": "2362" - }, - { - "examine": "A place where you can pan for gold.", - "ids": "2363" - }, - { - "examine": "A wooden container.", - "ids": "2364,2365" - }, - { - "examine": "A signpost!", - "ids": "2366,2367,2368,2369,2370,2371" - }, - { - "examine": "Shelves filled with interesting books.", - "ids": "2372" - }, - { - "examine": "A cupboard.", - "ids": "2373,2374" - }, - { - "examine": "Used to keep specimens on.", - "ids": "2375" - }, - { - "examine": "Soil", - "ids": "2376,2377,2378" - }, - { - "examine": "Technically a bed.", - "ids": "2379" - }, - { - "examine": "I wonder what's inside...", - "ids": "2380" - }, - { - "examine": "Swampy dirt.", - "ids": "2381,2382,2383,2384,2385,2386,2387,2388,2389,2390" - }, - { - "examine": "A very solid looking gate.", - "ids": "2391,2392" - }, - { - "examine": "Aim for the goal!", - "ids": "2393" - }, - { - "examine": "A gate to the Gnomeball pitch.", - "ids": "2394,2395" - }, - { - "examine": "Distinctly feminine.", - "ids": "2396" - }, - { - "examine": "A solid looking door.", - "ids": "2397,2398,2399" - }, - { - "examine": "Might be worth searching.", - "ids": "2400,2401" - }, - { - "examine": "A good source of books!", - "ids": "2402" - }, - { - "examine": "Might be worth searching.", - "ids": "2403,2404" - }, - { - "examine": "I can climb this.", - "ids": "2405" - }, - { - "examine": "The door is closed.", - "ids": "2406,2407" - }, - { - "examine": "I can climb down this.", - "ids": "2408" - }, - { - "examine": "A commonly found tree.", - "ids": "2409" - }, - { - "examine": "I can climb this.", - "ids": "2410" - }, - { - "examine": "The door is closed.", - "ids": "2411" - }, - { - "examine": "Handy for boarding the ship.", - "ids": "2412,2413,2414,2415" - }, - { - "examine": "Pull me!", - "ids": "2416" - }, - { - "examine": "Place party drop items here.", - "ids": "2417,2418,2419,2420" - }, - { - "examine": "This lever is down.", - "ids": "2421" - }, - { - "examine": "This lever is up.", - "ids": "2422" - }, - { - "examine": "This lever is down.", - "ids": "2423" - }, - { - "examine": "This lever is up.", - "ids": "2424" - }, - { - "examine": "This lever is down.", - "ids": "2425" - }, - { - "examine": "This lever is up.", - "ids": "2426" - }, - { - "examine": "This door is closed by an unknown mechanism.", - "ids": "2427,2428,2429,2430,2431" - }, - { - "examine": "This gate is closed shut.", - "ids": "2432,2433" - }, - { - "examine": "This may be worth opening.", - "ids": "2434,2435" - }, - { - "examine": "I wonder what's inside.", - "ids": "2436,2437" - }, - { - "examine": "A wooden gate.", - "ids": "2438,2439" - }, - { - "examine": "Wonder what this pillar is for?", - "ids": "2440,2441,2442,2443" - }, - { - "examine": "I wonder what's under it?", - "ids": "2444,2445" - }, - { - "examine": "This leads to somewhere...but where?", - "ids": "2446" - }, - { - "examine": "I reckon I could climb that!", - "ids": "2447,2448" - }, - { - "examine": "Some magical rocks.", - "ids": "2449,2450" - }, - { - "examine": "There's something behind these roots.", - "ids": "2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464" - }, - { - "examine": "A portal from this mystical place.", - "ids": "2465,2466,2467,2468,2469,2470,2471,2472,2473,2474,2475,2476,2477" - }, - { - "examine": "A mysterious power emanates from this shrine.", - "ids": "2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490" - }, - { - "examine": "The source of all Rune Stones.", - "ids": "2491" - }, - { - "examine": "A portal from this mystical place.", - "ids": "2492" - }, - { - "examine": "A part of an old temple.", - "ids": "2493,2494,2495,2496" - }, - { - "examine": "Unusual energy is gathered here.", - "ids": "2497,2498,2499,2500,2501,2502" - }, - { - "examine": "An old crumbled pillar.", - "ids": "2503,2504,2505,2506,2507" - }, - { - "examine": "Broken parts of an old temple.", - "ids": "2508,2509,2510" - }, - { - "examine": "This leads to the practice tower.", - "ids": "2511" - }, - { - "examine": "This leads back down to the guild.", - "ids": "2512" - }, - { - "examine": "I wonder if I can hit a bullseye?", - "ids": "2513" - }, - { - "examine": "The door to the Ranging Guild.", - "ids": "2514" - }, - { - "examine": "I hope it doesn't sink.", - "ids": "2515" - }, - { - "examine": "It's sinking!", - "ids": "2516" - }, - { - "examine": "Going up?", - "ids": "2517" - }, - { - "examine": "Typical shoddy workmanship.", - "ids": "2518" - }, - { - "examine": "Crates of packed fish.", - "ids": "2519" - }, - { - "examine": "It's used for loading and unloading fishing boats.", - "ids": "2520,2521" - }, - { - "examine": "It's a flight of stairs.", - "ids": "2522,2523" - }, - { - "examine": "I wonder what's inside?", - "ids": "2524" - }, - { - "examine": "It's open.", - "ids": "2525" - }, - { - "examine": "The door of a small prison.", - "ids": "2526,2527" - }, - { - "examine": "The door is closed.", - "ids": "2528" - }, - { - "examine": "A door.", - "ids": "2529" - }, - { - "examine": "Used for storage.", - "ids": "2530" - }, - { - "examine": "Looks like someone's tried digging here.", - "ids": "2531,2532" - }, - { - "examine": "A pile of what is hopefully mud.", - "ids": "2533" - }, - { - "examine": "There's black cross on the door.", - "ids": "2534" - }, - { - "examine": "The door is closed.", - "ids": "2535" - }, - { - "examine": "A door.", - "ids": "2536" - }, - { - "examine": "The door is closed.", - "ids": "2537" - }, - { - "examine": "A door.", - "ids": "2538" - }, - { - "examine": "A flight of stairs.", - "ids": "2539,2540" - }, - { - "examine": "An outlet to the sewer.", - "ids": "2541,2542" - }, - { - "examine": "There's a cover over this manhole.", - "ids": "2543" - }, - { - "examine": "How dangerous, someone has left this manhole open.", - "ids": "2544" - }, - { - "examine": "This is supposed to stop people falling down the manhole.", - "ids": "2545" - }, - { - "examine": "A door.", - "ids": "2546,2547,2548,2549" - }, - { - "examine": "A secure door.", - "ids": "2550,2551" - }, - { - "examine": "A metal gate bars your way.", - "ids": "2552,2553" - }, - { - "examine": "A secure door.", - "ids": "2554,2555,2556,2557,2558,2559" - }, - { - "examine": "Fine silk woven by experts.", - "ids": "2560" - }, - { - "examine": "This stall smells great.", - "ids": "2561" - }, - { - "examine": "Precious stones from around the world.", - "ids": "2562" - }, - { - "examine": "All manner of animal clothing.", - "ids": "2563" - }, - { - "examine": "Spices to tingle your taste buds.", - "ids": "2564" - }, - { - "examine": "Fine silver items are for sale here.", - "ids": "2565" - }, - { - "examine": "I wonder what's inside it.", - "ids": "2566,2567,2568,2569,2570,2571,2572,2573,2574" - }, - { - "examine": "A marshy jungle vine.", - "ids": "2575,2576" - }, - { - "examine": "A tall palm tree with some plants growing at its stem.", - "ids": "2577" - }, - { - "examine": "A tall palm tree.", - "ids": "2578" - }, - { - "examine": "The burnt ground has a plant growing on it.", - "ids": "2579" - }, - { - "examine": "The ground has been burnt here by a fire.", - "ids": "2580" - }, - { - "examine": "A rock with green moss growing on it.", - "ids": "2581" - }, - { - "examine": "A rock, it looks like something was growing here.", - "ids": "2582" - }, - { - "examine": "It looks as if it is covered in some fungus.", - "ids": "2583" - }, - { - "examine": "A collection of rocks over what looks like a depression.", - "ids": "2584" - }, - { - "examine": "Rocky walls, you may be able to climb it.", - "ids": "2585" - }, - { - "examine": "It's closed.", - "ids": "2586" - }, - { - "examine": "I wonder what's inside?", - "ids": "2587" - }, - { - "examine": "It's open.", - "ids": "2588" - }, - { - "examine": "This ship's not getting far with that.", - "ids": "2589" - }, - { - "examine": "Takes me into the ship.", - "ids": "2590,2591" - }, - { - "examine": "Takes me back on deck.", - "ids": "2592" - }, - { - "examine": "Handy for boarding the ship.", - "ids": "2593,2594" - }, - { - "examine": "It's closed.", - "ids": "2595" - }, - { - "examine": "A blood-red door.", - "ids": "2596" - }, - { - "examine": "A lurid orange door.", - "ids": "2597" - }, - { - "examine": "A sickly yellow door.", - "ids": "2598" - }, - { - "examine": "A gloomy blue door.", - "ids": "2599" - }, - { - "examine": "A groovy magenta door.", - "ids": "2600" - }, - { - "examine": "A putrid green door.", - "ids": "2601" - }, - { - "examine": "Probably the nicest door in this ghastly place.", - "ids": "2602" - }, - { - "examine": "It's closed.", - "ids": "2603" - }, - { - "examine": "It's open.", - "ids": "2604" - }, - { - "examine": "It's a ladder.", - "ids": "2605" - }, - { - "examine": "There is something strange about this wall...", - "ids": "2606" - }, - { - "examine": "It's closed.", - "ids": "2607,2608" - }, - { - "examine": "A collection of rocks over what looks like a depression.", - "ids": "2609" - }, - { - "examine": "A rope hangs here that I can climb.", - "ids": "2610" - }, - { - "examine": "It's a ladder.", - "ids": "2611" - }, - { - "examine": "Wonder what's in here...", - "ids": "2612" - }, - { - "examine": "Smells kind of funny...", - "ids": "2613" - }, - { - "examine": "Looks comfortable...", - "ids": "2615" - }, - { - "examine": "Looks spooky...", - "ids": "2616" - }, - { - "examine": "Phew, an exit!", - "ids": "2617" - }, - { - "examine": "The fence is broken at this point.", - "ids": "2618" - }, - { - "examine": "A wooden barrel for storage.", - "ids": "2619" - }, - { - "examine": "An old crate for storage.", - "ids": "2620" - }, - { - "examine": "It's a door.", - "ids": "2621,2622" - }, - { - "examine": "It's a gate.", - "ids": "2623" - }, - { - "examine": "The entrance to the Hero's Guild.", - "ids": "2624,2625" - }, - { - "examine": "It's a door.", - "ids": "2626,2627,2628" - }, - { - "examine": "There is something unusual about this wall...", - "ids": "2629" - }, - { - "examine": "I can see eels swimming in the lava.", - "ids": "2630" - }, - { - "examine": "It's a door.", - "ids": "2631" - }, - { - "examine": "I wonder what's inside?", - "ids": "2632" - }, - { - "examine": "It's open.", - "ids": "2633" - }, - { - "examine": "Rocks that contain nothing interesting, but block the way.", - "ids": "2634" - }, - { - "examine": "I wonder what's inside?", - "ids": "2635" - }, - { - "examine": "It's open.", - "ids": "2636,2637" - }, - { - "examine": "A source of pure water.", - "ids": "2638,2639" - }, - { - "examine": "A shrine for the faithful to worship at.", - "ids": "2640" - }, - { - "examine": "I can climb this.", - "ids": "2641" - }, - { - "examine": "Used for fashioning clay items.", - "ids": "2642" - }, - { - "examine": "Bake your clay items in here.", - "ids": "2643" - }, - { - "examine": "Used for spinning thread.", - "ids": "2644" - }, - { - "examine": "A tray of sand.", - "ids": "2645" - }, - { - "examine": "A plant cultivated for fibres.", - "ids": "2646" - }, - { - "examine": "The door to the Crafting Guild.", - "ids": "2647,2648,2649" - }, - { - "examine": "A pile of smelly rotting waste.", - "ids": "2650" - }, - { - "examine": "A sturdy home for bees.", - "ids": "2651" - }, - { - "examine": "Looks like this is where waste from the kitchen comes out.", - "ids": "2652" - }, - { - "examine": "It looks like a spiders' nest to me.", - "ids": "2653" - }, - { - "examine": "This fountain suits the garden.", - "ids": "2654" - }, - { - "examine": "It's the Sinclair Family coat of arms.", - "ids": "2655" - }, - { - "examine": "Looks like this is where Anna keeps her stuff.", - "ids": "2656" - }, - { - "examine": "Looks like this is where Bob keeps his stuff.", - "ids": "2657" - }, - { - "examine": "Looks like this is where Carol keeps her stuff.", - "ids": "2658" - }, - { - "examine": "Looks like this is where David keeps his stuff.", - "ids": "2659" - }, - { - "examine": "Looks like this is where Elizabeth keeps her stuff.", - "ids": "2660" - }, - { - "examine": "Looks like this is where Frank keeps his stuff.", - "ids": "2661" - }, - { - "examine": "Looks like this is where the cook stores flour.", - "ids": "2662" - }, - { - "examine": "Some sacks of general garden items.", - "ids": "2663" - }, - { - "examine": "Luckily it seems able to keep that vicious dog inside.", - "ids": "2664,2665" - }, - { - "examine": "Looks like the killer smashed this to leave the mansion.", - "ids": "2666" - }, - { - "examine": "Slaves are using this to pull up barrels of rocks from down below.", - "ids": "2667" - }, - { - "examine": "Slaves are placing barrels on this to haul them to the surface.", - "ids": "2668,2669" - }, - { - "examine": "A succulent cactus.", - "ids": "2670" - }, - { - "examine": "A less-than-succulent succulent.", - "ids": "2671" - }, - { - "examine": "I can try to make new items on this.", - "ids": "2672" - }, - { - "examine": "A solid looking gate.", - "ids": "2673,2674" - }, - { - "examine": "Large doors made of solid oak.", - "ids": "2675,2676" - }, - { - "examine": "A solid looking chest, it belongs to Captain Siad.", - "ids": "2677" - }, - { - "examine": "A selection of Captain Siad's Books.", - "ids": "2678" - }, - { - "examine": "A sturdy looking desk on which the Captain works.", - "ids": "2679" - }, - { - "examine": "A barrel containing rocks and mining debris.", - "ids": "2680" - }, - { - "examine": "An empty mining barrel.", - "ids": "2681" - }, - { - "examine": "The cart which takes barrels of rocks from the mining encampment to Al-Kharid.", - "ids": "2682" - }, - { - "examine": "Gives a lovely view of the desert, complete with metalic bars.", - "ids": "2683" - }, - { - "examine": "It seems to be used for moving rocks into and out of this area.", - "ids": "2684" - }, - { - "examine": "A solid looking gate.", - "ids": "2685,2686,2687,2688" - }, - { - "examine": "There's no getting around it, it's a fairly sturdy prison cell door.", - "ids": "2689" - }, - { - "examine": "Large solid, thick set oak doors", - "ids": "2690,2691" - }, - { - "examine": "It looks fairly sturdy.", - "ids": "2692" - }, - { - "examine": "This is used by Shantay and his men to take items to the bank on your behalf.", - "ids": "2693" - }, - { - "examine": "A rocky rock.", - "ids": "2694,2695,2696,2697" - }, - { - "examine": "A cave which has been mined out.", - "ids": "2698,2699" - }, - { - "examine": "The entrance to the tent.", - "ids": "2700,2701" - }, - { - "examine": "Tracks in the sand.", - "ids": "2702,2703" - }, - { - "examine": "Rocky!", - "ids": "2704" - }, - { - "examine": "It's a door.", - "ids": "2705" - }, - { - "examine": "A nicely fitted door.", - "ids": "2706" - }, - { - "examine": "A wooden crate for storage.", - "ids": "2707,2708" - }, - { - "examine": "I wonder what's inside?", - "ids": "2709" - }, - { - "examine": "It's open.", - "ids": "2710" - }, - { - "examine": "It's a flight of stairs.", - "ids": "2711" - }, - { - "examine": "The door is closed.", - "ids": "2712" - }, - { - "examine": "Grain goes in here.", - "ids": "2713,2714,2715,2716,2717" - }, - { - "examine": "These control the flow of grain from the hopper to the millstones.", - "ids": "2718,2719,2720,2721,2722" - }, - { - "examine": "A spicy meat kebab is cooking here.", - "ids": "2723" - }, - { - "examine": "A fire burns brightly here.", - "ids": "2724" - }, - { - "examine": "A grand old fireplace.", - "ids": "2725,2726" - }, - { - "examine": "Something is cooking nicely here.", - "ids": "2727" - }, - { - "examine": "Ideal for cooking on.", - "ids": "2728,2729,2730,2731,3039,9682,12102,36973,40110" - }, - { - "examine": "Hot!", - "ids": "2732,2733,2734,2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765,2766,2767,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779,2780" - }, - { - "examine": "A hot place for forging things in.", - "ids": "2781" - }, - { - "examine": "It says \"Property of Doric the dwarf.\" on the side.", - "ids": "2782" - }, - { - "examine": "Used for fashioning metal items.", - "ids": "2783" - }, - { - "examine": "Various implements for working with metal.", - "ids": "2784" - }, - { - "examine": "A hot place for forging things in.", - "ids": "2785" - }, - { - "examine": "An entrance to Gu'Tanoth.", - "ids": "2786,2787,2788,2789" - }, - { - "examine": "I wonder what's inside it.", - "ids": "2790" - }, - { - "examine": "A counter made from a stone block.", - "ids": "2791,2792,2876,3800,29356" - }, - { - "examine": "An ogre is selling cakes here.", - "ids": "2793" - }, - { - "examine": "A lever for activating something.", - "ids": "2794,2795" - }, - { - "examine": "This leads to the watchtower.", - "ids": "2796" - }, - { - "examine": "This leads downwards.", - "ids": "2797" - }, - { - "examine": "A leafy bush.", - "ids": "2798,2799,2800,2801,2802,2803" - }, - { - "examine": "I think I can squeeze through here...", - "ids": "2804,2805,2806,2807,2808,2809,2810,2811" - }, - { - "examine": "This leads downwards.", - "ids": "2812" - }, - { - "examine": "I think I can squeeze through here...", - "ids": "2813" - }, - { - "examine": "The gate has been locked shut.", - "ids": "2814,2815" - }, - { - "examine": "A strange looking rock...", - "ids": "2816" - }, - { - "examine": "I think I can get out here...", - "ids": "2817,2818,2819,2820,2821,2822" - }, - { - "examine": "A hole.", - "ids": "2823" - }, - { - "examine": "A hole", - "ids": "2824" - }, - { - "examine": "I hope this leads the way out.", - "ids": "2825" - }, - { - "examine": "I wonder what's inside it?", - "ids": "2826,2827,2828,2829" - }, - { - "examine": "You can probably jump over from here.", - "ids": "2830,2831" - }, - { - "examine": "A low wall is blocking your path.", - "ids": "2832" - }, - { - "examine": "This goes up!", - "ids": "2833" - }, - { - "examine": "A low wall is blocking your path.", - "ids": "2834" - }, - { - "examine": "Looks hard.", - "ids": "2835,2836,2837,2838,2839,2840,2841" - }, - { - "examine": "It has a special magical quality.", - "ids": "2842" - }, - { - "examine": "This drain leads from the sink to the sewers below.", - "ids": "2843" - }, - { - "examine": "This looks like it can be turned.", - "ids": "2844,2845,2846,2847,2848" - }, - { - "examine": "A rudimentary type of boat.", - "ids": "2849" - }, - { - "examine": "It's closed.", - "ids": "2850" - }, - { - "examine": "It's open.", - "ids": "2851" - }, - { - "examine": "I can probably squeeze my way inside there.", - "ids": "2852" - }, - { - "examine": "It's a flight of stairs.", - "ids": "2853" - }, - { - "examine": "Made of wood.", - "ids": "2854,2855" - }, - { - "examine": "I wonder what's inside?", - "ids": "2856" - }, - { - "examine": "It's open.", - "ids": "2857" - }, - { - "examine": "Used for storage.", - "ids": "2858" - }, - { - "examine": "A hot range, where some soup is bubbling nicely.", - "ids": "2859" - }, - { - "examine": "Very ominous.", - "ids": "2860" - }, - { - "examine": "It's a door.", - "ids": "2861,2862" - }, - { - "examine": "The door is closed.", - "ids": "2863" - }, - { - "examine": "A water feature.", - "ids": "2864" - }, - { - "examine": "It's a gate.", - "ids": "2865,2866" - }, - { - "examine": "Very decorative!", - "ids": "2867" - }, - { - "examine": "It's a cupboard.", - "ids": "2868,2869" - }, - { - "examine": "The mouse equivalent of a door.", - "ids": "2870" - }, - { - "examine": "I can climb down this.", - "ids": "2871" - }, - { - "examine": "I can climb this.", - "ids": "2872" - }, - { - "examine": "What a good likeness!", - "ids": "2873" - }, - { - "examine": "A depiction that chills one to the bone.", - "ids": "2874" - }, - { - "examine": "I wonder how accurate a representation this is.", - "ids": "2875" - }, - { - "examine": "A barrel full of staffs...", - "ids": "2877" - }, - { - "examine": "Patterns of light dance hypnotically across the pool's surface.", - "ids": "2878,2879" - }, - { - "examine": "There seems to be some sort of magical field here.", - "ids": "2880" - }, - { - "examine": "It's closed.", - "ids": "2881" - }, - { - "examine": "The toll gate from Lumbridge to Al-Kharid.", - "ids": "2882,2883" - }, - { - "examine": "I can climb this.", - "ids": "2884" - }, - { - "examine": "An antique by anyone's standards.", - "ids": "2885" - }, - { - "examine": "An antique by anyone's standards, however, it might have something interesting in it.", - "ids": "2886" - }, - { - "examine": "Looks good for logging.", - "ids": "2887" - }, - { - "examine": "A leafy jungle palm, dense foliage.", - "ids": "2888" - }, - { - "examine": "Home to many unusual creatures.", - "ids": "2889" - }, - { - "examine": "A leafy tree.", - "ids": "2890" - }, - { - "examine": "This tree has been cut down.", - "ids": "2891" - }, - { - "examine": "A jungle bush, quite common to these areas.", - "ids": "2892,2893" - }, - { - "examine": "A jungle bush that has been chopped down.", - "ids": "2894,2895" - }, - { - "examine": "A door to the Legends Guild.", - "ids": "2896,2897,2898,2899" - }, - { - "examine": "Looks slippery!", - "ids": "2900,2901" - }, - { - "examine": "Stoney!", - "ids": "2902" - }, - { - "examine": "A rather shadowy cavern entrance.", - "ids": "2903,2904" - }, - { - "examine": "An old crate for storage.", - "ids": "2905" - }, - { - "examine": "Useful for putting things on.", - "ids": "2906" - }, - { - "examine": "I guess I could sleep in it if I was really tired.", - "ids": "2907" - }, - { - "examine": "A wall of magic fire.", - "ids": "2908,2909" - }, - { - "examine": "Rickety bamboo tables that are lashed together to make a desk.", - "ids": "2910" - }, - { - "examine": "Bamboo storage!", - "ids": "2911" - }, - { - "examine": "This gate has a horribly complex locking mechanism.", - "ids": "2912,2913,2914,2915" - }, - { - "examine": "A dark cave entrance leading to the surface.", - "ids": "2916,2917" - }, - { - "examine": "A large crack in the wall.", - "ids": "2918" - }, - { - "examine": "A huge lump of rock.", - "ids": "2919,2920,2921" - }, - { - "examine": "A heavily constructed, cast-iron, ancient gateway.", - "ids": "2922,2923,2924,2925" - }, - { - "examine": "A high wall made up of rocks with sharp edges.", - "ids": "2926" - }, - { - "examine": "It looks like ancient graffiti.", - "ids": "2927" - }, - { - "examine": "An ornately carved rock with a pointed receptacle.", - "ids": "2928" - }, - { - "examine": "A half buried skeleton, probably dwarven remains.", - "ids": "2929" - }, - { - "examine": "A huge, strangely constructed doorway, not sure how this opens.", - "ids": "2930" - }, - { - "examine": "It looks like a magical portal of some kind, who knows where it leads?", - "ids": "2931" - }, - { - "examine": "Strangely, there's a locked barrel here, I wonder what's inside.", - "ids": "2932,2933" - }, - { - "examine": "A large scaffold platform most likely used for lifting and lowering heavy items.", - "ids": "2934" - }, - { - "examine": "Used to move earth to, and from, the dig shaft. A rope has been thrown over it.", - "ids": "2935" - }, - { - "examine": "A sculpted trunk of wood.", - "ids": "2936,2937,2938,2939" - }, - { - "examine": "A wonderfully dressed table.", - "ids": "2940" - }, - { - "examine": "Sparkling sacred water straight from the source.", - "ids": "2941" - }, - { - "examine": "A sparkling, babbling flow of water from an underground source.", - "ids": "2942" - }, - { - "examine": "Disgusting filthy quagmire that oozes from the ground.", - "ids": "2943" - }, - { - "examine": "Found near the water's edge.", - "ids": "2944" - }, - { - "examine": "A baby Yommi tree, it has a mystical aura.", - "ids": "2945" - }, - { - "examine": "An adolescent-looking Yommi tree, it has a mystical aura.", - "ids": "2946" - }, - { - "examine": "A dead Yommi tree sapling, you'll need a tough axe to remove this.", - "ids": "2947" - }, - { - "examine": "A fully grown Yommi tree, it won't get much taller than this.", - "ids": "2948" - }, - { - "examine": "A dead fully grown Yommi tree, you'll need a tough, sharp axe to remove this.", - "ids": "2949" - }, - { - "examine": "A felled adult Yommi tree, perhaps you should trim those branches?", - "ids": "2950" - }, - { - "examine": "A rotten felled Yommi tree, it was left too long in the damp jungle.", - "ids": "2951" - }, - { - "examine": "A trimmed Yommi tree log, perfect for sculpting into a totem pole.", - "ids": "2952" - }, - { - "examine": "A rotten Yommi tree log, it was left too long in the damp jungle air.", - "ids": "2953" - }, - { - "examine": "A beautifully crafted totem pole carved from the trunk of the sacred Yommi tree.", - "ids": "2954" - }, - { - "examine": "A rotten totem pole, it was left to rot in the jungle.", - "ids": "2955" - }, - { - "examine": "This earth is particularly fertile.", - "ids": "2956" - }, - { - "examine": "This earth is damaged by cultivation, time will restore the fertility.", - "ids": "2957" - }, - { - "examine": "A rope hangs here.", - "ids": "2958" - }, - { - "examine": "A dangerous path onwards.", - "ids": "2959,2960,2961" - }, - { - "examine": "Stoney!", - "ids": "2962,2963,2964" - }, - { - "examine": "Looks slippery!", - "ids": "2965" - }, - { - "examine": "A hot place for forging things in.", - "ids": "2966,2967,2968" - }, - { - "examine": "It looks like a strangely shaped depression in the rock.", - "ids": "2969" - }, - { - "examine": "This recess has a glowing heart shaped crystal in it.", - "ids": "2970" - }, - { - "examine": "This looks like some sort of shimmering surface.", - "ids": "2971" - }, - { - "examine": "A huge lump of rock.", - "ids": "2972,2973,2974" - }, - { - "examine": "This tree is particularly leafy.", - "ids": "2975,2976" - }, - { - "examine": "A large pile of sand", - "ids": "2977" - }, - { - "examine": "A medium pile of sand", - "ids": "2978" - }, - { - "examine": "A small pile of sand", - "ids": "2979" - }, - { - "examine": "Some beautiful flowers.", - "ids": "2980,2981,2982,2983,2984,2985,2986,2987,2988" - }, - { - "examine": "Known commonly as Red worm vine.", - "ids": "2989,2990,2991,2992,2993,2994" - }, - { - "examine": "I wonder what's inside.", - "ids": "2995,2996" - }, - { - "examine": "The door is closed.", - "ids": "2997" - }, - { - "examine": "The door is open.", - "ids": "2998,2999" - }, - { - "examine": "These obviously mean keep out!", - "ids": "3000,3001,3002,3003,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013" - }, - { - "examine": "A nicely fitted door.", - "ids": "3014" - }, - { - "examine": "A wooden gate.", - "ids": "3015,3016" - }, - { - "examine": "A nicely fitted door.", - "ids": "3017,3018,3019" - }, - { - "examine": "A wrought iron gate.", - "ids": "3020,3021,3022,3023" - }, - { - "examine": "A closed door.", - "ids": "3024,3025,3026,3027" - }, - { - "examine": "I can climb this.", - "ids": "3028" - }, - { - "examine": "I can climb down this.", - "ids": "3029" - }, - { - "examine": "I can climb this.", - "ids": "3030" - }, - { - "examine": "I can climb down this.", - "ids": "3031" - }, - { - "examine": "I can see fish swimming in the water.", - "ids": "3032" - }, - { - "examine": "One of the most common trees in RuneScape.", - "ids": "3033,3034,3035,3036" - }, - { - "examine": "A beautiful old oak.", - "ids": "3037" - }, - { - "examine": "Hot!", - "ids": "3038" - }, - { - "examine": "It's closed.", - "ids": "3040" - }, - { - "examine": "It's open.", - "ids": "3041" - }, - { - "examine": "A rocky outcrop.", - "ids": "3042,3043" - }, - { - "examine": "A hot place for forging things in.", - "ids": "3044" - }, - { - "examine": "The bank teller will serve you from here.", - "ids": "3045" - }, - { - "examine": "These obviously mean keep out!", - "ids": "3046,3047,3048,3049,3050,3051,3052,3053,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068,3069,3070,3071,3072,3073,3074,3075,3076" - }, - { - "examine": "Lean against the wall to get a better view!", - "ids": "3077,3078,3079,3080,3081,3082,3083,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107" - }, - { - "examine": "Flags marking the entrance to the arena.", - "ids": "3108,3109,3110,3111,3112,3113,3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158" - }, - { - "examine": "Beats the operating table.", - "ids": "3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3178,3179,3180,3181" - }, - { - "examine": "Wait here for a nurse.", - "ids": "3182" - }, - { - "examine": "Storage for drugs and bandages.", - "ids": "3183,3184,3185" - }, - { - "examine": "I don't really want to end up here...", - "ids": "3186,3187,3188,3189" - }, - { - "examine": "These open and close!", - "ids": "3190" - }, - { - "examine": "This may be worth searching.", - "ids": "3191" - }, - { - "examine": "You can see the last 50 fights on here.", - "ids": "3192" - }, - { - "examine": "Used to store players' items whilst they duel.", - "ids": "3193" - }, - { - "examine": "Use for quick access to your bank.", - "ids": "3194" - }, - { - "examine": "Smells pretty bad!", - "ids": "3195" - }, - { - "examine": "Not much use for anything except scrap.", - "ids": "3196" - }, - { - "examine": "Entrance to the Duel Arena.", - "ids": "3197,3198" - }, - { - "examine": "A wooden double bed.", - "ids": "3199,3200,3201,3202" - }, - { - "examine": "Access to the duel arena.", - "ids": "3203,3204" - }, - { - "examine": "I can climb down this.", - "ids": "3205" - }, - { - "examine": "A barrel full of staffs...", - "ids": "3206,3207,3208" - }, - { - "examine": "A strange crack cuts into the wall.", - "ids": "3209,3210,3211,3212,29360,29362" - }, - { - "examine": "It looks like I can squeeze through here.", - "ids": "3213" - }, - { - "examine": "The easy way out...", - "ids": "3214,3215" - }, - { - "examine": "A pile of mud.", - "ids": "3216" - }, - { - "examine": "I can go through here.", - "ids": "3217" - }, - { - "examine": "Dark and intimidating.", - "ids": "3218,3219" - }, - { - "examine": "That skull's looking at me....", - "ids": "3220,3221" - }, - { - "examine": "I can climb down these.", - "ids": "3222" - }, - { - "examine": "I can climb up these.", - "ids": "3223" - }, - { - "examine": "I can go through here.", - "ids": "3224,3225,3226" - }, - { - "examine": "Best not to get caught!", - "ids": "3227" - }, - { - "examine": "Best avoided.", - "ids": "3228,3229" - }, - { - "examine": "Looks suspicious.", - "ids": "3230" - }, - { - "examine": "A plank.", - "ids": "3231" - }, - { - "examine": "Formed over many years.", - "ids": "3232" - }, - { - "examine": "Best avoided.", - "ids": "3233" - }, - { - "examine": "Looks suspicious.", - "ids": "3234" - }, - { - "examine": "I can open the grill from this side.", - "ids": "3235" - }, - { - "examine": "Maybe I can cross that.", - "ids": "3238" - }, - { - "examine": "It looks like only the guide ropes are holding the bridge up.", - "ids": "3239" - }, - { - "examine": "A bridge.", - "ids": "3240" - }, - { - "examine": "This lever can be operated.", - "ids": "3241,3242" - }, - { - "examine": "The base of a platform.", - "ids": "3243,3244,3245,3246" - }, - { - "examine": "A bridge.", - "ids": "3247,3248,3249,3250,3251,3252,3253" - }, - { - "examine": "I can't get past that very easily...", - "ids": "3254,3255,3256" - }, - { - "examine": "A platform.", - "ids": "3257" - }, - { - "examine": "A bridge.", - "ids": "3258,3259,3260,3261,3262" - }, - { - "examine": "This area may not be safe...", - "ids": "3263" - }, - { - "examine": "A well of nastiness.", - "ids": "3264" - }, - { - "examine": "I can climb this pile of rocks...", - "ids": "3265" - }, - { - "examine": "Usually used for storing living things...", - "ids": "3266,3267,3268,3269" - }, - { - "examine": "The door is closed.", - "ids": "3270" - }, - { - "examine": "The door is open.", - "ids": "3271" - }, - { - "examine": "I wonder what's inside.", - "ids": "3272,3273,3274,3275" - }, - { - "examine": "A stone bridge.", - "ids": "3276,3277,3278,3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293" - }, - { - "examine": "A hot place for forging things in.", - "ids": "3294" - }, - { - "examine": "It's covered in strange writing.", - "ids": "3295,3296,3297,3298,3299,3300,3301,3302" - }, - { - "examine": "You won't be able to lift that.", - "ids": "3303" - }, - { - "examine": "A portcullis.", - "ids": "3304" - }, - { - "examine": "It doesn't look like water in there...", - "ids": "3305,3306" - }, - { - "examine": "A pile of rocks.", - "ids": "3307" - }, - { - "examine": "The boulder has smashed through the cage.", - "ids": "3308" - }, - { - "examine": "A deposit of rocks.", - "ids": "3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326" - }, - { - "examine": "Intimidating!", - "ids": "3327" - }, - { - "examine": "A depiction of a great dwarf miner.", - "ids": "3328,3329,3330,3331" - }, - { - "examine": "A temple door.", - "ids": "3332" - }, - { - "examine": "A very big door.", - "ids": "3333,3334,3335,3336" - }, - { - "examine": "This lever can be operated.", - "ids": "3337" - }, - { - "examine": "Why would a log be hung up there?", - "ids": "3338" - }, - { - "examine": "Looks suspicious.", - "ids": "3339" - }, - { - "examine": "Looks like the rope is holding the bridge up.", - "ids": "3340" - }, - { - "examine": "Looks like the other rope is holding the bridge up.", - "ids": "3341,3342" - }, - { - "examine": "A pile of rocks.", - "ids": "3343" - }, - { - "examine": "It's got dwarf brew in it.", - "ids": "3344,3345,3346,3347" - }, - { - "examine": "Lord Iban's Throne.", - "ids": "3348,3349,3350" - }, - { - "examine": "I'm glad I'm on this side of those bars.", - "ids": "3351,3352" - }, - { - "examine": "I don't much like the look of that...", - "ids": "3353,3354,3355,3356,3357,3358" - }, - { - "examine": "Well of the damned.", - "ids": "3359" - }, - { - "examine": "A wooden crate for storage.", - "ids": "3360" - }, - { - "examine": "A magical sphere that glimmers within.", - "ids": "3361" - }, - { - "examine": "A window.", - "ids": "3362,3363" - }, - { - "examine": "An odd looking rock formation.", - "ids": "3364" - }, - { - "examine": "Looks like I can climb these.", - "ids": "3365" - }, - { - "examine": "Makes you cry.", - "ids": "3366" - }, - { - "examine": "Steel bars that are locked securely.", - "ids": "3367,3368,3369,3370" - }, - { - "examine": "An Achey tree stump.", - "ids": "3371" - }, - { - "examine": "Useful for ogre dinners.", - "ids": "3372,3373,3374,3375" - }, - { - "examine": "An ogre bench, useful for taking all that weight off their legs.", - "ids": "3376" - }, - { - "examine": "Hmmm, a rock for a lock....", - "ids": "3377" - }, - { - "examine": "Some stealthy thief must have picked the ogre lock!", - "ids": "3378" - }, - { - "examine": "The entrance to Rantz's cave", - "ids": "3379" - }, - { - "examine": "Exit this way for fresh air and daylight.", - "ids": "3380,3381" - }, - { - "examine": "Useful for ogre dinners.", - "ids": "3382,3383,3384,3385,3386,3387,3388" - }, - { - "examine": "A good source of books!", - "ids": "3389" - }, - { - "examine": "A strange crack cuts into the wall.", - "ids": "3390,3391,3392,3393" - }, - { - "examine": "A wooden crate for storage.", - "ids": "3394,3395" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "3396,3397" - }, - { - "examine": "A wooden crate for storage.", - "ids": "3398,3399,3400,3401" - }, - { - "examine": "Used for fashioning metal items.", - "ids": "3402" - }, - { - "examine": "A pile of Elemental rock.", - "ids": "3403" - }, - { - "examine": "A valve to start and stop the flow of water.", - "ids": "3404,3405" - }, - { - "examine": "This lever can be operated.", - "ids": "3406" - }, - { - "examine": "It spins.", - "ids": "3407,3408" - }, - { - "examine": "This lever can be operated.", - "ids": "3409" - }, - { - "examine": "Big bellows.", - "ids": "3410,3411,3412" - }, - { - "examine": "A furnace with an air blast pipe.", - "ids": "3413" - }, - { - "examine": "A small trough full of lava.", - "ids": "3414" - }, - { - "examine": "I can climb down these stairs.", - "ids": "3415" - }, - { - "examine": "I can climb up these stairs.", - "ids": "3416" - }, - { - "examine": "This lever can be operated.", - "ids": "3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428" - }, - { - "examine": "Water powered machinery.", - "ids": "3429,3430" - }, - { - "examine": "Stoney!", - "ids": "3431" - }, - { - "examine": "I wonder what's under it?", - "ids": "3432" - }, - { - "examine": "I can see a holy barrier at the bottom.", - "ids": "3433,3434,3435,3436,3437,3438,3439" - }, - { - "examine": "They let you walk up them!", - "ids": "3440" - }, - { - "examine": "They let you walk down them!", - "ids": "3441,3442" - }, - { - "examine": "A holy barrier blocking evil forces.", - "ids": "3443" - }, - { - "examine": "A sturdy metal gate.", - "ids": "3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462" - }, - { - "examine": "A hastily constructed yet sturdy prison door.", - "ids": "3463,3464,3465" - }, - { - "examine": "Dead animal parts dangling!", - "ids": "3466" - }, - { - "examine": "So how long has this been dead, then?", - "ids": "3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478" - }, - { - "examine": "A statue honouring Saradomin.", - "ids": "3479" - }, - { - "examine": "An ornately decorated coffin. Something seems to be alive inside.", - "ids": "3480,3481,3482,3483,3484" - }, - { - "examine": "An ornately carved well that draws water from the river Salve.", - "ids": "3485,3486,3487,3488" - }, - { - "examine": "Sturdy looking door.", - "ids": "3489,3490,3491,3492" - }, - { - "examine": "A monument to a fallen priest.", - "ids": "3493,3494,3495,3496,3497,3498,3499" - }, - { - "examine": "With skill I can play this.", - "ids": "3500,3501,3502,3503,3504,3505" - }, - { - "examine": "A wrought iron gate.", - "ids": "3506,3507" - }, - { - "examine": "Not sure if I should sit on that.", - "ids": "3508,3509" - }, - { - "examine": "Not sure if that's any use.", - "ids": "3510,3511" - }, - { - "examine": "It'll probably die soon.", - "ids": "3512,3513" - }, - { - "examine": "Not sure if I should stand under that.", - "ids": "3514,3515" - }, - { - "examine": "A dark dank entrance to below.", - "ids": "3516" - }, - { - "examine": "A large tree thriving where trees normally die.", - "ids": "3517,3518,3519" - }, - { - "examine": "Special grotto area.", - "ids": "3520" - }, - { - "examine": "A grotto transformed into an altar of nature.", - "ids": "3521" - }, - { - "examine": "It's a bridge.", - "ids": "3522" - }, - { - "examine": "An old bench, probably made from wood in this swamp.", - "ids": "3523,3524" - }, - { - "examine": "The door.", - "ids": "3525" - }, - { - "examine": "The green door.", - "ids": "3526" - }, - { - "examine": "A roughly hewn stone with some sort of symbol on it.", - "ids": "3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545" - }, - { - "examine": "There is a rope leading to the bottom of this well.", - "ids": "3546,3547,3548,3549,3550" - }, - { - "examine": "I can balance on this rope.", - "ids": "3551,3552" - }, - { - "examine": "I can balance on this log.", - "ids": "3553,3554,3555,3556,3557,3558" - }, - { - "examine": "Tread carefully!", - "ids": "3559,3560,3561,3562" - }, - { - "examine": "Now, I'm the king of the swingers, oh, the jungle VIP.", - "ids": "3563,3564" - }, - { - "examine": "It looks like I can clamber over this.", - "ids": "3565" - }, - { - "examine": "Use this to swing over to the next platform.", - "ids": "3566" - }, - { - "examine": "I might want to avoid this nasty blade.", - "ids": "3567,3568,3569" - }, - { - "examine": "Walk the plank! Ya land lubber!", - "ids": "3570,3571,3572,3573,3574,3575,3576,3577" - }, - { - "examine": "It's a small step for a player, a giant leap for player kind.", - "ids": "3578,3579" - }, - { - "examine": "Makes sliced human.", - "ids": "3580" - }, - { - "examine": "Releases a ticket when the flashing arrow is above it.", - "ids": "3581" - }, - { - "examine": "Makes human colanders.", - "ids": "3582" - }, - { - "examine": "I can use these to climb across to the other side.", - "ids": "3583,3584" - }, - { - "examine": "Looks suspiciously like a pressure pad to me.", - "ids": "3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607" - }, - { - "examine": "Releases a ticket when the flashing arrow is above it.", - "ids": "3608,3609" - }, - { - "examine": "I can use this rope to climb back to the top.", - "ids": "3610,3611,3612,3613,3614,3615,3616" - }, - { - "examine": "Leads down into the Agility Arena.", - "ids": "3617" - }, - { - "examine": "Leads back to Brimhaven.", - "ids": "3618,3619,3620,3621,3622,3623,3624,3625" - }, - { - "examine": "It looks very sturdy.", - "ids": "3626,3627,3628,3629,3630,3631,3632,3633" - }, - { - "examine": "A shrine of the gods!", - "ids": "3634" - }, - { - "examine": "I wonder what's inside...", - "ids": "3635" - }, - { - "examine": "It looks empty.", - "ids": "3636,3637,3638,3639,3640,3641,3642,3643" - }, - { - "examine": "That's really bright!", - "ids": "3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661" - }, - { - "examine": "Smells more like \"innocent villager\" stew to me...", - "ids": "3662,3663,3664" - }, - { - "examine": "Troll leftovers...", - "ids": "3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675" - }, - { - "examine": "A stone mechanism that unlocks the equipment room.", - "ids": "3676,3677,3678" - }, - { - "examine": "A standard of Asgarnia.", - "ids": "3679" - }, - { - "examine": "The flag of Asgarnia.", - "ids": "3680" - }, - { - "examine": "Town of Burthorpe", - "ids": "3681,3682,3683,3684" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "3685,3686,3687,3688,3689,3690,3691" - }, - { - "examine": "Some of the Sherpa's climbing rope.", - "ids": "3692,3693" - }, - { - "examine": "Looks like the Imperial Guard's supply of Dual Claws.", - "ids": "3694,3695" - }, - { - "examine": "The Imperial Guard's ceremonial armour.", - "ids": "3696" - }, - { - "examine": "A barrel full of swords...", - "ids": "3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721" - }, - { - "examine": "A rocky outcrop.", - "ids": "3722,3723,3724" - }, - { - "examine": "A wooden gate.", - "ids": "3725,3726,3727,3728,3729" - }, - { - "examine": "I can climb over the fence with this.", - "ids": "3730,3731,3732,3733,3734" - }, - { - "examine": "Looks like a small cave.", - "ids": "3735" - }, - { - "examine": "The exit to the outside.", - "ids": "3736,3737,3738,3739,3740" - }, - { - "examine": "No solicitors!", - "ids": "3741" - }, - { - "examine": "Danger", - "ids": "3742" - }, - { - "examine": "A sturdy wooden door.", - "ids": "3743,3744" - }, - { - "examine": "The door is closed.", - "ids": "3745,3746" - }, - { - "examine": "A nicely fitted door.", - "ids": "3747" - }, - { - "examine": "A rocky outcrop.", - "ids": "3748,3749,3750,3751" - }, - { - "examine": "Unusual energy is gathered here.", - "ids": "3752,3753,3754,3755,3756" - }, - { - "examine": "Looks like a small cave.", - "ids": "3757" - }, - { - "examine": "The way out.", - "ids": "3758" - }, - { - "examine": "Looks like a small cave.", - "ids": "3759" - }, - { - "examine": "The way out.", - "ids": "3760" - }, - { - "examine": "The back way out.", - "ids": "3761" - }, - { - "examine": "The back way into the stronghold.", - "ids": "3762" - }, - { - "examine": "The door is closed.", - "ids": "3763" - }, - { - "examine": "The door is open.", - "ids": "3764" - }, - { - "examine": "The door to Mad Eadgar's cell.", - "ids": "3765,3766" - }, - { - "examine": "The door to Godric's cell.", - "ids": "3767,3768" - }, - { - "examine": "Hot!", - "ids": "3769" - }, - { - "examine": "This window looks down onto the Troll Camp.", - "ids": "3770" - }, - { - "examine": "The entrance to the Troll Stronghold.", - "ids": "3771" - }, - { - "examine": "The way out of the Troll Stronghold.", - "ids": "3772,3773,3774" - }, - { - "examine": "Looks like stew of some kind...", - "ids": "3775" - }, - { - "examine": "A door in the Stronghold.", - "ids": "3776,3777,3778,3779" - }, - { - "examine": "The door to the prison.", - "ids": "3780,3781" - }, - { - "examine": "The entrance to the Troll Arena.", - "ids": "3782,3783,3784" - }, - { - "examine": "The exit to the Troll Arena.", - "ids": "3785,3786,3787" - }, - { - "examine": "I can climb these stairs.", - "ids": "3788" - }, - { - "examine": "They go down.", - "ids": "3789" - }, - { - "examine": "A rocky outcrop.", - "ids": "3790,3791,3792,3793" - }, - { - "examine": "Looks tasty! If you're a goat, that is.", - "ids": "3794,3795,3796" - }, - { - "examine": "Not so good for sitting on.", - "ids": "3802" - }, - { - "examine": "A rocky outcrop.", - "ids": "3803,3804" - }, - { - "examine": "They don't seem to roll.", - "ids": "3805" - }, - { - "examine": "They're troll eggs. Or perhaps rocks.", - "ids": "3806" - }, - { - "examine": "These make great pets.", - "ids": "3807" - }, - { - "examine": "It's a troll! Run! No, wait, it's just a rock.", - "ids": "3808,3809" - }, - { - "examine": "The door to the storeroom.", - "ids": "3810,3811,3812" - }, - { - "examine": "It probably hasn't got anything interesting inside.", - "ids": "3813" - }, - { - "examine": "They probably haven't got anything interesting inside.", - "ids": "3814,3815" - }, - { - "examine": "They're closed.", - "ids": "3816" - }, - { - "examine": "They're open.", - "ids": "3817,3818,3819" - }, - { - "examine": "It doesn't contain anything particularly appetising.", - "ids": "3820" - }, - { - "examine": "Makes you taller.", - "ids": "3821" - }, - { - "examine": "It's full of dried goutweed.", - "ids": "3822" - }, - { - "examine": "Welcome to Mad Eadgar's! If I'm not in, I've probably been captured again.", - "ids": "3823" - }, - { - "examine": "I don't want to look too closely at what's cooking in there...", - "ids": "3824" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "3825" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "3826" - }, - { - "examine": "How am I going to get down there?", - "ids": "3827,3828" - }, - { - "examine": "I hope this holds!", - "ids": "3829" - }, - { - "examine": "How am I going to get down there?", - "ids": "3830,3831" - }, - { - "examine": "I hope this holds!", - "ids": "3832" - }, - { - "examine": "It's moving...", - "ids": "3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847,3848,3849,3850,3851,3852,3853,3854,3855" - }, - { - "examine": "It's a half buried crate.", - "ids": "3856,3857" - }, - { - "examine": "It's stuck in the sand.", - "ids": "3858,3859" - }, - { - "examine": "It's full of brightly coloured fish.", - "ids": "3860,3861" - }, - { - "examine": "It's a pile of burning bones.", - "ids": "3862" - }, - { - "examine": "The tribal statue of Tai Bwo Wannai Village.", - "ids": "3863,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878" - }, - { - "examine": "A tree.", - "ids": "3879" - }, - { - "examine": "It's a tree stump.", - "ids": "3880" - }, - { - "examine": "A tree.", - "ids": "3881,3882,3883" - }, - { - "examine": "It's a tree stump.", - "ids": "3884" - }, - { - "examine": "A tree.", - "ids": "3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897,3898,3899,3900" - }, - { - "examine": "A rare flower.", - "ids": "3901" - }, - { - "examine": "Don't flowers make you feel better?", - "ids": "3902" - }, - { - "examine": "A rare flower.", - "ids": "3903" - }, - { - "examine": "Don't flowers make you feel better?", - "ids": "3904" - }, - { - "examine": "A rare flower.", - "ids": "3905" - }, - { - "examine": "Don't flowers make you feel better?", - "ids": "3906,3907,3908,3909,3910,3911" - }, - { - "examine": "Fungal growth.", - "ids": "3912" - }, - { - "examine": "Poisonous no doubt.", - "ids": "3913,3914" - }, - { - "examine": "Fungal growth.", - "ids": "3915" - }, - { - "examine": "Poisonous no doubt.", - "ids": "3916,3917" - }, - { - "examine": "The lamp has a glowing crystal at its core.", - "ids": "3918" - }, - { - "examine": "It's a trap!", - "ids": "3919,3920" - }, - { - "examine": "A tripwire.", - "ids": "3921" - }, - { - "examine": "An odd-looking group of tied sticks.", - "ids": "3922" - }, - { - "examine": "A pile of leaves.", - "ids": "3923,3924,3925,3926" - }, - { - "examine": "Looks like I can climb these.", - "ids": "3927" - }, - { - "examine": "It's a tree.", - "ids": "3928" - }, - { - "examine": "I can balance on this log.", - "ids": "3929,3930,3931,3932,3933,3934,3935,3936" - }, - { - "examine": "There are some broken twigs here.", - "ids": "3937" - }, - { - "examine": "It looks as if the grass here has been flattened.", - "ids": "3938" - }, - { - "examine": "You notice the leaf litter here has been disturbed.", - "ids": "3939,3940" - }, - { - "examine": "Tracks in the soil.", - "ids": "3941,3942,3943" - }, - { - "examine": "You'd need a big siege engine to force that.", - "ids": "3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961" - }, - { - "examine": "A yellow crystal structure.", - "ids": "3962,3963,3964,3965,3966" - }, - { - "examine": "A tree", - "ids": "3967,3968" - }, - { - "examine": "Disturbing but tidy.", - "ids": "3969" - }, - { - "examine": "I don't even want to think about what did that...", - "ids": "3970" - }, - { - "examine": "I don't wanna fall in that!", - "ids": "3975" - }, - { - "examine": "Good for smashing city walls.", - "ids": "3976" - }, - { - "examine": "There's a winch on this side.", - "ids": "3977" - }, - { - "examine": "There's a lever on this side.", - "ids": "3978,3979" - }, - { - "examine": "A way to get into the tent.", - "ids": "3980,3981,3982" - }, - { - "examine": "A tent.", - "ids": "3983,3984,3985,3986" - }, - { - "examine": "A small barrel.", - "ids": "3987,3988,3989,3990,3991" - }, - { - "examine": "A standard of Kandarin.", - "ids": "3992,3993" - }, - { - "examine": "A small furnace.", - "ids": "3994" - }, - { - "examine": "A tent wall.", - "ids": "3995" - }, - { - "examine": "A way to get into the tent.", - "ids": "3996" - }, - { - "examine": "A tent wall.", - "ids": "3997" - }, - { - "examine": "It looks as if the grass here has been flattened.", - "ids": "3998" - }, - { - "examine": "There are some broken twigs here.", - "ids": "3999" - }, - { - "examine": "A tent roof.", - "ids": "4000" - }, - { - "examine": "A tent wall.", - "ids": "4001" - }, - { - "examine": "The charred remains of a tent pole.", - "ids": "4002,4003" - }, - { - "examine": "Well of voyage.", - "ids": "4004,4005" - }, - { - "examine": "It looks like I can squeeze through here.", - "ids": "4006" - }, - { - "examine": "The exit to the outside.", - "ids": "4007" - }, - { - "examine": "Shrine to the glory of Saradomin.", - "ids": "4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023" - }, - { - "examine": "A barrel.", - "ids": "4024,4025" - }, - { - "examine": "A still for fractionalizing oils.", - "ids": "4026" - }, - { - "examine": "A pile of lime rock.", - "ids": "4027,4028,4029,4030" - }, - { - "examine": "Looks like a stone doorway to me.", - "ids": "4031,4032,4033,4034" - }, - { - "examine": "These clean Druid's robes are drying off.", - "ids": "4035,4036,4037,4038" - }, - { - "examine": "It's full of dirty robes.", - "ids": "4039,4040,4041,4042" - }, - { - "examine": "Presumably the hatch the zookeeper feeds the parrots through.", - "ids": "4043" - }, - { - "examine": "A red standard.", - "ids": "4044,4045,4046,4047" - }, - { - "examine": "It doesn't look healthy...", - "ids": "4051" - }, - { - "examine": "It's covered in slime.", - "ids": "4052" - }, - { - "examine": "It doesn't look healthy...", - "ids": "4053,4054" - }, - { - "examine": "These fat fungi take up so much room.", - "ids": "4055,4056,4057" - }, - { - "examine": "I can balance on this rope.", - "ids": "4059" - }, - { - "examine": "It's hollow...", - "ids": "4060" - }, - { - "examine": "This tree has been cut down.", - "ids": "4061" - }, - { - "examine": "Strange items are stored here.", - "ids": "4062" - }, - { - "examine": "This table has seen better days.", - "ids": "4064" - }, - { - "examine": "It looks very ornamental in a practical 'solid rock' way.", - "ids": "4065" - }, - { - "examine": "Welcome to Mort'ton", - "ids": "4066" - }, - { - "examine": "Danger! Sickness and affliction... stay away!", - "ids": "4067" - }, - { - "examine": "Looks like a pile of ancient rubble.", - "ids": "4068" - }, - { - "examine": "The starting of a wall.", - "ids": "4069,4070,4071,4072,4073,4074,4075,4076" - }, - { - "examine": "It's nearly a wall.", - "ids": "4077" - }, - { - "examine": "A rebuilt wall.", - "ids": "4078" - }, - { - "examine": "The broken wall of a desecrated temple.", - "ids": "4079" - }, - { - "examine": "The partially repaired wall of a desecrated temple.", - "ids": "4080,4081,4082,4083,4084,4085,4086,4087" - }, - { - "examine": "The nearly repaired wall of a desecrated temple.", - "ids": "4088" - }, - { - "examine": "The repaired wall of a temple.", - "ids": "4089" - }, - { - "examine": "A flaming Fire altar.", - "ids": "4090" - }, - { - "examine": "An ancient Fire altar.", - "ids": "4091" - }, - { - "examine": "An ancient, totally desecrated, broken down fire altar.", - "ids": "4092" - }, - { - "examine": "A place to cremate the dead. Needs some wood.", - "ids": "4093" - }, - { - "examine": "A place to cremate the dead. Needs a body.", - "ids": "4094,4095,4096,4097,4098,4099" - }, - { - "examine": "A place to cremate the dead. Needs a light.", - "ids": "4100,4101,4102,4103,4104,4105" - }, - { - "examine": "Heavy metal!", - "ids": "4106,4107,4108,4109,4110" - }, - { - "examine": "A strangely decorated bronze chest, the lock is painted blood red.", - "ids": "4111" - }, - { - "examine": "A strangely decorated chest, the lock is painted brown.", - "ids": "4112" - }, - { - "examine": "A strangely decorated chest, the lock is painted crimson.", - "ids": "4113" - }, - { - "examine": "A strangely decorated chest, the lock is painted black.", - "ids": "4114" - }, - { - "examine": "A strangely decorated chest, the lock is painted purple.", - "ids": "4115" - }, - { - "examine": "A strangely decorated steel chest, the lock is painted blood red.", - "ids": "4116" - }, - { - "examine": "A strangely decorated steel chest, the lock is painted brown.", - "ids": "4117" - }, - { - "examine": "A strangely decorated steel chest, the lock is painted crimson.", - "ids": "4118" - }, - { - "examine": "A strangely decorated steel chest, the lock is painted black.", - "ids": "4119" - }, - { - "examine": "A strangely decorated steel chest, the lock is painted purple.", - "ids": "4120" - }, - { - "examine": "A strangely decorated black chest, the lock is painted blood red.", - "ids": "4121" - }, - { - "examine": "A strangely decorated black chest, the lock is painted brown.", - "ids": "4122" - }, - { - "examine": "A strangely decorated black chest, the lock is painted crimson.", - "ids": "4123" - }, - { - "examine": "A strangely decorated black chest, the lock is painted black.", - "ids": "4124" - }, - { - "examine": "A strangely decorated black chest, the lock is painted purple.", - "ids": "4125" - }, - { - "examine": "A strangely decorated silver chest, the lock is painted blood red.", - "ids": "4126" - }, - { - "examine": "A strangely decorated silver chest, the lock is painted brown.", - "ids": "4127" - }, - { - "examine": "A strangely decorated silver chest, the lock is painted crimson.", - "ids": "4128" - }, - { - "examine": "A strangely decorated silver chest, the lock is painted black.", - "ids": "4129" - }, - { - "examine": "A strangely decorated silver chest, the lock is painted purple.", - "ids": "4130" - }, - { - "examine": "A strangely decorated bronze chest, the open lock is painted blood red.", - "ids": "4131" - }, - { - "examine": "These doors look very ominous. A sign says 'To the tombs'.", - "ids": "4132,4133" - }, - { - "examine": "Large doors set into the hillside.", - "ids": "4134,4135,4136,4137" - }, - { - "examine": "Items are for sale here.", - "ids": "4138" - }, - { - "examine": "Entrance to the Duel Arena.", - "ids": "4139,4140" - }, - { - "examine": "This seems to be some kind of shrine.", - "ids": "4141" - }, - { - "examine": "The wind makes a musical sound as it blows through it...", - "ids": "4142" - }, - { - "examine": "Danger", - "ids": "4143" - }, - { - "examine": "Those golden fruit look good enough to eat!", - "ids": "4144,4145,4146" - }, - { - "examine": "This must be Lalli's home.", - "ids": "4147" - }, - { - "examine": "Allows performers backstage at the Longhall.", - "ids": "4148" - }, - { - "examine": "It smells pretty good actually.", - "ids": "4149" - }, - { - "examine": "A portal from this mystical place.", - "ids": "4150,4151,4152,4153,4154,4155,4156,4157" - }, - { - "examine": "Takes me into the maze.", - "ids": "4158" - }, - { - "examine": "Takes me out of the maze.", - "ids": "4159,4160,4161" - }, - { - "examine": "A thin metal pipe, presumably for rainwater to run through.", - "ids": "4162" - }, - { - "examine": "I can climb this.", - "ids": "4163,4164" - }, - { - "examine": "Keeps the wind out.", - "ids": "4165,4166" - }, - { - "examine": "I wonder what's inside?", - "ids": "4167,4168" - }, - { - "examine": "Brrrrrr!", - "ids": "4169" - }, - { - "examine": "There is some kind of balancing mechanism as a lock.", - "ids": "4170" - }, - { - "examine": "A good source of books!", - "ids": "4171" - }, - { - "examine": "An appliance for cooking with.", - "ids": "4172" - }, - { - "examine": "I wonder what's down there?", - "ids": "4173" - }, - { - "examine": "I wonder what's under it?", - "ids": "4174" - }, - { - "examine": "Kind of funny smelling...", - "ids": "4175" - }, - { - "examine": "Water comes out of it.", - "ids": "4176" - }, - { - "examine": "This may be worth opening.", - "ids": "4177" - }, - { - "examine": "This may be worth searching.", - "ids": "4178" - }, - { - "examine": "A very unusual piece of artwork...", - "ids": "4179,4180" - }, - { - "examine": "Alas poor unicorn, I knew him well.", - "ids": "4181" - }, - { - "examine": "Looks like the bull lost.", - "ids": "4182" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "4183,4184" - }, - { - "examine": "A wooden crate for storage.", - "ids": "4185" - }, - { - "examine": "An old crate for storage.", - "ids": "4186" - }, - { - "examine": "I can climb this.", - "ids": "4187" - }, - { - "examine": "This leads upwards.", - "ids": "4188" - }, - { - "examine": "This leads downwards.", - "ids": "4189,4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223,4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238,4239,4240,4241,4242,4243,4244,4245,4246" - }, - { - "examine": "Keeps the cold winds out.", - "ids": "4247" - }, - { - "examine": "Doesn't keep the wind out if it's open!", - "ids": "4248,4249" - }, - { - "examine": "I'm sure the animal is glad its fur was put to good use.", - "ids": "4250,4251" - }, - { - "examine": "I wonder what's inside?", - "ids": "4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264" - }, - { - "examine": "Toasty.", - "ids": "4265" - }, - { - "examine": "Hot!", - "ids": "4266" - }, - { - "examine": "Finger licking good!", - "ids": "4267,4268" - }, - { - "examine": "It's a long wooden table.", - "ids": "4269" - }, - { - "examine": "It's a table with candles on.", - "ids": "4270" - }, - { - "examine": "Generally used for sitting.", - "ids": "4271" - }, - { - "examine": "Generally used for putting things on.", - "ids": "4272" - }, - { - "examine": "Actually, I could do with a drink...", - "ids": "4273" - }, - { - "examine": "For leaning against...", - "ids": "4274" - }, - { - "examine": "A wooden barrel containing lots of fish.", - "ids": "4275" - }, - { - "examine": "Always a source of good bargains.", - "ids": "4276" - }, - { - "examine": "There's something fishy about this stall.", - "ids": "4277" - }, - { - "examine": "Around here, this is where you buy new doors.", - "ids": "4278,4279,4280,4281,4282" - }, - { - "examine": "Has a peculiar odour.", - "ids": "4283,4284" - }, - { - "examine": "Water comes out of this.", - "ids": "4285" - }, - { - "examine": "It's kind of like a barrel.", - "ids": "4286" - }, - { - "examine": "Looks pretty comfy.", - "ids": "4287" - }, - { - "examine": "I guess I could sleep in it if I was really tired.", - "ids": "4288,4289,4290,4291,4292,4293,4294,4295" - }, - { - "examine": "Yup, looks like this neighbourhood is perfectly safe for travellers...", - "ids": "4296" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "4297,4298,4299" - }, - { - "examine": "A wooden crate for storage.", - "ids": "4300" - }, - { - "examine": "An old crate for storage.", - "ids": "4301,4302,4303" - }, - { - "examine": "A hot place for forging things in.", - "ids": "4304,4305" - }, - { - "examine": "Used for fashioning metal items.", - "ids": "4306" - }, - { - "examine": "Various implements for working with metal.", - "ids": "4307" - }, - { - "examine": "Bake your clay pots in here.", - "ids": "4308" - }, - { - "examine": "Used for spinning thread.", - "ids": "4309" - }, - { - "examine": "Used for fashioning clay items.", - "ids": "4310" - }, - { - "examine": "A wooden gate.", - "ids": "4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326,4327" - }, - { - "examine": "This tree has been cut down.", - "ids": "4328,4329,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4347" - }, - { - "examine": "Best left on the beach.", - "ids": "4348" - }, - { - "examine": "This once belonged to a sea animal.", - "ids": "4349" - }, - { - "examine": "Best left for the crabs.", - "ids": "4350" - }, - { - "examine": "I can hear the sea with this.", - "ids": "4351" - }, - { - "examine": "A half-buried bowl.", - "ids": "4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365" - }, - { - "examine": "An empty shelf...", - "ids": "4366,4367" - }, - { - "examine": "There are some pots and pans here.", - "ids": "4368" - }, - { - "examine": "There are some tankards here.", - "ids": "4369" - }, - { - "examine": "A shelf with a bucket on it.", - "ids": "4370" - }, - { - "examine": "There are a few books on this shelf.", - "ids": "4371" - }, - { - "examine": "It's a small table.", - "ids": "4372" - }, - { - "examine": "A tray of sand.", - "ids": "4373,4374,4375,4376" - }, - { - "examine": "There should be a standard here!", - "ids": "4377,4378,4379" - }, - { - "examine": "I could climb this if I wanted.", - "ids": "4380" - }, - { - "examine": "It's a war machine.", - "ids": "4381,4382" - }, - { - "examine": "I could climb this if I wanted.", - "ids": "4383,4384" - }, - { - "examine": "The catapult is damaged.", - "ids": "4385,4386" - }, - { - "examine": "A portal to join the Saradomin team.", - "ids": "4387" - }, - { - "examine": "A portal to join the Zamorak team.", - "ids": "4388" - }, - { - "examine": "A portal to leave the Saradomin team.", - "ids": "4389" - }, - { - "examine": "A portal to leave the Zamorak team.", - "ids": "4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405" - }, - { - "examine": "A portal to leave the Saradomin team.", - "ids": "4406" - }, - { - "examine": "A portal to leave the Zamorak team.", - "ids": "4407" - }, - { - "examine": "A portal to join a random team.", - "ids": "4408,4409,4410" - }, - { - "examine": "A very slippery stepping stone", - "ids": "4411" - }, - { - "examine": "I could climb this if I wanted.", - "ids": "4412,4413" - }, - { - "examine": "A rickety old staircase.", - "ids": "4414" - }, - { - "examine": "A solid stone staircase.", - "ids": "4415,4416,4417,4418,4419,4420" - }, - { - "examine": "A spiky barricade.", - "ids": "4421,4422" - }, - { - "examine": "A large broken door.", - "ids": "4431,4432,4433,4434,4435,4436" - }, - { - "examine": "We could use our pickaxes to get past these...", - "ids": "4437" - }, - { - "examine": "We can almost get past these...", - "ids": "4438,4439,4440,4441,4442,4443" - }, - { - "examine": "Should be long enough to scale castle walls.", - "ids": "4444,4445" - }, - { - "examine": "Maybe I could tie a rope to this...", - "ids": "4446,4447" - }, - { - "examine": "It doesn't look very stable...", - "ids": "4448" - }, - { - "examine": "A solid stone staircase.", - "ids": "4449,4450,4451,4452,4453,4454,4455,4456,4457" - }, - { - "examine": "There are some bandages on this table.", - "ids": "4458" - }, - { - "examine": "There are some toolboxes on this table.", - "ids": "4459" - }, - { - "examine": "There's some rock I can use with the catapult here.", - "ids": "4460" - }, - { - "examine": "There are some barricades here.", - "ids": "4461" - }, - { - "examine": "There is some rope here.", - "ids": "4462" - }, - { - "examine": "There are some explosive potions here.", - "ids": "4463" - }, - { - "examine": "There are some pickaxes on this table.", - "ids": "4464" - }, - { - "examine": "An elf-fashioned door.", - "ids": "4465,4466" - }, - { - "examine": "The door is closed.", - "ids": "4467" - }, - { - "examine": "The door is open.", - "ids": "4468" - }, - { - "examine": "Only the Saradomin team may pass.", - "ids": "4469" - }, - { - "examine": "Only the Zamorak team may pass.", - "ids": "4470" - }, - { - "examine": "I wonder what's under it?", - "ids": "4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481" - }, - { - "examine": "Water comes out of this.", - "ids": "4482" - }, - { - "examine": "It's open.", - "ids": "4483" - }, - { - "examine": "Keep track of which team has the most victories.", - "ids": "4484" - }, - { - "examine": "I could climb this if I wanted.", - "ids": "4485,4486" - }, - { - "examine": "A big wooden door.", - "ids": "4487,4488,4489,4490,4491,4492" - }, - { - "examine": "I can climb these stairs.", - "ids": "4493" - }, - { - "examine": "They go down.", - "ids": "4494" - }, - { - "examine": "I can climb these stairs.", - "ids": "4495" - }, - { - "examine": "They go down.", - "ids": "4496" - }, - { - "examine": "I can climb these stairs.", - "ids": "4497" - }, - { - "examine": "They go down.", - "ids": "4498" - }, - { - "examine": "Wow", - "ids": "4499" - }, - { - "examine": "This leads back to the cruel world above.", - "ids": "4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512" - }, - { - "examine": "I doubt that was built to last...", - "ids": "4513,4514,4515,4516,4517" - }, - { - "examine": "How do you make a skeleton laugh?", - "ids": "4518" - }, - { - "examine": "Tickle his funny bone.", - "ids": "4519" - }, - { - "examine": "Clearly has been relaxing too long....", - "ids": "4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538,4539,4540" - }, - { - "examine": "Did that thing just twitch?", - "ids": "4541,4542" - }, - { - "examine": "A strange wall with objects engraved upon it.", - "ids": "4543,4544" - }, - { - "examine": "A strange wall that appears to be hinged.", - "ids": "4545,4546,4547,4548,4549" - }, - { - "examine": "I can jump off this one.", - "ids": "4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567" - }, - { - "examine": "I can climb up these stairs.", - "ids": "4568" - }, - { - "examine": "I can climb up or go down these stairs.", - "ids": "4569,36777" - }, - { - "examine": "I can climb down these stairs.", - "ids": "4570,4571,4572,4573,4574,4575,4576" - }, - { - "examine": "Lets you walk through walls!", - "ids": "4577,4578,4579,4580,4581,4582,4583,4584,4585,4586" - }, - { - "examine": "It turns the light around.", - "ids": "4587" - }, - { - "examine": "It looks broken.", - "ids": "4588" - }, - { - "examine": "Part of the Lighthouse's lighting mechanism.", - "ids": "4589" - }, - { - "examine": "Part of the lighthouse Light mechanism.", - "ids": "4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609" - }, - { - "examine": "Best left on the beach.", - "ids": "4610" - }, - { - "examine": "This once belonged to a sea animal.", - "ids": "4611" - }, - { - "examine": "Best left for the crabs.", - "ids": "4612" - }, - { - "examine": "I can hear the sea with this.", - "ids": "4613" - }, - { - "examine": "It is a buried bowl.", - "ids": "4614" - }, - { - "examine": "I can jump off this one.", - "ids": "4615,4616" - }, - { - "examine": "Shelves filled with interesting books.", - "ids": "4617" - }, - { - "examine": "A nice and cosy fire.", - "ids": "4618,4619" - }, - { - "examine": "They go down.", - "ids": "4620,4621" - }, - { - "examine": "They go up.", - "ids": "4622,4623" - }, - { - "examine": "They go down.", - "ids": "4624,4625" - }, - { - "examine": "They go up.", - "ids": "4626,4627,4628" - }, - { - "examine": "An elf-fashioned door.", - "ids": "4636,4637,4638,4639,4640" - }, - { - "examine": "Mmm...beer!", - "ids": "4641" - }, - { - "examine": "Has some board games news on it.", - "ids": "4642" - }, - { - "examine": "The ladder to the Runelink challenge room for experienced players.", - "ids": "4643" - }, - { - "examine": "The ladder back to the Runelink challenge room.", - "ids": "4644" - }, - { - "examine": "The ladder to the Draughts challenge room for experienced players.", - "ids": "4645" - }, - { - "examine": "The ladder back to the Draughts challenge room.", - "ids": "4646" - }, - { - "examine": "The ladder back up to the Toad and Chicken.", - "ids": "4647" - }, - { - "examine": "Leads down to the Burthorpe Games Rooms.", - "ids": "4648,4649" - }, - { - "examine": "A nice and cosy fire.", - "ids": "4650" - }, - { - "examine": "A table for board games.", - "ids": "4651" - }, - { - "examine": "A game of draughts is being played on this table.", - "ids": "4652" - }, - { - "examine": "A game of Runelink is being played on this table.", - "ids": "4653,4654,4655" - }, - { - "examine": "A comfy stool.", - "ids": "4656,4657" - }, - { - "examine": "Draughts challenge room.", - "ids": "4658" - }, - { - "examine": "Runelink challenge room.", - "ids": "4659" - }, - { - "examine": "Draughts challenge room for experienced players.", - "ids": "4660" - }, - { - "examine": "Runelink challenge room for experienced players.", - "ids": "4661,4662,4663,4664,4665,4666,4667,4668" - }, - { - "examine": "A statue of Arrav (the Fountain of Heroes has moved downstairs.)", - "ids": "4669" - }, - { - "examine": "A statue of Camorra (the Fountain of Heroes has moved downstairs.)", - "ids": "4670" - }, - { - "examine": "A good source of books!", - "ids": "4671" - }, - { - "examine": "The door into the throne room.", - "ids": "4672,4673" - }, - { - "examine": "I bet this makes good syrup!", - "ids": "4674" - }, - { - "examine": "Scented herbs.", - "ids": "4675" - }, - { - "examine": "A rocky outcrop.", - "ids": "4676" - }, - { - "examine": "Scented herbs.", - "ids": "4677,4678,4679,4680,4681,4682" - }, - { - "examine": "A large harp.", - "ids": "4683" - }, - { - "examine": "Good for shooting fish in.", - "ids": "4684" - }, - { - "examine": "This old tree is rotting away", - "ids": "4685" - }, - { - "examine": "This old tree is rotting away.", - "ids": "4686,4687,4688,4689" - }, - { - "examine": "Fancy.", - "ids": "4690,4691" - }, - { - "examine": "Flag, pole...Yep, it's a flagpole.", - "ids": "4692,4693,4694,4695" - }, - { - "examine": "The door is closed.", - "ids": "4696" - }, - { - "examine": "The door is open.", - "ids": "4697,4698,4699,4700" - }, - { - "examine": "Keeps the cold winds out.", - "ids": "4701" - }, - { - "examine": "A nicely carved wooden chair.", - "ids": "4702" - }, - { - "examine": "A wooden double bed. For sleeping in...", - "ids": "4703,4704" - }, - { - "examine": "There's something fishy about this stall.", - "ids": "4705" - }, - { - "examine": "You should eat your greens!", - "ids": "4706" - }, - { - "examine": "There's something fishy about this stall.", - "ids": "4707" - }, - { - "examine": "You should eat your greens!", - "ids": "4708,4709" - }, - { - "examine": "A doorway made from bamboo.", - "ids": "4710,4711" - }, - { - "examine": "It's a trapdoor.", - "ids": "4712" - }, - { - "examine": "It's an open trapdoor.", - "ids": "4713" - }, - { - "examine": "A wooden crate for storage.", - "ids": "4714" - }, - { - "examine": "This crate is making chattering sounds.", - "ids": "4715" - }, - { - "examine": "This crate smells slightly nauseous.", - "ids": "4716" - }, - { - "examine": "This crate says 'Property of Hamab' on its side.", - "ids": "4717,4718" - }, - { - "examine": "This crate says 'Property of Ifaba' on its side.", - "ids": "4719" - }, - { - "examine": "This crate says 'Property of Daga' on its side.", - "ids": "4720,4721" - }, - { - "examine": "This crate smells slightly of banana.", - "ids": "4722,4723" - }, - { - "examine": "This crate says 'Property of Hamab' on its side.", - "ids": "4724" - }, - { - "examine": "This crate says 'Property of Ifaba' on its side.", - "ids": "4725,4726,4727" - }, - { - "examine": "I can climb up this.", - "ids": "4728" - }, - { - "examine": "A dried up bush, void of life.", - "ids": "4729,4730,4731,4732,4733,4734,4735,4736,4737,4738,4739,4740,4741,4742" - }, - { - "examine": "It's a bamboo ladder.", - "ids": "4743,4744" - }, - { - "examine": "Looks good for jumping off.", - "ids": "4745" - }, - { - "examine": "This crate is marked 'Deliver to Glough'...", - "ids": "4746,4747,4748" - }, - { - "examine": "It's a banana tree, full of bananas.", - "ids": "4749" - }, - { - "examine": "It's a banana tree, with lots of bananas.", - "ids": "4750" - }, - { - "examine": "It's a banana tree, with many bananas.", - "ids": "4751" - }, - { - "examine": "It's a banana tree, with a few bananas.", - "ids": "4752" - }, - { - "examine": "It's a banana tree, with but one banana.", - "ids": "4753" - }, - { - "examine": "It's a banana tree, with no more bananas left.", - "ids": "4754" - }, - { - "examine": "They go down.", - "ids": "4755" - }, - { - "examine": "They go up.", - "ids": "4756,4757,4758,4759,4760,4761,4762,4763" - }, - { - "examine": "It's a bamboo stool.", - "ids": "4764" - }, - { - "examine": "It's a sheer wall of fire, rising as high as you can see.", - "ids": "4765,4766" - }, - { - "examine": "The contents of this pyre are rapidly bubbling and burning.", - "ids": "4767" - }, - { - "examine": "It's a wall of bricks.", - "ids": "4768" - }, - { - "examine": "It's a pile of bricks.", - "ids": "4769,4770" - }, - { - "examine": "A rather dapper little monkey sitting on a throne.", - "ids": "4771" - }, - { - "examine": "It's a bamboo ladder.", - "ids": "4772,4773,4774,4775,4776,4777,4778,4779,4780,4781,4782,4783,4784,4785" - }, - { - "examine": "A plant in a bamboo pot.", - "ids": "4786" - }, - { - "examine": "It's an absolutely huge bamboo gate.", - "ids": "4787,4788,4789,4790" - }, - { - "examine": "It's a big comfy bed, made of strips of bamboo.", - "ids": "4791,4792,4793" - }, - { - "examine": "It's a pot made out of bamboo.", - "ids": "4794" - }, - { - "examine": "They are pots made out of bamboo.", - "ids": "4795,4796" - }, - { - "examine": "Bamboo strips have been lashed together to make this rickety desk.", - "ids": "4797" - }, - { - "examine": "It's a bookcase made out of bamboo.", - "ids": "4798" - }, - { - "examine": "It's a bamboo door with a large iron padlock.", - "ids": "4799,4800,4801,4802,4803,4804,4805,4806" - }, - { - "examine": "It's a door made from bamboo.", - "ids": "4807,4808,4809,4810,4811" - }, - { - "examine": "Looks good for hiding in...", - "ids": "4812,4813,4814" - }, - { - "examine": "Looks good for hiding in.", - "ids": "4815" - }, - { - "examine": "This is a fairly tall tree with sparse foliage.", - "ids": "4816" - }, - { - "examine": "A jungle palm with dense foliage.", - "ids": "4817" - }, - { - "examine": "This is a fairly tall tree with sparse foliage.", - "ids": "4818" - }, - { - "examine": "This was a fairly tall tree with sparse foliage.", - "ids": "4819" - }, - { - "examine": "A leafy tree.", - "ids": "4820" - }, - { - "examine": "This was a fairly tall tree with sparse foliage.", - "ids": "4821" - }, - { - "examine": "This tree has been cut down.", - "ids": "4822" - }, - { - "examine": "This flower is found only in jungle areas.", - "ids": "4823" - }, - { - "examine": "A very rare and exotic flower.", - "ids": "4824" - }, - { - "examine": "A small fernlike plant.", - "ids": "4825" - }, - { - "examine": "Leaves a nasty mark.", - "ids": "4826" - }, - { - "examine": "Looks spiky.", - "ids": "4827" - }, - { - "examine": "This has broad leaves.", - "ids": "4828" - }, - { - "examine": "A type of jungle fern.", - "ids": "4829" - }, - { - "examine": "A large waxy jungle plant.", - "ids": "4830" - }, - { - "examine": "Commonly found in these parts.", - "ids": "4831" - }, - { - "examine": "The tendrils of a creeping plant.", - "ids": "4832" - }, - { - "examine": "A jungle bush, quite common to these areas.", - "ids": "4833,4834" - }, - { - "examine": "A jungle bush that has been chopped down.", - "ids": "4835,4836,4837,4838,4839,4840,4841,4842,4843,4844" - }, - { - "examine": "A terribly tall tropical tree.", - "ids": "4845,4846,4847" - }, - { - "examine": "Leaves of a tropical tree.", - "ids": "4848" - }, - { - "examine": "This tree no doubt contains dangerous insects.", - "ids": "4849,4850,4851" - }, - { - "examine": "This old tree is rotting away.", - "ids": "4852" - }, - { - "examine": "A gnarly old tree root.", - "ids": "4853" - }, - { - "examine": "The roots of this tree are exposed.", - "ids": "4854" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "4855" - }, - { - "examine": "It's a small patch of glowing fungus.", - "ids": "4856" - }, - { - "examine": "It's a medium patch of glowing fungus.", - "ids": "4857" - }, - { - "examine": "It's an absolutely colossal statue of some kind of gorilla.", - "ids": "4858" - }, - { - "examine": "It's a huge statue of some kind of gorilla.", - "ids": "4859,4860" - }, - { - "examine": "It's a huge statue of some kind of monkey.", - "ids": "4861" - }, - { - "examine": "It's a large patch of glowing fungus.", - "ids": "4862" - }, - { - "examine": "Legs of this watchtower.", - "ids": "4863,4864" - }, - { - "examine": "It's the middle of this watchtower.", - "ids": "4865" - }, - { - "examine": "It's a deactivated military Gnome glider.", - "ids": "4866" - }, - { - "examine": "It's an activated military Gnome glider.", - "ids": "4867" - }, - { - "examine": "It's some kind of Gnome teleportation device.", - "ids": "4868,4869,4870" - }, - { - "examine": "This is the panel controlling the hangar reinitialisation.", - "ids": "4871" - }, - { - "examine": "There must be an exit nearby.", - "ids": "4872" - }, - { - "examine": "It's probably too risky to try pulling this.", - "ids": "4873" - }, - { - "examine": "This table has crafting paraphernalia on it.", - "ids": "4874" - }, - { - "examine": "This table has ripe bananas on show.", - "ids": "4875" - }, - { - "examine": "This table has items of a general nature on it.", - "ids": "4876" - }, - { - "examine": "This table has runes on display.", - "ids": "4877" - }, - { - "examine": "This table has a scimitar on it.", - "ids": "4878" - }, - { - "examine": "It's a trapdoor.", - "ids": "4879" - }, - { - "examine": "It's an open trapdoor.", - "ids": "4880" - }, - { - "examine": "I can climb up this.", - "ids": "4881" - }, - { - "examine": "Looks suspicious.", - "ids": "4882,4883" - }, - { - "examine": "A plank.", - "ids": "4884" - }, - { - "examine": "Formed over many years.", - "ids": "4885" - }, - { - "examine": "Human sieve creation.", - "ids": "4886" - }, - { - "examine": "Looks suspicious.", - "ids": "4887" - }, - { - "examine": "It's a trapdoor.", - "ids": "4888" - }, - { - "examine": "I can climb up this.", - "ids": "4889,4890,4891,4892,4893,4894,4895,4896,4897,4898,4899" - }, - { - "examine": "The Saradomin Team Standard.", - "ids": "4900" - }, - { - "examine": "The Zamorak Team Standard.", - "ids": "4901" - }, - { - "examine": "The Saradomin Team Standard.", - "ids": "4902" - }, - { - "examine": "The Zamorak Team Standard.", - "ids": "4903" - }, - { - "examine": "It's on fire!", - "ids": "4904,4905,4906,4907,4908,4909" - }, - { - "examine": "A wooden barrel for storage.", - "ids": "4910" - }, - { - "examine": "I can climb down this.", - "ids": "4911,4912" - }, - { - "examine": "It looks cramped and dark.", - "ids": "4913,4914,4915,4916,4917" - }, - { - "examine": "This cart is blocking the way up the ridge.", - "ids": "4918" - }, - { - "examine": "These stairs seem to have been hewn out of the rock itself.", - "ids": "4919" - }, - { - "examine": "It looks cramped and dark.", - "ids": "4920,4921,4922" - }, - { - "examine": "These stairs seem to have been hewn out of the rock itself.", - "ids": "4923" - }, - { - "examine": "A valve to start and stop the flow of water.", - "ids": "4924,4925" - }, - { - "examine": "Shiny!", - "ids": "4926,4927,4928" - }, - { - "examine": "It looks cramped and dark.", - "ids": "4929" - }, - { - "examine": "It's blocking the tunnel, but it looks dark down there anyway.", - "ids": "4930,4931" - }, - { - "examine": "A bizarre fungus. It emits a pale blue light.", - "ids": "4932,4933,4934" - }, - { - "examine": "If that wasn't there I'd probably be a lot flatter now.", - "ids": "4935" - }, - { - "examine": "It prevents the wall unceremoniously squashing people.", - "ids": "4936" - }, - { - "examine": "It looks like it's water powered.", - "ids": "4937,4938,4939,4940,4941" - }, - { - "examine": "It's a little waterlogged. I hope it still works.", - "ids": "4942,4943" - }, - { - "examine": "It doesn't look very watertight. Any contents will be ruined.", - "ids": "4944" - }, - { - "examine": "A sunken mine cart, how useful.", - "ids": "4945,4946,4947,4948" - }, - { - "examine": "This displays information on how the points on this level are set.", - "ids": "4949" - }, - { - "examine": "It has the letter 'B' inscribed on the end.", - "ids": "4950" - }, - { - "examine": "It has the letter 'A' inscribed on the end.", - "ids": "4951" - }, - { - "examine": "It has the letter 'C' inscribed on the end.", - "ids": "4952" - }, - { - "examine": "It has the letter 'D' inscribed on the end.", - "ids": "4953" - }, - { - "examine": "It has the letter 'E' inscribed on the end.", - "ids": "4954" - }, - { - "examine": "It has the letter 'I' inscribed on the end.", - "ids": "4955" - }, - { - "examine": "It has the letter 'J' inscribed on the end.", - "ids": "4956" - }, - { - "examine": "It has the letter 'K' inscribed on the end.", - "ids": "4957" - }, - { - "examine": "I think this changes one of the sets of points.", - "ids": "4958" - }, - { - "examine": "It has the letter 'F' inscribed on the end.", - "ids": "4959" - }, - { - "examine": "It has the letter 'G' inscribed on the end.", - "ids": "4960" - }, - { - "examine": "It has the letter 'H' inscribed on the end.", - "ids": "4961" - }, - { - "examine": "The door is closed.", - "ids": "4962" - }, - { - "examine": "I can climb down this.", - "ids": "4965" - }, - { - "examine": "I can climb this.", - "ids": "4966" - }, - { - "examine": "I can climb down this.", - "ids": "4967" - }, - { - "examine": "I can climb this.", - "ids": "4968" - }, - { - "examine": "I can climb down this.", - "ids": "4969" - }, - { - "examine": "I can climb this.", - "ids": "4970" - }, - { - "examine": "These stairs seem to have been hewn out of the rock itself.", - "ids": "4971,4972,4973" - }, - { - "examine": "Useful for moving items around the mine.", - "ids": "4974" - }, - { - "examine": "An old crate for storage.", - "ids": "4975" - }, - { - "examine": "A distinctive layer in the rock strata.", - "ids": "4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000" - }, - { - "examine": "This cave has been boarded up.", - "ids": "5001" - }, - { - "examine": "Useful for passing over inaccessible areas.", - "ids": "5002" - }, - { - "examine": "It's old and pretty broken.", - "ids": "5003" - }, - { - "examine": "A well used tree.", - "ids": "5004,5005" - }, - { - "examine": "These flowers only grow in one place in the mountains.", - "ids": "5006" - }, - { - "examine": "Looks like small cave.", - "ids": "5007" - }, - { - "examine": "Looks dark...", - "ids": "5008,5009,5010,5011,5012,5013,5014" - }, - { - "examine": "It's too dangerous to go down here by foot...", - "ids": "5015,5016,5017,5018,5019,5020,5021,5022,5023,5024" - }, - { - "examine": "A split in the cave wall...", - "ids": "5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036" - }, - { - "examine": "A large ice covered boulder.", - "ids": "5037" - }, - { - "examine": "An ice covered boulder.", - "ids": "5038" - }, - { - "examine": "A small ice covered boulder.", - "ids": "5039,5040,5041,5042" - }, - { - "examine": "A huge ice gate.", - "ids": "5043,5044" - }, - { - "examine": "An interesting-looking tree.", - "ids": "5045" - }, - { - "examine": "A small cave entrance.", - "ids": "5046" - }, - { - "examine": "Limestone ceiling growth.", - "ids": "5047" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "5048" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "5049" - }, - { - "examine": "Tooth shaped rock formations protruding from the floor.", - "ids": "5050" - }, - { - "examine": "A dirty little swamp boat.", - "ids": "5051" - }, - { - "examine": "If it had paint on it, you'd probably watch it dry.", - "ids": "5052,5053" - }, - { - "examine": "I can climb this.", - "ids": "5054" - }, - { - "examine": "I wonder what that's there for...", - "ids": "5055" - }, - { - "examine": "These doors look very ominous.", - "ids": "5056,5057" - }, - { - "examine": "Large doors set into the hillside.", - "ids": "5058,5059" - }, - { - "examine": "These doors seem to lead into the underground.", - "ids": "5060,5061" - }, - { - "examine": "Large doors set into the hillside.", - "ids": "5062,5063,5064,5065,5066,5067,5068,5069,5070,5071" - }, - { - "examine": "A Large Pillar", - "ids": "5072,5073,5074,5075,5076,5077,5078,5079,5080,5081" - }, - { - "examine": "An overgrown dungeon entrance.", - "ids": "5082" - }, - { - "examine": "A closed overgrown dungeon entrance.", - "ids": "5083" - }, - { - "examine": "The way to go when I get scared.", - "ids": "5084,5085,5086,5087" - }, - { - "examine": "I hope I don't fall off this.", - "ids": "5088,5089,5090,5091,5092,5093" - }, - { - "examine": "These stairs seem to have been hewn out of the rock itself.", - "ids": "5094,5095,5096,5097,5098" - }, - { - "examine": "Thick vines blocking your way.", - "ids": "5103,5104,5105,5106,5107,5108,5109" - }, - { - "examine": "I can jump from this stepping stone.", - "ids": "5110,5111" - }, - { - "examine": "Not good for eating.", - "ids": "5112" - }, - { - "examine": "I doubt that's edible.", - "ids": "5113,5114,5115" - }, - { - "examine": "A statue of a big monster.", - "ids": "5116,5117,5118,5119,5120,5121" - }, - { - "examine": "A tall wooden door.", - "ids": "5126" - }, - { - "examine": "A tall wooden door.", - "ids": "5128,5129" - }, - { - "examine": "Looks like whoever uses this has claws on their hands.", - "ids": "5130" - }, - { - "examine": "I wonder what's under it?", - "ids": "5131" - }, - { - "examine": "I wonder what's down there?", - "ids": "5132" - }, - { - "examine": "A hurdle.", - "ids": "5133,5134,5135" - }, - { - "examine": "A climbing wall made from skulls.", - "ids": "5136,5137" - }, - { - "examine": "A very slippery stepping stone.", - "ids": "5138" - }, - { - "examine": "A scary zip line for teeth??", - "ids": "5139,5140,5141,5142,5143,5144,5145" - }, - { - "examine": "A goal.", - "ids": "5146,5147,5148,5149,5150,5151" - }, - { - "examine": "This may be worth opening.", - "ids": "5156" - }, - { - "examine": "This may be worth searching.", - "ids": "5157" - }, - { - "examine": "A pile of garden canes.", - "ids": "5158" - }, - { - "examine": "There's no way to cross this bridge.", - "ids": "5159,5160" - }, - { - "examine": "An old grandfather clock.", - "ids": "5161" - }, - { - "examine": "A closed chest.", - "ids": "5162" - }, - { - "examine": "An open chest.", - "ids": "5163" - }, - { - "examine": "There is a note pinned to the signpost.", - "ids": "5164" - }, - { - "examine": "It looks like it needs a good sweep out.", - "ids": "5165" - }, - { - "examine": "Bookish.", - "ids": "5166" - }, - { - "examine": "A large, elaborately ornamented memorial stone.", - "ids": "5167" - }, - { - "examine": "A grave, marked by an ostentatious memorial stone.", - "ids": "5168" - }, - { - "examine": "A poor grave marked by a simple, wooden cross.", - "ids": "5169" - }, - { - "examine": "The entrance to an extravagantly decorated mausoleum.", - "ids": "5170,5171" - }, - { - "examine": "The door to the Tower.", - "ids": "5172,5173" - }, - { - "examine": "The door to the garden shed.", - "ids": "5174,5175" - }, - { - "examine": "At some point the lightning conductor has broken, rendering it useless.", - "ids": "5176" - }, - { - "examine": "A shocking piece of kit.", - "ids": "5177" - }, - { - "examine": "Mmm.. scented candles.", - "ids": "5178,5179" - }, - { - "examine": "I wouldn't eat there!", - "ids": "5180,5181,5182" - }, - { - "examine": "Looks wooden. Feels wooden. I wonder is it wooden?", - "ids": "5183,5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198,5199,5200,5201" - }, - { - "examine": "Dangerous, someone could trip on them.", - "ids": "5202,5203,5204,5205" - }, - { - "examine": "I can climb these stairs.", - "ids": "5206" - }, - { - "examine": "They go down.", - "ids": "5207,5208,5209,5210,5211,5212,5213,5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227" - }, - { - "examine": "Formed over many years of dripping limestone.", - "ids": "5228,5229,5230" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243" - }, - { - "examine": "Looks a bit tatty...", - "ids": "5244,5245" - }, - { - "examine": "A well slept in bed.", - "ids": "5246,5247" - }, - { - "examine": "It looks like a tree made of crystal.", - "ids": "5248" - }, - { - "examine": "Looks to me like a quickly built fire.", - "ids": "5249" - }, - { - "examine": "I can climb down this.", - "ids": "5250" - }, - { - "examine": "I can climb this.", - "ids": "5251" - }, - { - "examine": "A recently extinguished fire.", - "ids": "5252" - }, - { - "examine": "I wish I could sting other people.", - "ids": "5253" - }, - { - "examine": "Probably feels like a mild bee sting.", - "ids": "5254" - }, - { - "examine": "I wouldn't like to get stung.", - "ids": "5255" - }, - { - "examine": "Dock leaves at the ready.", - "ids": "5256" - }, - { - "examine": "Nettles sting my leggies.", - "ids": "5257" - }, - { - "examine": "These may hurt.", - "ids": "5258" - }, - { - "examine": "A ghostly barrier.", - "ids": "5259,5260,5261" - }, - { - "examine": "These stairs were carved out of the rock of the cavern.", - "ids": "5262,5263" - }, - { - "examine": "I can climb this.", - "ids": "5264" - }, - { - "examine": "I can climb up here.", - "ids": "5265" - }, - { - "examine": "I can go below decks with this ladder.", - "ids": "5266" - }, - { - "examine": "I wonder what's under it.", - "ids": "5267" - }, - { - "examine": "I wonder what's down there.", - "ids": "5268" - }, - { - "examine": "I can't see a rock!", - "ids": "5269" - }, - { - "examine": "I wonder what's inside.", - "ids": "5270,5271,5272" - }, - { - "examine": "Perhaps I should search it.", - "ids": "5273" - }, - { - "examine": "High above here is a tattered flag, blowing in the wind.", - "ids": "5274" - }, - { - "examine": "An appliance for cooking with.", - "ids": "5275" - }, - { - "examine": "The bank teller will serve you from here.", - "ids": "5276" - }, - { - "examine": "This booth is closed.", - "ids": "5277" - }, - { - "examine": "The resting place of Necrovarus' mortal body.", - "ids": "5278,5279" - }, - { - "examine": "I can climb these stairs.", - "ids": "5280" - }, - { - "examine": "They go down.", - "ids": "5281" - }, - { - "examine": "A ghastly fountain filled with slime and bones, the source of Necrovarus' power.", - "ids": "5282" - }, - { - "examine": "It's a small Ectofuntus.", - "ids": "5283" - }, - { - "examine": "A big grinding thing.", - "ids": "5284" - }, - { - "examine": "The tatty gangplank of a tatty ship.", - "ids": "5285,5286" - }, - { - "examine": "Seen better days.", - "ids": "5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305" - }, - { - "examine": "A wooden rowing boat.", - "ids": "5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329" - }, - { - "examine": "A pile of boxes for storage.", - "ids": "5330" - }, - { - "examine": "A leafy fern.", - "ids": "5331" - }, - { - "examine": "A small bushy plant.", - "ids": "5332" - }, - { - "examine": "A leafy shrub.", - "ids": "5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344" - }, - { - "examine": "This needs dusting before I'll sit on it.", - "ids": "5346,5347" - }, - { - "examine": "Not so good for sitting on.", - "ids": "5348" - }, - { - "examine": "Generally used for putting things on.", - "ids": "5349" - }, - { - "examine": "Actually, I could do with a drink...", - "ids": "5350" - }, - { - "examine": "For leaning against...", - "ids": "5351" - }, - { - "examine": "One horsepower; wooden suspension: a beauty.", - "ids": "5352" - }, - { - "examine": "A wooden wheelbarrow.", - "ids": "5353" - }, - { - "examine": "Animal feeder.", - "ids": "5354" - }, - { - "examine": "It probably hasn't got anything interesting inside.", - "ids": "5355" - }, - { - "examine": "They probably haven't got anything interesting inside.", - "ids": "5356,5357" - }, - { - "examine": "Looks like he's been dead a while now...", - "ids": "5358" - }, - { - "examine": "I don't think he'd mind us checking for his wallet now...", - "ids": "5359" - }, - { - "examine": "I'm sure he died of natural causes. Like a massive dragon or something...", - "ids": "5360,5361,5362,5363,5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396" - }, - { - "examine": "This figure brings luck to those who sail.", - "ids": "5397,5398,5399,5400,5401,5402" - }, - { - "examine": "Without this I'm going around in circles.", - "ids": "5403" - }, - { - "examine": "Holds up the sails.", - "ids": "5404,5405,5406,5407,5408,5409,5410,5411,5412,5413,5414" - }, - { - "examine": "Allows access to other parts of the ship.", - "ids": "5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428" - }, - { - "examine": "It's a table with candles on.", - "ids": "5429" - }, - { - "examine": "Doesn't look too good...", - "ids": "5430" - }, - { - "examine": "Looks a bit empty.", - "ids": "5431,5432,5433,5434,5435,5436,5437,5438" - }, - { - "examine": "It's been knocked off its hinges.", - "ids": "5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452" - }, - { - "examine": "It's a door.", - "ids": "5453,5454,5455,5456,5457,5458,5459,5460" - }, - { - "examine": "A subterranean pool of ectoplasm.", - "ids": "5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487" - }, - { - "examine": "I can climb this.", - "ids": "5488" - }, - { - "examine": "The finest rings.", - "ids": "5489" - }, - { - "examine": "I wonder what's under it?", - "ids": "5490" - }, - { - "examine": "I wonder what's down there?", - "ids": "5491,5492" - }, - { - "examine": "I can climb this.", - "ids": "5493" - }, - { - "examine": "A crude torch stuck in the ground.", - "ids": "5494" - }, - { - "examine": "I guess I could sleep in it if I was really tired.", - "ids": "5495,5496" - }, - { - "examine": "A well slept in bed.", - "ids": "5497,5498" - }, - { - "examine": "Hot!", - "ids": "5499,5500" - }, - { - "examine": "Stops people getting out.", - "ids": "5501,5502,5503,5504,5505,5506,5507" - }, - { - "examine": "It's a lectern.", - "ids": "5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537" - }, - { - "examine": "Makes you cry.", - "ids": "5538" - }, - { - "examine": "Found near the water's edge.", - "ids": "5539,5540,5541,5542" - }, - { - "examine": "Isn't Heather a girls' name?", - "ids": "5543" - }, - { - "examine": "Smells lovely!", - "ids": "5544" - }, - { - "examine": "Who is this Heather girl?", - "ids": "5545" - }, - { - "examine": "A purple haze of delight.", - "ids": "5546" - }, - { - "examine": "The colour purple on stems.", - "ids": "5547" - }, - { - "examine": "I ponder... Why is it purple?", - "ids": "5548" - }, - { - "examine": "I wonder why it's purple?", - "ids": "5549" - }, - { - "examine": "A cure for nettle stings.", - "ids": "5550" - }, - { - "examine": "A droopy tree.", - "ids": "5551" - }, - { - "examine": "These trees are found near water.", - "ids": "5552,5553" - }, - { - "examine": "This is what is left of a willow tree.", - "ids": "5554" - }, - { - "examine": "I don't know art, but I like it!", - "ids": "5555,5556,5557" - }, - { - "examine": "Aggie's cooking something, probably best not to think what...", - "ids": "5558,9736" - }, - { - "examine": "Trinkets and stuff.", - "ids": "5559" - }, - { - "examine": "Looking good!", - "ids": "5560" - }, - { - "examine": "Aggie's broomstick.", - "ids": "5561" - }, - { - "examine": "Aggie has already dyed this cloth.", - "ids": "5562,5563" - }, - { - "examine": "A thoroughly used bed.", - "ids": "5564" - }, - { - "examine": "A lovely comfy-looking big bed.", - "ids": "5565,5566,5567,5568" - }, - { - "examine": "The remains of a stone wall.", - "ids": "5569,5570" - }, - { - "examine": "An empty home for chickens.", - "ids": "5571" - }, - { - "examine": "A lovely bare chicken coop.", - "ids": "5572,5573" - }, - { - "examine": "Full of animal feed.", - "ids": "5574" - }, - { - "examine": "Animal feeder.", - "ids": "5575,5576,5577" - }, - { - "examine": "A traveller's companion.", - "ids": "5578" - }, - { - "examine": "Loaded with hay and ready to roll.", - "ids": "5579" - }, - { - "examine": "A wooden wheelbarrow.", - "ids": "5580" - }, - { - "examine": "Someone's been chopping logs.", - "ids": "5581,5582" - }, - { - "examine": "Baby bread.", - "ids": "5583,5584,5585,5586,5587,5588" - }, - { - "examine": "The river makes it spin.", - "ids": "5589,5590,5591,5592,5593" - }, - { - "examine": "I'd better not get my hands trapped in that.", - "ids": "5594" - }, - { - "examine": "Diango's Toy Stall.", - "ids": "5595" - }, - { - "examine": "Doesn't look too good...", - "ids": "5596" - }, - { - "examine": "Shows which way the wind blows.", - "ids": "5597" - }, - { - "examine": "A barrel for collecting rain water.", - "ids": "5598,5599,5600,5601,5602,5603" - }, - { - "examine": "A rock.", - "ids": "5604" - }, - { - "examine": "A small rock.", - "ids": "5605" - }, - { - "examine": "Some rock.", - "ids": "5606,5607" - }, - { - "examine": "This fire is already in use, I wouldn't mess with it if I were you...", - "ids": "5608" - }, - { - "examine": "They could do with a wash.", - "ids": "5609,5610,5611,5612" - }, - { - "examine": "It smells like the rats aren't washing often enough.", - "ids": "5613" - }, - { - "examine": "Sit back and relax...", - "ids": "5615" - }, - { - "examine": "This tells you which way is which.", - "ids": "5616,5617" - }, - { - "examine": "These open and close!", - "ids": "5618" - }, - { - "examine": "This may be worth searching.", - "ids": "5619,5620" - }, - { - "examine": "An elegant ceramic pot tarnished with the dirt of a hundred years.", - "ids": "5621" - }, - { - "examine": "I wonder what's inside...", - "ids": "5622" - }, - { - "examine": "It smells a bit stuffy.", - "ids": "5623" - }, - { - "examine": "It smells funny in there.", - "ids": "5624" - }, - { - "examine": "Ned is making some rope here.", - "ids": "5625,5626" - }, - { - "examine": "A blue standard.", - "ids": "5627" - }, - { - "examine": "A standard of Lumbridge Castle.", - "ids": "5628,5629,5630,36925" - }, - { - "examine": "Glowing embers.", - "ids": "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,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787" - }, - { - "examine": "The sort of bench you get in churches.", - "ids": "5788" - }, - { - "examine": "An expertly carved statue of a former King of Misthalin.", - "ids": "5789,5790" - }, - { - "examine": "A carving of a figure from the history of RuneScape.", - "ids": "5791,36749,36751" - }, - { - "examine": "When I've ground some flour, I'll be able to collect it here.", - "ids": "5792" - }, - { - "examine": "It's a large crack in the wall.", - "ids": "5793,5794,5795,5796,5797,5798" - }, - { - "examine": "It's a trapdoor.", - "ids": "5799,5800,5801,5802" - }, - { - "examine": "It's an open trapdoor.", - "ids": "5803,5804,5805,5806,5807" - }, - { - "examine": "An incredibly detailed stone sculpture.", - "ids": "5808" - }, - { - "examine": "Helps the Seers predict the weather when it's working.", - "ids": "5809" - }, - { - "examine": "Helps the Seers predict the weather.", - "ids": "5810,5811" - }, - { - "examine": "I can climb this.", - "ids": "5812" - }, - { - "examine": "I can climb down this.", - "ids": "5813,5814" - }, - { - "examine": "A candle, a lens and some sort of crystal all precariously balanced together.", - "ids": "5815" - }, - { - "examine": "A beacon so gnome gliders can safely land.", - "ids": "5816,5817,5818,5819,5820,5821,5822,5823,5824" - }, - { - "examine": "Fly Gnome Air.", - "ids": "5825" - }, - { - "examine": "This shop deals in antique swords.", - "ids": "5826,5827,5828,5829,5830" - }, - { - "examine": "Shop counter.", - "ids": "5831,5832,5833,5834" - }, - { - "examine": "Don't want to close this, I might not be able to get back down.", - "ids": "5835,5836,5837,5838,5839,5840,5841" - }, - { - "examine": "It overlooks the path below. Let's hope it won't fall on your head.", - "ids": "5842" - }, - { - "examine": "Someone is climbing down there! Looks dangerous.", - "ids": "5843" - }, - { - "examine": "Placed in a perfect position for stumbling over.", - "ids": "5844" - }, - { - "examine": "There is a rope going over it.", - "ids": "5845" - }, - { - "examine": "Someone is climbing down the rope.", - "ids": "5846" - }, - { - "examine": "You will need to climb over it.", - "ids": "5847" - }, - { - "examine": "The tree stands tall at the edge of the pool.", - "ids": "5848" - }, - { - "examine": "You can't stand on them, but maybe something can be wedged into there.", - "ids": "5849" - }, - { - "examine": "Something put on top of this wouldn't fall off, it's quite smooth and flat.", - "ids": "5850,5851" - }, - { - "examine": "Someone put a plank on top of the stone! Clever...", - "ids": "5852,5853,5854" - }, - { - "examine": "The pool looks very peaceful. You can also hear faint singing coming from it.", - "ids": "5855" - }, - { - "examine": "The legendary White Pearl fruit is growing on these thorny bushes!", - "ids": "5856" - }, - { - "examine": "There's a nasty stench coming from the cave.", - "ids": "5857" - }, - { - "examine": "Faint rays of daylight shine through.", - "ids": "5858" - }, - { - "examine": "It's very dark down there.", - "ids": "5859" - }, - { - "examine": "Very pointy, very sharp.", - "ids": "5860,5861" - }, - { - "examine": "This is the place where you buried Asleif.", - "ids": "5862" - }, - { - "examine": "Asleif was given a proper burial on this spot.", - "ids": "5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876,5877" - }, - { - "examine": "It's a long wooden table.", - "ids": "5878" - }, - { - "examine": "Generally used for putting things on.", - "ids": "5879" - }, - { - "examine": "A crude torch stuck in the ground.", - "ids": "5881,5882" - }, - { - "examine": "Sticky, dirty mud.", - "ids": "5883" - }, - { - "examine": "This stinks...", - "ids": "5884" - }, - { - "examine": "The roots go down into the mud.", - "ids": "5885" - }, - { - "examine": "Sticky, dirty roots covered in sticky, dirty mud.", - "ids": "5886" - }, - { - "examine": "It's barely a door, really.", - "ids": "5887,5888,5889,5890" - }, - { - "examine": "The entry to a special tent in the camp.", - "ids": "5891,5892,5893,5894" - }, - { - "examine": "It's just a big stone, really.", - "ids": "5895" - }, - { - "examine": "Uh oh, someone is going to be in trouble!", - "ids": "5896" - }, - { - "examine": "The pool looks very peaceful. You can also hear faint singing coming from it.", - "ids": "5897,5898,5899,5900,5901" - }, - { - "examine": "It's only useful for hiding behind now.", - "ids": "5902,5903,5904" - }, - { - "examine": "This tree has been cut down.", - "ids": "5905,5906,5907,5908" - }, - { - "examine": "A still for making lamp oil.", - "ids": "5909" - }, - { - "examine": "The still has oil in.", - "ids": "5910,5911,5912,5913,5914,5915,5916" - }, - { - "examine": "Noxious fumes bubble up from the bowels of the earth.", - "ids": "5917,5918,5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945" - }, - { - "examine": "You see a circle of light at the top.", - "ids": "5946" - }, - { - "examine": "An entrance to the dark caves.", - "ids": "5947" - }, - { - "examine": "I can jump from this stepping stone.", - "ids": "5948,5949,5950,5951,5952,5953,5954,5955,5956,5957,5958" - }, - { - "examine": "I wonder what this does...", - "ids": "5959,5960,5961,5962,5963" - }, - { - "examine": "Flying in mid-air!", - "ids": "5964,5965" - }, - { - "examine": "A blocked passage.", - "ids": "5966" - }, - { - "examine": "I'd better leave his expensive dwarven balls alone.", - "ids": "5967,5968,5969,5970,5971,5972" - }, - { - "examine": "A small cave entrance.", - "ids": "5973,5974" - }, - { - "examine": "A powerful ranging device that fires metal balls.", - "ids": "5975,5976" - }, - { - "examine": "It's a sheer wall of fire, rising as high as you can see.", - "ids": "5977,5978,5979,5980" - }, - { - "examine": "Lighting for the caves.", - "ids": "5981,5982,5983,5984" - }, - { - "examine": "A rock.", - "ids": "5985" - }, - { - "examine": "A small rock.", - "ids": "5986" - }, - { - "examine": "A rock.", - "ids": "5987,5988" - }, - { - "examine": "A mineral vein that looks distinctly like gold.", - "ids": "5989,5990,5991,5992,5993,5994,5995,5996,5997" - }, - { - "examine": "Functions like an open door...", - "ids": "5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008" - }, - { - "examine": "It's a tiny little blue flame. Is this the essence of the Arzinian Being?", - "ids": "6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031" - }, - { - "examine": "Powers the boat.", - "ids": "6032,6033,6034,6035" - }, - { - "examine": "Allows access to other parts of the ship.", - "ids": "6036,6037" - }, - { - "examine": "Without this I'm going around in circles.", - "ids": "6038,6039,6040,6041,6042" - }, - { - "examine": "Powers the boat.", - "ids": "6043,6044" - }, - { - "examine": "You can 'cart' things around on this.", - "ids": "6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063" - }, - { - "examine": "A stand for hats.", - "ids": "6064" - }, - { - "examine": "Some dwarf clothes that are obviously too small for me!", - "ids": "6065" - }, - { - "examine": "An empty weapon rack.", - "ids": "6066" - }, - { - "examine": "A weapon rack.", - "ids": "6067,6068,6069" - }, - { - "examine": "A method of dwarf storage", - "ids": "6070" - }, - { - "examine": "A method of dwarf storage.", - "ids": "6071,6072,6073,6074" - }, - { - "examine": "Useful for a dwarf.", - "ids": "6075" - }, - { - "examine": "A big desk, for a dwarf!", - "ids": "6076,6077,6078,6079" - }, - { - "examine": "Spins yarn.", - "ids": "6080" - }, - { - "examine": "Banking transactions are processed here.", - "ids": "6081" - }, - { - "examine": "Banking transactions are recorded here.", - "ids": "6082,37159" - }, - { - "examine": "This booth is closed.", - "ids": "6083" - }, - { - "examine": "The bank teller will serve you from here.", - "ids": "6084" - }, - { - "examine": "A way upwards.", - "ids": "6085" - }, - { - "examine": "A way down.", - "ids": "6086" - }, - { - "examine": "A way upwards.", - "ids": "6087" - }, - { - "examine": "A way down.", - "ids": "6088" - }, - { - "examine": "A way upwards.", - "ids": "6089" - }, - { - "examine": "A way down.", - "ids": "6090" - }, - { - "examine": "A treasure trove of knowledge.", - "ids": "6091,6092" - }, - { - "examine": "A lovely place to cook meat.", - "ids": "6093,6094,6095" - }, - { - "examine": "A great place to cook meat.", - "ids": "6096" - }, - { - "examine": "A good source of water.", - "ids": "6097,6098,6099" - }, - { - "examine": "The door is closed.", - "ids": "6100" - }, - { - "examine": "The door is open.", - "ids": "6101" - }, - { - "examine": "The door is closed.", - "ids": "6102" - }, - { - "examine": "The door is open.", - "ids": "6103" - }, - { - "examine": "The door is closed.", - "ids": "6104" - }, - { - "examine": "The door is open.", - "ids": "6105" - }, - { - "examine": "The door is closed.", - "ids": "6106" - }, - { - "examine": "The door is open.", - "ids": "6107" - }, - { - "examine": "The door is closed.", - "ids": "6108" - }, - { - "examine": "The door is open.", - "ids": "6109" - }, - { - "examine": "The door is closed.", - "ids": "6110" - }, - { - "examine": "The door is open.", - "ids": "6111" - }, - { - "examine": "The door is closed.", - "ids": "6112" - }, - { - "examine": "The door is open.", - "ids": "6113" - }, - { - "examine": "The door is closed.", - "ids": "6114" - }, - { - "examine": "The door is open.", - "ids": "6115,6116,6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132,6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,6144,6145" - }, - { - "examine": "Useful for keeping birds.", - "ids": "6146" - }, - { - "examine": "Tower of rock.", - "ids": "6147,6148,6149" - }, - { - "examine": "Useful for making weapons.", - "ids": "6150" - }, - { - "examine": "Used for getting water.", - "ids": "6151" - }, - { - "examine": "Used for getting drunk.", - "ids": "6152,6153" - }, - { - "examine": "Used for storage.", - "ids": "6154,6155,6156,6157" - }, - { - "examine": "Not good for eating.", - "ids": "6158,6159,6160,6161" - }, - { - "examine": "Finest precious stones.", - "ids": "6162" - }, - { - "examine": "Bread and cakes are spread out over it.", - "ids": "6163" - }, - { - "examine": "Finely wrought wares of silver.", - "ids": "6164" - }, - { - "examine": "I can get clothes made up from this stall.", - "ids": "6165" - }, - { - "examine": "A whole lot of tools for crafting.", - "ids": "6166,6167,6168,6169,6170,6171,6172" - }, - { - "examine": "A fine piece of sculpting.", - "ids": "6173,6174,6175" - }, - { - "examine": "Oblong boxes. You hope there isn't anything other than salt inside.", - "ids": "6176" - }, - { - "examine": "Big mysterious crates. You wonder what could be inside.", - "ids": "6177" - }, - { - "examine": "Wooden crates, contents unknown.", - "ids": "6178" - }, - { - "examine": "They could do with a wash.", - "ids": "6179,6180" - }, - { - "examine": "Dead animal parts dangling!", - "ids": "6181" - }, - { - "examine": "Dead meat. Dangling from the wall. Looks delicious.", - "ids": "6182" - }, - { - "examine": "It looks as hard as a rock, not very inviting.", - "ids": "6183" - }, - { - "examine": "A smelly old mattress.", - "ids": "6184" - }, - { - "examine": "Metal plating to protect the dwarf.", - "ids": "6185,6186" - }, - { - "examine": "Useful... for a dwarf!", - "ids": "6187" - }, - { - "examine": "Various implements for working with metal.", - "ids": "6188" - }, - { - "examine": "A hot place for forging things in.", - "ids": "6189,6190" - }, - { - "examine": "Being repaired.", - "ids": "6191,6192" - }, - { - "examine": "I look shorter, but not necessarily sweeter!", - "ids": "6193" - }, - { - "examine": "Used for sitting.", - "ids": "6194,6195" - }, - { - "examine": "Useful for a dwarf.", - "ids": "6196,6197,6198,6199" - }, - { - "examine": "Fit for a dwarven feast!", - "ids": "6200,6201" - }, - { - "examine": "Gives out light, but then you knew that already.", - "ids": "6202,6203" - }, - { - "examine": "A simple place to sleep.", - "ids": "6204" - }, - { - "examine": "A good dwarven bed. Too small for me!", - "ids": "6205" - }, - { - "examine": "Many important meetings are held here...", - "ids": "6206" - }, - { - "examine": "Draped in cloth.", - "ids": "6207" - }, - { - "examine": "A beautiful seat, fit for a King.", - "ids": "6208,6209,6210" - }, - { - "examine": "A dwarf table with chopping board.", - "ids": "6211" - }, - { - "examine": "This tree has been cut down.", - "ids": "6212" - }, - { - "examine": "A barrel full of ranging equipment...", - "ids": "6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229" - }, - { - "examine": "I suppose he wants my money.", - "ids": "6230" - }, - { - "examine": "A lovely table.", - "ids": "6231" - }, - { - "examine": "An expensive water feature.", - "ids": "6232,6233,6234,6235" - }, - { - "examine": "Stop for what, food?", - "ids": "6236" - }, - { - "examine": "Yellow blossom, lovely.", - "ids": "6237" - }, - { - "examine": "Closed.", - "ids": "6238" - }, - { - "examine": "Open.", - "ids": "6239" - }, - { - "examine": "Closed.", - "ids": "6240" - }, - { - "examine": "Open.", - "ids": "6241" - }, - { - "examine": "A stone staircase.", - "ids": "6242,6243,6244,6245" - }, - { - "examine": "Has upon it a beer, which I will not take for moral reasons.", - "ids": "6246,6247,6248" - }, - { - "examine": "No this is not a mirage!", - "ids": "6249" - }, - { - "examine": "It's a goldfish! No, wait, it's just a bed.", - "ids": "6250,6251,6252,6253,6254" - }, - { - "examine": "Animal feeder.", - "ids": "6255,6256" - }, - { - "examine": "Lovely... Fresh.", - "ids": "6257" - }, - { - "examine": "Dry dung.", - "ids": "6258" - }, - { - "examine": "Smelly.", - "ids": "6259" - }, - { - "examine": "I can climb down this.", - "ids": "6260" - }, - { - "examine": "I can climb this.", - "ids": "6261,6262" - }, - { - "examine": "Useful for pets.", - "ids": "6263" - }, - { - "examine": "Useful for keeping birds.", - "ids": "6264" - }, - { - "examine": "Bird cage.", - "ids": "6265,6266" - }, - { - "examine": "An empty cage, maybe the owner let the creature free... I do hope so.", - "ids": "6267" - }, - { - "examine": "Useful for pets.", - "ids": "6268,6269,6270,6271,6272,6273" - }, - { - "examine": "Lovely comfy looking big bed.", - "ids": "6274" - }, - { - "examine": "Laden with heaps of paper.", - "ids": "6275" - }, - { - "examine": "I don't know much about art, but I like this.", - "ids": "6276" - }, - { - "examine": "Look at the size of this cactus.", - "ids": "6277" - }, - { - "examine": "Don't you open that trapdoor!", - "ids": "6278" - }, - { - "examine": "There is a rope leading to the bottom of this smoke filled well.", - "ids": "6279" - }, - { - "examine": "I can climb this.", - "ids": "6280,6281" - }, - { - "examine": "A portal to another land?", - "ids": "6282" - }, - { - "examine": "Weird looking pillar.", - "ids": "6283,6284,6285,6286,6287,6288,6289,6290,6291" - }, - { - "examine": "Shelves filled with interesting books.", - "ids": "6292,6293" - }, - { - "examine": "A locked display case for valuable artefacts.", - "ids": "6294" - }, - { - "examine": "One of the sculptures is missing. Oh yes, that was me!", - "ids": "6295" - }, - { - "examine": "It looks like the demon didn't survive after all.", - "ids": "6296" - }, - { - "examine": "He looks old, older than me anyway.", - "ids": "6297" - }, - { - "examine": "I don't think he's going to make it.", - "ids": "6298" - }, - { - "examine": "I don't understand, why didn't he go back to Lumby?", - "ids": "6299,6300" - }, - { - "examine": "A throne encrusted with sparkling gems.", - "ids": "6301" - }, - { - "examine": "A throne from which you have removed the gems.", - "ids": "6302,6303,6304,6305,6306" - }, - { - "examine": "A statuette of a golem, facing right.", - "ids": "6307" - }, - { - "examine": "A statuette of a golem, facing left.", - "ids": "6308" - }, - { - "examine": "The statuette is missing from this alcove.", - "ids": "6309,6310" - }, - { - "examine": "Not good for eating.", - "ids": "6311" - }, - { - "examine": "I can climb this.", - "ids": "6312" - }, - { - "examine": "I can climb down this.", - "ids": "6313,6314,6315,6316,6317,6318,6319,6320,6321,6322" - }, - { - "examine": "A broken clay arm.", - "ids": "6323" - }, - { - "examine": "A clay foot.", - "ids": "6324" - }, - { - "examine": "Half a golem smashed and broken.", - "ids": "6325" - }, - { - "examine": "Smashed and half buried.", - "ids": "6326" - }, - { - "examine": "A device once used for making pottery.", - "ids": "6327" - }, - { - "examine": "Once upon a time this was used to make pottery.", - "ids": "6328" - }, - { - "examine": "Once upon a time this made clay hard.", - "ids": "6329" - }, - { - "examine": "There was a lot of pottery made here a long time ago.", - "ids": "6330" - }, - { - "examine": "A statue of a guy with a hammer.", - "ids": "6331" - }, - { - "examine": "A weathered old statue.", - "ids": "6332" - }, - { - "examine": "A reclining lady.", - "ids": "6333" - }, - { - "examine": "This statue has been tampered with.", - "ids": "6334" - }, - { - "examine": "Contains a statue of a woman.", - "ids": "6335,6336,6337" - }, - { - "examine": "Empty.", - "ids": "6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362" - }, - { - "examine": "A very heavy stone door.", - "ids": "6363" - }, - { - "examine": "A door to a demon's lair?", - "ids": "6364,6365,6366,6367,6368,6369,6370,6371" - }, - { - "examine": "A stone staircase.", - "ids": "6372,6373,6374,6375,6376,6377,6378,6379,6380" - }, - { - "examine": "A source of water for the river Elid.", - "ids": "6381" - }, - { - "examine": "Maybe I could swing on this somehow...", - "ids": "6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397,6398,6399,6400,6401" - }, - { - "examine": "A limestone ceiling growth.", - "ids": "6402" - }, - { - "examine": "A tooth shaped rock formation protruding from the floor.", - "ids": "6403" - }, - { - "examine": "An old mystical torch.", - "ids": "6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,6414,6415,6416,6417" - }, - { - "examine": "I can climb this.", - "ids": "6418,6419" - }, - { - "examine": "I wonder what's inside?", - "ids": "6420,6421,6422" - }, - { - "examine": "A magical aura seems to shimmer over the glass...", - "ids": "6423,6424,6425,6426,6427,6428,6429,6430,6431,6432,6433" - }, - { - "examine": "I wonder what that's there for...", - "ids": "6434,6435" - }, - { - "examine": "I can climb this.", - "ids": "6436" - }, - { - "examine": "An ancient looking tomb.", - "ids": "6437,6438" - }, - { - "examine": "Climb this rope to go up.", - "ids": "6439,6440" - }, - { - "examine": "Looks like a small cave.", - "ids": "6441" - }, - { - "examine": "Blocked by an icicle.", - "ids": "6442" - }, - { - "examine": "Blocked by two icicles.", - "ids": "6443" - }, - { - "examine": "Blocked by three icicles.", - "ids": "6444" - }, - { - "examine": "Blocked by four icicles.", - "ids": "6445" - }, - { - "examine": "Blocked by five icicles.", - "ids": "6446" - }, - { - "examine": "Looks like a small cave.", - "ids": "6447" - }, - { - "examine": "It looks very sturdy.", - "ids": "6448,6449" - }, - { - "examine": "I can climb this.", - "ids": "6450" - }, - { - "examine": "A wrought iron gate.", - "ids": "6451,6452,6453" - }, - { - "examine": "Even rocks could freeze in this cold!", - "ids": "6454" - }, - { - "examine": "Looks slippery.", - "ids": "6455,6456,6457,6458,6459,6460" - }, - { - "examine": "An ice gate.", - "ids": "6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471" - }, - { - "examine": "Chunky pieces of ice.", - "ids": "6472,6473,6474,6475,6476,6477,6478,6479,6480" - }, - { - "examine": "A mysterious tunnel-like structure.", - "ids": "6481,6482" - }, - { - "examine": "You can feel a mysterious power emanating from it...", - "ids": "6483,6484,6485,6486,6487,6488,6489,6490,6491,6492,6493" - }, - { - "examine": "This door is sealed by an ancient mystical power...", - "ids": "6494,6495,6496" - }, - { - "examine": "I can climb down this.", - "ids": "6497,6498,6499,6500" - }, - { - "examine": "I can climb this.", - "ids": "6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511" - }, - { - "examine": "A fancy name for a coffin.", - "ids": "6512" - }, - { - "examine": "A strange thing to leave lying around...", - "ids": "6513" - }, - { - "examine": "I hope this is just ornamental...", - "ids": "6514,6515,6516,6517,6518,6519,6520" - }, - { - "examine": "Uh-oh!", - "ids": "6521,6522" - }, - { - "examine": "This leads downwards.", - "ids": "6523" - }, - { - "examine": "You can see a small fissure in the ground here.", - "ids": "6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535,6536,6537" - }, - { - "examine": "The body of a lion, the head of a man?", - "ids": "6538,6539,6540,6541,6542,6543,6544" - }, - { - "examine": "A smooth, sandstone door that is slightly warm to the touch.", - "ids": "6545,6546,6547,6548" - }, - { - "examine": "A well down into the pyramid.", - "ids": "6549,6550" - }, - { - "examine": "A portal that leads you out of the pyramid.", - "ids": "6551" - }, - { - "examine": "A mysterious ancient altar to some forgotten god...", - "ids": "6552" - }, - { - "examine": "Opens into another area.", - "ids": "6553,6554,6555,6556,6557,6558,6559,6560" - }, - { - "examine": "A ladder that's almost not there at all...", - "ids": "6561,6562,6563,6564,6565" - }, - { - "examine": "Gate like?", - "ids": "6566,6567" - }, - { - "examine": "Garments for the discerning.", - "ids": "6568" - }, - { - "examine": "Gone-off bread, cakes and pastries.", - "ids": "6569" - }, - { - "examine": "Finest precious stones.", - "ids": "6570" - }, - { - "examine": "These will keep you warm.", - "ids": "6571" - }, - { - "examine": "The spice is right.", - "ids": "6572" - }, - { - "examine": "An empty market stall.", - "ids": "6573" - }, - { - "examine": "Fine brews from exotic regions.", - "ids": "6574" - }, - { - "examine": "Best used with a horse.", - "ids": "6575,6576,6577" - }, - { - "examine": "Grows a yellow fruit.", - "ids": "6578,6579,6580" - }, - { - "examine": "A way to get in the tent.", - "ids": "6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604" - }, - { - "examine": "Wet and salty.", - "ids": "6605" - }, - { - "examine": "The focus of the suntrap.", - "ids": "6606" - }, - { - "examine": "A shiny mirror.", - "ids": "6607,6608,6609,6610,6611,6612,6613" - }, - { - "examine": "A large door with a hieroglyph of a cat.", - "ids": "6614" - }, - { - "examine": "Gate like?", - "ids": "6615,6616,6617,6618,6619" - }, - { - "examine": "It looks like a hole in the wall.", - "ids": "6620,6621" - }, - { - "examine": "Rocky.", - "ids": "6622" - }, - { - "examine": "An entrance into a tunnel. I wonder where it leads to.", - "ids": "6623" - }, - { - "examine": "It looks like a door.", - "ids": "6624" - }, - { - "examine": "Hmm. A door.", - "ids": "6625" - }, - { - "examine": "A big wooden door.", - "ids": "6626,6627,6628" - }, - { - "examine": "That could hurt.", - "ids": "6629" - }, - { - "examine": "A strange thing to leave lying around...", - "ids": "6630,6631" - }, - { - "examine": "I can't see the bottom.", - "ids": "6632,6633,6634" - }, - { - "examine": "Has a lid shaped like a man. I think it contains someone's liver. Yuck.", - "ids": "6635,6636" - }, - { - "examine": "Has a lid shaped like a crocodile. Yuck, I think there are lungs inside.", - "ids": "6637,6638" - }, - { - "examine": "Has a lid shaped like a bug. Disgusting! I think there's a stomach inside.", - "ids": "6639,6640" - }, - { - "examine": "Has a lid shaped like an ape. Eeew! I think it contains someone's intestines.", - "ids": "6641" - }, - { - "examine": "Doorway*Opens into another area, come on, I know this.", - "ids": "6642" - }, - { - "examine": "Useful for putting things on.", - "ids": "6644" - }, - { - "examine": "A ladder!! Never seen one of those before.", - "ids": "6645" - }, - { - "examine": "I wonder what's inside.", - "ids": "6646" - }, - { - "examine": "Perhaps I should search it.", - "ids": "6647" - }, - { - "examine": "I wonder where this goes?", - "ids": "6648" - }, - { - "examine": "They go down.", - "ids": "6649" - }, - { - "examine": "Phew!! That's one big bridge.", - "ids": "6650" - }, - { - "examine": "A big bridge over the river.", - "ids": "6651,6652" - }, - { - "examine": "Wow. More bridge. Awesome.", - "ids": "6653,6654" - }, - { - "examine": "Oops.", - "ids": "6655" - }, - { - "examine": "Rolls of colourful cloth.", - "ids": "6656" - }, - { - "examine": "An ancient giant serpent.", - "ids": "6657" - }, - { - "examine": "A tunnel leading upwards.", - "ids": "6658" - }, - { - "examine": "A tunnel leading into the depths of the earth.", - "ids": "6659" - }, - { - "examine": "A rock wall infused with the power of Guthix.", - "ids": "6660" - }, - { - "examine": "The wall is weeping blue tears.", - "ids": "6661" - }, - { - "examine": "The wall is weeping green tears.", - "ids": "6662" - }, - { - "examine": "The wall is not weeping at the moment.", - "ids": "6663,6664" - }, - { - "examine": "The wall is weeping blue tears.", - "ids": "6665" - }, - { - "examine": "The wall is weeping green tears.", - "ids": "6666" - }, - { - "examine": "The wall is not weeping at the moment.", - "ids": "6667,6668" - }, - { - "examine": "Stone with blue veins.", - "ids": "6669,6670,6671" - }, - { - "examine": "They don't look too easy to climb.", - "ids": "6672" - }, - { - "examine": "I could climb these.", - "ids": "6673,6674,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701" - }, - { - "examine": "I can climb these stairs.", - "ids": "6702,6703,6704,6705,6706,6707" - }, - { - "examine": "I can climb this.", - "ids": "6708,6709,6710,6711,6712" - }, - { - "examine": "I wonder what is awaiting me on the other side?", - "ids": "6713,6714,6715,6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731" - }, - { - "examine": "I wonder what awaits me on the other side?", - "ids": "6732,6733,6734,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761,6762,6763,6764,6765,6766" - }, - { - "examine": "A crude torch stuck in the ground.", - "ids": "6767,6768,6769,6770" - }, - { - "examine": "A large stone coffin.", - "ids": "6771,6772,6773" - }, - { - "examine": "A large stone chest.", - "ids": "6774,6775" - }, - { - "examine": "Looks like he's been dead a while now...", - "ids": "6776" - }, - { - "examine": "I don't think he'd mind us checking for his wallet now...", - "ids": "6777" - }, - { - "examine": "I'm sure he died of natural causes. Like a massive dragon or something...", - "ids": "6778,6779,6780,6781,6782,6783,6784,6785,6786,6787,6788,6789,6790" - }, - { - "examine": "The groundskeeper's bed.", - "ids": "6791,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820" - }, - { - "examine": "A large stone coffin.", - "ids": "6821,6822,6823" - }, - { - "examine": "It's locked.", - "ids": "6824,6825,6826" - }, - { - "examine": "It might still work...", - "ids": "6827,6828,6829,6830,6831,6832,6833,6834" - }, - { - "examine": "Danger... weak surface beyond gate. Digging may lead to cave-ins.", - "ids": "6835" - }, - { - "examine": "Sturdy metal bars.", - "ids": "6836,6837,6838" - }, - { - "examine": "Smells pretty bad!", - "ids": "6839" - }, - { - "examine": "It looks as this is where some wall fungus used to be.", - "ids": "6840" - }, - { - "examine": "Some crude stone steps.", - "ids": "6841,6842,6843" - }, - { - "examine": "An old crude tomb.", - "ids": "6844,6845" - }, - { - "examine": "It's damaged.", - "ids": "6846" - }, - { - "examine": "A note says, 'Please ring for attention.'", - "ids": "6847" - }, - { - "examine": "An old crude tomb.", - "ids": "6848" - }, - { - "examine": "'Leave da dead-uns boxes 'lone or else ya goes down da 'ole'", - "ids": "6849" - }, - { - "examine": "An old crude tomb.", - "ids": "6850" - }, - { - "examine": "This coffin is spilt over the floor.", - "ids": "6851" - }, - { - "examine": "This coffin is open.", - "ids": "6852" - }, - { - "examine": "An old crude tomb.", - "ids": "6853" - }, - { - "examine": "This coffin is spilt over the floor.", - "ids": "6854" - }, - { - "examine": "This coffin is open.", - "ids": "6855" - }, - { - "examine": "A barricade made from skulls and bones.", - "ids": "6856,6857,6858,6859,6860,6861,6862,6863,6864" - }, - { - "examine": "Not very high.", - "ids": "6865" - }, - { - "examine": "Hot stuff.", - "ids": "6866" - }, - { - "examine": "Ogres bang these to make noise.", - "ids": "6867,6868,6869,6870" - }, - { - "examine": "A bulky door made from solid rock.", - "ids": "6871,6872,6873,6874" - }, - { - "examine": "These open and close!", - "ids": "6875" - }, - { - "examine": "This may be worth opening.", - "ids": "6876" - }, - { - "examine": "I wonder what this item contains.", - "ids": "6877,6878,6879" - }, - { - "examine": "A barricade made of skulls and bones.", - "ids": "6880" - }, - { - "examine": "A barricade made of skulls and bones which has been crushed.", - "ids": "6881,6882" - }, - { - "examine": "An old crude tomb.", - "ids": "6883,6884,6885,6886,6887" - }, - { - "examine": "A sick, frail old man.", - "ids": "6888" - }, - { - "examine": "A sick, frail old man", - "ids": "6889" - }, - { - "examine": "An old crude tomb.", - "ids": "6890,6891,6892" - }, - { - "examine": "I don't think he'd mind us checking for his wallet now...", - "ids": "6893" - }, - { - "examine": "A good source of books!", - "ids": "6894,6895" - }, - { - "examine": "A crude torch stuck in the ground.", - "ids": "6896" - }, - { - "examine": "A strange ogre plinth, this must be where the artifacts are stored.", - "ids": "6897,6898,6899,6900,6901,6902" - }, - { - "examine": "It looks like the hole in the wall has been blocked with rubble.", - "ids": "6903" - }, - { - "examine": "How exciting, some shelves.", - "ids": "6904" - }, - { - "examine": "A hole in the wall.", - "ids": "6905,6906,6907,6908" - }, - { - "examine": "A rock wall.", - "ids": "6909" - }, - { - "examine": "It has a letter 'S' on the lock.", - "ids": "6910" - }, - { - "examine": "Used for storage.", - "ids": "6911" - }, - { - "examine": "A narrow hole in the wall.", - "ids": "6912,6913,6914" - }, - { - "examine": "Rubble is blocking the passage.", - "ids": "6915" - }, - { - "examine": "A good source of books!", - "ids": "6916,6917,6918" - }, - { - "examine": "A heavy portal decorated with bone.", - "ids": "6919,6920" - }, - { - "examine": "A symbol is carved into the wall here.", - "ids": "6921,6922,6923,6924,6925,6926" - }, - { - "examine": "I don't think I can get through this way!", - "ids": "6927" - }, - { - "examine": "Looks like its no longer operational.", - "ids": "6928" - }, - { - "examine": "Big bones are being used to prop up the wall.", - "ids": "6929,6930" - }, - { - "examine": "It's a bit like walking through a giant rib cage.", - "ids": "6931,6932,6933,6934,6935,6936,6937,6938" - }, - { - "examine": "This arch is broken. I hope the ceiling doesn't come down!", - "ids": "6939,6940,6941,6942" - }, - { - "examine": "A rocky outcrop.", - "ids": "6943,6944,6945,6946,6947,6948,6949" - }, - { - "examine": "I dont think i can get through this way.", - "ids": "6950" - }, - { - "examine": "An eye-wrenching nexus of utter negation!", - "ids": "6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968" - }, - { - "examine": "A dirty little swamp boat.", - "ids": "6969,6970" - }, - { - "examine": "It looks dark down there.", - "ids": "6971" - }, - { - "examine": "A book sits on top.", - "ids": "6972,6973,6974" - }, - { - "examine": "The door is closed.", - "ids": "6975" - }, - { - "examine": "The door is open.", - "ids": "6976" - }, - { - "examine": "The door is closed.", - "ids": "6977" - }, - { - "examine": "The door is open.", - "ids": "6978,6979,6980,6981,6982,6983" - }, - { - "examine": "Empty market stall.", - "ids": "6984" - }, - { - "examine": "A steam powered crushing machine.", - "ids": "6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998,6999,7000,7001" - }, - { - "examine": "A dwarven statue crumbling away from old age.", - "ids": "7002" - }, - { - "examine": "It's crumbling away from old age, it's hardly recognizable as a dwarf.", - "ids": "7003" - }, - { - "examine": "This stone dwarf must be guarding the way into a dwarven area!", - "ids": "7004,7005" - }, - { - "examine": "Big. For a dwarf!", - "ids": "7006,7007" - }, - { - "examine": "A work in progress.", - "ids": "7008,7009" - }, - { - "examine": "A work that has yet to begin.", - "ids": "7010" - }, - { - "examine": "An underground limpwurt plant.", - "ids": "7011,7012" - }, - { - "examine": "Insect eating plant.", - "ids": "7013,7014,7015" - }, - { - "examine": "A stone carved Pillar.", - "ids": "7016,7017" - }, - { - "examine": "Dwarf storage.", - "ids": "7018" - }, - { - "examine": "Used for keeping beer or glasses.", - "ids": "7019,7020,7021" - }, - { - "examine": "Tracks for the carts to run over.", - "ids": "7022,7023,7024,7025,7026" - }, - { - "examine": "A steam powered cart.", - "ids": "7027,7028,7029,7030,7031" - }, - { - "examine": "A fine piece of sculpting.", - "ids": "7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042,7043" - }, - { - "examine": "Keeps mine carts from rolling away.", - "ids": "7044,7045,7046,7047,7048" - }, - { - "examine": "A wooden gate.", - "ids": "7049,7050,7051,7052" - }, - { - "examine": "Lots of seeds here.", - "ids": "7053,7054,7055" - }, - { - "examine": "I can climb down these stairs.", - "ids": "7056" - }, - { - "examine": "I can climb up these stairs.", - "ids": "7057" - }, - { - "examine": "Filled to the brim with knowledge.", - "ids": "7058,7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089" - }, - { - "examine": "'You are here.'", - "ids": "7090" - }, - { - "examine": "Lots of hard study has been done here.", - "ids": "7091" - }, - { - "examine": "A telescope pointing southwards...", - "ids": "7092,7093,7094,7095" - }, - { - "examine": "A map of some ancient land.", - "ids": "7096" - }, - { - "examine": "There are plenty of shelves", - "ids": "7097" - }, - { - "examine": "Maybe it shows the location of buried treasure?", - "ids": "7098" - }, - { - "examine": "Old songs, old stories...", - "ids": "7099" - }, - { - "examine": "Armour of a Saradominist warrior. Decorative, but still effective.", - "ids": "7100" - }, - { - "examine": "A cape from Saradomin, suitable for a battle-mage.", - "ids": "7101" - }, - { - "examine": "A staff as used by Saradominist magi.", - "ids": "7102" - }, - { - "examine": "There is a powerful presence about these ruins...", - "ids": "7103,7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122,7123,7124,7125,7126,7127,7128" - }, - { - "examine": "A tear in the dimensional weave of the Abyss.", - "ids": "7129,7130,7131,7132,7133,7134,7135,7136,7137,7138,7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153" - }, - { - "examine": "A tunnel through the abyss.", - "ids": "7154,7155" - }, - { - "examine": "This seems to be blocking the exit...", - "ids": "7156,7157" - }, - { - "examine": "I could probably break this up with a pickaxe.", - "ids": "7158" - }, - { - "examine": "I could probably break this up with a pickaxe. Oh wait, I just did.", - "ids": "7159" - }, - { - "examine": "Pickaxe power!", - "ids": "7160" - }, - { - "examine": "They don't look that solid, an axe could help me chop them down.", - "ids": "7161,7162" - }, - { - "examine": "I cannot tell a lie. I chopped them down.", - "ids": "7163" - }, - { - "examine": "If I'm agile enough I might be able to squeeze through...", - "ids": "7164" - }, - { - "examine": "I could probably burn this away with a tinderbox.", - "ids": "7165" - }, - { - "examine": "I could probably burn this with fire.", - "ids": "7166" - }, - { - "examine": "Burnt open.", - "ids": "7167" - }, - { - "examine": "I could probably distract these with thievery.", - "ids": "7168,7169,7170" - }, - { - "examine": "An unstable portal across the dimensions...", - "ids": "7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186,7187,7188" - }, - { - "examine": "Abyssal Tendrils.", - "ids": "7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7204,7205" - }, - { - "examine": "An opening into the crumbling wall.", - "ids": "7219,7220" - }, - { - "examine": "This leads downwards.", - "ids": "7221" - }, - { - "examine": "This way to the viewing gallery.", - "ids": "7222,7223" - }, - { - "examine": "I'll never be able to dodge that!", - "ids": "7224" - }, - { - "examine": "It's a wall...", - "ids": "7225,7226" - }, - { - "examine": "It's the floor...", - "ids": "7227" - }, - { - "examine": "It's a wall...", - "ids": "7228,7229" - }, - { - "examine": "It's the floor...", - "ids": "7230" - }, - { - "examine": "Not your average door...", - "ids": "7231,7232,7233,7234,7235" - }, - { - "examine": "I wonder what's inside.", - "ids": "7236,7237,7238" - }, - { - "examine": "It's a long way down...", - "ids": "7239,7240" - }, - { - "examine": "That's going to hurt if it hits me!", - "ids": "7241" - }, - { - "examine": "If only I knew Karate...", - "ids": "7242,7243,7244" - }, - { - "examine": "It's the floor...", - "ids": "7245" - }, - { - "examine": "Locked.", - "ids": "7246,7247" - }, - { - "examine": "It's a wall...", - "ids": "7248,7249" - }, - { - "examine": "It's the floor...", - "ids": "7250" - }, - { - "examine": "Bend your way through.", - "ids": "7251" - }, - { - "examine": "That could really hurt!", - "ids": "7252" - }, - { - "examine": "I must not fear, fear is the little death that brings total oblivion.", - "ids": "7253" - }, - { - "examine": "Blocking my way back.", - "ids": "7254,7255,7256" - }, - { - "examine": "I wonder where it leads.", - "ids": "7257" - }, - { - "examine": "An opening into the crumbling wall.", - "ids": "7258" - }, - { - "examine": "This way to exit.", - "ids": "7259,7260,7261,7262,7263,7264,7265,7266,7267,7268,7269,7270,7271" - }, - { - "examine": "A mystical teleport.", - "ids": "7272,7273" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7274,7275,7276" - }, - { - "examine": "He looks hungry, but I don't think he'll do anything while I'm here.", - "ids": "7277" - }, - { - "examine": "He's no longer looks hungry, although perhaps a little guilty-looking.", - "ids": "7278,7279,7280" - }, - { - "examine": "He's looking at the grain...", - "ids": "7281,7282,7283" - }, - { - "examine": "A sack full of grain.", - "ids": "7284" - }, - { - "examine": "I think the chicken enjoyed his meal...", - "ids": "7285" - }, - { - "examine": "It looks pretty rickety...", - "ids": "7286,7287" - }, - { - "examine": "A mystical teleport.", - "ids": "7288,7289,7290,7291,7292,7293,7294,7295,7296,7297,7298,7299,7300,7301" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7302" - }, - { - "examine": "A gold statue of a famous White Knight.", - "ids": "7303" - }, - { - "examine": "A silver statue of a famous White Knight.", - "ids": "7304" - }, - { - "examine": "A bronze statue of a famous White Knight.", - "ids": "7305" - }, - { - "examine": "A gold statue of an ancient warrior.", - "ids": "7306" - }, - { - "examine": "A silver statue of an ancient warrior.", - "ids": "7307" - }, - { - "examine": "A bronze statue of an ancient warrior.", - "ids": "7308" - }, - { - "examine": "A gold statue of a famous warrior.", - "ids": "7309" - }, - { - "examine": "A silver statue of a famous warrior.", - "ids": "7310" - }, - { - "examine": "A bronze statue of a famous warrior.", - "ids": "7311" - }, - { - "examine": "A gold statue of an ancient White Knight.", - "ids": "7312" - }, - { - "examine": "A silver statue of an ancient White Knight.", - "ids": "7313" - }, - { - "examine": "A bronze statue of an ancient White Knight.", - "ids": "7314" - }, - { - "examine": "A mystical teleport.", - "ids": "7315,7316" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7317" - }, - { - "examine": "A mystical teleport.", - "ids": "7318,7319" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7320" - }, - { - "examine": "A mystical teleport.", - "ids": "7321,7322" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7323" - }, - { - "examine": "A mystical teleport.", - "ids": "7324,7325" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7326" - }, - { - "examine": "Filled to the brim with knowledge.", - "ids": "7327,7328,7329,7330" - }, - { - "examine": "Generally used for putting things on.", - "ids": "7331" - }, - { - "examine": "A table with a Bunsen burner on it.", - "ids": "7332" - }, - { - "examine": "There are some containers of chemicals here.", - "ids": "7333,7334,7335,7336,7337,7338,7339" - }, - { - "examine": "There's an empty vial here.", - "ids": "7340,7341,7342" - }, - { - "examine": "There's a small hole in the centre.", - "ids": "7343" - }, - { - "examine": "The spade is stuck in the hole.", - "ids": "7344" - }, - { - "examine": "The spade opened the door.", - "ids": "7345" - }, - { - "examine": "It's chained to the wall.", - "ids": "7346" - }, - { - "examine": "For storage.", - "ids": "7347,7348,7349" - }, - { - "examine": "I wonder what's inside.", - "ids": "7350" - }, - { - "examine": "Perhaps I should search it.", - "ids": "7351" - }, - { - "examine": "A mystical teleport.", - "ids": "7352,7353" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7354" - }, - { - "examine": "Bridge will support a person carrying no more than 5 kg.", - "ids": "7355,7356,7357,7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372" - }, - { - "examine": "Lets me walk through walls...", - "ids": "7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384" - }, - { - "examine": "Sit back and relax...", - "ids": "7388,7389" - }, - { - "examine": "Designed specifically to ruin your health.", - "ids": "7390" - }, - { - "examine": "A rolled up magic carpet.", - "ids": "7391" - }, - { - "examine": "A pile of rolled up magic carpets.", - "ids": "7392" - }, - { - "examine": "A little shaded area.", - "ids": "7393" - }, - { - "examine": "A magic carpet.", - "ids": "7394,7395" - }, - { - "examine": "The infamous carpet of '76.", - "ids": "7396" - }, - { - "examine": "A leafy tree.", - "ids": "7397,7398" - }, - { - "examine": "This is what is left of a willow tree.", - "ids": "7399" - }, - { - "examine": "This is what is left of a maple tree.", - "ids": "7400" - }, - { - "examine": "This is what is left of a magic tree.", - "ids": "7401" - }, - { - "examine": "This is what is left of a yew tree.", - "ids": "7402" - }, - { - "examine": "An empty barrel.", - "ids": "7403" - }, - { - "examine": "A barrel full of mushed apples.", - "ids": "7404,7405" - }, - { - "examine": "This tap runs from the apple crushing barrel.", - "ids": "7406" - }, - { - "examine": "An empty ale barrel.", - "ids": "7407" - }, - { - "examine": "A barrel of bad ale.", - "ids": "7408" - }, - { - "examine": "A barrel of unfermented liquid.", - "ids": "7409" - }, - { - "examine": "A barrel of bad cider.", - "ids": "7410" - }, - { - "examine": "A barrel of Dwarven Stout.", - "ids": "7411" - }, - { - "examine": "A barrel of mature Dwarven Stout.", - "ids": "7412" - }, - { - "examine": "A barrel of Asgarnian Ale.", - "ids": "7413" - }, - { - "examine": "A barrel of mature Asgarnian Ale.", - "ids": "7414" - }, - { - "examine": "A barrel of Greenmans Ale.", - "ids": "7415" - }, - { - "examine": "A barrel of mature Greenmans Ale.", - "ids": "7416" - }, - { - "examine": "A barrel of Wizards Mind Bomb.", - "ids": "7417" - }, - { - "examine": "A barrel of mature Wizards Mind Bomb.", - "ids": "7418" - }, - { - "examine": "A barrel of Dragon Bitter.", - "ids": "7419" - }, - { - "examine": "A barrel of mature Dragon Bitter.", - "ids": "7420" - }, - { - "examine": "A barrel of Moonlight Mead.", - "ids": "7421" - }, - { - "examine": "A barrel of mature Moonlight Mead.", - "ids": "7422" - }, - { - "examine": "A barrel of Axeman's Folly.", - "ids": "7423" - }, - { - "examine": "A barrel of mature Axeman's Folly.", - "ids": "7424" - }, - { - "examine": "A barrel of Chef's Delight.", - "ids": "7425" - }, - { - "examine": "A barrel of mature Chef's Delight.", - "ids": "7426" - }, - { - "examine": "A barrel of Slayer's Respite.", - "ids": "7427" - }, - { - "examine": "A barrel of mature Slayer's Respite.", - "ids": "7428" - }, - { - "examine": "A barrel of Cider.", - "ids": "7429" - }, - { - "examine": "A barrel of mature Cider.", - "ids": "7430,7431,7432" - }, - { - "examine": "Goes up and down!", - "ids": "7433" - }, - { - "examine": "I wonder what's under it?", - "ids": "7434" - }, - { - "examine": "I wonder what's down there?", - "ids": "7435" - }, - { - "examine": "A wooden barrel.", - "ids": "7436" - }, - { - "examine": "This vat is empty.", - "ids": "7437" - }, - { - "examine": "This vat is filled with water.", - "ids": "7438" - }, - { - "examine": "This vat is filled with bad ale.", - "ids": "7439" - }, - { - "examine": "This vat is filled with bad cider.", - "ids": "7440" - }, - { - "examine": "This vat contains a mixture of water and barley malt.", - "ids": "7441" - }, - { - "examine": "This controls the flow of ale to the barrel.", - "ids": "7442,7443" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Hammerstone hops.", - "ids": "7444" - }, - { - "examine": "Dwarven Stout is fermenting in this vat.", - "ids": "7445,7446" - }, - { - "examine": "This vat is filled with Dwarven Stout.", - "ids": "7447" - }, - { - "examine": "This vat is filled with mature Dwarven Stout.", - "ids": "7448" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Asgarnian hops.", - "ids": "7449" - }, - { - "examine": "Asgarnian Ale is fermenting in this vat.", - "ids": "7450,7451" - }, - { - "examine": "This vat is filled with Asgarnian Ale.", - "ids": "7452" - }, - { - "examine": "This vat is filled with mature Asgarnian Ale.", - "ids": "7453" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Harralander.", - "ids": "7454" - }, - { - "examine": "Greenmans Ale is fermenting in this vat.", - "ids": "7455,7456" - }, - { - "examine": "This vat is filled with Greenmans Ale.", - "ids": "7457" - }, - { - "examine": "This vat is filled with mature Greenmans Ale.", - "ids": "7458" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Yanillian hops.", - "ids": "7459" - }, - { - "examine": "Wizards Mind Bomb is fermenting in this vat.", - "ids": "7460,7461" - }, - { - "examine": "This vat is filled with Wizards Mind Bomb.", - "ids": "7462" - }, - { - "examine": "This vat is filled with mature Wizards Mind Bomb.", - "ids": "7463" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Krandorian hops.", - "ids": "7464" - }, - { - "examine": "Dragon Bitter is fermenting in this vat.", - "ids": "7465,7466" - }, - { - "examine": "This vat is filled with Dragon Bitter.", - "ids": "7467" - }, - { - "examine": "This vat is filled with mature Dragon Bitter.", - "ids": "7468" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Bittercap mushrooms.", - "ids": "7469" - }, - { - "examine": "Moonlight Mead is fermenting in this vat.", - "ids": "7470,7471" - }, - { - "examine": "This vat is filled with Moonlight Mead.", - "ids": "7472" - }, - { - "examine": "This vat is filled with mature Moonlight Mead.", - "ids": "7473" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and oak roots.", - "ids": "7474" - }, - { - "examine": "Axeman's Folly is fermenting in this vat.", - "ids": "7475,7476" - }, - { - "examine": "This vat is filled with Axeman's Folly.", - "ids": "7477" - }, - { - "examine": "This vat is filled with mature Axeman's Folly.", - "ids": "7478" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and chocolate dust.", - "ids": "7479" - }, - { - "examine": "Chef's Delight is fermenting in this vat.", - "ids": "7480,7481" - }, - { - "examine": "This vat is filled with Chef's Delight.", - "ids": "7482" - }, - { - "examine": "This vat is filled with mature Chef's Delight.", - "ids": "7483" - }, - { - "examine": "This vat contains a mixture of water, barley malt, and Wildblood hops.", - "ids": "7484" - }, - { - "examine": "Slayer's Respite is fermenting in this vat.", - "ids": "7485,7486" - }, - { - "examine": "This vat is filled with Slayer's Respite.", - "ids": "7487" - }, - { - "examine": "This vat is filled with mature Slayer's Respite.", - "ids": "7488" - }, - { - "examine": "This vat contains some apple mush.", - "ids": "7489" - }, - { - "examine": "Cider is fermenting in this vat.", - "ids": "7490,7491" - }, - { - "examine": "This vat is filled with cider.", - "ids": "7492" - }, - { - "examine": "This vat is filled with mature cider.", - "ids": "7493,7494,7495" - }, - { - "examine": "Items are for sale here.", - "ids": "7496,7497" - }, - { - "examine": "They're empty.", - "ids": "7498" - }, - { - "examine": "Farming stock is kept here.", - "ids": "7499,7500,7501,7502,7503" - }, - { - "examine": "For sitting.", - "ids": "7504,7505" - }, - { - "examine": "They're empty.", - "ids": "7506" - }, - { - "examine": "Farming stock is kept here.", - "ids": "7507,7508,7509,7510,7511" - }, - { - "examine": "A pair of sacks.", - "ids": "7512" - }, - { - "examine": "For storage.", - "ids": "7513,7514,7515" - }, - { - "examine": "A farmer's spade and rake.", - "ids": "7516,7517,7518,7519,7520,7521,7522,7523,7524,7525,7526" - }, - { - "examine": "I can climb over the fence with this.", - "ids": "7527" - }, - { - "examine": "For fermenting beer.", - "ids": "7528,7529,7530" - }, - { - "examine": "An ale barrel.", - "ids": "7531" - }, - { - "examine": "This controls the flow of ale to the barrel.", - "ids": "7532,7533,7534,7535,7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551,7552,7553,7554,7555,7556" - }, - { - "examine": "You can grow Deadly Nightshade in this Farming patch.", - "ids": "7557,7558,7559,7560" - }, - { - "examine": "Deadly Nightshade has been sown in this farming patch.", - "ids": "7561" - }, - { - "examine": "Deadly Nightshade is growing in this farming patch.", - "ids": "7562,7563,7564" - }, - { - "examine": "I wouldn't pick this with my bare hands.", - "ids": "7565" - }, - { - "examine": "This Deadly Nightshade has become diseased.", - "ids": "7566,7567,7568" - }, - { - "examine": "This Deadly Nightshade has died.", - "ids": "7569,7570,7571,7572" - }, - { - "examine": "You can grow bushes in this Farming patch.", - "ids": "7573,7574,7575,7576,7577,7578,7579,7580" - }, - { - "examine": "A cadavaberry bush is growing in this Farming patch.", - "ids": "7581,7582,7583,7584,7585,7586" - }, - { - "examine": "A fully grown cadavaberry bush.", - "ids": "7587,7588,7589,7590,7591,7592" - }, - { - "examine": "This diseased cadavaberry bush needs pruning.", - "ids": "7593,7594,7595,7596,7597,7598" - }, - { - "examine": "This cadavaberry bush has died.", - "ids": "7599,7600,7601,7602,7603,7604" - }, - { - "examine": "A dwellberry bush is growing in this Farming patch.", - "ids": "7605,7606,7607,7608,7609,7610,7611" - }, - { - "examine": "A fully grown dwellberry bush.", - "ids": "7612,7613,7614,7615,7616,7617" - }, - { - "examine": "This diseased dwellberry bush needs pruning.", - "ids": "7618,7619,7620,7621,7622,7623,7624" - }, - { - "examine": "This dwellberry bush has died.", - "ids": "7625,7626,7627,7628,7629,7630,7631" - }, - { - "examine": "A jangerberry bush is growing in this Farming patch.", - "ids": "7632,7633,7634,7635,7636,7637,7638,7639" - }, - { - "examine": "A fully grown jangerberry bush.", - "ids": "7640,7641,7642,7643,7644,7645" - }, - { - "examine": "This diseased jangerberry bush needs pruning.", - "ids": "7646,7647,7648,7649,7650,7651,7652,7653" - }, - { - "examine": "This jangerberry bush has died.", - "ids": "7654,7655,7656,7657,7658,7659,7660,7661" - }, - { - "examine": "A Poison Ivy bush is growing in this Farming patch.", - "ids": "7662,7663,7664,7665,7666,7667,7668,7669" - }, - { - "examine": "A fully grown Poison Ivy bush.", - "ids": "7670,7671,7672,7673,7674,7675" - }, - { - "examine": "This diseased Poison Ivy bush needs pruning.", - "ids": "7676,7677,7678,7679,7680,7681,7682,7683" - }, - { - "examine": "This Poison Ivy bush has died.", - "ids": "7684,7685,7686,7687,7688,7689,7690,7691" - }, - { - "examine": "A redberry bush is growing in this Farming patch.", - "ids": "7692,7693,7694,7695,7696" - }, - { - "examine": "A fully grown redberry bush.", - "ids": "7697,7698,7699,7700,7701,7702" - }, - { - "examine": "This diseased redberry bush needs pruning.", - "ids": "7703,7704,7705,7706,7707" - }, - { - "examine": "This redberry bush has died.", - "ids": "7708,7709,7710,7711,7712" - }, - { - "examine": "A whiteberry bush is growing in this Farming patch.", - "ids": "7713,7714,7715,7716,7717,7718,7719,7720" - }, - { - "examine": "A fully grown whiteberry bush.", - "ids": "7721,7722,7723,7724,7725,7726" - }, - { - "examine": "This diseased whiteberry bush needs pruning.", - "ids": "7727,7728,7729,7730,7731,7732,7733,7734" - }, - { - "examine": "This whiteberry bush has died.", - "ids": "7735,7736,7737,7738,7739,7740,7741,7742" - }, - { - "examine": "You can grow a Cactus in this Farming patch.", - "ids": "7743,7744,7745,7746" - }, - { - "examine": "Cactus seeds have been planted in this patch.", - "ids": "7747" - }, - { - "examine": "A cactus is growing in this patch.", - "ids": "7748,7749,7750,7751,7752,7753" - }, - { - "examine": "A fully-grown cactus.", - "ids": "7754,7755,7756,7757,7758" - }, - { - "examine": "This cactus has become diseased.", - "ids": "7759,7760,7761,7762,7763,7764" - }, - { - "examine": "This cactus has died.", - "ids": "7765,7766,7767,7768,7769,7770,7771" - }, - { - "examine": "You can grow a Calquat Tree in this Farming patch.", - "ids": "7772,7773,7774,7775" - }, - { - "examine": "A Calquat tree is growing in this Farming patch.", - "ids": "7776,7777,7778,7779,7780,7781,7782,7783" - }, - { - "examine": "A fully grown Calquat tree stands in this Farming patch.", - "ids": "7784,7785,7786,7787,7788,7789,7790,7791" - }, - { - "examine": "This diseased Calquat tree needs pruning.", - "ids": "7792,7793,7794,7795,7796,7797,7798" - }, - { - "examine": "This Calquat tree has died.", - "ids": "7799,7800,7801,7802,7803,7804,7805" - }, - { - "examine": "A Calquat tree stump.", - "ids": "7806,7807" - }, - { - "examine": "Turns vegetation into compost.", - "ids": "7808" - }, - { - "examine": "This compost bin contains compostable items.", - "ids": "7809" - }, - { - "examine": "This compost bin is full of compostable items.", - "ids": "7810" - }, - { - "examine": "This compost bin contains supercompostable items.", - "ids": "7811" - }, - { - "examine": "This compost bin is full of supercompostable items.", - "ids": "7812" - }, - { - "examine": "Turns vegetation into compost.", - "ids": "7813" - }, - { - "examine": "This compost bin contains compost.", - "ids": "7814" - }, - { - "examine": "This compost bin is full of compost.", - "ids": "7815" - }, - { - "examine": "This compost bin contains super compost.", - "ids": "7816" - }, - { - "examine": "This compost bin is full of super compost.", - "ids": "7817" - }, - { - "examine": "Turns vegetation into compost.", - "ids": "7818" - }, - { - "examine": "This compost bin contains compostable items.", - "ids": "7819" - }, - { - "examine": "This compost bin is full of compostable items.", - "ids": "7820" - }, - { - "examine": "This compost bin contains supercompostable items.", - "ids": "7821" - }, - { - "examine": "This compost bin is full of supercompostable items.", - "ids": "7822" - }, - { - "examine": "Turns vegetation into compost.", - "ids": "7823" - }, - { - "examine": "This compost bin contains compost.", - "ids": "7824" - }, - { - "examine": "This compost bin is full of compost.", - "ids": "7825" - }, - { - "examine": "This compost bin contains super compost.", - "ids": "7826" - }, - { - "examine": "This compost bin is full of super compost.", - "ids": "7827" - }, - { - "examine": "This compost bin contains tomatoes.", - "ids": "7828" - }, - { - "examine": "This compost bin is full of tomatoes.", - "ids": "7829" - }, - { - "examine": "This compost bin contains rotten tomatoes.", - "ids": "7830" - }, - { - "examine": "This compost bin is full of rotten tomatoes.", - "ids": "7831" - }, - { - "examine": "This compost bin contains tomatoes.", - "ids": "7832" - }, - { - "examine": "This compost bin is full of tomatoes.", - "ids": "7833" - }, - { - "examine": "This compost bin contains rotten tomatoes.", - "ids": "7834" - }, - { - "examine": "This compost bin is full of rotten tomatoes.", - "ids": "7835,7836,7837,7838,7839" - }, - { - "examine": "You can grow flowers in this Farming patch.", - "ids": "7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850" - }, - { - "examine": "A limpwurt plant is growing in this patch.", - "ids": "7851,7852,7853,7854" - }, - { - "examine": "A fully grown limpwurt plant.", - "ids": "7855" - }, - { - "examine": "A limpwurt plant is growing in this patch.", - "ids": "7856,7857,7858,7859" - }, - { - "examine": "This limpwurt plant has become diseased.", - "ids": "7860,7861,7862" - }, - { - "examine": "This limpwurt plant has died while growing.", - "ids": "7863,7864,7865" - }, - { - "examine": "This limpwurt plant has died.", - "ids": "7866" - }, - { - "examine": "A marigold is growing in this patch.", - "ids": "7867,7868,7869,7870" - }, - { - "examine": "A fully grown marigold.", - "ids": "7871" - }, - { - "examine": "A marigold is growing in this patch.", - "ids": "7872,7873,7874,7875" - }, - { - "examine": "This marigold has become diseased.", - "ids": "7876,7877,7878" - }, - { - "examine": "This marigold has died while growing.", - "ids": "7879,7880,7881" - }, - { - "examine": "This marigold has died.", - "ids": "7882" - }, - { - "examine": "A nasturtium is growing in this patch.", - "ids": "7883,7884,7885,7886" - }, - { - "examine": "A fully grown nasturtium.", - "ids": "7887" - }, - { - "examine": "A nasturtium is growing in this patch.", - "ids": "7888,7889,7890,7891" - }, - { - "examine": "This nasturtium has become diseased.", - "ids": "7892,7893,7894" - }, - { - "examine": "This nasturtium has died while growing.", - "ids": "7895,7896,7897" - }, - { - "examine": "This nasturtium has died.", - "ids": "7898" - }, - { - "examine": "A rosemary is growing in this patch.", - "ids": "7899,7900,7901,7902" - }, - { - "examine": "A fully grown rosemary.", - "ids": "7903" - }, - { - "examine": "A rosemary is growing in this patch.", - "ids": "7904,7905,7906,7907" - }, - { - "examine": "This rosemary has become diseased.", - "ids": "7908,7909,7910" - }, - { - "examine": "This rosemary has died while growing.", - "ids": "7911,7912,7913" - }, - { - "examine": "This rosemary has died.", - "ids": "7914" - }, - { - "examine": "Should scare off the birds...", - "ids": "7915,7916,7917,7918" - }, - { - "examine": "A woad plant is growing in this patch.", - "ids": "7919,7920,7921,7922" - }, - { - "examine": "A fully grown woad plant.", - "ids": "7923" - }, - { - "examine": "A woad plant is growing in this patch.", - "ids": "7924,7925,7926,7927" - }, - { - "examine": "This woad plant has become diseased.", - "ids": "7928,7929,7930" - }, - { - "examine": "This woad plant has died while growing.", - "ids": "7931,7932,7933" - }, - { - "examine": "This woad plant has died.", - "ids": "7934" - }, - { - "examine": "An apple tree sapling has been planted in this fruit tree patch.", - "ids": "7935" - }, - { - "examine": "An apple tree is growing in this fruit tree patch.", - "ids": "7936,7937,7938,7939,7940" - }, - { - "examine": "A fully grown apple tree.", - "ids": "7941" - }, - { - "examine": "There is a single apple on this apple tree.", - "ids": "7942" - }, - { - "examine": "There are two apples on this apple tree.", - "ids": "7943" - }, - { - "examine": "There are three apples on this apple tree.", - "ids": "7944" - }, - { - "examine": "There are four apples on this apple tree.", - "ids": "7945" - }, - { - "examine": "There are five apples on this apple tree.", - "ids": "7946" - }, - { - "examine": "There are six apples on this apple tree.", - "ids": "7947" - }, - { - "examine": "A fully grown apple tree.", - "ids": "7948" - }, - { - "examine": "This diseased tree looks like it needs pruning with secateurs.", - "ids": "7949,7950,7951,7952,7953,7954" - }, - { - "examine": "This apple tree has become diseased and died.", - "ids": "7955,7956,7957,7958,7959,7960" - }, - { - "examine": "This apple tree has been cut down.", - "ids": "7961,7962,7963,7964,7965" - }, - { - "examine": "A pineapple plant has been planted in this fruit tree patch.", - "ids": "7966" - }, - { - "examine": "A pineapple plant is growing in this fruit tree patch.", - "ids": "7967,7968,7969,7970,7971" - }, - { - "examine": "A fully grown pineapple plant.", - "ids": "7972" - }, - { - "examine": "There is a single pineapple on this pineapple plant.", - "ids": "7973" - }, - { - "examine": "There are two pineapples on this pineapple plant.", - "ids": "7974" - }, - { - "examine": "There are three pineapples on this pineapple plant.", - "ids": "7975" - }, - { - "examine": "There are four pineapples on this pineapple plant.", - "ids": "7976" - }, - { - "examine": "There are five pineapples on this pineapple plant.", - "ids": "7977" - }, - { - "examine": "There are six pineapples on this pineapple plant.", - "ids": "7978" - }, - { - "examine": "A fully grown pineapple plant.", - "ids": "7979" - }, - { - "examine": "This pineapple plant looks like it could do with pruning with secateurs.", - "ids": "7980,7981,7982,7983,7984,7985" - }, - { - "examine": "This pineapple plant has become diseased and died.", - "ids": "7986,7987,7988,7989,7990,7991" - }, - { - "examine": "This pineapple plant has been cut down.", - "ids": "7992" - }, - { - "examine": "A banana tree sapling has been planted in this fruit tree patch.", - "ids": "7993" - }, - { - "examine": "A banana tree is growing in this fruit tree patch.", - "ids": "7994,7995,7996,7997,7998" - }, - { - "examine": "A fully grown banana tree.", - "ids": "7999,8000" - }, - { - "examine": "There is a single banana on this banana tree.", - "ids": "8001" - }, - { - "examine": "There are two bananas on this banana tree.", - "ids": "8002" - }, - { - "examine": "There are three bananas on this banana tree.", - "ids": "8003" - }, - { - "examine": "There are four bananas on this banana tree.", - "ids": "8004" - }, - { - "examine": "There are five bananas on this banana tree.", - "ids": "8005" - }, - { - "examine": "There are six bananas on this banana tree.", - "ids": "8006" - }, - { - "examine": "This banana looks like it could do with pruning with secateurs.", - "ids": "8007,8008,8009,8010,8011,8012" - }, - { - "examine": "This banana tree has become diseased and died.", - "ids": "8013,8014,8015,8016,8017,8018" - }, - { - "examine": "This banana tree has been cut down.", - "ids": "8019" - }, - { - "examine": "A curry tree sapling has been planted in this fruit tree patch.", - "ids": "8020" - }, - { - "examine": "A curry tree is growing in this fruit tree patch.", - "ids": "8021,8022,8023,8024,8025" - }, - { - "examine": "A fully grown curry tree.", - "ids": "8026" - }, - { - "examine": "There is a single curry leaf on this curry tree.", - "ids": "8027" - }, - { - "examine": "There are two curry leaves on this curry tree.", - "ids": "8028" - }, - { - "examine": "There are three curry leaves on this curry tree.", - "ids": "8029" - }, - { - "examine": "There are four curry leaves on this curry tree.", - "ids": "8030" - }, - { - "examine": "There are five curry leaves on this curry tree.", - "ids": "8031" - }, - { - "examine": "There are six curry leaves on this curry tree.", - "ids": "8032" - }, - { - "examine": "A fully grown curry tree.", - "ids": "8033" - }, - { - "examine": "This curry tree looks like it could do with pruning with secateurs.", - "ids": "8034,8035,8036,8037,8038,8039" - }, - { - "examine": "This curry tree has become diseased and died.", - "ids": "8040,8041,8042,8043,8044,8045" - }, - { - "examine": "This curry tree has been cut down.", - "ids": "8046" - }, - { - "examine": "You can grow Fruit Trees in this Farming patch.", - "ids": "8047,8048,8049,8050" - }, - { - "examine": "An orange tree sapling has been planted in this fruit tree patch.", - "ids": "8051" - }, - { - "examine": "An orange tree is growing in this fruit tree patch.", - "ids": "8052,8053,8054,8055,8056" - }, - { - "examine": "A fully grown orange tree.", - "ids": "8057" - }, - { - "examine": "There is a single orange on this orange tree.", - "ids": "8058" - }, - { - "examine": "There are two oranges on this orange tree.", - "ids": "8059" - }, - { - "examine": "There are three oranges on this orange tree.", - "ids": "8060" - }, - { - "examine": "There are four oranges on this orange tree.", - "ids": "8061" - }, - { - "examine": "There are five oranges on this orange tree.", - "ids": "8062" - }, - { - "examine": "There are six oranges on this orange tree.", - "ids": "8063" - }, - { - "examine": "A fully grown orange tree.", - "ids": "8064" - }, - { - "examine": "This orange tree looks like it could do with pruning with secateurs.", - "ids": "8065,8066,8067,8068,8069,8070" - }, - { - "examine": "This orange tree has become diseased and died.", - "ids": "8071,8072,8073,8074,8075,8076" - }, - { - "examine": "This orange tree has been cut down.", - "ids": "8077" - }, - { - "examine": "A palm tree sapling has been planted in this fruit tree patch.", - "ids": "8078" - }, - { - "examine": "A palm tree is growing in this fruit tree patch.", - "ids": "8079,8080,8081,8082,8083" - }, - { - "examine": "A fully grown palm tree.", - "ids": "8084" - }, - { - "examine": "There is a single coconut on this palm tree.", - "ids": "8085" - }, - { - "examine": "There are two coconuts on this palm tree.", - "ids": "8086" - }, - { - "examine": "There are three coconuts on this palm tree.", - "ids": "8087" - }, - { - "examine": "There are four coconuts on this palm tree.", - "ids": "8088" - }, - { - "examine": "There are five coconuts on this palm tree.", - "ids": "8089" - }, - { - "examine": "There are six coconuts on this palm tree.", - "ids": "8090" - }, - { - "examine": "A fully grown palm tree.", - "ids": "8091" - }, - { - "examine": "This palm tree looks like it could do with pruning with secateurs.", - "ids": "8092,8093,8094,8095,8096,8097" - }, - { - "examine": "This palm tree has become diseased and died.", - "ids": "8098,8099,8100,8101,8102,8103" - }, - { - "examine": "This palm tree has been cut down.", - "ids": "8104" - }, - { - "examine": "A papaya tree sapling has been planted in this fruit tree patch.", - "ids": "8105" - }, - { - "examine": "A papaya tree is growing in this fruit tree patch.", - "ids": "8106,8107,8108,8109,8110" - }, - { - "examine": "A fully grown papaya tree", - "ids": "8111" - }, - { - "examine": "There is a single papaya fruit on this tree.", - "ids": "8112" - }, - { - "examine": "There are two papaya fruits on this tree.", - "ids": "8113" - }, - { - "examine": "There are three papaya fruits on this tree.", - "ids": "8114" - }, - { - "examine": "There are four papaya fruits on this tree.", - "ids": "8115" - }, - { - "examine": "There are five papaya fruits on this tree.", - "ids": "8116" - }, - { - "examine": "There are six papaya fruits on this tree.", - "ids": "8117" - }, - { - "examine": "A fully grown papaya tree.", - "ids": "8118" - }, - { - "examine": "This papaya tree looks like it could do with pruning with secateurs.", - "ids": "8119,8120,8121,8122,8123,8124" - }, - { - "examine": "This papaya tree has become diseased and died.", - "ids": "8125,8126,8127,8128,8129,8130" - }, - { - "examine": "This papaya tree has been cut down.", - "ids": "8131" - }, - { - "examine": "You can grow herbs in this Farming patch.", - "ids": "8132,8133,8134,8135,8136,8137,8138" - }, - { - "examine": "Some herb seeds have been sown in this patch.", - "ids": "8139" - }, - { - "examine": "A herb is growing in this patch.", - "ids": "8140,8141,8142" - }, - { - "examine": "A fully grown herb.", - "ids": "8143" - }, - { - "examine": "These herbs have become diseased.", - "ids": "8144,8145,8146" - }, - { - "examine": "These herbs have become diseased and died.", - "ids": "8147,8148,8149,8150,8151,8152,8153" - }, - { - "examine": "Asgarnian hop seeds have been sown in this farming patch.", - "ids": "8154" - }, - { - "examine": "Asgarnian hops are growing in this farming patch.", - "ids": "8155,8156,8157,8158" - }, - { - "examine": "These are fully grown Asgarnian Hops.", - "ids": "8159" - }, - { - "examine": "Asgarnian hop seeds have been sown in this farming patch.", - "ids": "8160" - }, - { - "examine": "Asgarnian hops are growing in this farming patch.", - "ids": "8161,8162,8163,8164" - }, - { - "examine": "These Asgarnian Hop plants are diseased.", - "ids": "8165,8166,8167,8168" - }, - { - "examine": "These Asgarnian Hop plants have died from disease.", - "ids": "8169,8170,8171,8172,8173,8174,8175,8176" - }, - { - "examine": "Hammerstone Hop seeds have been sown in this farming patch.", - "ids": "8177" - }, - { - "examine": "Hammerstone Hops are growing in this farming patch.", - "ids": "8178,8179,8180" - }, - { - "examine": "These are fully grown Hammerstone Hops.", - "ids": "8181" - }, - { - "examine": "Hammerstone Hop seeds have been sown in this farming patch.", - "ids": "8182" - }, - { - "examine": "Hammerstone Hops are growing in this farming patch.", - "ids": "8183,8184,8185" - }, - { - "examine": "These Hammerstone Hops are diseased.", - "ids": "8186,8187,8188" - }, - { - "examine": "These Hammerstone Hops have died from disease.", - "ids": "8189,8190,8191" - }, - { - "examine": "Barley seeds have been sown in this farming patch.", - "ids": "8192" - }, - { - "examine": "Barley is growing in this farming patch.", - "ids": "8193,8194,8195" - }, - { - "examine": "This patch is full of Barley.", - "ids": "8196" - }, - { - "examine": "Barley seeds have been sown in this farming patch.", - "ids": "8197" - }, - { - "examine": "Barley is growing in this farming patch.", - "ids": "8198,8199,8200" - }, - { - "examine": "This Barley is diseased.", - "ids": "8201,8202,8203" - }, - { - "examine": "This Barley has died from disease.", - "ids": "8204,8205,8206" - }, - { - "examine": "You can grow hops in this Farming patch.", - "ids": "8207,8208,8209,8210" - }, - { - "examine": "Krandorian Hop seeds have been planted in this farming patch.", - "ids": "8211" - }, - { - "examine": "Krandorian Hops are growing in this farming patch.", - "ids": "8212,8213,8214,8215,8216,8217" - }, - { - "examine": "These are fully grown Krandorian Hops.", - "ids": "8218" - }, - { - "examine": "Krandorian Hop seeds have been planted in this farming patch.", - "ids": "8219" - }, - { - "examine": "Krandorian Hops are growing in this farming patch.", - "ids": "8220,8221,8222,8223,8224,8225" - }, - { - "examine": "These Krandorian Hops are diseased.", - "ids": "8226,8227,8228,8229,8230,8231" - }, - { - "examine": "These Krandorian Hops have died from disease.", - "ids": "8232,8233,8234,8235,8236,8237" - }, - { - "examine": "Jute seeds have been sown in this farming patch.", - "ids": "8238" - }, - { - "examine": "Jute plants are growing in this farming patch.", - "ids": "8239,8240,8241,8242" - }, - { - "examine": "These are fully grown Jute plants.", - "ids": "8243" - }, - { - "examine": "Jute seeds have been sown in this farming patch.", - "ids": "8244" - }, - { - "examine": "Jute plants are growing in this farming patch.", - "ids": "8245,8246,8247,8248" - }, - { - "examine": "These Jute plants are diseased.", - "ids": "8249,8250,8251,8252" - }, - { - "examine": "These Jute plants have died from disease.", - "ids": "8253,8254,8255,8256" - }, - { - "examine": "Wildblood hop seeds have been planted in this farming patch.", - "ids": "8257" - }, - { - "examine": "Wildblood Hops are growing in this farming patch.", - "ids": "8258,8259,8260,8261,8262,8263,8264" - }, - { - "examine": "These are fully grown Wildblood Hops.", - "ids": "8265" - }, - { - "examine": "Wildblood hop seeds have been planted in this farming patch.", - "ids": "8266" - }, - { - "examine": "Wildblood Hops are growing in this farming patch.", - "ids": "8267,8268,8269,8270,8271,8272,8273" - }, - { - "examine": "These Wildblood hops are diseased.", - "ids": "8274,8275,8276,8277,8278,8279,8280" - }, - { - "examine": "These Wildblood Hops have died from disease.", - "ids": "8281,8282,8283,8284,8285,8286,8287" - }, - { - "examine": "Yanillian Hop seeds have been sown in this farming patch.", - "ids": "8288" - }, - { - "examine": "Yanillian Hops are growing in this farming patch.", - "ids": "8289,8290,8291,8292,8293" - }, - { - "examine": "These are fully grown Yanillian Hops.", - "ids": "8294" - }, - { - "examine": "Yanillian Hop seeds have been sown in this farming patch.", - "ids": "8295" - }, - { - "examine": "Yanillian Hops are growing in this farming patch.", - "ids": "8296,8297,8298,8299,8300" - }, - { - "examine": "These Yanillian Hops are diseased.", - "ids": "8301,8302,8303,8304,8305" - }, - { - "examine": "These Yanillian Hops have died from disease.", - "ids": "8306,8307,8308,8309,8310" - }, - { - "examine": "You can grow Bittercap mushrooms in this Farming patch.", - "ids": "8311,8312,8313,8314" - }, - { - "examine": "Bittercap mushroom spores have been sown in this farming patch.", - "ids": "8315" - }, - { - "examine": "Bittercap mushrooms are growing in this farming patch.", - "ids": "8316,8317,8318,8319,8320" - }, - { - "examine": "A patch of Bittercap Mushrooms.", - "ids": "8321,8322,8323,8324,8325,8326" - }, - { - "examine": "These Bittercap mushrooms have become diseased.", - "ids": "8327,8328,8329,8330,8331" - }, - { - "examine": "These Bittercap mushrooms have become diseased and died.", - "ids": "8332,8333,8334,8335,8336,8337,8338" - }, - { - "examine": "You can grow a Spirit Tree in this Farming patch.", - "ids": "8339,8340,8341,8342" - }, - { - "examine": "A Spirit Tree.", - "ids": "8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356" - }, - { - "examine": "A Spirit Tree stump.", - "ids": "8357" - }, - { - "examine": "Needs pruning before it dies.", - "ids": "8358,8359,8360,8361,8362,8363,8364,8365,8366,8367,8368,8369" - }, - { - "examine": "Oh dear, your spirit tree has died.", - "ids": "8370,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389,8390,8391" - }, - { - "examine": "You can grow trees in this Farming patch.", - "ids": "8392,8393,8394,8395" - }, - { - "examine": "A Magic Tree sapling has been planted in this tree patch.", - "ids": "8396" - }, - { - "examine": "A Magic Tree is growing in this tree patch.", - "ids": "8397,8398,8399,8400,8401,8402,8403,8404,8405,8406,8407" - }, - { - "examine": "A fully grown Magic Tree.", - "ids": "8408,8409" - }, - { - "examine": "You can uproot this stump with a spade.", - "ids": "8410" - }, - { - "examine": "To remove all signs of disease, prune the tree with secateurs.", - "ids": "8411,8412,8413,8414,8415,8416,8417,8418,8419,8420,8421,8422" - }, - { - "examine": "This Magic Tree has become diseased and died.", - "ids": "8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434" - }, - { - "examine": "A Maple tree sapling has been planted in this tree patch.", - "ids": "8435" - }, - { - "examine": "A Maple tree is growing in this tree patch.", - "ids": "8436,8437,8438,8439,8440,8441,8442" - }, - { - "examine": "A fully grown Maple tree.", - "ids": "8443,8444" - }, - { - "examine": "You can uproot this stump with a spade.", - "ids": "8445" - }, - { - "examine": "To remove all signs of disease, prune the tree with secateurs.", - "ids": "8446,8447,8448,8449,8450,8451,8452,8453" - }, - { - "examine": "This Maple tree has become diseased and died.", - "ids": "8454,8455,8456,8457,8458,8459,8460,8461" - }, - { - "examine": "An Oak sapling has been planted in this tree patch.", - "ids": "8462" - }, - { - "examine": "An Oak tree is growing in this tree patch.", - "ids": "8463,8464,8465" - }, - { - "examine": "A fully grown Oak tree.", - "ids": "8466,8467" - }, - { - "examine": "You can uproot this stump with a spade.", - "ids": "8468,8469,8470,8471,8472" - }, - { - "examine": "To remove all signs of disease, prune the tree with secateurs.", - "ids": "8473,8474,8475,8476" - }, - { - "examine": "This Oak tree has become diseased and died.", - "ids": "8477,8478,8479,8480" - }, - { - "examine": "A Willow sapling has been planted in this tree patch.", - "ids": "8481" - }, - { - "examine": "A Willow tree is growing in this tree patch.", - "ids": "8482,8483,8484,8485,8486" - }, - { - "examine": "A fully grown Willow tree.", - "ids": "8487,8488" - }, - { - "examine": "You can uproot this stump with a spade.", - "ids": "8489" - }, - { - "examine": "To remove all signs of disease, prune the tree with secateurs.", - "ids": "8490,8491,8492,8493,8494,8495" - }, - { - "examine": "This Willow tree has become diseased and died.", - "ids": "8496,8497,8498,8499,8500,8501" - }, - { - "examine": "A Yew sapling has been planted in this tree patch.", - "ids": "8502" - }, - { - "examine": "A Yew tree is growing in this tree patch.", - "ids": "8503,8504,8505,8506,8507,8508,8509,8510,8511" - }, - { - "examine": "A fully grown Yew tree.", - "ids": "8512,8513" - }, - { - "examine": "You can uproot this tree stump with a spade.", - "ids": "8514" - }, - { - "examine": "To remove all signs of disease, prune the tree with secateurs.", - "ids": "8515,8516,8517,8518,8519,8520,8521,8522,8523,8524" - }, - { - "examine": "This Yew tree has become diseased and died.", - "ids": "8525,8526,8527,8528,8529,8530,8531,8532,8533,8534" - }, - { - "examine": "Cabbage seeds have been sown in this allotment.", - "ids": "8535" - }, - { - "examine": "Cabbages are growing in this allotment.", - "ids": "8536,8537,8538" - }, - { - "examine": "These cabbages could do with harvesting.", - "ids": "8539" - }, - { - "examine": "Cabbages are growing in this allotment.", - "ids": "8540,8541,8542,8543" - }, - { - "examine": "These cabbages have become diseased and need tending.", - "ids": "8544,8545,8546" - }, - { - "examine": "These cabbages have become diseased and died.", - "ids": "8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557" - }, - { - "examine": "Potato seeds have been planted in this allotment.", - "ids": "8558" - }, - { - "examine": "Potato plants are growing in this allotment.", - "ids": "8559,8560,8561" - }, - { - "examine": "These potato plants are fully grown.", - "ids": "8562" - }, - { - "examine": "Potato seeds have been planted in this allotment.", - "ids": "8563" - }, - { - "examine": "Potato plants are growing in this allotment.", - "ids": "8564,8565,8566" - }, - { - "examine": "These potato plants are diseased.", - "ids": "8567,8568,8569" - }, - { - "examine": "These potato plants are dead.", - "ids": "8570,8571,8572" - }, - { - "examine": "You can grow fruit and vegetables here.", - "ids": "8573,8574,8575,8576,8577,8578,8579" - }, - { - "examine": "Some onion seeds have sown in this allotment.", - "ids": "8580" - }, - { - "examine": "Some onion plants are growing in this allotment.", - "ids": "8581,8582,8583" - }, - { - "examine": "There are some fully grown onions in this allotment.", - "ids": "8584" - }, - { - "examine": "Some onion seeds have sown in this allotment.", - "ids": "8585" - }, - { - "examine": "Some onion plants are growing in this allotment.", - "ids": "8586,8587,8588" - }, - { - "examine": "These onions have become diseased.", - "ids": "8589,8590,8591" - }, - { - "examine": "These onions have become diseased and died.", - "ids": "8592,8593,8594" - }, - { - "examine": "Strawberry seeds have been sown in this allotment.", - "ids": "8595" - }, - { - "examine": "Strawberry plants are growing in this allotment.", - "ids": "8596,8597,8598,8599,8600" - }, - { - "examine": "These strawberry plants are fully grown.", - "ids": "8601" - }, - { - "examine": "Strawberry seeds have been sown in this allotment.", - "ids": "8602" - }, - { - "examine": "Strawberry plants are growing in this allotment.", - "ids": "8603,8604,8605,8606,8607" - }, - { - "examine": "These strawberry plants have become diseased.", - "ids": "8608,8609,8610,8611,8612" - }, - { - "examine": "These strawberry plants have become diseased and died.", - "ids": "8613,8614,8615,8616,8617" - }, - { - "examine": "Some sweetcorn seeds have been sown in this allotment.", - "ids": "8618" - }, - { - "examine": "Some sweetcorn plants are growing in this allotment.", - "ids": "8619,8620,8621,8622,8623" - }, - { - "examine": "These sweetcorn plants are fully grown.", - "ids": "8624" - }, - { - "examine": "Some sweetcorn seeds have been sown in this allotment.", - "ids": "8625" - }, - { - "examine": "Some sweetcorn plants are growing in this allotment.", - "ids": "8626,8627,8628,8629,8630" - }, - { - "examine": "These sweetcorn plants have been attacked by crows.", - "ids": "8631,8632,8633,8634,8635" - }, - { - "examine": "These sweetcorn plants have become diseased and died.", - "ids": "8636,8637,8638,8639,8640" - }, - { - "examine": "Tomato seeds have been sown in this allotment.", - "ids": "8641" - }, - { - "examine": "Tomato plants are growing in this allotment.", - "ids": "8642,8643,8644" - }, - { - "examine": "These tomato plants are fully grown.", - "ids": "8645" - }, - { - "examine": "Tomato seeds have been sown in this allotment.", - "ids": "8646" - }, - { - "examine": "Tomato plants are growing in this allotment.", - "ids": "8647,8648,8649" - }, - { - "examine": "These tomato plants have become diseased.", - "ids": "8650,8651,8652" - }, - { - "examine": "These tomato plants have become diseased and died.", - "ids": "8653,8654,8655" - }, - { - "examine": "Watermelon seeds have been sown in this allotment.", - "ids": "8656" - }, - { - "examine": "Watermelons are growing in this allotment.", - "ids": "8657,8658,8659,8660,8661,8662,8663" - }, - { - "examine": "These watermelons could do with harvesting.", - "ids": "8664" - }, - { - "examine": "Watermelon seeds have been sown in this allotment.", - "ids": "8665" - }, - { - "examine": "Watermelons are growing in this allotment.", - "ids": "8666,8667,8668,8669,8670,8671,8672" - }, - { - "examine": "These watermelons have become diseased and need tending.", - "ids": "8673,8674,8675,8676,8677,8678,8679" - }, - { - "examine": "These watermelon plants have become diseased and died.", - "ids": "8680" - }, - { - "examine": "These watermelons have become diseased and died.", - "ids": "8681,8682,8683,8684,8685,8686,8687" - }, - { - "examine": "The perfect accompaniment to a bedroom.", - "ids": "8688" - }, - { - "examine": "Fit for milking.", - "ids": "8689" - }, - { - "examine": "Each full of milk no doubt.", - "ids": "8690,8691,8692,8693,8694" - }, - { - "examine": "The door is closed.", - "ids": "8695" - }, - { - "examine": "The door is open.", - "ids": "8696,8697,8698" - }, - { - "examine": "After working with livestock, why not wash your hands?", - "ids": "8699" - }, - { - "examine": "For putting things on.", - "ids": "8700" - }, - { - "examine": "Someone's been preparing meat.", - "ids": "8701" - }, - { - "examine": "A barrel for collecting rain water.", - "ids": "8702,8703,8704,8705,8706,8707,8708,8709,8710,8711" - }, - { - "examine": "A grand old fireplace.", - "ids": "8712" - }, - { - "examine": "I bet there's a needle in it somewhere.", - "ids": "8713,8714,8715,8716" - }, - { - "examine": "A loom.", - "ids": "8717,8718,8719,8720,8721,8722,8723,8724" - }, - { - "examine": "A little rock.", - "ids": "8725" - }, - { - "examine": "A small chunk of rock.", - "ids": "8726" - }, - { - "examine": "They're not floating, even though it may look like they are!", - "ids": "8727" - }, - { - "examine": "A deposit of rocks.", - "ids": "8728,8729,8730,8731,8732,8733,8734,8735,8736" - }, - { - "examine": "This tap runs from the apple crushing barrel.", - "ids": "8737" - }, - { - "examine": "You can walk through these doors.", - "ids": "8738,8739,8740,8741" - }, - { - "examine": "An odd looking tree.", - "ids": "8742,8743" - }, - { - "examine": "I can climb this.", - "ids": "8744,8745" - }, - { - "examine": "I can climb down this.", - "ids": "8746" - }, - { - "examine": "A local water source.", - "ids": "8747" - }, - { - "examine": "Used for spinning thread.", - "ids": "8748" - }, - { - "examine": "A Shrine to the glory of Seren.", - "ids": "8749" - }, - { - "examine": "I can cook here.", - "ids": "8750" - }, - { - "examine": "A nice sturdy looking table.", - "ids": "8751" - }, - { - "examine": "A good source of books!", - "ids": "8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,8764,8765,8766" - }, - { - "examine": "The lamp has a glowing crystal at its core.", - "ids": "8767" - }, - { - "examine": "A table.", - "ids": "8768,8769" - }, - { - "examine": "A chair.", - "ids": "8770" - }, - { - "examine": "A painting of an elf standing in some woodland.", - "ids": "8771" - }, - { - "examine": "Sit back and relax...", - "ids": "8772" - }, - { - "examine": "A useful ranging device.", - "ids": "8773,8774" - }, - { - "examine": "Use these with bows.", - "ids": "8775" - }, - { - "examine": "Tailor-made for needlework supplies.", - "ids": "8776,8777" - }, - { - "examine": "I'm guessing it's for making elven clothes.", - "ids": "8778" - }, - { - "examine": "Helps make elf clothing.", - "ids": "8779,8780,8781,8782" - }, - { - "examine": "I wonder what's under it?", - "ids": "8783,8784" - }, - { - "examine": "I can climb this.", - "ids": "8785" - }, - { - "examine": "The door is closed.", - "ids": "8786,8787,8788,8789" - }, - { - "examine": "The door is open.", - "ids": "8790,8791,8792,8793,8794" - }, - { - "examine": "Makes gnomes taller.", - "ids": "8795" - }, - { - "examine": "Makes creatures taller.", - "ids": "8796" - }, - { - "examine": "I wonder what's inside.", - "ids": "8797" - }, - { - "examine": "Perhaps I should search it.", - "ids": "8798" - }, - { - "examine": "The desk of the head mourner.", - "ids": "8799" - }, - { - "examine": "I wonder what's inside.", - "ids": "8800" - }, - { - "examine": "A sack full of grain.", - "ids": "8801,8802,8803" - }, - { - "examine": "These have grain in them.", - "ids": "8804,8805,8806" - }, - { - "examine": "An empty barrel.", - "ids": "8807" - }, - { - "examine": "A barrel full of mushed apples.", - "ids": "8808" - }, - { - "examine": "A pile of rotten apples!", - "ids": "8809" - }, - { - "examine": "A wooden gate.", - "ids": "8810,8811,8812,8813" - }, - { - "examine": "A well slept in bed.", - "ids": "8814,8815,8816,8817" - }, - { - "examine": "The door is closed.", - "ids": "8818" - }, - { - "examine": "The door is open.", - "ids": "8819" - }, - { - "examine": "Solid iron bars.", - "ids": "8820,8821,8822,8823,8824,8825,8826,8827" - }, - { - "examine": "Heavy.", - "ids": "8828,8829,8830" - }, - { - "examine": "You can 'cart' things around on this.", - "ids": "8831,8832,8833,8834,8835,8836,8837,8838,8839" - }, - { - "examine": "I feel the need to throw a rotten cabbage!", - "ids": "8840,8841" - }, - { - "examine": "Nothing growing on this tree at the moment...", - "ids": "8842" - }, - { - "examine": "Mmmmm, nice juicy apples!", - "ids": "8843,8844,8845,8846,8847,8848,8849,8850,8851,8852,8853,8854,8855,8856,8857,8858,8859,8860" - }, - { - "examine": "This is a special hops patch.", - "ids": "8861,8862,8863,8864" - }, - { - "examine": "Kelda Hop seeds have been sown in this farming patch.", - "ids": "8865" - }, - { - "examine": "Kelda Hops are growing in this farming patch.", - "ids": "8866,8867,8868" - }, - { - "examine": "These are fully grown Kelda Hops.", - "ids": "8869" - }, - { - "examine": "A barrel of Kelda Stout.", - "ids": "8870" - }, - { - "examine": "This vat contains Kelda hops.", - "ids": "8871" - }, - { - "examine": "Kelda Stout is fermenting in this vat.", - "ids": "8872,8873" - }, - { - "examine": "This vat contains Kelda Stout.", - "ids": "8874,8875,8876,8877" - }, - { - "examine": "Some bizarre mixture between rocks and machinery. Definitely dwarven.", - "ids": "8878" - }, - { - "examine": "A strange box of some kind.", - "ids": "8879" - }, - { - "examine": "A strange box of some kind. It's open.", - "ids": "8880" - }, - { - "examine": "A small cave entrance.", - "ids": "8881,8882,8883,8884,8885" - }, - { - "examine": "Keeps mine carts from rolling away.", - "ids": "8886" - }, - { - "examine": "Cart tracks.", - "ids": "8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898" - }, - { - "examine": "A support for the tracks.", - "ids": "8899,8900,8901,8902,8903,8904,8905,8906,8907,8908,8909" - }, - { - "examine": "These books contain all sorts of data on the Red Axe.", - "ids": "8910" - }, - { - "examine": "Big mysterious crates. You wonder what could be inside.", - "ids": "8911" - }, - { - "examine": "Wooden crates with metal edges, contents unknown.", - "ids": "8912" - }, - { - "examine": "Big mysterious crates. There are some papers on top.", - "ids": "8913,8914,8915,8916,8917" - }, - { - "examine": "A symbol of the Red Axe.", - "ids": "8918,8919,8920,8921,8922,8923" - }, - { - "examine": "A steam powered cart.", - "ids": "8924,8925" - }, - { - "examine": "A short long boat!", - "ids": "8926" - }, - { - "examine": "Best used with a bucket.", - "ids": "8927,8928" - }, - { - "examine": "A deep and terrifying cave.", - "ids": "8929" - }, - { - "examine": "It looks cold in there.", - "ids": "8930,8931,8932,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,8944,8945,8946,8947,8948,8949" - }, - { - "examine": "A big slimy lump of rock.", - "ids": "8950" - }, - { - "examine": "A slimy lump of rock.", - "ids": "8951" - }, - { - "examine": "A slippery looking rock.", - "ids": "8952,8953" - }, - { - "examine": "A tall flag blowing in the wind.", - "ids": "8954,8955" - }, - { - "examine": "I could climb this if I wanted.", - "ids": "8956,8957" - }, - { - "examine": "There must be some trick to opening this...", - "ids": "8958,8959,8960" - }, - { - "examine": "It's opening...", - "ids": "8961" - }, - { - "examine": "Apparently there was some trick to opening it!", - "ids": "8962" - }, - { - "examine": "Not as difficult to walk through as it was a minute ago.", - "ids": "8963,8964,8965" - }, - { - "examine": "Steps*Leads to the surface.", - "ids": "1337" - }, - { - "examine": "A sturdy looking door, propped shut with a support.", - "ids": "8967,8968,8969,8970,8971" - }, - { - "examine": "A portal back to the real world...", - "ids": "8972" - }, - { - "examine": "One of the most common trees in RuneScape.", - "ids": "8973" - }, - { - "examine": "A commonly found tree.", - "ids": "8974,8975,8976,8977,8978,8979,8980,8981,8982,8983,8984" - }, - { - "examine": "Steam seems to be condensing into the pot. Neat trick!", - "ids": "8985" - }, - { - "examine": "I can see fish swimming in the water. Well, they sort of look like fish...", - "ids": "8986" - }, - { - "examine": "A portal that leads... somewhere?", - "ids": "8987" - }, - { - "examine": "I wonder what's inside...", - "ids": "8988" - }, - { - "examine": "Someone's looking through the chest.", - "ids": "8989,8990,8991,8992,8993,8994,8995,8996,8997" - }, - { - "examine": "An appendage for activating something.", - "ids": "8998,8999,9000,9001,9002,9003,9004,9005" - }, - { - "examine": "A place to cremate the dead. Needs a body.", - "ids": "9006,9007" - }, - { - "examine": "A place to cremate the dead. Needs a light.", - "ids": "9008,9009" - }, - { - "examine": "This has broad leaves.", - "ids": "9010,9011,9012,9013" - }, - { - "examine": "This has been chopped away.", - "ids": "9014" - }, - { - "examine": "This has broad leaves.", - "ids": "9015,9016,9017,9018" - }, - { - "examine": "This has been chopped away.", - "ids": "9019" - }, - { - "examine": "This has broad leaves.", - "ids": "9020,9021,9022,9023" - }, - { - "examine": "This has been chopped away.", - "ids": "9024" - }, - { - "examine": "A damaged wooden fence.", - "ids": "9025,9026,9027,9028" - }, - { - "examine": "A wooden fence.", - "ids": "9029" - }, - { - "examine": "Gems encrusted in stone.", - "ids": "9030,9031,9032" - }, - { - "examine": "Many rare plants such as this usually have exotic tubers.", - "ids": "9033" - }, - { - "examine": "A beautiful old mahogany tree.", - "ids": "9034" - }, - { - "examine": "This once was a beautiful tree.", - "ids": "9035" - }, - { - "examine": "A beautiful old teak tree.", - "ids": "9036" - }, - { - "examine": "This tree has been cut down.", - "ids": "9037" - }, - { - "examine": "A set of large, sturdy wooden doors.", - "ids": "9038,9039,9040,9041,9042,9043" - }, - { - "examine": "Some goutweed is growing in this patch.", - "ids": "9044,9045,9046,9047" - }, - { - "examine": "Some fully grown goutweed.", - "ids": "9048" - }, - { - "examine": "This goutweed has become diseased.", - "ids": "9049,9050,9051" - }, - { - "examine": "This goutweed has become diseased and died.", - "ids": "9052,9053,9054,9055,9056,9057" - }, - { - "examine": "He's not stocking much.", - "ids": "9058" - }, - { - "examine": "He's stocking rune caskets.", - "ids": "9059" - }, - { - "examine": "He's stocking blackjacks.", - "ids": "9060" - }, - { - "examine": "He's stocking fez hats.", - "ids": "9061" - }, - { - "examine": "He's stocking rune caskets.", - "ids": "9062" - }, - { - "examine": "He's stocking colourful clothes.", - "ids": "9063" - }, - { - "examine": "A wooden crate.", - "ids": "9064,9065" - }, - { - "examine": "A wooden crate containing caskets.", - "ids": "9066,9067" - }, - { - "examine": "A wooden crate containing Blackjacks.", - "ids": "9068,9069" - }, - { - "examine": "A wooden crate containing fez hats.", - "ids": "9070" - }, - { - "examine": "A wooden crate containing clothes.", - "ids": "9071" - }, - { - "examine": "Boxy.", - "ids": "9072,9073" - }, - { - "examine": "boxy", - "ids": "9074,9075" - }, - { - "examine": "This cloth has been dyed.", - "ids": "9076" - }, - { - "examine": "Mmmm pretty!", - "ids": "9077,9078" - }, - { - "examine": "These dyed fabrics are drying off.", - "ids": "9079,9080,9081" - }, - { - "examine": "Items for making clothes are kept on here.", - "ids": "9082" - }, - { - "examine": "Pots full of dye.", - "ids": "9083" - }, - { - "examine": "A way down.", - "ids": "9084" - }, - { - "examine": "There's not much coke in the stove.", - "ids": "9085" - }, - { - "examine": "There's a fair amount of coke in the stove.", - "ids": "9086" - }, - { - "examine": "There's lots of coke in the stove.", - "ids": "9087" - }, - { - "examine": "A big pile of refined coal.", - "ids": "9088" - }, - { - "examine": "You can read the furnace temperature here.", - "ids": "9089" - }, - { - "examine": "Used to pump hot air through the furnace.", - "ids": "9090" - }, - { - "examine": "Bars come out of the blast furnace here.", - "ids": "9091,9092" - }, - { - "examine": "Your bars will come out here.", - "ids": "9093,9094" - }, - { - "examine": "The bars are glowing hot!", - "ids": "9095" - }, - { - "examine": "Your bars are ready to take.", - "ids": "9096" - }, - { - "examine": "They power the conveyor belt.", - "ids": "9097" - }, - { - "examine": "The foreman refers to it as 'Bertha'.", - "ids": "9098" - }, - { - "examine": "It shows that the furnace is working.", - "ids": "9099" - }, - { - "examine": "Ore rides this into the blast furnace.", - "ids": "9100,9101" - }, - { - "examine": "It keeps the conveyor belt running.", - "ids": "9102" - }, - { - "examine": "Fix it, quick!", - "ids": "9103" - }, - { - "examine": "They keep the conveyor belt running.", - "ids": "9104" - }, - { - "examine": "Fix them, quick!", - "ids": "9105" - }, - { - "examine": "It keeps the conveyor belt turning.", - "ids": "9106,9107" - }, - { - "examine": "They keep the conveyor belt turning.", - "ids": "9108,9109,9110,9111,9112,9113,9114" - }, - { - "examine": "It shows that the furnace is working.", - "ids": "9115" - }, - { - "examine": "Hot air circulates through these.", - "ids": "9116" - }, - { - "examine": "Quick, fix them!", - "ids": "9117,9118,9119" - }, - { - "examine": "Hot air circulates through these.", - "ids": "9120" - }, - { - "examine": "Quick, fix them!", - "ids": "9121,9122" - }, - { - "examine": "It shows the furnace is working.", - "ids": "9123,9124,9125,9126,9127,9128,9129,9130,9131,9132,9133,9134,9135,9136,9137" - }, - { - "examine": "A way upwards.", - "ids": "9138,9139" - }, - { - "examine": "The gate is closed.", - "ids": "9140,9141" - }, - { - "examine": "The gate is open.", - "ids": "9142" - }, - { - "examine": "Used for getting water.", - "ids": "9143" - }, - { - "examine": "Some kind of jewellery is dangling from this ornament.", - "ids": "9144" - }, - { - "examine": "Someone's showing off their wealth!", - "ids": "9145" - }, - { - "examine": "Filled to the brim with knowledge.", - "ids": "9146,9147" - }, - { - "examine": "A nicely carved wooden chair.", - "ids": "9148" - }, - { - "examine": "An expensive privacy aid!", - "ids": "9149" - }, - { - "examine": "An old wall.", - "ids": "9150" - }, - { - "examine": "Security breach!", - "ids": "9151" - }, - { - "examine": "A patched up hole.", - "ids": "9152,9153,9154" - }, - { - "examine": "'You are here.'", - "ids": "9155" - }, - { - "examine": "An elegant desk with a curious ornament on it.", - "ids": "9156" - }, - { - "examine": "Someone's gold-trimmed this Saradomin armour!", - "ids": "9157,9158" - }, - { - "examine": "Lit to remember the souls of the departed.", - "ids": "9159,9160,9161,9162,9163,9164,9165" - }, - { - "examine": "You can grow delphiniums in this Farming patch.", - "ids": "9166,9167,9168,9169" - }, - { - "examine": "Delphinium seeds have been sown in this farming patch.", - "ids": "9170" - }, - { - "examine": "Beautiful.", - "ids": "9171,9172" - }, - { - "examine": "These delphiniums are fully grown.", - "ids": "9173,9174,9175,9176" - }, - { - "examine": "You can grow a pink rose bush in this patch.", - "ids": "9177,9178,9179,9180" - }, - { - "examine": "You can grow a white rose bush in this patch.", - "ids": "9181,9182,9183,9184" - }, - { - "examine": "You can grow a red rose bush in this patch.", - "ids": "9185,9186,9187,9188" - }, - { - "examine": "A rose bush.", - "ids": "9189,9190,9191" - }, - { - "examine": "This white rosebush is fully grown.", - "ids": "9192" - }, - { - "examine": "A rose bush.", - "ids": "9193" - }, - { - "examine": "This red rosebush is fully grown.", - "ids": "9194" - }, - { - "examine": "A pink rosebush.", - "ids": "9195" - }, - { - "examine": "This pink rosebush is fully grown.", - "ids": "9196,9197,9198" - }, - { - "examine": "A plantpot of pink orchids.", - "ids": "9199,9200,9201" - }, - { - "examine": "These pink orchids are fully grown.", - "ids": "9202" - }, - { - "examine": "An empty plantpot.", - "ids": "9203" - }, - { - "examine": "A plantpot filled with soil.", - "ids": "9204" - }, - { - "examine": "A plantpot of yellow orchids.", - "ids": "9205,9206,9207" - }, - { - "examine": "These yellow orchids are fully grown.", - "ids": "9208,9209" - }, - { - "examine": "You can grow a White Tree in this patch.", - "ids": "9210,9211,9212,9213" - }, - { - "examine": "A White Tree sapling has been planted in this patch.", - "ids": "9214" - }, - { - "examine": "A White Tree is growing in this patch.", - "ids": "9215,9216,9217" - }, - { - "examine": "This White Tree is fully grown.", - "ids": "9218" - }, - { - "examine": "This White Tree bears a single fruit.", - "ids": "9219" - }, - { - "examine": "This White Tree bears two fruits.", - "ids": "9220" - }, - { - "examine": "This White Tree bears three fruits.", - "ids": "9221" - }, - { - "examine": "This White Tree bears four fruits.", - "ids": "9222,9223" - }, - { - "examine": "You can grow snowdrops in this Farming patch.", - "ids": "9224,9225,9226,9227" - }, - { - "examine": "A patch of snowdrops.", - "ids": "9228,9229,9230" - }, - { - "examine": "These snowdrops are fully grown.", - "ids": "9231,9232" - }, - { - "examine": "You can grow Burthorpe Vine in this patch.", - "ids": "9233" - }, - { - "examine": "You can grow Burthorpe Vines in this patch.", - "ids": "9234,9235,9236" - }, - { - "examine": "Burthorpe vines are growing in this patch.", - "ids": "9237,9238,9239" - }, - { - "examine": "These Burthorpe vines are fully grown.", - "ids": "9240" - }, - { - "examine": "An empty plinth for a statue.", - "ids": "9241,9242" - }, - { - "examine": "What a good likeness!", - "ids": "9243" - }, - { - "examine": "An empty plinth for a statue.", - "ids": "9244" - }, - { - "examine": "An expertly carved statue of a former King of Misthalin.", - "ids": "9245" - }, - { - "examine": "An empty plinth for a statue.", - "ids": "9246" - }, - { - "examine": "What a good likeness!", - "ids": "9247" - }, - { - "examine": "An empty plinth for a statue.", - "ids": "9248" - }, - { - "examine": "An expertly carved statue of a former King of Misthalin.", - "ids": "9249,9250,9251,9252,9253,9254" - }, - { - "examine": "These grapevines look much healthier now.", - "ids": "9255" - }, - { - "examine": "These grapevines are suffering from some strange disease.", - "ids": "9256" - }, - { - "examine": "Some wild-looking grass.", - "ids": "9257,9258,9259" - }, - { - "examine": "Some red roses.", - "ids": "9260" - }, - { - "examine": "Some pink roses.", - "ids": "9261" - }, - { - "examine": "Some white roses.", - "ids": "9262" - }, - { - "examine": "A White Tree.", - "ids": "9263,9264,9265,9266,9267,9268,9269,9270,9271,9272,9273,9274,9275,9276,9277,9278,9279,9280,9281,9282,9283,9284,9285,9286,9287,9288,9289,9290" - }, - { - "examine": "Sit back and enjoy the view.", - "ids": "9291" - }, - { - "examine": "An expertly carved statue of a former Queen of Misthalin.", - "ids": "9292" - }, - { - "examine": "Funny looking holes that don't look too inviting.", - "ids": "9294" - }, - { - "examine": "A rocky outcrop.", - "ids": "9296,9297,9298" - }, - { - "examine": "Ornate railing.", - "ids": "9299" - }, - { - "examine": "Wooden fencing.", - "ids": "9300" - }, - { - "examine": "A well constructed castle wall.", - "ids": "9301" - }, - { - "examine": "A rather strategically placed hole, which appears to be in the ground.", - "ids": "9302" - }, - { - "examine": "A rocky outcrop.", - "ids": "9303,9304,9305,9306" - }, - { - "examine": "A well weathered wall.", - "ids": "9307,9308" - }, - { - "examine": "A tunnel leading under the wall.", - "ids": "9309,9310" - }, - { - "examine": "An underwall tunnel.", - "ids": "9311,9312" - }, - { - "examine": "Looks suspicious.", - "ids": "9313" - }, - { - "examine": "A wall jutting out into the path.", - "ids": "9314" - }, - { - "examine": "I can jump from this stepping stone.", - "ids": "9315" - }, - { - "examine": "A rocky outcrop.", - "ids": "9316,9317,9318" - }, - { - "examine": "A chain rope", - "ids": "9319,9320" - }, - { - "examine": "A few rocks short of a wall", - "ids": "9321" - }, - { - "examine": "The foam from the river makes this log very slippy.", - "ids": "9322,9323,9324" - }, - { - "examine": "Just another crack in the wall.", - "ids": "9325" - }, - { - "examine": "Funny looking holes that don't look too inviting.", - "ids": "9326" - }, - { - "examine": "They seem to fit in the with the rocky surroundings, so as not to stick out.", - "ids": "9327" - }, - { - "examine": "A slippery well worn log.", - "ids": "9328,9329,9330" - }, - { - "examine": "A rocky outcrop.", - "ids": "9331,9332,9333" - }, - { - "examine": "Used to be ornate, now it's a little bit vandalised.", - "ids": "9334" - }, - { - "examine": "A rocky outcrop.", - "ids": "9335,9336" - }, - { - "examine": "Used to be ornate, now it's a little bit vandalised.", - "ids": "9337" - }, - { - "examine": "Should scare off the birds...", - "ids": "9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,9350,9351,9352,9353" - }, - { - "examine": "This looks like the way out.", - "ids": "9354" - }, - { - "examine": "A cave deep into the volcano.", - "ids": "9355" - }, - { - "examine": "A cave deeper into the volcano.", - "ids": "9356,9357" - }, - { - "examine": "A cave deep into the volcano.", - "ids": "9358" - }, - { - "examine": "This looks like the way out.", - "ids": "9359,9360,9361,9362,9363,9364,9365" - }, - { - "examine": "The barrier of heat is down.", - "ids": "9366" - }, - { - "examine": "A barrier of heat.", - "ids": "9367,9368,9369,9370,9371,9372,9373" - }, - { - "examine": "Hot enough to cook your breakfast on.", - "ids": "9374,9375,9376" - }, - { - "examine": "An egg incubated in the lava.", - "ids": "9377,9378,9379" - }, - { - "examine": "Who's the man?", - "ids": "9380" - }, - { - "examine": "A wooden crate.", - "ids": "9381" - }, - { - "examine": "Some wooden crates.", - "ids": "9382,9383,9384,9385,9386,9387,9388,9389" - }, - { - "examine": "A natural forge using volcanic heat.", - "ids": "9390" - }, - { - "examine": "I spy with my little eye...", - "ids": "9391,9392,9393,9394,9395,9396,9397" - }, - { - "examine": "Lets you put items into your bank.", - "ids": "9398,9399,26969,36788" - }, - { - "examine": "Sparse weeds.", - "ids": "9400" - }, - { - "examine": "Weeds.", - "ids": "9401" - }, - { - "examine": "Thick weeds.", - "ids": "9402" - }, - { - "examine": "This is Unferth's patch for growing potatos.", - "ids": "9403" - }, - { - "examine": "You say Potato, I say Poh-tar-to.", - "ids": "9404,9405,9406,9407" - }, - { - "examine": "Looks like these potatoes have fully grown.", - "ids": "9408,9409,9410,9411,9412,9413,9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9424,9425,9426,9427,9428,9429" - }, - { - "examine": "A nice sturdy bare table.", - "ids": "9430" - }, - { - "examine": "A nice sturdy table.", - "ids": "9431" - }, - { - "examine": "A table with milk.", - "ids": "9432" - }, - { - "examine": "A table with cake.", - "ids": "9433" - }, - { - "examine": "A hearty meal for Unferth.", - "ids": "9434,9435" - }, - { - "examine": "Great for sleeping in.", - "ids": "9436" - }, - { - "examine": "Not so great for sleeping in, it's not made.", - "ids": "9437,9438" - }, - { - "examine": "A fire burns brightly here.", - "ids": "9439" - }, - { - "examine": "An empty fire place.", - "ids": "9440" - }, - { - "examine": "An unlit fire place.", - "ids": "9441,9442" - }, - { - "examine": "A good source of books!", - "ids": "9443,9444,9445,9446,9447,9448,9449,9450,9451,9452,9453,9454,9455,9456,9457,9458,9459,9460,9461,9462,9463,9464,9465,9466,9467,9468,9469" - }, - { - "examine": "I can climb these stairs.", - "ids": "9470" - }, - { - "examine": "They go down.", - "ids": "9471" - }, - { - "examine": "What could be down here?", - "ids": "9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497" - }, - { - "examine": "A small plant, suitable for sandy soil.", - "ids": "9498" - }, - { - "examine": "A decorative plant.", - "ids": "9499" - }, - { - "examine": "A fairly small plant.", - "ids": "9500" - }, - { - "examine": "Another plant.", - "ids": "9501,9502,9503,9504,9505,9506,9507" - }, - { - "examine": "Perfect for snoozing in the sun.", - "ids": "9508" - }, - { - "examine": "Lovely comfy-looking big bed.", - "ids": "9509" - }, - { - "examine": "East to Draynor Village :: South to Rimmington :: South-east to Port Sarim.", - "ids": "9510,9511,9512,9513" - }, - { - "examine": "Betty's chair.", - "ids": "9514" - }, - { - "examine": "I wonder what she's making?", - "ids": "9515" - }, - { - "examine": "Betty's counter.", - "ids": "9516,9517,9518" - }, - { - "examine": "A wooden barrel for storage.", - "ids": "9519" - }, - { - "examine": "A wooden barrel containing lots of fish.", - "ids": "9520" - }, - { - "examine": "A wooden barrel for storage.", - "ids": "9521" - }, - { - "examine": "How much does this weigh?", - "ids": "9522" - }, - { - "examine": "A case. With books.", - "ids": "9523" - }, - { - "examine": "A mooring chain.", - "ids": "9524" - }, - { - "examine": "One horsepower; wooden suspension: a beauty.", - "ids": "9525" - }, - { - "examine": "Betty keeps some rune objects here.", - "ids": "9526,9527,9528,9529" - }, - { - "examine": "A sinister fungus.", - "ids": "9530" - }, - { - "examine": "Sit back and relax...", - "ids": "9531" - }, - { - "examine": "Sit back and enjoy the view.", - "ids": "9532" - }, - { - "examine": "A wooden crate.", - "ids": "9533,9534" - }, - { - "examine": "Some wooden crates.", - "ids": "9535" - }, - { - "examine": "Some wooden boxes.", - "ids": "9536" - }, - { - "examine": "It's used for loading and unloading fishing boats.", - "ids": "9537" - }, - { - "examine": "I hope it doesn't sink.", - "ids": "9538,9539,9540,9541,9542,9543,9544,9545,9546,9547" - }, - { - "examine": "A bucket full of red hot coals.", - "ids": "9548,9549,9550,9551,9552,9553,9554,9555,9556,9557" - }, - { - "examine": "It leads up.", - "ids": "9558" - }, - { - "examine": "I can climb down this.", - "ids": "9559,9560" - }, - { - "examine": "I guess I could sleep in it if I were really tired.", - "ids": "9561" - }, - { - "examine": "A prison cell door.", - "ids": "9562" - }, - { - "examine": "A locked prison cell door.", - "ids": "9563" - }, - { - "examine": "A prison cell door.", - "ids": "9564" - }, - { - "examine": "Stops people getting out.", - "ids": "9565,9566" - }, - { - "examine": "Ewww!", - "ids": "9567" - }, - { - "examine": "En-suite facilities in every cell!", - "ids": "9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581" - }, - { - "examine": "I can climb up these stairs.", - "ids": "9582,9583" - }, - { - "examine": "I can climb down these stairs.", - "ids": "9584,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599,9600,9601,9602,9603" - }, - { - "examine": "A leafy plant.", - "ids": "9604" - }, - { - "examine": "A leafy fern.", - "ids": "9605" - }, - { - "examine": "A leafy shrub.", - "ids": "9606" - }, - { - "examine": "A case. With books.", - "ids": "9607,9608,9609" - }, - { - "examine": "An artist's easel.", - "ids": "9610" - }, - { - "examine": "A good source of books!", - "ids": "9611" - }, - { - "examine": "The Make-over Mage's bed.", - "ids": "9612" - }, - { - "examine": "A nice sturdy looking table.", - "ids": "9613" - }, - { - "examine": "Generally used for putting things on.", - "ids": "9614" - }, - { - "examine": "A small wooden table.", - "ids": "9615" - }, - { - "examine": "Items are for sale here.", - "ids": "9616" - }, - { - "examine": "All-purpose storage.", - "ids": "9617,9618,9619,9620" - }, - { - "examine": "For sitting.", - "ids": "9621" - }, - { - "examine": "Fancy.", - "ids": "9622,9623" - }, - { - "examine": "It's like a land rudder.", - "ids": "9624" - }, - { - "examine": "A wooden wheelbarrow.", - "ids": "9625,9626,9627,9628,9629,9630,9631,9632,9633,9634,9635,9636,9637,9638,9639,9640,9641,9642,9643,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9654,9655,9656,9657,9658,9659,9660" - }, - { - "examine": "This tree has been cut down.", - "ids": "9661" - }, - { - "examine": "Popular with farmers and treasure hunters.", - "ids": "9662" - }, - { - "examine": "This tree has fallen to the ground.", - "ids": "9663" - }, - { - "examine": "A gnarly old tree root.", - "ids": "9664" - }, - { - "examine": "The roots of a tree are exposed.", - "ids": "9665" - }, - { - "examine": "I hope I don't trip over any of these.", - "ids": "9666" - }, - { - "examine": "Disturbingly man-like.", - "ids": "9667" - }, - { - "examine": "Its eyes stare off into the distance...", - "ids": "9668,9669,9670,9671,9672,9673,9674,9675,9676,9677,9678,9679,9680,9681" - }, - { - "examine": "Expels smoke from the range.", - "ids": "9683" - }, - { - "examine": "Wash your hands!", - "ids": "9684" - }, - { - "examine": "Generally used for putting things on.", - "ids": "9685" - }, - { - "examine": "Some fabric ready for clothing.", - "ids": "9686" - }, - { - "examine": "I'm guessing it's for making women's clothes.", - "ids": "9687,9688" - }, - { - "examine": "Looks like it's for making men's clothes.", - "ids": "9689" - }, - { - "examine": "I think this one's for making men's clothes.", - "ids": "9690,9691,9692,9693" - }, - { - "examine": "Some helpful people have placed notes on here, how nice.", - "ids": "9694" - }, - { - "examine": "This notice board is full of scrolls and charts.", - "ids": "9695" - }, - { - "examine": "I wonder what's inside ?", - "ids": "9696" - }, - { - "examine": "A nice sturdy looking table.", - "ids": "9697,9698,9699,9700,9701" - }, - { - "examine": "This clearly isn't used for dining very often.", - "ids": "9702" - }, - { - "examine": "Not as nice as some.", - "ids": "9703" - }, - { - "examine": "This needs dusting before I'll sit on it.", - "ids": "9704,9705" - }, - { - "examine": "This must let me in to the arena somehow...", - "ids": "9706" - }, - { - "examine": "This must let me out of the arena somehow...", - "ids": "9707" - }, - { - "examine": "A rocky outcrop.", - "ids": "9708,9709,9710,9711,9712,9713,9714,9715,9716,9717,9718,9719,9720,9721,9722,9723,9724,9725,9726,9727,9728,9729,9730,9731,9732,9733,9734,9735,9737" - }, - { - "examine": "An old crumbled pillar.", - "ids": "9738,9739,9740" - }, - { - "examine": "Smells like fish.", - "ids": "9741,9742,9743" - }, - { - "examine": "I can climb up here.", - "ids": "9744" - }, - { - "examine": "I can go below decks with this ladder.", - "ids": "9745,9746,9747" - }, - { - "examine": "A collection point.", - "ids": "9748" - }, - { - "examine": "A Lever.", - "ids": "9749,9750" - }, - { - "examine": "A crystal that was broken a long time ago.", - "ids": "9751" - }, - { - "examine": "A repowered crystal.", - "ids": "9752" - }, - { - "examine": "Perhaps I should search it.", - "ids": "9753" - }, - { - "examine": "I wonder what's inside.", - "ids": "9754" - }, - { - "examine": "Perhaps I should search it.", - "ids": "9755" - }, - { - "examine": "I wonder what's inside.", - "ids": "9756" - }, - { - "examine": "Perhaps I should search it.", - "ids": "9757" - }, - { - "examine": "I wonder what's inside.", - "ids": "9758" - }, - { - "examine": "Perhaps I should search it.", - "ids": "9759" - }, - { - "examine": "I wonder what's inside.", - "ids": "9760" - }, - { - "examine": "Perhaps I should search it.", - "ids": "9761" - }, - { - "examine": "I wonder what's inside.", - "ids": "9762" - }, - { - "examine": "A recently killed guard.", - "ids": "9763,9764,9765" - }, - { - "examine": "A recently killed slave.", - "ids": "9766,9767" - }, - { - "examine": "A door of pure light!", - "ids": "9768,9769,9770,9771,9772,9773,9774,9775,9776,9777,9778,9779,9780,9781,9782,9783,9784,9785,9786,9787,9788,9789,9790,9791,9792,9793,9794,9795,9796,9797,9798,9799,9800,9801,9802,9803,9804,9805,9806,9807,9808,9809,9810,9811,9812,9813,9814,9815,9816,9817,9818,9819,9820,9821,9822,9823,9824,9825,9826,9827,9828,9829,9830,9831,9832,9833,9834,9835,9836,9837,9838,9839,9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854,9855,9856,9857,9858,9859,9860,9861,9862,9863,9864,9865,9866,9867,9868,9869,9870,9871,9872,9873,9874,9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885,9886,9887,9888,9889,9890,9891,9892,9893,9894,9895,9896,9897" - }, - { - "examine": "There are holes passing through this pillar.", - "ids": "9898,9899,9900,9901,9902,9903,9904,9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,9915,9916,9917,9918,9919,9920,9921,9922,9923,9924,9925,9926,9927,9928,9929,9930,9931,9932,9933,9934,9935,9936,9937,9938,9939,9940,9941,9942,9943,9944,9945,9946,9947,9948,9949,9950,9951,9952,9953,9954,9955,9956,9957,9958,9959,9960,9961,9962,9963,9964,9965,9966,9967,9968,9969,9970,9971,9972,9973,9974" - }, - { - "examine": "I can climb down these.", - "ids": "9975" - }, - { - "examine": "A cave wall.", - "ids": "9976" - }, - { - "examine": "I wonder where this goes?", - "ids": "9977" - }, - { - "examine": "A ladder carved into the wall.", - "ids": "9978,9979" - }, - { - "examine": "A good place for light to merge.", - "ids": "9980" - }, - { - "examine": "A good place for a light crystal.", - "ids": "9981,9982,9983,9984,9985,9986,9987" - }, - { - "examine": "A broken pillar.", - "ids": "9988,9989,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999,10000,10001" - }, { "examine": "An ancient elvish light doorway.", "ids": "10002,10003,10004,10005,10006,10007,10008,10009,10010,10011,10012,10013" @@ -14071,6 +55,10 @@ "examine": "A way up.", "ids": "10038,10039,10040" }, + { + "examine": "This section of railing has been broken", + "ids": "1004,1005" + }, { "examine": "The legs probably aren't a natural feature of the tree.", "ids": "10041,10042,10043,10044,10045,10046,10047,10048,10049,10050,10051,10052,10053" @@ -14087,6 +75,10 @@ "examine": "Flag, pole... Yep, it's a flagpole.", "ids": "10057,10058,10059,10060,10061" }, + { + "examine": "There is a hole in this section of the railing.", + "ids": "1006,1007,1008,1009" + }, { "examine": "This must be climbed over.", "ids": "10062" @@ -14100,7 +92,7 @@ "ids": "10080" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "10081" }, { @@ -14151,6 +143,14 @@ "examine": "Almost clear.", "ids": "10099" }, + { + "examine": "I can climb this.", + "ids": "101" + }, + { + "examine": "Someone was thirsty...", + "ids": "1010" + }, { "examine": "Free from weeds.", "ids": "10100" @@ -14171,10 +171,18 @@ "examine": "A stagnant lake.", "ids": "10105,10106,10107,10108,10109,10110,10111,10112,10113,10114,10115,10116,10117,10118,10119,10120,10121,10122,10123,10124,10125,10126,10127,10128" }, + { + "examine": "Full of lovely drinks!", + "ids": "1011,1012" + }, { "examine": "A very large pipe.", "ids": "10129,10130,10131,10132,10133,10134,10135" }, + { + "examine": "Storage for cookery items.", + "ids": "1013,32283,40055" + }, { "examine": "Rickety.", "ids": "10136,10137" @@ -14187,6 +195,10 @@ "examine": "It's a palm tree.", "ids": "10139,10140,10141" }, + { + "examine": "Storage for all needs.", + "ids": "1014,1015,1016,1017,1018,10495,10496,10497,10498,11768,11769,11770,11771,11772,11773,11774,11775,11776,11777,11778,11779,11780,11781,12980,30648,30649,30650,32284,40045,40046,40047" + }, { "examine": "This controls the entire brewery.", "ids": "10142" @@ -14283,10 +295,30 @@ "examine": "A lit torch.", "ids": "10179,10180,10181,10182,10183,10184,10185,10186,10187,10188,10189,10190,10191,10192" }, + { + "examine": "What kind of sick person keeps a skull on their wall?", + "ids": "1019" + }, { "examine": "A sturdy iron frame, for climbing up and down.", "ids": "10193,10194,10195,10196,10197,10198,10199,10200,10201,10202,10203,10204,10205,10206,10207,10208,10209,10210,10211,10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223,10224,10225,10226,10227,10228,10229,10230,10231,10232,10233,10234,10235,10236,10237,10238,10239,10240,10241,10242,10243,10244,10245" }, + { + "examine": "A sturdy door.", + "ids": "102" + }, + { + "examine": "Esoteric artefacts.", + "ids": "1020" + }, + { + "examine": "I don't want to know what's in those little urns.", + "ids": "1021,1022,1023" + }, + { + "examine": "Used for storing things on.", + "ids": "1024" + }, { "examine": "Once upon a time this was used to make pottery.", "ids": "10246" @@ -14299,6 +331,10 @@ "examine": "There was a lot of pottery made here a long time ago.", "ids": "10248,10249" }, + { + "examine": "Tailor made for needlework supplies", + "ids": "1025,1026" + }, { "examine": "Eric's been killed by stones falling from the ceiling!", "ids": "10250" @@ -14367,6 +403,10 @@ "examine": "A display of various relics.", "ids": "10269,10270,10271,10272" }, + { + "examine": "Prepared hides hang here drying.", + "ids": "1027" + }, { "examine": "A collection of rare books.", "ids": "10273" @@ -14383,6 +423,10 @@ "examine": "It's some crates.", "ids": "10279,10280" }, + { + "examine": "This contains dye for leather.", + "ids": "1028,1029" + }, { "examine": "It's a crate.", "ids": "10281" @@ -14439,6 +483,14 @@ "examine": "Opens the rat door.", "ids": "10299" }, + { + "examine": "I wonder what's inside.", + "ids": "103" + }, + { + "examine": "Used to squeeze moisture from the hide.", + "ids": "1030,1031" + }, { "examine": "Contains many rats.", "ids": "10300" @@ -14515,6 +567,10 @@ "examine": "Handy piece of garden decor.", "ids": "10319" }, + { + "examine": "Danger!", + "ids": "1032,5127" + }, { "examine": "Big enough for a cat to get in.", "ids": "10320" @@ -14547,6 +603,10 @@ "examine": "A magical device that enables one to walk through walls.", "ids": "10327,10328,10329,10330,10331,10332,10333,10334" }, + { + "examine": "This tells you which way is which.", + "ids": "1033,1034,1035" + }, { "examine": "Observe the rats.", "ids": "10335,10336,10337,10338,10339,10340,10341,10342,10343,10344,10345" @@ -14631,6 +691,10 @@ "examine": "It's empty.", "ids": "10386,10387,10388,10389,10390,10391,10392,10393,10394,10395,10396,10397,10398,10399,10400,10401" }, + { + "examine": "Perhaps I should search it.", + "ids": "104" + }, { "examine": "It looks like water once ran here.", "ids": "10402,10403" @@ -14755,6 +819,10 @@ "examine": "A small potted plant.", "ids": "10499" }, + { + "examine": "I wonder what that's there for...", + "ids": "105,106" + }, { "examine": "Someone has planted this.", "ids": "10500" @@ -14907,6 +975,10 @@ "examine": "A banner proclaiming your victory over the Giant Champion!", "ids": "10568,10569" }, + { + "examine": "Ye olde Shrimp and Parrot.", + "ids": "1057" + }, { "examine": "A banner proclaiming your victory over the Goblin Champion!", "ids": "10570,10571" @@ -15079,6 +1151,10 @@ "examine": "A box containing unpainted decorations.", "ids": "10679,10680,10681,10682,10683,10684,10685" }, + { + "examine": "The Blue Moon Inn", + "ids": "1068" + }, { "examine": "Box containing blue puppet heads.", "ids": "10686" @@ -15095,6 +1171,10 @@ "examine": "Box containing blue puppet legs.", "ids": "10689" }, + { + "examine": "The Jolly Boar", + "ids": "1069" + }, { "examine": "Box containing red puppet heads.", "ids": "10690" @@ -15135,6 +1215,10 @@ "examine": "You can climb up these.", "ids": "10699,10700" }, + { + "examine": "The Rusty Anchor", + "ids": "1070" + }, { "examine": "A blue crate. Probably containing puppets.", "ids": "10701" @@ -15163,10 +1247,18 @@ "examine": "A wooden ladder.", "ids": "10707,10708,10709" }, + { + "examine": "The Shrimp and Parrot", + "ids": "1071" + }, { "examine": "Pots full of paint.", "ids": "10710,10711,10712,10713,10714,10715,10716,10717,10718,10719,10720" }, + { + "examine": "The Dead Man's Chest", + "ids": "1072" + }, { "examine": "A doorway made of light.", "ids": "10721" @@ -15183,6 +1275,10 @@ "examine": "A pile of animal Bones.", "ids": "10725,10726,10727,10728,10729,10730,10731,10732" }, + { + "examine": "The Rising Sun", + "ids": "1073" + }, { "examine": "Hidden away, I wonder where it goes?", "ids": "10733" @@ -15207,10 +1303,26 @@ "examine": "A statue representing air.", "ids": "10739,10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751,10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,10762,10763,10764,10765,10766,10767" }, + { + "examine": "The Forester's Arms", + "ids": "1074" + }, + { + "examine": "The Flying Horse", + "ids": "1075" + }, + { + "examine": "The Dancing Donkey", + "ids": "1076" + }, { "examine": "A place to put your money.", "ids": "10768,10769,10770" }, + { + "examine": "The Dragon Inn", + "ids": "1077" + }, { "examine": "Stairs.", "ids": "10771,10772,10773,10774,10775,10776,10777" @@ -15223,6 +1335,10 @@ "examine": "A teleport to the Enchanting Chamber.", "ids": "10779" }, + { + "examine": "Karamja Spirits bar", + "ids": "1078" + }, { "examine": "A teleport to the Alchemists' Playground.", "ids": "10780" @@ -15239,10 +1355,18 @@ "examine": "This may be worth opening.", "ids": "10783,10784,10785,10786,10787,10788,10789,10790,10791,10792,10793,10794,10795,10796,10797,10798" }, + { + "examine": "North to Varrock :: East to Al-Kharid.", + "ids": "1079" + }, { "examine": "Some simple cubes.", "ids": "10799" }, + { + "examine": "North road to Draynor Village :: East road to Varrock.", + "ids": "1080" + }, { "examine": "A pile of cylindrical shapes.", "ids": "10800" @@ -15279,6 +1403,10 @@ "examine": "A bin full of paper.", "ids": "10809,10810,10811,10812" }, + { + "examine": "North to Draynor Village.", + "ids": "1081" + }, { "examine": "Betty's counter. There is a vial standing on it.", "ids": "10813" @@ -15295,6 +1423,10 @@ "examine": "Doesn't look like the way out.", "ids": "10818,10819" }, + { + "examine": "North to Draynor Manor :: West to Asgarnia.", + "ids": "1082" + }, { "examine": "A glowing barrier of energy prevents your escape.", "ids": "10820,10821,10822,10823" @@ -15311,10 +1443,22 @@ "examine": "Smells like fish.", "ids": "10828,10829,10830,10831,10832,10833,10834,10835,10836,10837,10838,10839,10840,10841,10842,10843,10844,10845,10846,10847" }, + { + "examine": "South to the Wizards' Tower :: East to Lumbridge.", + "ids": "1083" + }, + { + "examine": "South to Lumbridge :: West to Varrock :: East to the Digsite.", + "ids": "1084" + }, { "examine": "This leads to a tunnel too steep for me to scale.", "ids": "10848,10849,10850" }, + { + "examine": "South to Al-Kharid :: West to Lumbridge :: East to the Digsite.", + "ids": "1085" + }, { "examine": "I can climb this rocky outcrop.", "ids": "10851" @@ -15339,6 +1483,10 @@ "examine": "A gap.", "ids": "10859" }, + { + "examine": "North to Varrock :: East to the Digsite.", + "ids": "1086" + }, { "examine": "A narrow ledge.", "ids": "10860" @@ -15355,10 +1503,18 @@ "examine": "A wooden plank.", "ids": "10867,10868,10869,10870,10871,10872,10873,10874" }, + { + "examine": "South to the Wizards' Tower :: North to Draynor Village :: East to Lumbridge.", + "ids": "1087" + }, { "examine": "A stone block.", "ids": "10875,10876,10877,10878,10879,10880,10881" }, + { + "examine": "The ideal thing to sit on.", + "ids": "1088,1115,3801,5345,5614,7385,7386,7387,10491,11489,12885,12886,13582,32032,36830,40039" + }, { "examine": "A gap.", "ids": "10882,10883,10884,10885" @@ -15367,6 +1523,18 @@ "examine": "A narrow ledge.", "ids": "10886,10887,10888,10889,10890,10891,10892,10893,10894,10895,10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906,10907,10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919,10920,10921,10922,10923,10924,10925,10926,10927,10928,10929,10930,10931,10932,10933,10934,10935,10936,10937,10938,10939,10940,10941,10942,10943" }, + { + "examine": "Not so good for sitting on.", + "ids": "1089" + }, + { + "examine": "A comfortable seat.", + "ids": "1090,1091,1092,11760,13583,13584,13585,13586,13587,32286" + }, + { + "examine": "Good for sitting on.", + "ids": "1093,1094,1095,1102,5880,10492,12440,12441,12973,32033" + }, { "examine": "A rocky outcrop.", "ids": "10944,10945,10946,10947,10948,10949" @@ -15391,10 +1559,18 @@ "examine": "Made of sandstone and granite.", "ids": "10958,10959,10960,10961" }, + { + "examine": "Good for rocking in!", + "ids": "1096" + }, { "examine": "A large statue.", "ids": "10962,10963,10964,10965,10966,10967,10968,10969,10970" }, + { + "examine": "A kingly seat for a royal behind.", + "ids": "1097,1098,1099,1105,1108,1109,32285" + }, { "examine": "It has no limbs.", "ids": "10971" @@ -15507,6 +1683,14 @@ "examine": "The shadow and smoke globes are lit.", "ids": "10999" }, + { + "examine": "I can climb up this.", + "ids": "11" + }, + { + "examine": "The perfect way to sit at the bar.", + "ids": "1100,1101" + }, { "examine": "The ice, shadow and smoke globes are lit.", "ids": "11000" @@ -15555,6 +1739,10 @@ "examine": "A finished wall.", "ids": "11028,11029,11030,11031" }, + { + "examine": "Gnomes sit on these.", + "ids": "1103" + }, { "examine": "A nearly finished wall.", "ids": "11032,11033" @@ -15571,6 +1759,10 @@ "examine": "A ruined wall.", "ids": "11038,11039" }, + { + "examine": "Gnomes are found sitting here.", + "ids": "1104" + }, { "examine": "A pile of bricks dislodged from the wall nearby.", "ids": "11040" @@ -15615,6 +1807,10 @@ "examine": "There's a recess on the top shaped like a head.", "ids": "11059" }, + { + "examine": "Sit back and relax...", + "ids": "1106,1107" + }, { "examine": "There's a sigil on it shaped like a Z.", "ids": "11060" @@ -15675,6 +1871,14 @@ "examine": "A rock.", "ids": "11098,11099,11100,11101,11102,11103,11104,11105,11106,11107,11108,11109,11110,11111" }, + { + "examine": "Used for sitting on.", + "ids": "1110" + }, + { + "examine": "Do I dare sit on this?", + "ids": "1111" + }, { "examine": "This tree has long been dead.", "ids": "11112" @@ -15683,6 +1887,18 @@ "examine": "A dried up bush, void of life.", "ids": "11113,11114,11115,11116,11117,11118,11119,11120,11121,11122,11123,11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,11135,11136,11137,11138,11139,11140" }, + { + "examine": "Church seating.", + "ids": "1112" + }, + { + "examine": "Park it here...", + "ids": "1113" + }, + { + "examine": "Wouldn't like to sit on that for too long...", + "ids": "1114" + }, { "examine": "He's been trapped in there for a long time.", "ids": "11141,11142,11143,11144,11145,11146,11147,11148,11149,11150" @@ -15699,6 +1915,10 @@ "examine": "A simple stone pedestal.", "ids": "11153,11154,11155,11156,11157,11158,11159,11160,11161" }, + { + "examine": "Keeps the neighbours out!", + "ids": "1116,1117" + }, { "examine": "A big grinding thing.", "ids": "11162,11163,11164" @@ -15707,6 +1927,14 @@ "examine": "A rocky outcrop.", "ids": "11165,11166,11167,11168,11169,11170,11171,11172,11173,11174,11175,11176,11177,11178,11179,11180,11181,11182,11183,11184,11185,11186,11187,11188,11189,11190,11191,11192,11193,11194,11195" }, + { + "examine": "Found in wild areas.", + "ids": "1118" + }, + { + "examine": "These are commonly found.", + "ids": "1119" + }, { "examine": "An elf-fashioned door.", "ids": "11196,11197" @@ -15719,6 +1947,10 @@ "examine": "The ladder back to the Runeversi challenge room.", "ids": "11199" }, + { + "examine": "An unusual bush.", + "ids": "1120" + }, { "examine": "The ladder to the Runesquares challenge room for experienced players.", "ids": "11200" @@ -15759,6 +1991,10 @@ "examine": "Permission to board?", "ids": "11209,11210,11211,11212,11213" }, + { + "examine": "A wild bush.", + "ids": "1121" + }, { "examine": "Ready...aim...fire!", "ids": "11214" @@ -15779,6 +2015,10 @@ "examine": "If I can't destroy this we're sunk.", "ids": "11219" }, + { + "examine": "All the leaves have fallen off.", + "ids": "1122,1123" + }, { "examine": "Let's hope they don't repair it.", "ids": "11220,11221,11222,11223,11224,11225,11226,11227" @@ -15811,6 +2051,10 @@ "examine": "I wouldn't be surprised if bits land over in Lumbridge.", "ids": "11239" }, + { + "examine": "A commonly found bush.", + "ids": "1124,36782" + }, { "examine": "Rigged to blow.", "ids": "11240,11241,11242,11243" @@ -15831,6 +2075,14 @@ "examine": "Cannons. Lots of cannons.", "ids": "11248,11249,11250,11251,11252,11253,11254,11255,11256,11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272,11273,11274,11275" }, + { + "examine": "Bushy!", + "ids": "1125" + }, + { + "examine": "A rose by any other name would still have prickly bits.", + "ids": "1126" + }, { "examine": "Holds up the sails.", "ids": "11276,11277,11278,11279" @@ -15964,7 +2216,7 @@ "ids": "11354,11355" }, { - "examine": "A gateway back to RuneScape.", + "examine": "A gateway back to 2009Scape.", "ids": "11356" }, { @@ -16047,6 +2299,10 @@ "examine": "Interface button up.", "ids": "11399" }, + { + "examine": "An appliance for cooking with.", + "ids": "114" + }, { "examine": "Interface button down.", "ids": "11400" @@ -16059,6 +2315,10 @@ "examine": "Interface button right.", "ids": "11402" }, + { + "examine": "The bank teller will serve you from here.", + "ids": "11402" + }, { "examine": "Interface button ignite.", "ids": "11403" @@ -16099,6 +2359,10 @@ "examine": "A rocky outcrop.", "ids": "11424,11425,11426,11427,11428,11429,11430,11431,11432,11433,11434,11435,11436,11437,11438,11439,11440,11441,11442,11443,11444,11445,11446,11447,11448" }, + { + "examine": "A leafy bush.", + "ids": "1144,1145,1146" + }, { "examine": "This door is locked.", "ids": "11449" @@ -16163,6 +2427,10 @@ "examine": "Disturbingly its eyes look everywhere in the room except at you.", "ids": "11469" }, + { + "examine": "The abode of a fairy.", + "ids": "1147,1148,1149" + }, { "examine": "The door is closed.", "ids": "11470" @@ -16239,6 +2507,14 @@ "examine": "These stairs look spooky!", "ids": "11499" }, + { + "examine": "A party balloon.", + "ids": "115,116,117,118,119,120" + }, + { + "examine": "Fairies need money too!", + "ids": "1150" + }, { "examine": "A filthy but sturdy looking table.", "ids": "11500" @@ -16247,6 +2523,10 @@ "examine": "I'm not eating off that.", "ids": "11501,11502,11503,11504,11505,11506,11507,11508,11509" }, + { + "examine": "A fairy is selling items here.", + "ids": "1151" + }, { "examine": "This tree has long been dead.", "ids": "11510" @@ -16255,10 +2535,26 @@ "examine": "I can climb up these stairs.", "ids": "11511,11512,11513,11514,11515,11516,11517,11518,11519,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,11530,11531,11532,11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545" }, + { + "examine": "A small potted plant.", + "ids": "1152" + }, + { + "examine": "Someone has planted this.", + "ids": "1153" + }, + { + "examine": "A nicely potted fern.", + "ids": "1154" + }, { "examine": "It's a strange machine.", "ids": "11546,11547,11548,11549,11550" }, + { + "examine": "Better than weeding.", + "ids": "1155" + }, { "examine": "It's a table for putting objects on.", "ids": "11551" @@ -16267,6 +2563,14 @@ "examine": "A rocky outcrop.", "ids": "11552,11553,11554,11555,11556,11557,11558,11559,11560,11561,11562,11563,11564,11565,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575,11576,11577,11578,11579,11580,11581,11582,11583,11584,11585,11586,11587,11588" }, + { + "examine": "I bet bees like this.", + "ids": "1156,1157" + }, + { + "examine": "A large potted plant.", + "ids": "1158,1159" + }, { "examine": "A wooden barrel for storage.", "ids": "11589,11590,11591,11592,11593,11594,11595,11596,11597,11598,29333" @@ -16275,6 +2579,10 @@ "examine": "A wooden crate.", "ids": "11599" }, + { + "examine": "A plant in a bamboo pot.", + "ids": "1160" + }, { "examine": "Convenient for storage.", "ids": "11600" @@ -16291,6 +2599,10 @@ "examine": "A simple torch stuck in the ground.", "ids": "11606,11607,11608,11609,11610,11611,11612,11613" }, + { + "examine": "Cabbage... yuck!", + "ids": "1161" + }, { "examine": "A barbarian flag.", "ids": "11614,11615" @@ -16307,6 +2619,10 @@ "examine": "Generally used for putting things on.", "ids": "11619" }, + { + "examine": "Found near the water's edge.", + "ids": "1162" + }, { "examine": "The entrance to the barbarians' longhall.", "ids": "11620,11621,11622,11623,11624,11625" @@ -16324,7 +2640,11 @@ "ids": "11629" }, { - "examine": "South to Falador :: West to Taverley :: East to Varrock.", + "examine": "Not good for eating.", + "ids": "1163,1164" + }, + { + "examine": "This tells you which way is which.", "ids": "11630,11631,11632,11633" }, { @@ -16335,6 +2655,10 @@ "examine": "Keeps unwelcome folk out of Falador.", "ids": "11640,11641,11642,11643,11644,11645,11646,11647,11648,11649,11650,11651,11652,11653,11654,11655,11656" }, + { + "examine": "Fungal growth.", + "ids": "1165" + }, { "examine": "A rack for displaying fine maces.", "ids": "11657" @@ -16347,6 +2671,10 @@ "examine": "A barrel of maces.", "ids": "11659" }, + { + "examine": "Poisonous no doubt.", + "ids": "1166" + }, { "examine": "A table bearing a fine selection of maces.", "ids": "11660" @@ -16367,6 +2695,10 @@ "examine": "A hot furnace.", "ids": "11666,11667,11668,11669,11670,11671,11672,11673,36956" }, + { + "examine": "Fungal growth.", + "ids": "1167" + }, { "examine": "Rising Sun Tavern.", "ids": "11674" @@ -16383,6 +2715,10 @@ "examine": "The traditional sign of a barber.", "ids": "11678,11679,11680,11681" }, + { + "examine": "Poisonous no doubt.", + "ids": "1168,1169" + }, { "examine": "A small wooden table.", "ids": "11682,11683,11684,11685,11686,11687,11688,11689,11690,11691,11692" @@ -16399,6 +2735,10 @@ "examine": "The flag of Asgarnia.", "ids": "11699" }, + { + "examine": "I doubt that's edible.", + "ids": "1170,1171,1172" + }, { "examine": "I thought he'd be bigger than that.", "ids": "11700" @@ -16471,6 +2811,10 @@ "examine": "I can climb up these stairs.", "ids": "11729,11730" }, + { + "examine": "A commonly found fern.", + "ids": "1173" + }, { "examine": "I can climb down these stairs.", "ids": "11731" @@ -16507,6 +2851,10 @@ "examine": "Allows access to above level.", "ids": "11739,11740" }, + { + "examine": "Spiky!", + "ids": "1174" + }, { "examine": "Allows access to level below.", "ids": "11741,11742" @@ -16539,6 +2887,10 @@ "examine": "A sturdy-looking round table.", "ids": "11749,11750,11751,11752" }, + { + "examine": "A home for frogs.", + "ids": "1175" + }, { "examine": "Armour of a White Knight. Decorative, but still effective.", "ids": "11753,11754" @@ -16564,7 +2916,11 @@ "ids": "11759" }, { - "examine": "A carving of a figure from the history of RuneScape.", + "examine": "Otherwise known as a lilypad.", + "ids": "1176" + }, + { + "examine": "A carving of a figure from the history of 2009Scape.", "ids": "11761" }, { @@ -16591,6 +2947,14 @@ "examine": "Dusty old books.", "ids": "11767" }, + { + "examine": "A home for frogs.", + "ids": "1177" + }, + { + "examine": "Smells lovely!", + "ids": "1178" + }, { "examine": "What ancient rites were performed here?", "ids": "11782,11783,11784,11785,11786,11787,11788" @@ -16599,6 +2963,10 @@ "examine": "Weathered rocks.", "ids": "11789,11790,11791,11792" }, + { + "examine": "Who is this Heather girl?", + "ids": "1179" + }, { "examine": "Best used with a bucket.", "ids": "11793" @@ -16619,6 +2987,14 @@ "examine": "Makes your hair stay curly.", "ids": "11799,11800,11801,11802,11803,11804,11805,11806,11807,11808,11809,11810,11811" }, + { + "examine": "I wonder why it's purple?", + "ids": "1180" + }, + { + "examine": "I'd better not get stung.", + "ids": "1181,1182,1183" + }, { "examine": "Running water", "ids": "11812" @@ -16627,6 +3003,10 @@ "examine": "Might be handy for a workman.", "ids": "11813,11814,11815,11816,11817,11818,11819,11820,11821,11822,11823,11824,11825,11826,11827,11828,11829,11830,11831,11832,11833,11834,11835,11836,11837,11838,11839" }, + { + "examine": "The tendrils of a creeping plant.", + "ids": "1184" + }, { "examine": "A pile of bricks.", "ids": "11840,11841,11842,11843" @@ -16643,6 +3023,14 @@ "examine": "Contains washing items.", "ids": "11849,11850,11851,11852,11853,11854,11855,11856,11857,11858,11859,11860" }, + { + "examine": "Nicely planted.", + "ids": "1185" + }, + { + "examine": "A large curling plant.", + "ids": "1186" + }, { "examine": "A dwarven flag.", "ids": "11861" @@ -16667,6 +3055,10 @@ "examine": "A pile of parts for building a multicannon.", "ids": "11869,11870,11871,11872" }, + { + "examine": "That's pretty.", + "ids": "1187" + }, { "examine": "A crate containing parts for building a multicannon.", "ids": "11873,11874" @@ -16683,6 +3075,10 @@ "examine": "A simple place for a simple dwarf to sleep and dream of gold.", "ids": "11877,11878,11879,11880,11881,11882,11883,11884,11885,11886,11887" }, + { + "examine": "Blooming!", + "ids": "1188" + }, { "examine": "I can climb up these stairs.", "ids": "11888" @@ -16691,6 +3087,10 @@ "examine": "I can climb these stairs.", "ids": "11889" }, + { + "examine": "Commonly found in grassy areas.", + "ids": "1189" + }, { "examine": "I can climb down these stairs.", "ids": "11890" @@ -16719,6 +3119,14 @@ "examine": "Not suitable for sitting on.", "ids": "11897,11898,11899,11900,11901,11902,11903,11904,11905,11906,11907,11908,11909" }, + { + "examine": "Is this the biggest flower around?", + "ids": "1190" + }, + { + "examine": "These are huge!", + "ids": "1191" + }, { "examine": "I hope I never meet one of those.", "ids": "11910" @@ -16731,6 +3139,38 @@ "examine": "A rocky outcrop.", "ids": "11915,11916,11917,11918,11919,11920,11921,11922,11923,11924,11925,11926,11927,11928,11929,11930,11931,11932,11933,11934,11935,11936,11937,11938,11939,11940,11941,11942,11943,11944,11945,11946,11947,11948,11949,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959,11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971,11972,11973,11974,11975,11976,11977,11978,11979,11980,11981,11982,11983,11984,11985,11986,11987,11988,11989,11990,11991,11992" }, + { + "examine": "A rare flower.", + "ids": "1192" + }, + { + "examine": "Don't flowers make you feel better?", + "ids": "1193" + }, + { + "examine": "Can this talk?", + "ids": "1194" + }, + { + "examine": "You don't see that many of these.", + "ids": "1195" + }, + { + "examine": "I wonder what these are?", + "ids": "1196" + }, + { + "examine": "A rarely found flower.", + "ids": "1197" + }, + { + "examine": "Very rare flowers.", + "ids": "1198" + }, + { + "examine": "These smell nice.", + "ids": "1199" + }, { "examine": "The door is closed.", "ids": "11993" @@ -16739,9 +3179,17 @@ "examine": "The door is open.", "ids": "11994,11995,11996,11997,11998,11999,12000,12001,12002" }, + { + "examine": "A pile of rocks is blocking my path.", + "ids": "12" + }, + { + "examine": "A bed of red flowers.", + "ids": "1200" + }, { "examine": "Better not eat them!", - "ids": "12003" + "ids": "12003,12128" }, { "examine": "It's going to be a tight squeeze!", @@ -16751,6 +3199,22 @@ "examine": "Illuminating!", "ids": "12005,12006,12007,12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019,12020,12021,12022,12023,12024,12025,12026,12027,12028,12029,12030,12031,12032,12033,12034,12035,12036,12037,12038,12039,12040" }, + { + "examine": "These are pretty.", + "ids": "1201" + }, + { + "examine": "Found only in jungle areas.", + "ids": "1202" + }, + { + "examine": "A very rare exotic flower.", + "ids": "1203" + }, + { + "examine": "A small fernlike plant.", + "ids": "1204" + }, { "examine": "It'll be a tight squeeze.", "ids": "12041" @@ -16763,6 +3227,14 @@ "examine": "A magic door.", "ids": "12045,12046,12047,12048,12049,12050,12051,12052,12053,12054,12055,12056,12057,12058,12059" }, + { + "examine": "The leaves of a nasty creeping plant.", + "ids": "1205" + }, + { + "examine": "The flower of a nasty creeping plant.", + "ids": "1206,1207,1208" + }, { "examine": "Shelves of colourful potion gourds!", "ids": "12060,12061" @@ -16807,6 +3279,10 @@ "examine": "Better not eat them!", "ids": "12094,12095,12096,12097,12098,12099,14121" }, + { + "examine": "Aren't they pretty?", + "ids": "121" + }, { "examine": "A hot furnace.", "ids": "12100,12101" @@ -16881,7 +3357,7 @@ }, { "examine": "A wall jutting out into the path.", - "ids": "12127,12128,12129,12130" + "ids": "12127,12129,12130" }, { "examine": "Spooky.", @@ -16923,6 +3399,10 @@ "examine": "canoe", "ids": "12168,12169,12170,12171,12172,12173,12174,12175,12176,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187,12188,12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199,12200" }, + { + "examine": "Paaaarty!", + "ids": "122" + }, { "examine": "A well, it is not safe to climb down.", "ids": "12201" @@ -17071,6 +3551,10 @@ "examine": "Tick tock.", "ids": "12293,12294,12295,12296,12297,12298,12299,12300" }, + { + "examine": "A party balloon.", + "ids": "123,124,125,126,127,128,129,130" + }, { "examine": "Little candles flickering.", "ids": "12301,12302,12303,12304,12305,12306,12307,12308" @@ -17079,6 +3563,10 @@ "examine": "It's open.", "ids": "12309" }, + { + "examine": "Nasty curling tendrils of a creeping plant.", + "ids": "1231" + }, { "examine": "Someone should be sitting here.", "ids": "12310,12311" @@ -17587,6 +4075,10 @@ "examine": "You seem to make out some writing.", "ids": "12750,12751,12752,12753,12754,12755,12756,12757,12758,12759,12760" }, + { + "examine": "One of the most common trees in 2009Scape.", + "ids": "1276" + }, { "examine": "A rotten looking door.", "ids": "12761,12762" @@ -17615,6 +4107,10 @@ "examine": "Perhaps I should search it.", "ids": "12769" }, + { + "examine": "A healthy young tree.", + "ids": "1277" + }, { "examine": "A small cave entrance.", "ids": "12770,12771,12772" @@ -17647,6 +4143,10 @@ "examine": "I can climb this.", "ids": "12779" }, + { + "examine": "A commonly found tree.", + "ids": "1278,1279" + }, { "examine": "A wooden ladder.", "ids": "12780,12781" @@ -17699,6 +4199,10 @@ "examine": "This bank booth could probably be repaired.", "ids": "12799" }, + { + "examine": "A healthy young tree.", + "ids": "1280" + }, { "examine": "This bank booth is too damaged to use.", "ids": "12800,12801" @@ -17727,6 +4231,10 @@ "examine": "A repaired furnace, hot and ready for action.", "ids": "12809,12810,12811" }, + { + "examine": "A beautiful old oak.", + "ids": "1281" + }, { "examine": "Hardened earth, with bits of rock in it, not easy to move.", "ids": "12812" @@ -17747,6 +4255,10 @@ "examine": "A wooden gate.", "ids": "12816,12817,12818,12819" }, + { + "examine": "This tree has long been dead.", + "ids": "1282,1283,1284" + }, { "examine": "A path leading out of this swampy dead end.", "ids": "12820,12821,12822,12823,12824,12825,12826,12827,12828,12829,12830,12831,12832,12833,12834,12835,12836,12837,12838,12839,12840,12841,12842,12843" @@ -17755,6 +4267,10 @@ "examine": "A veritable pile of bricks.", "ids": "12844,12845,12846,12847,12848,12849,12850,12851,12852,12853,12854" }, + { + "examine": "An old weather beaten tree.", + "ids": "1285" + }, { "examine": "It's a bit broken.", "ids": "12855" @@ -17767,6 +4283,10 @@ "examine": "Actually, I could do with a drink...never mind...I wasn't thirsty anyway.", "ids": "12857,12858,12859,12860,12861,12862,12863,12864,12865,12866,12867,12868,12869,12870,12871" }, + { + "examine": "It's only useful for firewood now.", + "ids": "1286,1287,1288,1289,1290,1291" + }, { "examine": "Where does this go?", "ids": "12872" @@ -17851,6 +4371,14 @@ "examine": "A wooden ladder.", "ids": "12906,12907,12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,12924,12925,12926,12927,12928,12929,12930,12931,12932" }, + { + "examine": "An ancient magical tree.", + "ids": "1292" + }, + { + "examine": "An ancient sentient tree.", + "ids": "1293,1294,1295" + }, { "examine": "Items are for sale here.", "ids": "12933,12934,12935,12936,12937,12938,12939,12940" @@ -17863,6 +4391,10 @@ "examine": "It's not a schooner, it's a sailboat.", "ids": "12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955,12956,12957,12958,12959,12960" }, + { + "examine": "This tree has fallen to the ground.", + "ids": "1296" + }, { "examine": "A wooden crate.", "ids": "12961" @@ -17891,6 +4423,10 @@ "examine": "Something is cooking nicely here.", "ids": "12969" }, + { + "examine": "This tree was either the victim of weather or mankind.", + "ids": "1297" + }, { "examine": "A well slept in bed.", "ids": "12970" @@ -17915,6 +4451,10 @@ "examine": "This chair rocks", "ids": "12979" }, + { + "examine": "A commonly found fern.", + "ids": "1298" + }, { "examine": "A stand for hats!", "ids": "12981" @@ -17927,6 +4467,18 @@ "examine": "A wooden gate.", "ids": "12986,12987,12988,12989,12990,12991,12992,12993,12994,12995,12996,12997,12998,12999,13000" }, + { + "examine": "Is this a weed?", + "ids": "1299" + }, + { + "examine": "Mud caved in from above.", + "ids": "13" + }, + { + "examine": "A fern is growing here.", + "ids": "1300" + }, { "examine": "The door is closed.", "ids": "13001" @@ -17947,10 +4499,46 @@ "examine": "A desert door.", "ids": "13006,13007,13008,13009,13010,13011,13012,13013,13014" }, + { + "examine": "Home to many unusual creatures.", + "ids": "1301" + }, { "examine": "A timber door.", "ids": "13015,13016,13017,13018,13019,13020,13021,13022,13023,13024,13025,13026,13027,13028,13029,13030,13031,13032,13033,13034,13035,13036,13037,13038,13039,13040,13041,13042,13043,13044,13045,13046,13047,13048,13049,13050,13051,13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063,13064,13065,13066,13067,13068,13069,13070,13071,13072,13073,13074,13075,13076,13077,13078,13079,13080,13081,13082,13083,13084,13085,13086,13087,13088,13089,13090,13091,13092,13093" }, + { + "examine": "A leafy tree.", + "ids": "1302" + }, + { + "examine": "A tree found in tropical areas.", + "ids": "1303,1304,1305" + }, + { + "examine": "The tree shimmers with a magical force.", + "ids": "1306" + }, + { + "examine": "I bet this makes good syrup!", + "ids": "1307" + }, + { + "examine": "These trees are found near water.", + "ids": "1308" + }, + { + "examine": "A splendid tree.", + "ids": "1309" + }, + { + "examine": "The door is closed.", + "ids": "131" + }, + { + "examine": "This tree has vines hanging from it.", + "ids": "1310" + }, { "examine": "A way in to the house.", "ids": "13100,13101,13102,13103,13104,13105,13106" @@ -17959,14 +4547,26 @@ "examine": "I'm sure the animal is glad its fur was put to good use.", "ids": "13107,13108,13109,13110,13111,13112,13113,13114,13115,13116,13117" }, + { + "examine": "This would be good to sleep under.", + "ids": "1311" + }, { "examine": "A posh door.", "ids": "13118,13119,13120,13121,13122,13123,13124,13125" }, + { + "examine": "An essential for tropical islands.", + "ids": "1312" + }, { "examine": "A place to train unarmed combat with your friends.", "ids": "13126,13127,13128,13129,13130,13131,13132" }, + { + "examine": "An unusual tree grows here.", + "ids": "1313,1314" + }, { "examine": "A place to train weapon skills with your friends.", "ids": "13133,13134,13135,13136" @@ -17995,6 +4595,10 @@ "examine": "Great for sleeping in.", "ids": "13148,13149,13150,13151,13152,13153,13154" }, + { + "examine": "A hardy evergreen tree.", + "ids": "1315,1316" + }, { "examine": "A place to keep your shoes.", "ids": "13155" @@ -18019,6 +4623,10 @@ "examine": "No little mouse to be seen.", "ids": "13169,13170,13171" }, + { + "examine": "A young sentient tree.", + "ids": "1317" + }, { "examine": "The holy symbol of the god of light.", "ids": "13172" @@ -18051,6 +4659,10 @@ "examine": "An oak altar with a symbol of Saradomin.", "ids": "13179" }, + { + "examine": "This would make good firewood.", + "ids": "1318,1319" + }, { "examine": "An oak altar with a symbol of Zamorak.", "ids": "13180" @@ -18131,6 +4743,14 @@ "examine": "A gilded marble altar with a symbol of Guthix.", "ids": "13199" }, + { + "examine": "An escape route.", + "ids": "132" + }, + { + "examine": "The branch is infested with moss.", + "ids": "1320,1321" + }, { "examine": "The smoke goes up to the gods.", "ids": "13200,13201,13202,13203,13204,13205,13206,13207,13208,13209,13210,13211,13212,13213" @@ -18159,6 +4779,10 @@ "examine": "It fills the room with coloured light.", "ids": "13219" }, + { + "examine": "The trunk of this tree is hollow.", + "ids": "1322" + }, { "examine": "What nice shapes you can make out of glass!", "ids": "13220" @@ -18199,6 +4823,10 @@ "examine": "What nice shapes you can make out of glass!", "ids": "13229" }, + { + "examine": "A gnarly old tree root.", + "ids": "1323" + }, { "examine": "It fills the room with coloured light.", "ids": "13230" @@ -18239,6 +4867,10 @@ "examine": "It fills the room with coloured light.", "ids": "13239" }, + { + "examine": "The roots of a tree are exposed.", + "ids": "1324" + }, { "examine": "What nice shapes you can make out of glass!", "ids": "13240" @@ -18279,6 +4911,10 @@ "examine": "What nice shapes you can make out of glass!", "ids": "13249" }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "1325" + }, { "examine": "It fills the room with coloured light.", "ids": "13250" @@ -18319,6 +4955,10 @@ "examine": "It fills the room with coloured light.", "ids": "13259" }, + { + "examine": "A terribly tall tropical tree.", + "ids": "1326,1327,1328" + }, { "examine": "What nice shapes you can make out of glass!", "ids": "13260" @@ -18427,6 +5067,10 @@ "examine": "There must surely be treasure in a chest this expensive!", "ids": "13289,13290" }, + { + "examine": "Leaves of a bamboo tree.", + "ids": "1329" + }, { "examine": "Could the magic be guarding some treasure?", "ids": "13291,13292" @@ -18459,6 +5103,14 @@ "examine": "Marble and mahogany, ooh.", "ids": "13299" }, + { + "examine": "Looks spooky down there.", + "ids": "133" + }, + { + "examine": "It's thick with snow.", + "ids": "1330,1331,1332" + }, { "examine": "A basic wooden dining bench.", "ids": "13300" @@ -18531,6 +5183,10 @@ "examine": "A teak ladder.", "ids": "13329" }, + { + "examine": "This old tree has been taken over by parasitic plants.", + "ids": "1333" + }, { "examine": "A mahogany ladder.", "ids": "13330" @@ -18551,6 +5207,10 @@ "examine": "Invisible?", "ids": "13338,13339,13340" }, + { + "examine": "This old tree is rotting away.", + "ids": "1334,1335,1336,1337,1338" + }, { "examine": "Even a dungeon needs light!", "ids": "13341,13342,13343" @@ -18599,6 +5259,10 @@ "examine": "A pet Troll!", "ids": "13369" }, + { + "examine": "Steps*Leads to the surface.", + "ids": "1337" + }, { "examine": "No spider could get that big! It's unrealistic!", "ids": "13370" @@ -18675,6 +5339,10 @@ "examine": "Is there a prize inside?", "ids": "13389" }, + { + "examine": "They're just waiting to trip someone up.", + "ids": "1339,1340" + }, { "examine": "A private jester.", "ids": "13390,13391" @@ -18695,6 +5363,10 @@ "examine": "Someone hooped it!", "ids": "13399" }, + { + "examine": "A large double door.", + "ids": "134,135,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,4423,4424,4425,4426,4427,4428,4429,4430,4629,4630,4631,4632,4633,4634,4635,4963,4964,12349,12350,12446,12447,12448,12449,13094,13095,13096,13097,13098,13099,31814,31815,31816" + }, { "examine": "More humane than using an actual bull's eye.", "ids": "13400,13401" @@ -18728,7 +5400,11 @@ "ids": "13409,13410" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "This tree has been cut down.", + "ids": "1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359" + }, + { + "examine": "One of the most common trees in 2009Scape.", "ids": "13411,13412" }, { @@ -18996,11 +5672,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" }, { @@ -19127,6 +5803,14 @@ "examine": "A good source of books!", "ids": "13597,13598,13599" }, + { + "examine": "The door is closed.", + "ids": "136" + }, + { + "examine": "This tree contains dangerous insects no doubt.", + "ids": "1360,1361,1362" + }, { "examine": "A good source of scrolls!", "ids": "13600,13601,13602" @@ -19168,87 +5852,91 @@ "ids": "13614" }, { - "examine": "A gateway to Varrock", + "examine": "A gateway to Varrock.", "ids": "13615" }, { - "examine": "A gateway to Lumbridge", + "examine": "A gateway to Lumbridge.", "ids": "13616" }, { - "examine": "A gateway to Falador", + "examine": "A gateway to Falador.", "ids": "13617" }, { - "examine": "A gateway to Camelot", + "examine": "A gateway to Camelot.", "ids": "13618" }, { - "examine": "A gateway to Ardougne", + "examine": "A gateway to Ardougne.", "ids": "13619" }, { - "examine": "A gateway to the Yanille watchtower", + "examine": "A gateway to the Yanille watchtower.", "ids": "13620" }, { - "examine": "A gateway to Trollheim", + "examine": "A gateway to Kharyrll.", "ids": "13621" }, { - "examine": "A gateway to Varrock", + "examine": "A gateway to Varrock.", "ids": "13622" }, { - "examine": "A gateway to Lumbridge", + "examine": "A gateway to Lumbridge.", "ids": "13623" }, { - "examine": "A gateway to Falador", + "examine": "A gateway to Falador.", "ids": "13624" }, { - "examine": "A gateway to Camelot", + "examine": "A gateway to Camelot.", "ids": "13625" }, { - "examine": "A gateway to Ardougne", + "examine": "A gateway to Ardougne.", "ids": "13626" }, { - "examine": "A gateway to the Yanille watchtower", + "examine": "A gateway to the Yanille watchtower.", "ids": "13627" }, { - "examine": "A gateway to Trollheim", + "examine": "A gateway to Kharyrll.", "ids": "13628" }, { - "examine": "A gateway to Varrock", + "examine": "A gateway to Varrock.", "ids": "13629" }, { - "examine": "A gateway to Lumbridge", + "examine": "They're just waiting to trip someone up.", + "ids": "1363,1364" + }, + { + "examine": "A gateway to Lumbridge.", "ids": "13630" }, { - "examine": "A gateway to Falador", + "examine": "A gateway to Falador.", "ids": "13631" }, { - "examine": "A gateway to Camelot", + "examine": "A gateway to Camelot.", "ids": "13632" }, { - "examine": "A gateway to Ardougne", + "examine": "A gateway to Ardougne.", "ids": "13633" }, { - "examine": "A gateway to the Yanille watchtower", + "examine": "A gateway to the Yanille watchtower.", "ids": "13634" }, { - "examine": "A gateway to Trollheim", + "examine": "A gateway to Kharyrll.", "ids": "13635" }, { @@ -19279,6 +5967,10 @@ "examine": "A wooden planet of your very own.", "ids": "13649,13650,13651,13652,13653" }, + { + "examine": "It's only useful for firewood now.", + "ids": "1365" + }, { "examine": "A wooden solar system of your very own.", "ids": "13654,13655" @@ -19291,6 +5983,10 @@ "examine": "Use an elemental staff on it.", "ids": "13659,13660,13661" }, + { + "examine": "A gnarly old tree root.", + "ids": "1366" + }, { "examine": "If there's a recipe for the philosopher's stone there, you can't see it.", "ids": "13662" @@ -19307,6 +6003,10 @@ "examine": "Sit here and rule all you survey.", "ids": "13665,13666,13667,13668,13669,13670,13671" }, + { + "examine": "The roots of this tree are exposed.", + "ids": "1367" + }, { "examine": "Pull this to activate your machinery of doom!", "ids": "13672,13673,13674" @@ -19331,6 +6031,10 @@ "examine": "Go through this teak trapdoor to see the pit below your throne room.", "ids": "13679" }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "1368" + }, { "examine": "Go through this mahogany trapdoor to see the pit below your throne room.", "ids": "13680" @@ -19347,6 +6051,10 @@ "examine": "What could happen on this spot?", "ids": "13684,13685,13686,13687,13688,13689" }, + { + "examine": "An unusual tree grows here.", + "ids": "1369" + }, { "examine": "An elengant cloth hanging.", "ids": "13690" @@ -19387,6 +6095,10 @@ "examine": "Hammer, chisel, saw and shears.", "ids": "13699" }, + { + "examine": "This old tree is rotting away.", + "ids": "1370" + }, { "examine": "Bucket, spade, tinderbox and knife.", "ids": "13700" @@ -19411,6 +6123,10 @@ "examine": "You can do delicate crafting here.", "ids": "13709,13710,13711,13712" }, + { + "examine": "This tree contains dangerous insects no doubt.", + "ids": "1371,1372,1373,1374,1375,1376,1377" + }, { "examine": "You can repair broken staffs and arrows here.", "ids": "13713" @@ -19635,6 +6351,10 @@ "examine": "A shield with a picture of a skull.", "ids": "13779" }, + { + "examine": "This old tree is rotting away.", + "ids": "1378,1379" + }, { "examine": "A shield with the symbol of Varrock.", "ids": "13780" @@ -19715,6 +6435,10 @@ "examine": "The symbol of Asgarnia.", "ids": "13799" }, + { + "examine": "A gnarly old tree root.", + "ids": "1380" + }, { "examine": "The symbol of the Dorgeshuun.", "ids": "13800" @@ -19755,6 +6479,10 @@ "examine": "A picture of a money bag.", "ids": "13809" }, + { + "examine": "The roots of this tree are exposed.", + "ids": "1381" + }, { "examine": "The symbol of Saradomin.", "ids": "13810" @@ -19795,6 +6523,10 @@ "examine": "The symbol of Guthix.", "ids": "13819" }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "1382" + }, { "examine": "The symbol of the HAM cult.", "ids": "13820" @@ -19835,6 +6567,10 @@ "examine": "The symbol of Zamorak.", "ids": "13829" }, + { + "examine": "This tree has long been dead.", + "ids": "1383" + }, { "examine": "This should have resolved!", "ids": "13830" @@ -19867,6 +6603,10 @@ "examine": "Gloopy, sticky, muddy bog...", "ids": "13838,13839" }, + { + "examine": "It's only useful for firewood now.", + "ids": "1384" + }, { "examine": "A bush made up of long slender branches.", "ids": "13840" @@ -19903,6 +6643,14 @@ "examine": "Small swamp tree.", "ids": "13848,13849,13850,13851,13852,13853,13854,13855,13856,13857,13858,13859,13860,13861,13862,13863" }, + { + "examine": "A gnarly old tree root.", + "ids": "1385" + }, + { + "examine": "The roots of this tree are exposed.", + "ids": "1386" + }, { "examine": "It's our transport across the swamp.", "ids": "13864,13865" @@ -19911,6 +6659,10 @@ "examine": "A path leading out of this swampy dead end.", "ids": "13866,13867,13868,13869,13870" }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "1387" + }, { "examine": "A path leading out of this swampy dead end, you'll be running away if you go down here.", "ids": "13871" @@ -19931,6 +6683,10 @@ "examine": "Get me out of here!", "ids": "13879,13880" }, + { + "examine": "They're just waiting to trip someone up.", + "ids": "1388,1389" + }, { "examine": "Very hot!", "ids": "13881" @@ -19947,6 +6703,10 @@ "examine": "There's no way I'm going in there!", "ids": "13899" }, + { + "examine": "Commonly found in these parts.", + "ids": "1390" + }, { "examine": "Doesn't look so scary now.", "ids": "13900" @@ -19959,14 +6719,26 @@ "examine": "The exit to the surface.", "ids": "13904,13905,13906,13907,13908,13909,13910,13911,13912" }, + { + "examine": "A leafy plant.", + "ids": "1391" + }, { "examine": "Where does it lead?", "ids": "13913,13914,13915,13916,13917,13918,13919,13920,13921,13922,13923,13924" }, + { + "examine": "A leafy fern.", + "ids": "1392" + }, { "examine": "What's that?", "ids": "13925,13926,13927,13928,13929,13930,13931" }, + { + "examine": "A small bushy plant.", + "ids": "1393" + }, { "examine": "I think it's the way out.", "ids": "13932" @@ -19979,10 +6751,26 @@ "examine": "Does that really exist?", "ids": "13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951,13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,13964,13965,13966" }, + { + "examine": "A leafy shrub.", + "ids": "1394" + }, + { + "examine": "No flies on me.", + "ids": "1395" + }, + { + "examine": "How do these things manage to grow?", + "ids": "1396" + }, { "examine": "Should I go down there?", "ids": "13967,13968,13969" }, + { + "examine": "This cactus has recently had a chunk taken off.", + "ids": "1397" + }, { "examine": "I can use that rope now.", "ids": "13970" @@ -19991,6 +6779,14 @@ "examine": "Should I go down there?", "ids": "13971,13972,13973,13974,13975,13976,13977,13978,13979,13980,13981,13982,13983,13984,13985,13986,13987,13988,13989,13990,13991,13992,13993" }, + { + "examine": "A leafy fern.", + "ids": "1398" + }, + { + "examine": "This has broad leaves.", + "ids": "1399" + }, { "examine": "Oooo, sharp, pointy things!", "ids": "13994,13995,13996,13997,13998" @@ -19999,6 +6795,14 @@ "examine": "This will take me back to the surface.", "ids": "13999,14000,14001" }, + { + "examine": "Intended to keep the goblins away from the mines.", + "ids": "14,15,16,17,18,19,20" + }, + { + "examine": "A large waxy jungle plant.", + "ids": "1400" + }, { "examine": "A sign warning of danger.", "ids": "14002" @@ -20007,6 +6811,150 @@ "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": "A type of jungle fern.", + "ids": "1401" + }, + { + "examine": "A large waxy jungle plant.", + "ids": "1402" + }, + { + "examine": "How do these things manage to grow?", + "ids": "1403,1404,1405,1406" + }, + { + "examine": "Leaves a nasty mark.", + "ids": "1407" + }, + { + "examine": "Looks spiky.", + "ids": "1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503" + }, + { + "examine": "The wood burning here must be full of salt.", + "ids": "14169" + }, + { + "examine": "Doesn't look like it'll be sailing again.", + "ids": "14173" + }, + { + "examine": "Looks a bit damp.", + "ids": "14174" + }, + { + "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": "Rough but adequate.", + "ids": "14918" + }, + { + "examine": "A wooden crate.", + "ids": "15030" + }, + { + "examine": "A wooden crate.", + "ids": "15031" + }, + { + "examine": "Some wooden crates.", + "ids": "15032" + }, + { + "examine": "The door is open.", + "ids": "1504" + }, + { + "examine": "The door is closed.", + "ids": "1505" + }, + { + "examine": "A sturdy wooden door.", + "ids": "1506,1507,1508,1509,1510,1511,1512,1513,1514,1515" + }, + { + "examine": "Smelly.", + "ids": "152" + }, + { + "examine": "The curtain is closed.", + "ids": "1526,1527,1528" + }, + { + "examine": "The curtain is open.", + "ids": "1529" + }, + { + "examine": "An ornate fountain.", + "ids": "153,36781" + }, + { + "examine": "The door is closed.", + "ids": "1530" + }, + { + "examine": "The door is open.", + "ids": "1531,1532" + }, + { + "examine": "A nicely fitted door.", + "ids": "1533,1534,1535" + }, + { + "examine": "An ornately-fashioned door.", + "ids": "1536" + }, + { + "examine": "An elf-fashioned door.", + "ids": "1537,1538" + }, + { + "examine": "Solid bars of iron.", + "ids": "1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550" + }, + { + "examine": "A pipe I can squeeze through.", + "ids": "154,2287,2288,2290,2291,2292,2293,3236,3237,4058,5099,5100,5101,5102,5152,5153,5154,5155,9293,9295,10063,10064,10065,10066,10067,10068,10069,10070,10071,10072,10073,10074,10075,29370" + }, { "examine": "Home sweet home?", "ids": "15477" @@ -20015,26 +6963,790 @@ "examine": "Home sweet home?", "ids": "15480" }, + { + "examine": "Lots of books.", + "ids": "155,156,157" + }, + { + "examine": "Baby bread.", + "ids": "15506,15507" + }, + { + "examine": "A wooden gate.", + "ids": "1551,1552,1553,1554,1555,1556" + }, + { + "examine": "Probably looked better mounted on the dragon's neck.", + "ids": "15564" + }, + { + "examine": "A wrought iron gate.", + "ids": "1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567" + }, + { + "examine": "The body of a Dwarf savaged by Goblins.", + "ids": "15596" + }, + { + "examine": "I wonder what's under it?", + "ids": "1568,1569" + }, + { + "examine": "I wonder what's down there?", + "ids": "1570,1571" + }, + { + "examine": "This door has been boarded up.", + "ids": "1572" + }, + { + "examine": "A door to a grand place.", + "ids": "1573,1574,1575,1576" + }, { "examine": "Home sweet home?", "ids": "15748" }, + { + "examine": "Looks like part of the entrance is blocked by rubble.", + "ids": "15767" + }, + { + "examine": "The top of the archway.", + "ids": "1577,1578,1579" + }, + { + "examine": "This wall looks odd...", + "ids": "158,159" + }, + { + "examine": "The base of the archway.", + "ids": "1580,1581,1582" + }, + { + "examine": "Cave exit.", + "ids": "15812" + }, + { + "examine": "There is something strange about this wall...", + "ids": "1583,1584,1585,1586,1587,1588" + }, + { + "examine": "A wrought iron gate.", + "ids": "1589,1590" + }, + { + "examine": "The door is closed.", + "ids": "1591" + }, + { + "examine": "Something is strange about this panel.", + "ids": "1592,1593" + }, + { + "examine": "An entranceway into the dungeon.", + "ids": "1594" + }, + { + "examine": "The way in.", + "ids": "1595" + }, + { + "examine": "A wrought iron gate.", + "ids": "1596,1597" + }, + { + "examine": "A wooden gate.", + "ids": "1598,1599" + }, + { + "examine": "I wonder what this does...", + "ids": "160" + }, + { + "examine": "The doors to the Magic Guild.", + "ids": "1600" + }, + { + "examine": "The doors to the Magic guild.", + "ids": "1601,1602,1603,1604,1605,1606,1607,1608" + }, + { + "examine": "Danger - Possibly deadly creatures below!", + "ids": "16083,16151,16117" + }, + { + "examine": "A wooden structure used in the process of building.", + "ids": "1609,1610,1611,1612" + }, + { + "examine": "This timber is broken", + "ids": "1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660" + }, + { + "examine": "I'm not touching that.", + "ids": "162,163,164" + }, + { + "examine": "A special furnace for destroying contaminated items.", + "ids": "165" + }, + { + "examine": "A wooden gate.", + "ids": "166,167" + }, + { + "examine": "A pile of bricks.", + "ids": "1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721" + }, + { + "examine": "I wonder what's inside.", + "ids": "170" + }, + { + "examine": "Perhaps I should search it.", + "ids": "171" + }, + { + "examine": "I wonder what's inside.", + "ids": "172" + }, + { + "examine": "I can climb these stairs.", + "ids": "1722,36791" + }, + { + "examine": "They go down.", + "ids": "1723" + }, + { + "examine": "I can climb down these stairs.", + "ids": "1724" + }, + { + "examine": "I can climb up this.", + "ids": "1725,36768,36771" + }, + { + "examine": "I can climb down this.", + "ids": "1726,1727,1728" + }, + { + "examine": "I can climb this.", + "ids": "1729,36347,36363,36644" + }, + { + "examine": "A sealed barrel with a warning sign on it.", + "ids": "17296" + }, + { + "examine": "A barrel with a warning sign on it.", + "ids": "17297" + }, + { + "examine": "Perhaps I should search it.", + "ids": "173" + }, + { + "examine": "Dare I go up?", + "ids": "1730" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "17308,31136,36798" + }, + { + "examine": "These stairs look spooky!", + "ids": "1731,1732" + }, + { + "examine": "I can use these stairs to climb down.", + "ids": "1733" + }, + { + "examine": "I can climb these stairs.", + "ids": "1734" + }, + { + "examine": "A rickety old staircase.", + "ids": "1735" + }, + { + "examine": "I can climb down these stairs.", + "ids": "1736" + }, + { + "examine": "Large urn.", + "ids": "17362" + }, + { + "examine": "I can climb up these stairs.", + "ids": "1737,1738" + }, + { + "examine": "I can climb up or go down these stairs.", + "ids": "1739" + }, + { + "examine": "I can climb down these stairs.", + "ids": "1740,1741" + }, + { + "examine": "I can climb up these stairs.", + "ids": "1742" + }, + { + "examine": "I can climb up or go down these stairs.", + "ids": "1743" + }, + { + "examine": "I can climb down these stairs.", + "ids": "1744,1745" + }, + { + "examine": "I can climb down this.", + "ids": "1746" + }, + { + "examine": "Where people may put themselves.", + "ids": "17462" + }, + { + "examine": "I can climb this.", + "ids": "1747,1748" + }, + { + "examine": "I can climb down this.", + "ids": "1749" + }, + { + "examine": "I can climb this.", + "ids": "1750,1751" + }, + { + "examine": "I can't climb this.", + "ids": "1752" + }, + { + "examine": "It's useless.", + "ids": "1753" + }, + { + "examine": "I can climb down this.", + "ids": "1754" + }, + { + "examine": "I can climb this.", + "ids": "1755" + }, + { + "examine": "I can climb down this.", + "ids": "1756" + }, + { + "examine": "I can climb this.", + "ids": "1757" + }, + { + "examine": "Not much use really.", + "ids": "1758" + }, + { + "examine": "This leads downwards.", + "ids": "1759" + }, + { + "examine": "These can be useful.", + "ids": "1760" + }, + { + "examine": "Steps made from natural rock.", + "ids": "1761" + }, + { + "examine": "A rope hangs here.", + "ids": "1762,1763,1764" + }, + { + "examine": "I can climb down this.", + "ids": "1765" + }, + { + "examine": "I can climb this.", + "ids": "1766" + }, + { + "examine": "I can climb down this.", + "ids": "1767" + }, + { + "examine": "I can climb this.", + "ids": "1768,1769,1770,1771,1772,1773,1774,1775,1776,1777" + }, + { + "examine": "These huge stone discs grind grain to make flour.", + "ids": "1778" + }, + { + "examine": "These sails use the wind to drive the windmill.", + "ids": "1779,1780,1781" + }, + { + "examine": "I'll need an empty pot so I can collect my flour.", + "ids": "1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796" + }, + { + "examine": "Some very hard rocks, I wouldn't want to fall on those.", + "ids": "1797,1798,1799,1800,1801,1802,1803" + }, + { + "examine": "This door requires a key.", + "ids": "1804" + }, + { + "examine": "The door to the Champions' Guild.", + "ids": "1805,1806,1807,1808,1809" + }, + { + "examine": "Here be spiders!", + "ids": "1810" + }, + { + "examine": "I can walk through this tattered web.", + "ids": "1811" + }, + { + "examine": "A magical border of teleportation.", + "ids": "1812" + }, + { + "examine": "For putting things on.", + "ids": "1813" + }, + { + "examine": "I wonder what this does...", + "ids": "1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851" + }, { "examine": "A large fish.", "ids": "18482" }, + { + "examine": "A wooden crate for storage.", + "ids": "18506" + }, + { + "examine": "A pile of crates for storage.", + "ids": "18508" + }, + { + "examine": "This lets in more light.", + "ids": "1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863" + }, + { + "examine": "These obviously mean keep out!", + "ids": "1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875" + }, + { + "examine": "Fly Gnome Air.", + "ids": "187" + }, + { + "examine": "I shudder to think what has taken place here.", + "ids": "1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,32289" + }, + { + "examine": "Probably pilot error.", + "ids": "188" + }, + { + "examine": "Popular on sandy beaches where fruity cocktails may be found.", + "ids": "18856" + }, + { + "examine": "A sign on a cactus.", + "ids": "18876" + }, + { + "examine": "The Blurberry bar", + "ids": "189" + }, + { + "examine": "This hasn't seen a duster for a while.", + "ids": "1896" + }, + { + "examine": "Not as nice as some.", + "ids": "1897" + }, + { + "examine": "This needs dusting before I'll sit on it.", + "ids": "1898,1899" + }, + { + "examine": "A large gate in the gnome style.", + "ids": "190" + }, + { + "examine": "This could do with a bit of a spit and polish.", + "ids": "1900" + }, + { + "examine": "This clearly isn't used for dining very often.", + "ids": "1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938" + }, + { + "examine": "A tower.", + "ids": "1939,1940,1941,1942,1943,1944,1945,1946" + }, + { + "examine": "The entrance to the cave.", + "ids": "194" + }, + { + "examine": "It looks like I might be able to climb over this piece of the wall...", + "ids": "1947,1948" + }, + { + "examine": "A pile of bricks.", + "ids": "1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966" + }, + { + "examine": "I can climb this.", + "ids": "195" + }, + { + "examine": "A grand door for a grand tree.", + "ids": "1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984" + }, + { + "examine": "A large root.", + "ids": "1985,1986" + }, + { + "examine": "A primitive looking boat.", + "ids": "1987" + }, + { + "examine": "Oops!", + "ids": "1988" + }, + { + "examine": "A good source of books!", + "ids": "1989" + }, + { + "examine": "A wooden crate for storage.", + "ids": "1990" + }, + { + "examine": "Solid bars of iron.", + "ids": "1991" + }, + { + "examine": "There is an indent the size of a pebble in the stone's centre.", + "ids": "1992" + }, + { + "examine": "A stone tomb surrounded by flowers.", + "ids": "1993" + }, + { + "examine": "I wonder what's inside.", + "ids": "1994,37016,37015" + }, + { + "examine": "Perhaps I should search it.", + "ids": "1995" + }, + { + "examine": "I should be safe if I can reach this.", + "ids": "1996,1997" + }, + { + "examine": "Wet rope.", + "ids": "1998" + }, + { + "examine": "A wooden crate for storage.", + "ids": "1999" + }, + { + "examine": "Dead and half-buried.", + "ids": "19996" + }, + { + "examine": "Dead animal head. Lovely.", + "ids": "19997" + }, + { + "examine": "Looks and smells like a place where goblins live.", + "ids": "2" + }, + { + "examine": "A quirky gnome lamp.", + "ids": "200" + }, + { + "examine": "An elf-fashioned door.", + "ids": "2000,2001,2002,2003" + }, + { + "examine": "The pillar has a rune shaped dent in it.", + "ids": "2004" + }, + { + "examine": "A statue of Baxtorian chiselled in stone.", + "ids": "2005" + }, + { + "examine": "A statue of Glarial chiselled in stone.", + "ids": "2006,2007,2008,2009" + }, + { + "examine": "These insects love this light.", + "ids": "201" + }, + { + "examine": "A ledge.", + "ids": "2010,2011,2012" + }, + { + "examine": "Known commonly as Red worm vine.", + "ids": "2013" + }, + { + "examine": "A magically elevated chalice full of tears.", + "ids": "2014" + }, + { + "examine": "A magically elevated chalice full of ash.", + "ids": "2015,2016,2017,2018" + }, + { + "examine": "A whirlpool.", + "ids": "2019" + }, + { + "examine": "It's amazing what bees produce!", + "ids": "202" + }, + { + "examine": "It's too wet for firewood.", + "ids": "2020,2021" + }, + { + "examine": "A wooden barrel, maybe a way off this rock.", + "ids": "2022" + }, + { + "examine": "An interesting tree with long straight branches.", + "ids": "2023" + }, + { + "examine": "A large bubbling cauldron, smells... Ugh!", + "ids": "2024" + }, + { + "examine": "The door is closed.", + "ids": "2025" + }, + { + "examine": "I can see fish swimming in the water.", + "ids": "2026,2027,2028,2029,2030,2031" + }, + { + "examine": "Posh candlesticks.", + "ids": "203,204,32287,40074" + }, + { + "examine": "It's closed.", + "ids": "2032" + }, + { + "examine": "It's open.", + "ids": "2033" + }, + { + "examine": "It's closed.", + "ids": "2034" + }, + { + "examine": "It's open.", + "ids": "2035" + }, + { + "examine": "It's closed.", + "ids": "2036" + }, + { + "examine": "It's open.", + "ids": "2037" + }, + { + "examine": "I've met less intelligent conversationalists.", + "ids": "2038" + }, + { + "examine": "It's closed.", + "ids": "2039,2040,2041,2042" + }, + { + "examine": "Something's bubbling away in it.", + "ids": "2043" + }, + { + "examine": "Like an animal dinner table.", + "ids": "2044,2045,2046,2047" + }, + { + "examine": "It's a door.", + "ids": "2048,2049" + }, + { + "examine": "A wooden gate.", + "ids": "2050,2051,2052,2053" + }, + { + "examine": "It's a door.", + "ids": "2054,2055" + }, + { + "examine": "It's closed.", + "ids": "2056" + }, + { + "examine": "It's open.", + "ids": "2057" + }, + { + "examine": "It's closed.", + "ids": "2058,2059,2060,2061,2062" + }, + { + "examine": "It's open.", + "ids": "2063" + }, + { + "examine": "For storage.", + "ids": "2064" + }, + { + "examine": "Used for climbing.", + "ids": "2065" + }, + { + "examine": "A wall.", + "ids": "2066" + }, + { + "examine": "A watchtower.", + "ids": "2067" + }, + { + "examine": "A fence.", + "ids": "2068" + }, + { + "examine": "A closed door.", + "ids": "2069,2070" + }, + { + "examine": "Used for storage.", + "ids": "2071,2072" + }, + { + "examine": "Grows weird yellow fruit.", + "ids": "2073,2074,2075,2076,2077,2078" + }, + { + "examine": "I wonder what's inside?", + "ids": "2079,2080" + }, + { + "examine": "Scary lighting apparatus.", + "ids": "208" + }, + { + "examine": "Handy for boarding boats.", + "ids": "2081,2082,2083,2084,2085,2086,2087,2088" + }, + { + "examine": "Ornamental.", + "ids": "2089" + }, + { + "examine": "An ornamental lighting fixture.", + "ids": "209" + }, + { + "examine": "A rocky outcrop.", + "ids": "2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111" + }, + { + "examine": "Its eyes stare off into the distance...", + "ids": "21" + }, + { + "examine": "A mysterious glowing ice crystal.", + "ids": "210" + }, + { + "examine": "It's amazing what bees produce!", + "ids": "211" + }, + { + "examine": "The door is closed.", + "ids": "2112" + }, + { + "examine": "I can climb down this.", + "ids": "2113" + }, + { + "examine": "This transports coal!", + "ids": "2114" + }, + { + "examine": "The left hand side of the gate.", + "ids": "2115" + }, + { + "examine": "The right hand side of the gate.", + "ids": "2116" + }, + { + "examine": "It's an old wall.", + "ids": "2117" + }, + { + "examine": "Sturdy looking.", + "ids": "21176" + }, + { + "examine": "Lots of water swirling around", + "ids": "2118" + }, + { + "examine": "A rocky outcrop.", + "ids": "2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140" + }, + { + "examine": "Useful for making ships move.", + "ids": "212,218" + }, { "examine": "Might be worth opening?", "ids": "21299" }, - { - "examine": "A sturdy metal door.", - "ids": "21400,21402,21399" - }, { "examine": "I can climb this.", "ids": "21395,29355" }, + { + "examine": "A sturdy metal door.", + "ids": "21400,21402,21399" + }, { "examine": "A closed, sturdy metal door.", "ids": "21403,21405" @@ -20043,6 +7755,18 @@ "examine": "An open, sturdy metal door.", "ids": "21404" }, + { + "examine": "An open chest.", + "ids": "2141" + }, + { + "examine": "An eerie glow permeates the cauldron.", + "ids": "2142" + }, + { + "examine": "Looks secure.", + "ids": "2143,2144" + }, { "examine": "A display case, full of axes.", "ids": "21437" @@ -20079,6 +7803,10 @@ "examine": "A shelf, displaying armour.", "ids": "21447" }, + { + "examine": "Ooooh! Spooky!", + "ids": "2145,2146" + }, { "examine": "I can climb down these stairs.", "ids": "21455" @@ -20091,6 +7819,34 @@ "examine": "A stone torch.", "ids": "21465" }, + { + "examine": "Going down?", + "ids": "2147" + }, + { + "examine": "Going up?", + "ids": "2148" + }, + { + "examine": "An imposing stone structure.", + "ids": "2149" + }, + { + "examine": "A column of elemental power.", + "ids": "2150,2151,2152,2153,29415" + }, + { + "examine": "A wrought iron gate.", + "ids": "2154,2155" + }, + { + "examine": "A magical portal of teleportation.", + "ids": "2156,2157,2158" + }, + { + "examine": "It's a floating barrel.", + "ids": "2159,2160,2161" + }, { "examine": "A chair made from stone.", "ids": "21607" @@ -20099,6 +7855,10 @@ "examine": "A table with swords on it.", "ids": "21608" }, + { + "examine": "A table with plates and mugs on it.", + "ids": "21609,21610" + }, { "examine": "A bed to sleep in.", "ids": "21611" @@ -20107,205 +7867,1325 @@ "examine": "A stone crate with some stuff in it.", "ids": "21612" }, - { - "examine": "A table with plates and mugs on it.", - "ids": "21609,21610" - }, { "examine": "A shelf with some cups on it.", "ids": "21614,21615" }, + { + "examine": "Smells like fish.", + "ids": "2162,2163,2164,2165,2166" + }, { "examine": "A Fremennik flag.", "ids": "21637" }, + { + "examine": "Water is pouring in through the hole.", + "ids": "2167" + }, + { + "examine": "A patch of swamp tar has bunged up the hole.", + "ids": "2168" + }, + { + "examine": "There's a strong wind whipping up the sea.", + "ids": "2169,2170,2171" + }, + { + "examine": "A huge net to catch little fish.", + "ids": "2172,2173" + }, + { + "examine": "The upper deck can be accessed with this ladder.", + "ids": "2174" + }, + { + "examine": "Back down to the main deck.", + "ids": "2175" + }, + { + "examine": "This old trawler has seen better days.", + "ids": "2176,2177" + }, + { + "examine": "Handy for boarding boats.", + "ids": "2178,2179" + }, + { + "examine": "It's a war machine.", + "ids": "2180,2181" + }, + { + "examine": "It can't hurt to just have a look, can it?", + "ids": "2182" + }, + { + "examine": "I wonder what is inside...", + "ids": "2183" + }, { "examine": "A short longboat!", "ids": "21834" }, { - "examine": "Contains traces of summoning energy.", - "ids": "29939,29943,29944,29945,29947,29951,29952,29953,29954" + "examine": "The door is closed.", + "ids": "2184" }, { - "examine": "I wonder what this spooky contains.", - "ids": "37051" + "examine": "This wall has seen better days.", + "ids": "2185" }, { - "examine": "It really was this big!", - "ids": "40043" + "examine": "Hmmm... I wonder if it's loose enough to get through.", + "ids": "2186" }, { - "examine": "It really was this big!", - "ids": "40042" + "examine": "I can climb down this ladder.", + "ids": "2187" }, { - "examine": "A wooden barrel for storage.", - "ids": "40018" + "examine": "I can climb up this ladder.", + "ids": "2188,2189" }, { - "examine": "A wooden barrel containing lots of fish.", - "ids": "40019" + "examine": "I can climb down this ladder.", + "ids": "2190" }, { - "examine": "A nice sturdy looking table.", - "ids": "40044" + "examine": "I wonder what's inside?", + "ids": "2191,2192,2193" }, { - "examine": "A wooden crate.", - "ids": "40021" + "examine": "Perhaps I should search it.", + "ids": "2194,2195,2196" }, { - "examine": "Some wooden crates.", - "ids": "40022" + "examine": "I wonder what's inside?", + "ids": "2197" }, { - "examine": "A large potted plant.", - "ids": "40111" + "examine": "Perhaps I should search it.", + "ids": "2198" }, { - "examine": "Tick-tock, it's a clock.", - "ids": "40029" + "examine": "An old gate for locking up undesirables.", + "ids": "2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209" }, { - "examine": "I wonder what's inside.", - "ids": "29400,32010,40093" + "examine": "The door is shut.", + "ids": "22" }, { - "examine": "Useful for putting things on.", - "ids": "40031" + "examine": "This figure brings luck to those who sail.", + "ids": "221,222" }, { - "examine": "Items are for sale here.", - "ids": "40028" + "examine": "Looking through it makes far off things look closer!", + "ids": "2210" + }, + { + "examine": "A weathered old gravestone.", + "ids": "2211" + }, + { + "examine": "Looks like fun.", + "ids": "2212" + }, + { + "examine": "The bank teller will serve you from here.", + "ids": "2213" + }, + { + "examine": "This booth is for private customers only.", + "ids": "2214,34205" + }, + { + "examine": "This booth is closed.", + "ids": "2215" + }, + { + "examine": "This looks like it's being used to block entrance to the village.", + "ids": "2216" + }, + { + "examine": "Something seems to be buried under this.", + "ids": "2217" + }, + { + "examine": "A narrow fissure in the ground.", + "ids": "2218,2219" + }, + { + "examine": "A pile of rubble.", + "ids": "2220" + }, + { + "examine": "A stone with some carvings on it.", + "ids": "2221" + }, + { + "examine": "A pile of loose rocks.", + "ids": "2222" + }, + { + "examine": "Probably nothing useful in these.", + "ids": "2223" + }, + { + "examine": "A rotten and barely standing set of gallows.", + "ids": "2224" + }, + { + "examine": "Wet rocks, it looks like you might be able to climb them.", + "ids": "2225" + }, + { + "examine": "This table has seen better days.", + "ids": "2226" + }, + { + "examine": "A crudely constructed raft.", + "ids": "2227,2228" + }, + { + "examine": "A statue to mark Tai Bwo Wannai sacred grounds.", + "ids": "2229" + }, + { + "examine": "A sturdy cart for travelling in.", + "ids": "2230" + }, + { + "examine": "A rocky outcrop.", + "ids": "2231,2232,2233" + }, + { + "examine": "Rocks that have been stacked at regular intervals.", + "ids": "2234" + }, + { + "examine": "An ancient construct for supporting the bones of the deceased.", + "ids": "2235" + }, + { + "examine": "I can climb the rocky outcrop.", + "ids": "2236" + }, + { + "examine": "An exotic looking tree.", + "ids": "2237" + }, + { + "examine": "Large doors set into the hillside.", + "ids": "2238,2239" + }, + { + "examine": "These doors must have been hidden here for years.", + "ids": "2240,2241" + }, + { + "examine": "Perhaps you should give them a push.", + "ids": "2242,2243" + }, + { + "examine": "Commonly found in these parts.", + "ids": "2244" + }, + { + "examine": "A leafy fern.", + "ids": "2245" + }, + { + "examine": "Large carved tomb doors, they look terrifying.", + "ids": "2246,2247,2248,2249,2250,2251,2252" + }, + { + "examine": "Useful for making ships move.", + "ids": "225,226" + }, + { + "examine": "Perhaps you should give them a push.", + "ids": "2253,2254" + }, + { + "examine": "An ancient metal gate, but still pretty secure.", + "ids": "2255,2256" + }, + { + "examine": "A rocky outcrop.", + "ids": "2257" + }, + { + "examine": "An ancient construct for supporting the bones of the deceased.", + "ids": "2258" + }, + { + "examine": "The gate is closed.", + "ids": "2259,2260,2261,2262,2263,2264" + }, + { + "examine": "A sturdy cart for travelling in.", + "ids": "2265" + }, + { + "examine": "The door is closed.", + "ids": "2266,2267" + }, + { + "examine": "This leads up to the sleeping area.", + "ids": "2268" + }, + { + "examine": "This leads downwards.", + "ids": "2269" + }, + { + "examine": "Barnacle infested rope.", + "ids": "227" + }, + { + "examine": "A jungle plant.", + "ids": "2270" + }, + { + "examine": "I wonder what's inside?", + "ids": "2271" + }, + { + "examine": "It's open.", + "ids": "2272" + }, + { + "examine": "An odd-looking rock with a rope tied to it.", + "ids": "2273,2274" + }, + { + "examine": "An odd-looking rock formation.", + "ids": "2275,2276,2277,2278" + }, + { + "examine": "Holds the rope up. Hopefuly.", + "ids": "2279,2280,2281" + }, + { + "examine": "Use this to swing over crevices.", + "ids": "2282,2283" + }, + { + "examine": "This must be climbed over.", + "ids": "2284,2285,2286" + }, + { + "examine": "It's hollow...", + "ids": "2289" + }, + { + "examine": "A slippery log I can walk across.", + "ids": "2294,2295,2296,2297" + }, + { + "examine": "The irregular surface can be climbed.", + "ids": "2298,2299,2300" + }, + { + "examine": "Unusually lumpy looking.", + "ids": "23" + }, + { + "examine": "Tread carefully!", + "ids": "2301,2302" + }, + { + "examine": "I can just about edge across here.", + "ids": "2303" + }, + { + "examine": "Pointy!", + "ids": "2304,2305,2306" + }, + { + "examine": "A wrought iron gate.", + "ids": "2307,2308" + }, + { + "examine": "Solid bars of iron.", + "ids": "2309" + }, + { + "examine": "This tree has been cut down.", + "ids": "2310" + }, + { + "examine": "I can jump from this stepping stone.", + "ids": "2311" + }, + { + "examine": "I can balance on this rope.", + "ids": "2312" + }, + { + "examine": "I can climb up this.", + "ids": "2313" + }, + { + "examine": "I can climb down here.", + "ids": "2314,2315" + }, + { + "examine": "I can climb up these stairs.", + "ids": "2316,36776" + }, + { + "examine": "I can climb up these rocks to another cave.", + "ids": "2317" + }, + { + "examine": "I can climb down these rocks to another cave.", + "ids": "2318" + }, + { + "examine": "I can traverse these.", + "ids": "2319,2320,2321,12573,12574,12575,29375" + }, + { + "examine": "Use this to swing across gaps.", + "ids": "2322,2323,2324" + }, + { + "examine": "I can swing on this tree.", + "ids": "2325" + }, + { + "examine": "A branch protrudes here.", + "ids": "2326" + }, + { + "examine": "This tree has an unusually long branch on it.", + "ids": "2327" + }, + { + "examine": "Jump here.", + "ids": "23271" + }, + { + "examine": "A rocky outcrop.", + "ids": "2328,2329,2330,2331" + }, + { + "examine": "A nearly rotten wooden log that crosses the river.", + "ids": "2332" + }, + { + "examine": "A rocky outcrop from the running water.", + "ids": "2333,2334,2335" + }, + { + "examine": "You can see a cauldron directly below.", + "ids": "2336" + }, + { + "examine": "This door is very sturdy looking.", + "ids": "2337" + }, + { + "examine": "This door is closed.", + "ids": "2338" + }, + { + "examine": "This door is very sturdy looking.", + "ids": "2339,2340" + }, + { + "examine": "There is something strange about this wall...", + "ids": "2341" + }, + { + "examine": "Looks like a ventilation grill.", + "ids": "2342" + }, + { + "examine": "A large stone block.", + "ids": "2343,2344,2345,2346,2347,2348,2349" + }, + { + "examine": "Used to move earth to, and from, the dig shaft.", + "ids": "2350,2351" + }, + { + "examine": "This leads back up the dig shaft.", + "ids": "2352,2353" + }, + { + "examine": "Yep, they're sacks!", + "ids": "2354,2355,2356" + }, + { + "examine": "A leafy bush.", + "ids": "2357,2358" + }, + { + "examine": "A sealed barrel with a warning sign on it.", + "ids": "2359" + }, + { + "examine": "I wonder what's inside...", + "ids": "2360" + }, + { + "examine": "A sturdy chest to keep things in.", + "ids": "2361" + }, + { + "examine": "A large stone block.", + "ids": "2362" + }, + { + "examine": "A place where you can pan for gold.", + "ids": "2363" + }, + { + "examine": "A wooden container.", + "ids": "2364,2365" + }, + { + "examine": "A signpost!", + "ids": "2366,2367,2368,2369,2370,2371" + }, + { + "examine": "Shelves filled with interesting books.", + "ids": "2372" + }, + { + "examine": "A cupboard.", + "ids": "2373,2374" + }, + { + "examine": "Used to keep specimens on.", + "ids": "2375" + }, + { + "examine": "Soil. Dug up!", + "ids": "2376,2377,2378" + }, + { + "examine": "Technically a bed.", + "ids": "2379" + }, + { + "examine": "I wonder what's inside...", + "ids": "2380" + }, + { + "examine": "Swampy dirt.", + "ids": "2381,2382,2383,2384,2385,2386,2387,2388,2389,2390" + }, + { + "examine": "A very solid looking gate.", + "ids": "2391,2392" + }, + { + "examine": "'Pay back all your debts by borrowing more money from us!' Clever.", + "ids": "23924" + }, + { + "examine": "Contains quite a lot of someone else's property.", + "ids": "23925" + }, + { + "examine": "This chest looks strong.", + "ids": "23926" + }, + { + "examine": "A handy place for filling in forms and paperwork.", + "ids": "23928" + }, + { + "examine": "They say money can't buy happiness.", + "ids": "23929" + }, + { + "examine": "Aim for the goal!", + "ids": "2393" + }, + { + "examine": "A gilt sculpture of a monarch.", + "ids": "23930" + }, + { + "examine": "A gate to the Gnomeball pitch.", + "ids": "2394,2395" + }, + { + "examine": "Distinctly feminine.", + "ids": "2396" + }, + { + "examine": "A solid looking door.", + "ids": "2397,2398,2399" + }, + { + "examine": "The door is shut.", + "ids": "24" + }, + { + "examine": "Might be worth searching.", + "ids": "2400,2401" + }, + { + "examine": "A good source of books!", + "ids": "2402" + }, + { + "examine": "Might be worth searching.", + "ids": "2403,2404" }, { "examine": "I can climb this.", - "ids": "40026" + "ids": "2405" }, { - "examine": "Some wooden crates.", - "ids": "15032" + "examine": "The door is closed.", + "ids": "2406,2407" }, { - "examine": "A wooden crate.", - "ids": "15030" + "examine": "I can climb down this.", + "ids": "2408,36693,36694" }, { - "examine": "A wooden crate.", - "ids": "15031" + "examine": "A commonly found tree.", + "ids": "2409" + }, + { + "examine": "I can climb this.", + "ids": "2410" + }, + { + "examine": "The door is closed.", + "ids": "2411" + }, + { + "examine": "Handy for boarding the ship.", + "ids": "2412,2413,2414,2415" + }, + { + "examine": "Pull me!", + "ids": "2416" + }, + { + "examine": "Place party drop items here.", + "ids": "2417,2418,2419,2420" }, { "examine": "A beautiful landscape.", "ids": "24184" }, { - "examine": "These open and close!", - "ids": "40050" + "examine": "This lever is down.", + "ids": "2421" }, { - "examine": "A case. With books.", - "ids": "40033" + "examine": "This lever is up.", + "ids": "2422" }, { - "examine": "A drab looking bed.", - "ids": "40035" + "examine": "This lever is down.", + "ids": "2423" }, { - "examine": "I can climb down this.", - "ids": "40027,20987,29358" + "examine": "This lever is up.", + "ids": "2424" }, { - "examine": "These open and close!", - "ids": "40052" + "examine": "This lever is down.", + "ids": "2425" }, { - "examine": "The perfect place to store things.", - "ids": "40054" + "examine": "This lever is up.", + "ids": "2426" }, { - "examine": "Break glass in case in case of emergency.", - "ids": "40076" + "examine": "This door is closed by an unknown mechanism.", + "ids": "2427,2428,2429,2430,2431" + }, + { + "examine": "This gate is closed shut.", + "ids": "2432,2433" + }, + { + "examine": "This may be worth opening.", + "ids": "2434,2435" }, { "examine": "I can climb up these stairs.", - "ids": "40057" + "ids": "24349" }, { - "examine": "Trinkets and stuff.", - "ids": "40075" + "examine": "I wonder what's inside.", + "ids": "2436,2437" }, { - "examine": "Looking good!", - "ids": "40065" + "examine": "A wooden gate.", + "ids": "2438,2439" }, { - "examine": "It looks slightly different from the rest.", - "ids": "30532" + "examine": "Wonder what this pillar is for?", + "ids": "2440,2441,2442,2443" }, { - "examine": "'Here lies...' is all I can read.", - "ids": "30671,30672" + "examine": "I wonder what's under it?", + "ids": "2444,2445" }, { - "examine": "The inscription is worn away and unreadable.", - "ids": "30667,30669" + "examine": "This leads to somewhere...but where?", + "ids": "2446" }, { - "examine": "Whoever bought this must have really cared for the inhabitant.", - "ids": "30668" + "examine": "I reckon I could climb that!", + "ids": "2447,2448" }, { - "examine": "Make a wish.", - "ids": "28715" + "examine": "Some magical rocks.", + "ids": "2449,2450" }, { - "examine": "Contains no summoning energy.", - "ids": "28720,28721" + "examine": "Allows access to other parts of the ship.", + "ids": "245,246" }, { - "examine": "Glowing with barely-suppressed energies.", - "ids": "28719" + "examine": "There's something behind these roots.", + "ids": "2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464" }, { - "examine": "Looks kind of like a man made of metal.", - "ids": "32292" + "examine": "A portal from this mystical place.", + "ids": "2465,2466,2467,2468,2469,2470,2471,2472,2473,2474,2475,2476,2477" }, { - "examine": "Technically a bed.", - "ids": "32031" + "examine": "A mysterious power emanates from this shrine.", + "ids": "2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490" }, { - "examine": "A really bad portrait of the King.", - "ids": "32326" + "examine": "The source of all Rune Stones.", + "ids": "2491" }, { - "examine": "A tatty old standard.", - "ids": "32295" + "examine": "A portal from this mystical place.", + "ids": "2492" }, { - "examine": "I can climb this.", - "ids": "32015" + "examine": "For keeping things in.", + "ids": "24924" }, { - "examine": "An old crate for storage.", - "ids": "29351,29352,31137,31140" + "examine": "A part of an old temple.", + "ids": "2493,2494,2495,2496" + }, + { + "examine": "Unusual energy is gathered here.", + "ids": "2497,2498,2499,2500,2501,2502" + }, + { + "examine": "Seems to be some kind of clock part.", + "ids": "25,26,27,28,29,30,31,32" + }, + { + "examine": "An old crumbled pillar.", + "ids": "2503,2504,2505,2506,2507" + }, + { + "examine": "Broken parts of an old temple.", + "ids": "2508,2509,2510" + }, + { + "examine": "This leads to the practice tower.", + "ids": "2511" + }, + { + "examine": "This leads back down to the guild.", + "ids": "2512" + }, + { + "examine": "I wonder if I can hit a bullseye?", + "ids": "2513" + }, + { + "examine": "The door to the Ranging Guild.", + "ids": "2514" + }, + { + "examine": "I hope it doesn't sink.", + "ids": "2515" + }, + { + "examine": "It's sinking!", + "ids": "2516" + }, + { + "examine": "Going up?", + "ids": "2517" + }, + { + "examine": "Typical shoddy workmanship.", + "ids": "2518" + }, + { + "examine": "Crates of packed fish.", + "ids": "2519" + }, + { + "examine": "Without this I'm going around in circles.", + "ids": "252,253" + }, + { + "examine": "It's used for loading and unloading fishing boats.", + "ids": "2520,2521" + }, + { + "examine": "It's a flight of stairs.", + "ids": "2522,2523" + }, + { + "examine": "I wonder what's inside?", + "ids": "2524" + }, + { + "examine": "It's open.", + "ids": "2525" + }, + { + "examine": "The door of a small prison.", + "ids": "2526,2527" + }, + { + "examine": "The door is closed.", + "ids": "2528" + }, + { + "examine": "A door.", + "ids": "2529" + }, + { + "examine": "Used for storage.", + "ids": "2530" + }, + { + "examine": "Looks like someone's tried digging here.", + "ids": "2531,2532" + }, + { + "examine": "A pile of what is hopefully mud.", + "ids": "2533" + }, + { + "examine": "There's black cross on the door.", + "ids": "2534" + }, + { + "examine": "The door is closed.", + "ids": "2535" + }, + { + "examine": "A door.", + "ids": "2536" + }, + { + "examine": "The door is closed.", + "ids": "2537" + }, + { + "examine": "A door.", + "ids": "2538" + }, + { + "examine": "A flight of stairs.", + "ids": "2539,2540" + }, + { + "examine": "This figure brings luck to those who sail.", + "ids": "254" + }, + { + "examine": "An outlet to the sewer.", + "ids": "2541,2542" + }, + { + "examine": "There's a cover over this manhole.", + "ids": "2543" + }, + { + "examine": "I wonder where they will take me.", + "ids": "25432" + }, + { + "examine": "How dangerous, someone has left this manhole open.", + "ids": "2544" + }, + { + "examine": "This is supposed to stop people falling down the manhole.", + "ids": "2545" + }, + { + "examine": "A door.", + "ids": "2546,2547,2548,2549" + }, + { + "examine": "Potentially hot!", + "ids": "25464" + }, + { + "examine": "A goblin fire. Looks fierce.", + "ids": "25465" + }, + { + "examine": "Used for pulling things up.", + "ids": "255" + }, + { + "examine": "A secure door.", + "ids": "2550,2551" + }, + { + "examine": "A metal gate bars your way.", + "ids": "2552,2553" + }, + { + "examine": "A secure door.", + "ids": "2554,2555,2556,2557,2558,2559" + }, + { + "examine": "Fine silk woven by experts.", + "ids": "2560" + }, + { + "examine": "This stall smells great.", + "ids": "2561" + }, + { + "examine": "Precious stones from around the world.", + "ids": "2562" + }, + { + "examine": "All manner of animal clothing.", + "ids": "2563" + }, + { + "examine": "Spices to tingle your taste buds.", + "ids": "2564" + }, + { + "examine": "Fine silver items are for sale here.", + "ids": "2565" + }, + { + "examine": "I wonder what's inside it.", + "ids": "2566,2567,2568,2569,2570,2571,2572,2573,2574" + }, + { + "examine": "A marshy jungle vine.", + "ids": "2575,2576" + }, + { + "examine": "A tall palm tree with some plants growing at its stem.", + "ids": "2577" + }, + { + "examine": "A tall palm tree.", + "ids": "2578" + }, + { + "examine": "The burnt ground has a plant growing on it.", + "ids": "2579" + }, + { + "examine": "The ground has been burnt here by a fire.", + "ids": "2580" + }, + { + "examine": "A rock with green moss growing on it.", + "ids": "2581" + }, + { + "examine": "A rock, it looks like something was growing here.", + "ids": "2582" + }, + { + "examine": "It looks as if it is covered in some fungus.", + "ids": "2583" + }, + { + "examine": "A collection of rocks over what looks like a depression.", + "ids": "2584" + }, + { + "examine": "Rocky walls, you may be able to climb it.", + "ids": "2585" + }, + { + "examine": "It's closed.", + "ids": "2586" + }, + { + "examine": "I wonder what's inside?", + "ids": "2587" + }, + { + "examine": "It's open.", + "ids": "2588" + }, + { + "examine": "This ship's not getting far with that.", + "ids": "2589" + }, + { + "examine": "Takes me into the ship.", + "ids": "2590,2591" + }, + { + "examine": "Takes me back on deck.", + "ids": "2592" + }, + { + "examine": "Handy for boarding the ship.", + "ids": "2593,2594" + }, + { + "examine": "It's closed.", + "ids": "2595" + }, + { + "examine": "A blood-red door.", + "ids": "2596" + }, + { + "examine": "A lurid orange door.", + "ids": "2597" + }, + { + "examine": "A sickly yellow door.", + "ids": "2598" + }, + { + "examine": "A gloomy blue door.", + "ids": "2599" + }, + { + "examine": "This figure brings luck to those who sail.", + "ids": "260" + }, + { + "examine": "A groovy magenta door.", + "ids": "2600" + }, + { + "examine": "A putrid green door.", + "ids": "2601" + }, + { + "examine": "Probably the nicest door in this ghastly place.", + "ids": "2602" + }, + { + "examine": "It's closed.", + "ids": "2603" + }, + { + "examine": "It's open.", + "ids": "2604" + }, + { + "examine": "It's a ladder.", + "ids": "2605" + }, + { + "examine": "There is something strange about this wall...", + "ids": "2606" + }, + { + "examine": "It's closed.", + "ids": "2607,2608" + }, + { + "examine": "A collection of rocks over what looks like a depression.", + "ids": "2609" + }, + { + "examine": "A rope hangs here that I can climb.", + "ids": "2610" + }, + { + "examine": "It's a ladder.", + "ids": "2611" + }, + { + "examine": "Wonder what's in here...", + "ids": "2612" + }, + { + "examine": "Smells kind of funny...", + "ids": "2613" + }, + { + "examine": "Looks comfortable...", + "ids": "2615" + }, + { + "examine": "Looks spooky...", + "ids": "2616" + }, + { + "examine": "Phew, an exit!", + "ids": "2617" + }, + { + "examine": "The fence is broken at this point.", + "ids": "2618" }, { "examine": "A wooden barrel for storage.", - "ids": "31136,36798" + "ids": "2619" }, { - "examine": "A home for baby creatures.", - "ids": "32312" + "examine": "An old crate for storage.", + "ids": "2620" }, { - "examine": "Must have been laid by a huge bird.", - "ids": "32313" + "examine": "It's a door.", + "ids": "2621,2622" }, { - "examine": "Stops bits of building falling on me.", - "ids": "37451" + "examine": "It's a gate.", + "ids": "2623" + }, + { + "examine": "The entrance to the Hero's Guild.", + "ids": "2624,2625" + }, + { + "examine": "It's a door.", + "ids": "2626,2627,2628" + }, + { + "examine": "There is something unusual about this wall...", + "ids": "2629" + }, + { + "examine": "I can see eels swimming in the lava.", + "ids": "2630" + }, + { + "examine": "It's a door.", + "ids": "2631" + }, + { + "examine": "I wonder what's inside?", + "ids": "2632" + }, + { + "examine": "It's open.", + "ids": "2633" + }, + { + "examine": "Rocks that contain nothing interesting, but block the way.", + "ids": "2634" + }, + { + "examine": "I wonder what's inside?", + "ids": "2635" + }, + { + "examine": "It's open.", + "ids": "2636,2637" + }, + { + "examine": "A source of pure water.", + "ids": "2638,2639" + }, + { + "examine": "A shrine for the faithful to worship at.", + "ids": "2640" + }, + { + "examine": "I can climb this.", + "ids": "2641" + }, + { + "examine": "Used for fashioning clay items.", + "ids": "2642" + }, + { + "examine": "Bake your clay items in here.", + "ids": "2643" + }, + { + "examine": "Used for spinning thread.", + "ids": "2644" + }, + { + "examine": "A tray of sand.", + "ids": "2645" + }, + { + "examine": "A plant cultivated for fibres.", + "ids": "2646" + }, + { + "examine": "The door to the Crafting Guild.", + "ids": "2647,2648,2649" + }, + { + "examine": "Useful if there is any wind.", + "ids": "265" + }, + { + "examine": "A pile of smelly rotting waste.", + "ids": "2650" + }, + { + "examine": "A sturdy home for bees.", + "ids": "2651" + }, + { + "examine": "Looks like this is where waste from the kitchen comes out.", + "ids": "2652" + }, + { + "examine": "It looks like a spiders' nest to me.", + "ids": "2653" + }, + { + "examine": "This fountain suits the garden.", + "ids": "2654" + }, + { + "examine": "It's the Sinclair Family coat of arms.", + "ids": "2655" + }, + { + "examine": "Looks like this is where Anna keeps her stuff.", + "ids": "2656" + }, + { + "examine": "Looks like this is where Bob keeps his stuff.", + "ids": "2657" + }, + { + "examine": "Looks like this is where Carol keeps her stuff.", + "ids": "2658" + }, + { + "examine": "Looks like this is where David keeps his stuff.", + "ids": "2659" + }, + { + "examine": "Useful for making ships move.", + "ids": "266,267" + }, + { + "examine": "Looks like this is where Elizabeth keeps her stuff.", + "ids": "2660" + }, + { + "examine": "Looks like this is where Frank keeps his stuff.", + "ids": "2661" + }, + { + "examine": "Looks like this is where the cook stores flour.", + "ids": "2662" + }, + { + "examine": "Some sacks of general garden items.", + "ids": "2663" + }, + { + "examine": "Luckily it seems able to keep that vicious dog inside.", + "ids": "2664,2665" + }, + { + "examine": "Looks like the killer smashed this to leave the mansion.", + "ids": "2666" + }, + { + "examine": "Slaves are using this to pull up barrels of rocks from down below.", + "ids": "2667" + }, + { + "examine": "Slaves are placing barrels on this to haul them to the surface.", + "ids": "2668,2669" + }, + { + "examine": "A succulent cactus.", + "ids": "2670" + }, + { + "examine": "A less-than-succulent succulent.", + "ids": "2671" + }, + { + "examine": "I can try to make new items on this.", + "ids": "2672" + }, + { + "examine": "A solid looking gate.", + "ids": "2673,2674" + }, + { + "examine": "Large doors made of solid oak.", + "ids": "2675,2676" + }, + { + "examine": "A solid looking chest, it belongs to Captain Siad.", + "ids": "2677" + }, + { + "examine": "A selection of Captain Siad's Books.", + "ids": "2678" + }, + { + "examine": "A sturdy looking desk on which the Captain works.", + "ids": "2679" + }, + { + "examine": "Useful for making ships move", + "ids": "268" + }, + { + "examine": "A barrel containing rocks and mining debris.", + "ids": "2680" + }, + { + "examine": "An empty mining barrel.", + "ids": "2681" + }, + { + "examine": "The cart which takes barrels of rocks from the mining encampment to Al-Kharid.", + "ids": "2682" + }, + { + "examine": "Gives a lovely view of the desert, complete with metalic bars.", + "ids": "2683" + }, + { + "examine": "It seems to be used for moving rocks into and out of this area.", + "ids": "2684" + }, + { + "examine": "A solid looking gate.", + "ids": "2685,2686,2687,2688" + }, + { + "examine": "There's no getting around it, it's a fairly sturdy prison cell door.", + "ids": "2689" + }, + { + "examine": "Large solid, thick set oak doors", + "ids": "2690,2691" + }, + { + "examine": "It looks fairly sturdy.", + "ids": "2692" + }, + { + "examine": "This is used by Shantay and his men to take items to the bank on your behalf.", + "ids": "2693" }, { "examine": "I wonder what's under it?", @@ -20316,105 +9196,649 @@ "ids": "26934,36687" }, { - "examine": "A strong metal door.", - "ids": "29315,29316,29317,29318,29319,29320,29321,29322" + "examine": "A rocky rock.", + "ids": "2694,2695,2696,2697" }, { - "examine": "The body of a Dwarf savaged by Goblins.", - "ids": "15596" + "examine": "A cave which has been mined out.", + "ids": "2698,2699" }, { - "examine": "I don't know art, but I like it!", - "ids": "36809" + "examine": "The entrance to the tent.", + "ids": "2700,2701" }, { - "examine": "The wood burning here must be full of salt.", - "ids": "14169" + "examine": "Tracks in the sand.", + "ids": "2702,2703" }, { - "examine": "Sturdy looking.", - "ids": "21176" + "examine": "Rocky!", + "ids": "2704" }, { - "examine": "Doesn't look like it'll be sailing again.", - "ids": "14173" + "examine": "It's a door.", + "ids": "2705" }, { - "examine": "Looks a bit damp.", - "ids": "14174" + "examine": "A nicely fitted door.", + "ids": "2706" + }, + { + "examine": "A wooden crate for storage.", + "ids": "2707,2708" + }, + { + "examine": "I wonder what's inside?", + "ids": "2709" + }, + { + "examine": "How much does this weigh?", + "ids": "271" + }, + { + "examine": "It's open.", + "ids": "2710" + }, + { + "examine": "It's a flight of stairs.", + "ids": "2711" + }, + { + "examine": "The door is closed.", + "ids": "2712" + }, + { + "examine": "Grain goes in here.", + "ids": "2713,2714,2715,2716,2717" + }, + { + "examine": "These control the flow of grain from the hopper to the millstones.", + "ids": "2718,2719,2720,2721,2722" + }, + { + "examine": "I can climb up here.", + "ids": "272" + }, + { + "examine": "A spicy meat kebab is cooking here.", + "ids": "2723" + }, + { + "examine": "A fire burns brightly here.", + "ids": "2724" + }, + { + "examine": "A grand old fireplace.", + "ids": "2725,2726" + }, + { + "examine": "Something is cooking nicely here.", + "ids": "2727" + }, + { + "examine": "Ideal for cooking on.", + "ids": "2728,2729,2730,2731,3039,9682,12102,36973,40110" + }, + { + "examine": "I can go below decks with this ladder.", + "ids": "273" + }, + { + "examine": "Hot!", + "ids": "2732,2733,2734,2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765,2766,2767,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779,2780" + }, + { + "examine": "A conveniently rolled sail.", + "ids": "274,275,276,277,278,279" + }, + { + "examine": "A hot place for forging things in.", + "ids": "2781" + }, + { + "examine": "It says \"Property of Doric the dwarf.\" on the side.", + "ids": "2782" + }, + { + "examine": "Used for fashioning metal items.", + "ids": "2783" + }, + { + "examine": "Various implements for working with metal.", + "ids": "2784" + }, + { + "examine": "A hot place for forging things in.", + "ids": "2785" + }, + { + "examine": "An entrance to Gu'Tanoth.", + "ids": "2786,2787,2788,2789" + }, + { + "examine": "I wonder what's inside it.", + "ids": "2790" + }, + { + "examine": "A counter made from a stone block.", + "ids": "2791,2792,2876,3800,29356" + }, + { + "examine": "An ogre is selling cakes here.", + "ids": "2793" + }, + { + "examine": "A lever for activating something.", + "ids": "2794,2795" + }, + { + "examine": "This leads to the watchtower.", + "ids": "2796" + }, + { + "examine": "This leads downwards.", + "ids": "2797" + }, + { + "examine": "A leafy bush.", + "ids": "2798,2799,2800,2801,2802,2803" + }, + { + "examine": "I think I can squeeze through here...", + "ids": "2804,2805,2806,2807,2808,2809,2810,2811" + }, + { + "examine": "This leads downwards.", + "ids": "2812" + }, + { + "examine": "I think I can squeeze through here...", + "ids": "2813" + }, + { + "examine": "The gate has been locked shut.", + "ids": "2814,2815" + }, + { + "examine": "A strange looking rock...", + "ids": "2816" + }, + { + "examine": "I think I can get out here...", + "ids": "2817,2818,2819,2820,2821,2822" + }, + { + "examine": "A hole.", + "ids": "2823" + }, + { + "examine": "A hole", + "ids": "2824" + }, + { + "examine": "I hope this leads the way out.", + "ids": "2825" + }, + { + "examine": "I wonder what's inside it?", + "ids": "2826,2827,2828,2829" + }, + { + "examine": "You can probably jump over from here.", + "ids": "2830,2831" + }, + { + "examine": "A low wall is blocking your path.", + "ids": "2832" + }, + { + "examine": "This goes up!", + "ids": "2833" + }, + { + "examine": "Sure it's gilded, but would you want to live in it?", + "ids": "28335" + }, + { + "examine": "It incubates eggs.", + "ids": "28336" + }, + { + "examine": "It's a basket for cats.", + "ids": "28337" + }, + { + "examine": "It's a basket for dogs.", + "ids": "28338" + }, + { + "examine": "Enough baskets for any dog's needs.", + "ids": "28339" + }, + { + "examine": "A low wall is blocking your path.", + "ids": "2834" + }, + { + "examine": "Lots of little fishies.", + "ids": "28340" + }, + { + "examine": "Keeps the puppies out of trouble.", + "ids": "28341" + }, + { + "examine": "It's good for bedding and stuffing.", + "ids": "28343" + }, + { + "examine": "Things are cheaper by the pound.", + "ids": "28344" + }, + { + "examine": "Made by the A. Wilson Company.", + "ids": "28345" + }, + { + "examine": "Looks hard.", + "ids": "2835,2836,2837,2838,2839,2840,2841" + }, + { + "examine": "They know how much that dog in the window is.", + "ids": "28351" + }, + { + "examine": "Lots of things for a growing pup.", + "ids": "28353" + }, + { + "examine": "Lots of things for a growing kitten.", + "ids": "28354" + }, + { + "examine": "Lots of things for a growing fish.", + "ids": "28355" + }, + { + "examine": "It seems to be sat happily on the counter.", + "ids": "28356" + }, + { + "examine": "Lots of things for a growing pup/kitten/fish.", + "ids": "28357" + }, + { + "examine": "There are a lot of pet supplies here.", + "ids": "28357,28358,28363,28364" + }, + { + "examine": "Fetch!", + "ids": "28360" + }, + { + "examine": "It's covered in hairs.", + "ids": "28361" + }, + { + "examine": "It has a special magical quality.", + "ids": "2842" + }, + { + "examine": "This drain leads from the sink to the sewers below.", + "ids": "2843" + }, + { + "examine": "This looks like it can be turned.", + "ids": "2844,2845,2846,2847,2848" + }, + { + "examine": "A rudimentary type of boat.", + "ids": "2849" + }, + { + "examine": "It's closed.", + "ids": "2850" + }, + { + "examine": "It's open.", + "ids": "2851" + }, + { + "examine": "I can probably squeeze my way inside there.", + "ids": "2852" + }, + { + "examine": "It's a flight of stairs.", + "ids": "2853" + }, + { + "examine": "Made of wood.", + "ids": "2854,2855" + }, + { + "examine": "I wonder what's inside?", + "ids": "2856" + }, + { + "examine": "It's open.", + "ids": "2857" + }, + { + "examine": "Used for storage.", + "ids": "2858" }, { "examine": "Those druids certainly make the best of natural resources.", "ids": "28588,28589" }, + { + "examine": "A hot range, where some soup is bubbling nicely.", + "ids": "2859" + }, + { + "examine": "Very ominous.", + "ids": "2860" + }, + { + "examine": "It's a door.", + "ids": "2861,2862" + }, + { + "examine": "A place to sit and watch furniture grow.", + "ids": "28627" + }, + { + "examine": "The door is closed.", + "ids": "2863" + }, + { + "examine": "It's not rolling, but it seems to have gathered some moss.", + "ids": "28635" + }, { "examine": "Sit back and relax...", "ids": "28636,18488" }, { - "examine": "One horsepower; wooden suspension: a beauty.", - "ids": "34376" + "examine": "A water feature.", + "ids": "2864" }, { - "examine": "I don't even want to think about what did that...", - "ids": "32071" + "examine": "It's a gate.", + "ids": "2865,2866" }, { - "examine": "It's a door.", - "ids": "31838" + "examine": "Very decorative!", + "ids": "2867" }, { - "examine": "Wooden crates for storage.", - "ids": "31138,31139" + "examine": "It's a cupboard.", + "ids": "2868,2869" }, { - "examine": "These may have something in them.", - "ids": "32049,36929" + "examine": "A thick metal gate.", + "ids": "28690,28691,28692,28693" }, { - "examine": "Probably looked better mounted on the dragon's neck.", - "ids": "15564" + "examine": "I'm not allowed to climb this ladder.", + "ids": "287" }, { - "examine": "A magical wall decoration.", - "ids": "32324,32325" + "examine": "The mouse equivalent of a door.", + "ids": "2870" }, { - "examine": "Perhaps I should search this.", - "ids": "32009" + "examine": "I can climb down this.", + "ids": "2871" }, { - "examine": "The door is closed.", - "ids": "31808" + "examine": "Make a wish.", + "ids": "28715" }, { - "examine": "The door is open.", - "ids": "31809" + "examine": "Glowing with barely-suppressed energies.", + "ids": "28719" }, { - "examine": "The bank teller will serve you from here.", - "ids": "36786" + "examine": "I can climb this.", + "ids": "2872" }, { - "examine": "Closed for business.", - "ids": "36787" + "examine": "Contains no summoning energy.", + "ids": "28720,28721" }, { - "examine": "Banking transactions are processed here.", - "ids": "36789,37158" + "examine": "What a good likeness!", + "ids": "2873" }, { - "examine": "This will give me a bit more information.", - "ids": "29461,29462,29463,29464" + "examine": "A depiction that chills one to the bone.", + "ids": "2874" + }, + { + "examine": "I wonder how accurate a representation this is.", + "ids": "2875" + }, + { + "examine": "A barrel full of staffs...", + "ids": "2877" + }, + { + "examine": "Patterns of light dance hypnotically across the pool's surface.", + "ids": "2878,2879" + }, + { + "examine": "There seems to be some sort of magical field here.", + "ids": "2880" + }, + { + "examine": "It's closed.", + "ids": "2881" + }, + { + "examine": "The toll gate from Lumbridge to Al-Kharid.", + "ids": "2882,2883" + }, + { + "examine": "I can climb this.", + "ids": "2884" + }, + { + "examine": "An antique by anyone's standards.", + "ids": "2885" + }, + { + "examine": "An antique by anyone's standards, however, it might have something interesting in it.", + "ids": "2886" + }, + { + "examine": "Looks good for logging.", + "ids": "2887" + }, + { + "examine": "A leafy jungle palm, dense foliage.", + "ids": "2888" + }, + { + "examine": "Home to many unusual creatures.", + "ids": "2889" + }, + { + "examine": "A leafy tree.", + "ids": "2890" + }, + { + "examine": "This tree has been cut down.", + "ids": "2891" + }, + { + "examine": "A jungle bush, quite common to these areas.", + "ids": "2892,2893" + }, + { + "examine": "A jungle bush that has been chopped down.", + "ids": "2894,2895" + }, + { + "examine": "A door to the Legends Guild.", + "ids": "2896,2897,2898,2899" + }, + { + "examine": "Looks slippery!", + "ids": "2900,2901" + }, + { + "examine": "Stoney!", + "ids": "2902" + }, + { + "examine": "A rather shadowy cavern entrance.", + "ids": "2903,2904" + }, + { + "examine": "An old crate for storage.", + "ids": "2905" + }, + { + "examine": "Useful for putting things on.", + "ids": "2906" + }, + { + "examine": "I guess I could sleep in it if I was really tired.", + "ids": "2907" + }, + { + "examine": "A wall of magic fire.", + "ids": "2908,2909" + }, + { + "examine": "Rickety bamboo tables that are lashed together to make a desk.", + "ids": "2910" + }, + { + "examine": "Bamboo storage!", + "ids": "2911" + }, + { + "examine": "This gate has a horribly complex locking mechanism.", + "ids": "2912,2913,2914,2915" + }, + { + "examine": "A dark cave entrance leading to the surface.", + "ids": "2916,2917" + }, + { + "examine": "A large crack in the wall.", + "ids": "2918" + }, + { + "examine": "A huge lump of rock.", + "ids": "2919,2920,2921" + }, + { + "examine": "A heavily constructed, cast-iron, ancient gateway.", + "ids": "2922,2923,2924,2925" + }, + { + "examine": "A high wall made up of rocks with sharp edges.", + "ids": "2926" + }, + { + "examine": "It looks like ancient graffiti.", + "ids": "2927" + }, + { + "examine": "An ornately carved rock with a pointed receptacle.", + "ids": "2928" + }, + { + "examine": "A half buried skeleton, probably dwarven remains.", + "ids": "2929" + }, + { + "examine": "A huge, strangely constructed doorway, not sure how this opens.", + "ids": "2930" + }, + { + "examine": "It looks like a magical portal of some kind, who knows where it leads?", + "ids": "2931" + }, + { + "examine": "A strong metal door.", + "ids": "29315,29316,29317,29318,29319,29320,29321,29322" + }, + { + "examine": "Strangely, there's a locked barrel here, I wonder what's inside.", + "ids": "2932,2933" + }, + { + "examine": "A large scaffold platform most likely used for lifting and lowering heavy items.", + "ids": "2934" + }, + { + "examine": "Used to move earth to, and from, the dig shaft. A rope has been thrown over it.", + "ids": "2935" + }, + { + "examine": "An old crate for storage.", + "ids": "29351,29352,31137,31140" + }, + { + "examine": "A sculpted trunk of wood.", + "ids": "2936,2937,2938,2939" + }, + { + "examine": "Warning! Danger of death.", + "ids": "29373" + }, + { + "examine": "A wonderfully dressed table.", + "ids": "2940" + }, + { + "examine": "I wonder what's inside.", + "ids": "29400,32010,40093" + }, + { + "examine": "Sparkling sacred water straight from the source.", + "ids": "2941" + }, + { + "examine": "A sparkling, babbling flow of water from an underground source.", + "ids": "2942" + }, + { + "examine": "Disgusting filthy quagmire that oozes from the ground.", + "ids": "2943" + }, + { + "examine": "Found near the water's edge.", + "ids": "2944" + }, + { + "examine": "A baby Yommi tree, it has a mystical aura.", + "ids": "2945" + }, + { + "examine": "An adolescent-looking Yommi tree, it has a mystical aura.", + "ids": "2946" }, { "examine": "I can climb over the fence using this.", "ids": "29460" }, + { + "examine": "This will give me a bit more information.", + "ids": "29461,29462,29463,29464" + }, { "examine": "It looks slightly rickety.", "ids": "29468" }, + { + "examine": "A dead Yommi tree sapling, you'll need a tough axe to remove this.", + "ids": "2947" + }, { "examine": "These probably contain Farming supplies.", "ids": "29471" @@ -20431,6 +9855,30 @@ "examine": "I wonder what's buried here.", "ids": "29476,29477,29478" }, + { + "examine": "A fully grown Yommi tree, it won't get much taller than this.", + "ids": "2948" + }, + { + "examine": "A dead fully grown Yommi tree, you'll need a tough, sharp axe to remove this.", + "ids": "2949" + }, + { + "examine": "A felled adult Yommi tree, perhaps you should trim those branches?", + "ids": "2950" + }, + { + "examine": "A rotten felled Yommi tree, it was left too long in the damp jungle.", + "ids": "2951" + }, + { + "examine": "A trimmed Yommi tree log, perfect for sculpting into a totem pole.", + "ids": "2952" + }, + { + "examine": "A rotten Yommi tree log, it was left too long in the damp jungle air.", + "ids": "2953" + }, { "examine": "So small only a rabbit could squeeze down.", "ids": "29533" @@ -20440,167 +9888,10847 @@ "ids": "29534" }, { - "examine": "Potentially hot!", - "ids": "25464" + "examine": "A beautifully crafted totem pole carved from the trunk of the sacred Yommi tree.", + "ids": "2954" }, { - "examine": "A goblin fire. Looks fierce.", - "ids": "25465" + "examine": "A rotten totem pole, it was left to rot in the jungle.", + "ids": "2955" }, { - "examine": "A wooden crate for storage.", - "ids": "18506" + "examine": "This earth is particularly fertile.", + "ids": "2956" }, { - "examine": "A pile of crates for storage.", - "ids": "18508" + "examine": "This earth is damaged by cultivation, time will restore the fertility.", + "ids": "2957" }, { - "examine": "I wonder where they will take me.", - "ids": "25432" + "examine": "A rope hangs here.", + "ids": "2958" }, { - "examine": "'Pay back all your debts by borrowing more money from us!' Clever.", - "ids": "23924" + "examine": "A dangerous path onwards.", + "ids": "2959,2960,2961" + }, + { + "examine": "It's like a land rudder.", + "ids": "296" + }, + { + "examine": "Stoney!", + "ids": "2962,2963,2964" + }, + { + "examine": "Looks slippery!", + "ids": "2965" + }, + { + "examine": "A hot place for forging things in.", + "ids": "2966,2967,2968" + }, + { + "examine": "It looks like a strangely shaped depression in the rock.", + "ids": "2969" + }, + { + "examine": "Disturbingly man-like.", + "ids": "297" + }, + { + "examine": "This recess has a glowing heart shaped crystal in it.", + "ids": "2970" + }, + { + "examine": "This looks like some sort of shimmering surface.", + "ids": "2971" + }, + { + "examine": "A huge lump of rock.", + "ids": "2972,2973,2974" + }, + { + "examine": "This tree is particularly leafy.", + "ids": "2975,2976" + }, + { + "examine": "A large pile of sand", + "ids": "2977" + }, + { + "examine": "A medium pile of sand", + "ids": "2978" + }, + { + "examine": "A small pile of sand", + "ids": "2979" + }, + { + "examine": "I bet there's a needle in it somewhere.", + "ids": "298,299,300" + }, + { + "examine": "Some beautiful flowers.", + "ids": "2980,2981,2982,2983,2984,2985,2986,2987,2988" + }, + { + "examine": "Known commonly as Red worm vine.", + "ids": "2989,2990,2991,2992,2993,2994" + }, + { + "examine": "Contains traces of summoning energy.", + "ids": "29939,29943,29944,29945,29947,29951,29952,29953,29954" + }, + { + "examine": "I wonder what's inside.", + "ids": "2995,2996" + }, + { + "examine": "The door is closed.", + "ids": "2997" + }, + { + "examine": "The door is open.", + "ids": "2998,2999" + }, + { + "examine": "The door is closed.", + "ids": "3,4" + }, + { + "examine": "These obviously mean keep out!", + "ids": "3000,3001,3002,3003,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013" + }, + { + "examine": "Animals have no table manners.", + "ids": "301" + }, + { + "examine": "A nicely fitted door.", + "ids": "3014" + }, + { + "examine": "A wooden gate.", + "ids": "3015,3016" + }, + { + "examine": "A nicely fitted door.", + "ids": "3017,3018,3019" + }, + { + "examine": "A home for baby creatures.", + "ids": "302" + }, + { + "examine": "A wrought iron gate.", + "ids": "3020,3021,3022,3023" + }, + { + "examine": "A closed door.", + "ids": "3024,3025,3026,3027" + }, + { + "examine": "I can climb this.", + "ids": "3028" + }, + { + "examine": "I can climb down this.", + "ids": "3029" + }, + { + "examine": "In the city we would call this mouldy rubbish.", + "ids": "303" + }, + { + "examine": "I can climb this.", + "ids": "3030" + }, + { + "examine": "I can climb down this.", + "ids": "3031" + }, + { + "examine": "I can see fish swimming in the water.", + "ids": "3032" + }, + { + "examine": "One of the most common trees in 2009Scape.", + "ids": "3033,3034,3035,3036" + }, + { + "examine": "A beautiful old oak.", + "ids": "3037" + }, + { + "examine": "Hot!", + "ids": "3038" + }, + { + "examine": "I bet there's a needle in it somewhere.", + "ids": "304" + }, + { + "examine": "It's closed.", + "ids": "3040" + }, + { + "examine": "It's open.", + "ids": "3041" + }, + { + "examine": "A rocky outcrop.", + "ids": "3042,3043" + }, + { + "examine": "A hot place for forging things in.", + "ids": "3044" }, { "examine": "The bank teller will serve you from here.", - "ids": "11402" + "ids": "3045" + }, + { + "examine": "These obviously mean keep out!", + "ids": "3046,3047,3048,3049,3050,3051,3052,3053,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068,3069,3070,3071,3072,3073,3074,3075,3076" + }, + { + "examine": "Where bees live.", + "ids": "305" + }, + { + "examine": "It looks slightly different from the rest.", + "ids": "30532" + }, + { + "examine": "A used cart seller will probably try and sell it later.", + "ids": "306" + }, + { + "examine": "The inscription is worn away and unreadable.", + "ids": "30667,30669" + }, + { + "examine": "Whoever bought this must have really cared for the inhabitant.", + "ids": "30668" + }, + { + "examine": "'Here lies...' is all I can read.", + "ids": "30671,30672" + }, + { + "examine": "One horsepower; wooden suspension: a beauty.", + "ids": "307,308" + }, + { + "examine": "Lean against the wall to get a better view!", + "ids": "3077,3078,3079,3080,3081,3082,3083,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107" + }, + { + "examine": "Dead tree parts piled together neatly.", + "ids": "309" + }, + { + "examine": "A patch of soft dark brown matter. Probably mud.", + "ids": "310" + }, + { + "examine": "Flags marking the entrance to the arena.", + "ids": "3108,3109,3110,3111,3112,3113,3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158" + }, + { + "examine": "A pile of something I hope is mud.", + "ids": "311" + }, + { + "examine": "Wooden crates for storage.", + "ids": "31138,31139" + }, + { + "examine": "Potato-licious!", + "ids": "312" + }, + { + "examine": "An interesting sign.", + "ids": "31297,31298,31299,31300,31301" + }, + { + "examine": "Baby bread.", + "ids": "313" + }, + { + "examine": "Beats the operating table.", + "ids": "3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3178,3179,3180,3181" + }, + { + "examine": "The door is closed.", + "ids": "31808" + }, + { + "examine": "The door is open.", + "ids": "31809" + }, + { + "examine": "Wait here for a nurse.", + "ids": "3182" + }, + { + "examine": "Storage for drugs and bandages.", + "ids": "3183,3184,3185" + }, + { + "examine": "It's a door.", + "ids": "31838" + }, + { + "examine": "I don't really want to end up here...", + "ids": "3186,3187,3188,3189" + }, + { + "examine": "These open and close!", + "ids": "3190" + }, + { + "examine": "This may be worth searching.", + "ids": "3191" + }, + { + "examine": "You can see the last 50 fights on here.", + "ids": "3192" + }, + { + "examine": "Used to store players' items whilst they duel.", + "ids": "3193" + }, + { + "examine": "Use for quick access to your bank.", + "ids": "3194" + }, + { + "examine": "Smells pretty bad!", + "ids": "3195" + }, + { + "examine": "Not much use for anything except scrap.", + "ids": "3196" + }, + { + "examine": "Entrance to the Duel Arena.", + "ids": "3197,3198" + }, + { + "examine": "A wooden double bed.", + "ids": "3199,3200,3201,3202" + }, + { + "examine": "Perhaps I should search this.", + "ids": "32009" + }, + { + "examine": "I can climb this.", + "ids": "32015" + }, + { + "examine": "Access to the duel arena.", + "ids": "3203,3204" + }, + { + "examine": "Technically a bed.", + "ids": "32031" + }, + { + "examine": "These may have something in them.", + "ids": "32049,36929" + }, + { + "examine": "I can climb down this.", + "ids": "3205" + }, + { + "examine": "A barrel full of staffs...", + "ids": "3206,3207,3208" + }, + { + "examine": "I don't even want to think about what did that...", + "ids": "32071" + }, + { + "examine": "Now that's what I call slimline!", + "ids": "32072" + }, + { + "examine": "A strange crack cuts into the wall.", + "ids": "3209,3210,3211,3212,29360,29362" + }, + { + "examine": "It looks like I can squeeze through here.", + "ids": "3213" + }, + { + "examine": "The easy way out...", + "ids": "3214,3215" + }, + { + "examine": "A pile of mud.", + "ids": "3216" + }, + { + "examine": "I can go through here.", + "ids": "3217" + }, + { + "examine": "Dark and intimidating.", + "ids": "3218,3219" + }, + { + "examine": "That skull's looking at me....", + "ids": "3220,3221" + }, + { + "examine": "I can climb down these.", + "ids": "3222" + }, + { + "examine": "I can climb up these.", + "ids": "3223" + }, + { + "examine": "I can go through here.", + "ids": "3224,3225,3226" + }, + { + "examine": "Best not to get caught!", + "ids": "3227" + }, + { + "examine": "Best avoided.", + "ids": "3228,3229" + }, + { + "examine": "Looks kind of like a man made of metal.", + "ids": "32292" + }, + { + "examine": "A tatty old standard.", + "ids": "32295" + }, + { + "examine": "Looks suspicious.", + "ids": "3230" + }, + { + "examine": "A plank.", + "ids": "3231" + }, + { + "examine": "A home for baby creatures.", + "ids": "32312" + }, + { + "examine": "Must have been laid by a huge bird.", + "ids": "32313" + }, + { + "examine": "Formed over many years.", + "ids": "3232" + }, + { + "examine": "A magical wall decoration.", + "ids": "32324,32325" + }, + { + "examine": "A really bad portrait of the King.", + "ids": "32326" + }, + { + "examine": "Best avoided.", + "ids": "3233" + }, + { + "examine": "Looks suspicious.", + "ids": "3234" + }, + { + "examine": "I can open the grill from this side.", + "ids": "3235" + }, + { + "examine": "Maybe I can cross that.", + "ids": "3238" + }, + { + "examine": "It looks like only the guide ropes are holding the bridge up.", + "ids": "3239" + }, + { + "examine": "A bridge.", + "ids": "3240" + }, + { + "examine": "This lever can be operated.", + "ids": "3241,3242" + }, + { + "examine": "The base of a platform.", + "ids": "3243,3244,3245,3246" + }, + { + "examine": "A bridge.", + "ids": "3247,3248,3249,3250,3251,3252,3253" + }, + { + "examine": "I can't get past that very easily...", + "ids": "3254,3255,3256" + }, + { + "examine": "A platform.", + "ids": "3257" + }, + { + "examine": "A bridge.", + "ids": "3258,3259,3260,3261,3262" + }, + { + "examine": "This area may not be safe...", + "ids": "3263" + }, + { + "examine": "A well of nastiness.", + "ids": "3264" + }, + { + "examine": "I can climb this pile of rocks...", + "ids": "3265" + }, + { + "examine": "Usually used for storing living things...", + "ids": "3266,3267,3268,3269" + }, + { + "examine": "The remains of a bad driver.", + "ids": "327" + }, + { + "examine": "The door is closed.", + "ids": "3270" + }, + { + "examine": "The door is open.", + "ids": "3271" + }, + { + "examine": "I wonder what's inside.", + "ids": "3272,3273,3274,3275" + }, + { + "examine": "A stone bridge.", + "ids": "3276,3277,3278,3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293" + }, + { + "examine": "A hot place for forging things in.", + "ids": "3294" + }, + { + "examine": "It's covered in strange writing.", + "ids": "3295,3296,3297,3298,3299,3300,3301,3302" + }, + { + "examine": "Yup, definitely a lever.", + "ids": "33,34,35,36" + }, + { + "examine": "You won't be able to lift that.", + "ids": "3303" + }, + { + "examine": "A portcullis.", + "ids": "3304" + }, + { + "examine": "It doesn't look like water in there...", + "ids": "3305,3306" + }, + { + "examine": "A pile of rocks.", + "ids": "3307" + }, + { + "examine": "The boulder has smashed through the cage.", + "ids": "3308" + }, + { + "examine": "A deposit of rocks.", + "ids": "3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326" + }, + { + "examine": "Intimidating!", + "ids": "3327" + }, + { + "examine": "A depiction of a great dwarf miner.", + "ids": "3328,3329,3330,3331" + }, + { + "examine": "A temple door.", + "ids": "3332" + }, + { + "examine": "A very big door.", + "ids": "3333,3334,3335,3336" + }, + { + "examine": "This lever can be operated.", + "ids": "3337" + }, + { + "examine": "Why would a log be hung up there?", + "ids": "3338" + }, + { + "examine": "Looks suspicious.", + "ids": "3339" + }, + { + "examine": "Looks like the rope is holding the bridge up.", + "ids": "3340" + }, + { + "examine": "Looks like the other rope is holding the bridge up.", + "ids": "3341,3342" + }, + { + "examine": "A pile of rocks.", + "ids": "3343" + }, + { + "examine": "It's got dwarf brew in it.", + "ids": "3344,3345,3346,3347" + }, + { + "examine": "Lord Iban's Throne.", + "ids": "3348,3349,3350" + }, + { + "examine": "I'm glad I'm on this side of those bars.", + "ids": "3351,3352" + }, + { + "examine": "I don't much like the look of that...", + "ids": "3353,3354,3355,3356,3357,3358" + }, + { + "examine": "Well of the damned.", + "ids": "3359" + }, + { + "examine": "A wooden crate for storage.", + "ids": "3360" + }, + { + "examine": "A magical sphere that glimmers within.", + "ids": "3361" + }, + { + "examine": "A window.", + "ids": "3362,3363" + }, + { + "examine": "An odd looking rock formation.", + "ids": "3364" + }, + { + "examine": "Looks like I can climb these.", + "ids": "3365" + }, + { + "examine": "Makes you cry.", + "ids": "3366" + }, + { + "examine": "Steel bars that are locked securely.", + "ids": "3367,3368,3369,3370" + }, + { + "examine": "An Achey tree stump.", + "ids": "3371" + }, + { + "examine": "Useful for ogre dinners.", + "ids": "3372,3373,3374,3375" + }, + { + "examine": "An ogre bench, useful for taking all that weight off their legs.", + "ids": "3376" + }, + { + "examine": "Hmmm, a rock for a lock....", + "ids": "3377" + }, + { + "examine": "Some stealthy thief must have picked the ogre lock!", + "ids": "3378" + }, + { + "examine": "The entrance to Rantz's cave", + "ids": "3379" + }, + { + "examine": "Exit this way for fresh air and daylight.", + "ids": "3380,3381" + }, + { + "examine": "Useful for ogre dinners.", + "ids": "3382,3383,3384,3385,3386,3387,3388" + }, + { + "examine": "A good source of books!", + "ids": "3389" + }, + { + "examine": "A strange crack cuts into the wall.", + "ids": "3390,3391,3392,3393" + }, + { + "examine": "A wooden crate for storage.", + "ids": "3394,3395" + }, + { + "examine": "A pile of boxes for storage.", + "ids": "3396,3397" + }, + { + "examine": "A wooden crate for storage.", + "ids": "3398,3399,3400,3401" + }, + { + "examine": "Used for fashioning metal items.", + "ids": "3402" + }, + { + "examine": "A pile of Elemental rock.", + "ids": "3403" + }, + { + "examine": "A valve to start and stop the flow of water.", + "ids": "3404,3405" + }, + { + "examine": "This lever can be operated.", + "ids": "3406" + }, + { + "examine": "It spins.", + "ids": "3407,3408" + }, + { + "examine": "This lever can be operated.", + "ids": "3409" + }, + { + "examine": "Big bellows.", + "ids": "3410,3411,3412" + }, + { + "examine": "A furnace with an air blast pipe.", + "ids": "3413" + }, + { + "examine": "A small trough full of lava.", + "ids": "3414" + }, + { + "examine": "I can climb down these stairs.", + "ids": "3415" }, { "examine": "I can climb up these stairs.", - "ids": "24349" + "ids": "3416" }, { - "examine": "A handy place for filling in forms and paperwork.", - "ids": "23928" + "examine": "This lever can be operated.", + "ids": "3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428" }, { - "examine": "Where people may put themselves.", - "ids": "17462" + "examine": "Water powered machinery.", + "ids": "3429,3430" }, { - "examine": "For keeping things in.", - "ids": "24924" + "examine": "Stoney!", + "ids": "3431" }, { - "examine": "Contains quite a lot of someone else's property.", - "ids": "23925" + "examine": "I wonder what's under it?", + "ids": "3432" }, { - "examine": "This chest looks strong.", - "ids": "23926" + "examine": "I can see a holy barrier at the bottom.", + "ids": "3433,3434,3435,3436,3437,3438,3439" }, { - "examine": "They say money can't buy happiness.", - "ids": "23929" + "examine": "One horsepower; wooden suspension: a beauty.", + "ids": "34376" }, { - "examine": "A gilt sculpture of a monarch.", - "ids": "23930" + "examine": "They let you walk up them!", + "ids": "3440" }, { - "examine": "Cave exit.", - "ids": "15812" + "examine": "They let you walk down them!", + "ids": "3441,3442" }, { - "examine": "A flag on a pole.", - "ids": "37335" + "examine": "A holy barrier blocking evil forces.", + "ids": "3443" + }, + { + "examine": "A sturdy metal gate.", + "ids": "3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462" + }, + { + "examine": "The perfect place to store things.", + "ids": "346,347" + }, + { + "examine": "A hastily constructed yet sturdy prison door.", + "ids": "3463,3464,3465" + }, + { + "examine": "Dead animal parts dangling!", + "ids": "3466" + }, + { + "examine": "So how long has this been dead, then?", + "ids": "3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478" + }, + { + "examine": "A statue honouring Saradomin.", + "ids": "3479" + }, + { + "examine": "These open and close!", + "ids": "348" + }, + { + "examine": "An ornately decorated coffin. Something seems to be alive inside.", + "ids": "3480,3481,3482,3483,3484" + }, + { + "examine": "An ornately carved well that draws water from the river Salve.", + "ids": "3485,3486,3487,3488" + }, + { + "examine": "Sturdy looking door.", + "ids": "3489,3490,3491,3492" + }, + { + "examine": "This may be worth searching.", + "ids": "349" + }, + { + "examine": "A monument to a fallen priest.", + "ids": "3493,3494,3495,3496,3497,3498,3499" + }, + { + "examine": "These open and close!", + "ids": "350" + }, + { + "examine": "With skill I can play this.", + "ids": "3500,3501,3502,3503,3504,3505" + }, + { + "examine": "A wrought iron gate.", + "ids": "3506,3507" + }, + { + "examine": "Not sure if I should sit on that.", + "ids": "3508,3509" + }, + { + "examine": "This may be worth searching.", + "ids": "351" + }, + { + "examine": "Not sure if that's any use.", + "ids": "3510,3511" + }, + { + "examine": "It'll probably die soon.", + "ids": "3512,3513" + }, + { + "examine": "Not sure if I should stand under that.", + "ids": "3514,3515" + }, + { + "examine": "A dark dank entrance to below.", + "ids": "3516" + }, + { + "examine": "A large tree thriving where trees normally die.", + "ids": "3517,3518,3519" + }, + { + "examine": "These open and close!", + "ids": "352" + }, + { + "examine": "Special grotto area.", + "ids": "3520" + }, + { + "examine": "A grotto transformed into an altar of nature.", + "ids": "3521" + }, + { + "examine": "It's a bridge.", + "ids": "3522" + }, + { + "examine": "An old bench, probably made from wood in this swamp.", + "ids": "3523,3524" + }, + { + "examine": "The door.", + "ids": "3525" + }, + { + "examine": "The green door.", + "ids": "3526" + }, + { + "examine": "A roughly hewn stone with some sort of symbol on it.", + "ids": "3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545" + }, + { + "examine": "This may be worth searching.", + "ids": "353" + }, + { + "examine": "A wooden crate for storage.", + "ids": "354,355" + }, + { + "examine": "There is a rope leading to the bottom of this well.", + "ids": "3546,3547,3548,3549,3550" + }, + { + "examine": "I can balance on this rope.", + "ids": "3551,3552" + }, + { + "examine": "I can balance on this log.", + "ids": "3553,3554,3555,3556,3557,3558" + }, + { + "examine": "Tread carefully!", + "ids": "3559,3560,3561,3562" + }, + { + "examine": "An old crate for storage.", + "ids": "356,357,358" + }, + { + "examine": "Now, I'm the king of the swingers, oh, the jungle VIP.", + "ids": "3563,3564" + }, + { + "examine": "It looks like I can clamber over this.", + "ids": "3565" + }, + { + "examine": "Use this to swing over to the next platform.", + "ids": "3566" + }, + { + "examine": "I might want to avoid this nasty blade.", + "ids": "3567,3568,3569" + }, + { + "examine": "Walk the plank! Ya land lubber!", + "ids": "3570,3571,3572,3573,3574,3575,3576,3577" + }, + { + "examine": "It's a small step for a player, a giant leap for player kind.", + "ids": "3578,3579" + }, + { + "examine": "Makes sliced human.", + "ids": "3580" + }, + { + "examine": "Releases a ticket when the flashing arrow is above it.", + "ids": "3581" + }, + { + "examine": "Makes human colanders.", + "ids": "3582" + }, + { + "examine": "I can use these to climb across to the other side.", + "ids": "3583,3584" + }, + { + "examine": "Looks suspiciously like a pressure pad to me.", + "ids": "3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607" + }, + { + "examine": "A pile of boxes for storage.", + "ids": "359,360,361" + }, + { + "examine": "Releases a ticket when the flashing arrow is above it.", + "ids": "3608,3609" + }, + { + "examine": "I can use this rope to climb back to the top.", + "ids": "3610,3611,3612,3613,3614,3615,3616" + }, + { + "examine": "Leads down into the Agility Arena.", + "ids": "3617" + }, + { + "examine": "Leads back to Brimhaven.", + "ids": "3618,3619,3620,3621,3622,3623,3624,3625" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "362" + }, + { + "examine": "It looks very sturdy.", + "ids": "3626,3627,3628,3629,3630,3631,3632,3633" + }, + { + "examine": "A wooden barrel containing lots of fish.", + "ids": "363" + }, + { + "examine": "A shrine of the gods!", + "ids": "3634" + }, + { + "examine": "I wonder what's inside...", + "ids": "3635" + }, + { + "examine": "It looks empty.", + "ids": "3636,3637,3638,3639,3640,3641,3642,3643" + }, + { + "examine": "It's got ale in it.", + "ids": "364,32063" + }, + { + "examine": "That's really bright!", + "ids": "3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661" + }, + { + "examine": "These may have something in them.", + "ids": "365" + }, + { + "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": "Useful for transportation of delicate items.", + "ids": "366" + }, + { + "examine": "Smells more like \"innocent villager\" stew to me...", + "ids": "3662,3663,3664" + }, + { + "examine": "Troll leftovers...", + "ids": "3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675" + }, + { + "examine": "This may be worth opening.", + "ids": "367,368,369" + }, + { + "examine": "A stone mechanism that unlocks the equipment room.", + "ids": "3676,3677,3678" }, { "examine": "I can climb down these stairs.", "ids": "36775,36778" }, { - "examine": "Jump here.", - "ids": "23271" + "examine": "The bank teller will serve you from here.", + "ids": "36786" }, { - "examine": "Popular on sandy beaches where fruity cocktails may be found.", - "ids": "18856" + "examine": "Closed for business.", + "ids": "36787" }, { - "examine": "Stop examining signs! You're in the Wilderness now!", - "ids": "14503" + "examine": "Banking transactions are processed here.", + "ids": "36789,37158" }, { - "examine": "Looks like part of the entrance is blocked by rubble.", - "ids": "15767" + "examine": "A standard of Asgarnia.", + "ids": "3679" }, { - "examine": "They know how much that dog in the window is.", - "ids": "28351" + "examine": "The flag of Asgarnia.", + "ids": "3680" }, { - "examine": "Lots of things for a growing fish.", - "ids": "28355" + "examine": "I don't know art, but I like it!", + "ids": "36809" }, { - "examine": "It's a basket for dogs.", - "ids": "28338" + "examine": "Town of Burthorpe", + "ids": "3681,3682,3683,3684" }, { - "examine": "Enough baskets for any dog's needs.", - "ids": "28339" + "examine": "A pile of boxes for storage.", + "ids": "3685,3686,3687,3688,3689,3690,3691" }, { - "examine": "Made by the A. Wilson Company.", - "ids": "28345" + "examine": "Some of the Sherpa's climbing rope.", + "ids": "3692,3693" }, { - "examine": "Sure it's gilded, but would you want to live in it?", - "ids": "28335" + "examine": "Looks like the Imperial Guard's supply of Dual Claws.", + "ids": "3694,3695" }, { - "examine": "It's covered in hairs.", - "ids": "28361" + "examine": "The Imperial Guard's ceremonial armour.", + "ids": "3696" }, { - "examine": "It's good for bedding and stuffing.", - "ids": "28343" + "examine": "A barrel full of swords...", + "ids": "3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721" }, { - "examine": "Lots of little fishies.", - "ids": "28340" + "examine": "Stops people walking past.", + "ids": "37,38,39" }, { - "examine": "Fetch!", - "ids": "28360" + "examine": "This may be worth searching.", + "ids": "370,371,372" }, { - "examine": "It's a basket for cats.", - "ids": "28337" + "examine": "I wonder what this spooky contains.", + "ids": "37051" }, { - "examine": "It seems to be sat happily on the counter.", - "ids": "28356" + "examine": "A rocky outcrop.", + "ids": "3722,3723,3724" }, { - "examine": "Lots of things for a growing pup.", - "ids": "28353" + "examine": "A wooden gate.", + "ids": "3725,3726,3727,3728,3729" }, { - "examine": "Lots of things for a growing kitten.", - "ids": "28354" + "examine": "The perfect accompaniment to a bedroom.", + "ids": "373" }, { - "examine": "Lots of things for a growing pup/kitten/fish.", - "ids": "28357" + "examine": "I can climb over the fence with this.", + "ids": "3730,3731,3732,3733,3734" }, { - "examine": "Things are cheaper by the pound.", - "ids": "28344" + "examine": "A flag on a pole.", + "ids": "37335" }, { - "examine": "It incubates eggs.", - "ids": "28336" + "examine": "Looks like a small cave.", + "ids": "3735" }, { - "examine": "There are a lot of pet supplies here.", - "ids": "28357,28358,28363,28364" + "examine": "The exit to the outside.", + "ids": "3736,3737,3738,3739,3740" }, { - "examine": "Keeps the puppies out of trouble.", - "ids": "28341" + "examine": "A stand for hats!", + "ids": "374" + }, + { + "examine": "No solicitors!", + "ids": "3741" + }, + { + "examine": "Danger", + "ids": "3742" + }, + { + "examine": "A sturdy wooden door.", + "ids": "3743,3744" + }, + { + "examine": "The door is closed.", + "ids": "3745,3746" + }, + { + "examine": "Stops bits of building falling on me.", + "ids": "37451" + }, + { + "examine": "A nicely fitted door.", + "ids": "3747" + }, + { + "examine": "A rocky outcrop.", + "ids": "3748,3749,3750,3751" + }, + { + "examine": "I wonder what's inside.", + "ids": "375,376,377" + }, + { + "examine": "Unusual energy is gathered here.", + "ids": "3752,3753,3754,3755,3756" + }, + { + "examine": "Looks like a small cave.", + "ids": "3757" + }, + { + "examine": "The way out.", + "ids": "3758" + }, + { + "examine": "Looks like a small cave.", + "ids": "3759" + }, + { + "examine": "The way out.", + "ids": "3760" + }, + { + "examine": "The back way out.", + "ids": "3761" + }, + { + "examine": "The back way into the stronghold.", + "ids": "3762" + }, + { + "examine": "The door is closed.", + "ids": "3763" + }, + { + "examine": "The mine has collapsed.", + "ids": "37634" + }, + { + "examine": "The door is open.", + "ids": "3764" + }, + { + "examine": "The door to Mad Eadgar's cell.", + "ids": "3765,3766" + }, + { + "examine": "The door to Godric's cell.", + "ids": "3767,3768" + }, + { + "examine": "Hot!", + "ids": "3769" + }, + { + "examine": "This window looks down onto the Troll Camp.", + "ids": "3770" + }, + { + "examine": "The entrance to the Troll Stronghold.", + "ids": "3771" + }, + { + "examine": "The way out of the Troll Stronghold.", + "ids": "3772,3773,3774" + }, + { + "examine": "Looks like stew of some kind...", + "ids": "3775" + }, + { + "examine": "A door in the Stronghold.", + "ids": "3776,3777,3778,3779" + }, + { + "examine": "Perhaps I should search it.", + "ids": "378,379" + }, + { + "examine": "The door to the prison.", + "ids": "3780,3781" + }, + { + "examine": "The entrance to the Troll Arena.", + "ids": "3782,3783,3784" + }, + { + "examine": "The exit to the Troll Arena.", + "ids": "3785,3786,3787" + }, + { + "examine": "I can climb these stairs.", + "ids": "3788" + }, + { + "examine": "They go down.", + "ids": "3789" + }, + { + "examine": "A rocky outcrop.", + "ids": "3790,3791,3792,3793" + }, + { + "examine": "Looks tasty! If you're a goat, that is.", + "ids": "3794,3795,3796" + }, + { + "examine": "A good source of books!", + "ids": "380,381" + }, + { + "examine": "Not so good for sitting on.", + "ids": "3802" + }, + { + "examine": "A rocky outcrop.", + "ids": "3803,3804" + }, + { + "examine": "They don't seem to roll.", + "ids": "3805" + }, + { + "examine": "They're troll eggs. Or perhaps rocks.", + "ids": "3806" + }, + { + "examine": "These make great pets.", + "ids": "3807" + }, + { + "examine": "It's a troll! Run! No, wait, it's just a rock.", + "ids": "3808,3809" + }, + { + "examine": "The door to the storeroom.", + "ids": "3810,3811,3812" + }, + { + "examine": "It probably hasn't got anything interesting inside.", + "ids": "3813" + }, + { + "examine": "They probably haven't got anything interesting inside.", + "ids": "3814,3815" + }, + { + "examine": "They're closed.", + "ids": "3816" + }, + { + "examine": "They're open.", + "ids": "3817,3818,3819" + }, + { + "examine": "A woven storage basket.", + "ids": "382,383,384,385" + }, + { + "examine": "It doesn't contain anything particularly appetising.", + "ids": "3820" + }, + { + "examine": "Makes you taller.", + "ids": "3821" + }, + { + "examine": "It's full of dried goutweed.", + "ids": "3822" + }, + { + "examine": "Welcome to Mad Eadgar's! If I'm not in, I've probably been captured again.", + "ids": "3823" + }, + { + "examine": "I don't want to look too closely at what's cooking in there...", + "ids": "3824" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "3825" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "3826" + }, + { + "examine": "How am I going to get down there?", + "ids": "3827,3828" + }, + { + "examine": "I hope this holds!", + "ids": "3829" + }, + { + "examine": "How am I going to get down there?", + "ids": "3830,3831" + }, + { + "examine": "I hope this holds!", + "ids": "3832" + }, + { + "examine": "It's moving...", + "ids": "3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847,3848,3849,3850,3851,3852,3853,3854,3855" + }, + { + "examine": "It's a half buried crate.", + "ids": "3856,3857" + }, + { + "examine": "It's stuck in the sand.", + "ids": "3858,3859" + }, + { + "examine": "A gigantic pottery urn.", + "ids": "386" + }, + { + "examine": "It's full of brightly coloured fish.", + "ids": "3860,3861" + }, + { + "examine": "It's a pile of burning bones.", + "ids": "3862" + }, + { + "examine": "The tribal statue of Tai Bwo Wannai Village.", + "ids": "3863,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878" + }, + { + "examine": "This no doubt contains arcane knowledge.", + "ids": "387" + }, + { + "examine": "A tree.", + "ids": "3879" + }, + { + "examine": "I wonder what this spooky item contains.", + "ids": "388" + }, + { + "examine": "It's a tree stump.", + "ids": "3880" + }, + { + "examine": "A tree.", + "ids": "3881,3882,3883" + }, + { + "examine": "It's a tree stump.", + "ids": "3884" + }, + { + "examine": "A tree.", + "ids": "3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897,3898,3899,3900" + }, + { + "examine": "It smells funny in there.", + "ids": "389,390" + }, + { + "examine": "A rare flower.", + "ids": "3901" + }, + { + "examine": "Don't flowers make you feel better?", + "ids": "3902" + }, + { + "examine": "A rare flower.", + "ids": "3903" + }, + { + "examine": "Don't flowers make you feel better?", + "ids": "3904" + }, + { + "examine": "A rare flower.", + "ids": "3905" + }, + { + "examine": "Don't flowers make you feel better?", + "ids": "3906,3907,3908,3909,3910,3911" + }, + { + "examine": "Fungal growth.", + "ids": "3912" + }, + { + "examine": "Poisonous no doubt.", + "ids": "3913,3914" + }, + { + "examine": "Fungal growth.", + "ids": "3915" + }, + { + "examine": "Poisonous no doubt.", + "ids": "3916,3917" + }, + { + "examine": "The lamp has a glowing crystal at its core.", + "ids": "3918" + }, + { + "examine": "It's a trap!", + "ids": "3919,3920" + }, + { + "examine": "A fishing net full of fish.", + "ids": "392" + }, + { + "examine": "A tripwire.", + "ids": "3921" + }, + { + "examine": "An odd-looking group of tied sticks.", + "ids": "3922" + }, + { + "examine": "A pile of leaves.", + "ids": "3923,3924,3925,3926" + }, + { + "examine": "Looks like I can climb these.", + "ids": "3927" + }, + { + "examine": "It's a tree.", + "ids": "3928" + }, + { + "examine": "I can balance on this log.", + "ids": "3929,3930,3931,3932,3933,3934,3935,3936" + }, + { + "examine": "A good source of books!", + "ids": "393" + }, + { + "examine": "There are some broken twigs here.", + "ids": "3937" + }, + { + "examine": "It looks as if the grass here has been flattened.", + "ids": "3938" + }, + { + "examine": "You notice the leaf litter here has been disturbed.", + "ids": "3939,3940" + }, + { + "examine": "Bamboo storage!", + "ids": "394,395,396" + }, + { + "examine": "Tracks in the soil.", + "ids": "3941,3942,3943" + }, + { + "examine": "You'd need a big siege engine to force that.", + "ids": "3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961" + }, + { + "examine": "A yellow crystal structure.", + "ids": "3962,3963,3964,3965,3966" + }, + { + "examine": "A tree", + "ids": "3967,3968" + }, + { + "examine": "Disturbing but tidy.", + "ids": "3969" + }, + { + "examine": "The perfect place to store things.", + "ids": "397" + }, + { + "examine": "I don't even want to think about what did that...", + "ids": "3970" + }, + { + "examine": "I don't wanna fall in that!", + "ids": "3975" + }, + { + "examine": "Good for smashing city walls.", + "ids": "3976" + }, + { + "examine": "There's a winch on this side.", + "ids": "3977" + }, + { + "examine": "There's a lever on this side.", + "ids": "3978,3979" + }, + { + "examine": "I hope no-one's home...", + "ids": "398,26931,26935,26937,26939,32290" + }, + { + "examine": "A way to get into the tent.", + "ids": "3980,3981,3982" + }, + { + "examine": "A tent.", + "ids": "3983,3984,3985,3986" + }, + { + "examine": "A small barrel.", + "ids": "3987,3988,3989,3990,3991" + }, + { + "examine": "I see dead people.", + "ids": "399,36737,32291" + }, + { + "examine": "A standard of Kandarin.", + "ids": "3992,3993" + }, + { + "examine": "A small furnace.", + "ids": "3994" + }, + { + "examine": "A tent wall.", + "ids": "3995" + }, + { + "examine": "A way to get into the tent.", + "ids": "3996" + }, + { + "examine": "A tent wall.", + "ids": "3997" + }, + { + "examine": "It looks as if the grass here has been flattened.", + "ids": "3998" + }, + { + "examine": "There are some broken twigs here.", + "ids": "3999" + }, + { + "examine": "Animals have no table manners.", + "ids": "40" + }, + { + "examine": "A small simple gravestone.", + "ids": "400" + }, + { + "examine": "A tent roof.", + "ids": "4000" + }, + { + "examine": "A tent wall.", + "ids": "4001" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "40018" + }, + { + "examine": "A wooden barrel containing lots of fish.", + "ids": "40019" + }, + { + "examine": "The charred remains of a tent pole.", + "ids": "4002,4003" + }, + { + "examine": "A wooden crate.", + "ids": "40021" + }, + { + "examine": "Some wooden crates.", + "ids": "40022" + }, + { + "examine": "I can climb this.", + "ids": "40026" + }, + { + "examine": "I can climb down this.", + "ids": "40027,20987,29358" + }, + { + "examine": "Items are for sale here.", + "ids": "40028" + }, + { + "examine": "Tick-tock, it's a clock.", + "ids": "40029" + }, + { + "examine": "Useful for putting things on.", + "ids": "40031" + }, + { + "examine": "A case. With books.", + "ids": "40033" + }, + { + "examine": "A drab looking bed.", + "ids": "40035" + }, + { + "examine": "Well of voyage.", + "ids": "4004,4005" + }, + { + "examine": "It really was this big!", + "ids": "40042" + }, + { + "examine": "It really was this big!", + "ids": "40043" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "40044" + }, + { + "examine": "These open and close!", + "ids": "40050" + }, + { + "examine": "These open and close!", + "ids": "40052" + }, + { + "examine": "The perfect place to store things.", + "ids": "40054" + }, + { + "examine": "I can climb up these stairs.", + "ids": "40057" + }, + { + "examine": "It looks like I can squeeze through here.", + "ids": "4006" + }, + { + "examine": "Looking good!", + "ids": "40065" + }, + { + "examine": "The exit to the outside.", + "ids": "4007" + }, + { + "examine": "Trinkets and stuff.", + "ids": "40075" + }, + { + "examine": "Break glass in case in case of emergency.", + "ids": "40076" + }, + { + "examine": "Shrine to the glory of Saradomin.", + "ids": "4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023" + }, + { + "examine": "A simple marker for a forgotten person.", + "ids": "401" + }, + { + "examine": "A large potted plant.", + "ids": "40111" + }, + { + "examine": "The remains of someone lie inside.", + "ids": "402" + }, + { + "examine": "A barrel.", + "ids": "4024,4025" + }, + { + "examine": "A still for fractionalizing oils.", + "ids": "4026" + }, + { + "examine": "A pile of lime rock.", + "ids": "4027,4028,4029,4030" + }, + { + "examine": "A monument to a special person.", + "ids": "403" + }, + { + "examine": "Looks like a stone doorway to me.", + "ids": "4031,4032,4033,4034" + }, + { + "examine": "These clean Druid's robes are drying off.", + "ids": "4035,4036,4037,4038" + }, + { + "examine": "It's full of dirty robes.", + "ids": "4039,4040,4041,4042" + }, + { + "examine": "The inscription is worn away and unreadable.", + "ids": "404" + }, + { + "examine": "Presumably the hatch the zookeeper feeds the parrots through.", + "ids": "4043" + }, + { + "examine": "A red standard.", + "ids": "4044,4045,4046,4047" + }, + { + "examine": "'Here lies...' is all I can read.", + "ids": "405" + }, + { + "examine": "It doesn't look healthy...", + "ids": "4051" + }, + { + "examine": "It's covered in slime.", + "ids": "4052" + }, + { + "examine": "It doesn't look healthy...", + "ids": "4053,4054" + }, + { + "examine": "These fat fungi take up so much room.", + "ids": "4055,4056,4057" + }, + { + "examine": "I can balance on this rope.", + "ids": "4059" + }, + { + "examine": "Whoever bought this must have really cared for the inhabitant.", + "ids": "406" + }, + { + "examine": "It's hollow...", + "ids": "4060" + }, + { + "examine": "This tree has been cut down.", + "ids": "4061" + }, + { + "examine": "Strange items are stored here.", + "ids": "4062" + }, + { + "examine": "This table has seen better days.", + "ids": "4064" + }, + { + "examine": "It looks very ornamental in a practical 'solid rock' way.", + "ids": "4065" + }, + { + "examine": "Welcome to Mort'ton", + "ids": "4066" + }, + { + "examine": "Danger! Sickness and affliction... stay away!", + "ids": "4067" + }, + { + "examine": "Looks like a pile of ancient rubble.", + "ids": "4068" + }, + { + "examine": "The starting of a wall.", + "ids": "4069,4070,4071,4072,4073,4074,4075,4076" + }, + { + "examine": "Looks like a stone doorway to me.", + "ids": "407" + }, + { + "examine": "It's nearly a wall.", + "ids": "4077" + }, + { + "examine": "A rebuilt wall.", + "ids": "4078" + }, + { + "examine": "The broken wall of a desecrated temple.", + "ids": "4079" + }, + { + "examine": "Looks like a stone doorway to my untrained eye.", + "ids": "408" + }, + { + "examine": "The partially repaired wall of a desecrated temple.", + "ids": "4080,4081,4082,4083,4084,4085,4086,4087" + }, + { + "examine": "The nearly repaired wall of a desecrated temple.", + "ids": "4088" + }, + { + "examine": "The repaired wall of a temple.", + "ids": "4089" + }, + { + "examine": "Shrine to the glory of Saradomin.", + "ids": "409" + }, + { + "examine": "A flaming Fire altar.", + "ids": "4090" + }, + { + "examine": "An ancient Fire altar.", + "ids": "4091" + }, + { + "examine": "An ancient, totally desecrated, broken down fire altar.", + "ids": "4092" + }, + { + "examine": "A place to cremate the dead. Needs some wood.", + "ids": "4093" + }, + { + "examine": "A place to cremate the dead. Needs a body.", + "ids": "4094,4095,4096,4097,4098,4099" + }, + { + "examine": "Looks like an outlet pipe to the lake.", + "ids": "41" + }, + { + "examine": "An ancient altar to the glory of Guthix.", + "ids": "410" + }, + { + "examine": "A place to cremate the dead. Needs a light.", + "ids": "4100,4101,4102,4103,4104,4105" + }, + { + "examine": "Heavy metal!", + "ids": "4106,4107,4108,4109,4110" + }, + { + "examine": "Shrine to the glory of Zamorak.", + "ids": "411,412" + }, + { + "examine": "A strangely decorated bronze chest, the lock is painted blood red.", + "ids": "4111" + }, + { + "examine": "A strangely decorated chest, the lock is painted brown.", + "ids": "4112" + }, + { + "examine": "A strangely decorated chest, the lock is painted crimson.", + "ids": "4113" + }, + { + "examine": "A strangely decorated chest, the lock is painted black.", + "ids": "4114" + }, + { + "examine": "A strangely decorated chest, the lock is painted purple.", + "ids": "4115" + }, + { + "examine": "A strangely decorated steel chest, the lock is painted blood red.", + "ids": "4116" + }, + { + "examine": "A strangely decorated steel chest, the lock is painted brown.", + "ids": "4117" + }, + { + "examine": "A strangely decorated steel chest, the lock is painted crimson.", + "ids": "4118" + }, + { + "examine": "A strangely decorated steel chest, the lock is painted black.", + "ids": "4119" + }, + { + "examine": "A strangely decorated steel chest, the lock is painted purple.", + "ids": "4120" + }, + { + "examine": "A strangely decorated black chest, the lock is painted blood red.", + "ids": "4121" + }, + { + "examine": "A strangely decorated black chest, the lock is painted brown.", + "ids": "4122" + }, + { + "examine": "A strangely decorated black chest, the lock is painted crimson.", + "ids": "4123" + }, + { + "examine": "A strangely decorated black chest, the lock is painted black.", + "ids": "4124" + }, + { + "examine": "A strangely decorated black chest, the lock is painted purple.", + "ids": "4125" + }, + { + "examine": "A strangely decorated silver chest, the lock is painted blood red.", + "ids": "4126" + }, + { + "examine": "A strangely decorated silver chest, the lock is painted brown.", + "ids": "4127" + }, + { + "examine": "A strangely decorated silver chest, the lock is painted crimson.", + "ids": "4128" + }, + { + "examine": "A strangely decorated silver chest, the lock is painted black.", + "ids": "4129" + }, + { + "examine": "Looks suspiciously like a big stone table.", + "ids": "413" + }, + { + "examine": "A strangely decorated silver chest, the lock is painted purple.", + "ids": "4130" + }, + { + "examine": "A strangely decorated bronze chest, the open lock is painted blood red.", + "ids": "4131" + }, + { + "examine": "This tells you which way is which.", + "ids": "4132,4133,4134,4135" + }, + { + "examine": "These doors look very ominous. A sign says 'To the tombs'.", + "ids": "4136,4137" + }, + { + "examine": "Items are for sale here.", + "ids": "4138" + }, + { + "examine": "Entrance to the Duel Arena.", + "ids": "4139,4140" + }, + { + "examine": "A really posh upright coffin.", + "ids": "414" + }, + { + "examine": "This seems to be some kind of shrine.", + "ids": "4141" + }, + { + "examine": "The wind makes a musical sound as it blows through it...", + "ids": "4142" + }, + { + "examine": "Danger", + "ids": "4143" + }, + { + "examine": "Those golden fruit look good enough to eat!", + "ids": "4144,4145,4146" + }, + { + "examine": "This must be Lalli's home.", + "ids": "4147" + }, + { + "examine": "Allows performers backstage at the Longhall.", + "ids": "4148" + }, + { + "examine": "It smells pretty good actually.", + "ids": "4149" + }, + { + "examine": "Whoever's inside must have been rich.", + "ids": "415" + }, + { + "examine": "A portal from this mystical place.", + "ids": "4150,4151,4152,4153,4154,4155,4156,4157" + }, + { + "examine": "Takes me into the maze.", + "ids": "4158" + }, + { + "examine": "Takes me out of the maze.", + "ids": "4159,4160,4161" + }, + { + "examine": "With skill I can play this.", + "ids": "416" + }, + { + "examine": "A thin metal pipe, presumably for rainwater to run through.", + "ids": "4162" + }, + { + "examine": "I can climb this.", + "ids": "4163,4164" + }, + { + "examine": "Keeps the wind out.", + "ids": "4165,4166" + }, + { + "examine": "I wonder what's inside?", + "ids": "4167,4168" + }, + { + "examine": "Brrrrrr!", + "ids": "4169" + }, + { + "examine": "Great for sleeping in.", + "ids": "417" + }, + { + "examine": "There is some kind of balancing mechanism as a lock.", + "ids": "4170" + }, + { + "examine": "A good source of books!", + "ids": "4171" + }, + { + "examine": "An appliance for cooking with.", + "ids": "4172" + }, + { + "examine": "I wonder what's down there?", + "ids": "4173" + }, + { + "examine": "I wonder what's under it?", + "ids": "4174" + }, + { + "examine": "Kind of funny smelling...", + "ids": "4175" + }, + { + "examine": "Water comes out of it.", + "ids": "4176" + }, + { + "examine": "This may be worth opening.", + "ids": "4177" + }, + { + "examine": "This may be worth searching.", + "ids": "4178" + }, + { + "examine": "A very unusual piece of artwork...", + "ids": "4179,4180" + }, + { + "examine": "A stylish-looking bed.", + "ids": "418" + }, + { + "examine": "Alas poor unicorn, I knew him well.", + "ids": "4181" + }, + { + "examine": "Looks like the bull lost.", + "ids": "4182" + }, + { + "examine": "A pile of boxes for storage.", + "ids": "4183,4184" + }, + { + "examine": "A wooden crate for storage.", + "ids": "4185" + }, + { + "examine": "An old crate for storage.", + "ids": "4186" + }, + { + "examine": "I can climb this.", + "ids": "4187" + }, + { + "examine": "This leads upwards.", + "ids": "4188" + }, + { + "examine": "This leads downwards.", + "ids": "4189,4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223,4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238,4239,4240,4241,4242,4243,4244,4245,4246" + }, + { + "examine": "A drab-looking bed.", + "ids": "419" + }, + { + "examine": "I can see fish swimming in the water.", + "ids": "42,326" + }, + { + "examine": "A stylish-looking bed.", + "ids": "420,421" + }, + { + "examine": "Technically a bed.", + "ids": "422" + }, + { + "examine": "I guess I could sleep in it if I were really tired.", + "ids": "423" + }, + { + "examine": "Lovely comfy-looking big bed.", + "ids": "424,425" + }, + { + "examine": "Keeps the cold winds out.", + "ids": "4247" + }, + { + "examine": "Doesn't keep the wind out if it's open!", + "ids": "4248,4249" + }, + { + "examine": "I'm sure the animal is glad its fur was put to good use.", + "ids": "4250,4251" + }, + { + "examine": "I wonder what's inside?", + "ids": "4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264" + }, + { + "examine": "Posh-looking bed.", + "ids": "426" + }, + { + "examine": "Toasty.", + "ids": "4265" + }, + { + "examine": "Hot!", + "ids": "4266" + }, + { + "examine": "Finger licking good!", + "ids": "4267,4268" + }, + { + "examine": "It's a long wooden table.", + "ids": "4269" + }, + { + "examine": "A bed fit for a king.", + "ids": "427,428" + }, + { + "examine": "It's a table with candles on.", + "ids": "4270" + }, + { + "examine": "Generally used for sitting.", + "ids": "4271" + }, + { + "examine": "Generally used for putting things on.", + "ids": "4272" + }, + { + "examine": "Actually, I could do with a drink...", + "ids": "4273" + }, + { + "examine": "For leaning against...", + "ids": "4274" + }, + { + "examine": "A wooden barrel containing lots of fish.", + "ids": "4275" + }, + { + "examine": "Always a source of good bargains.", + "ids": "4276" + }, + { + "examine": "There's something fishy about this stall.", + "ids": "4277" + }, + { + "examine": "Around here, this is where you buy new doors.", + "ids": "4278,4279,4280,4281,4282" + }, + { + "examine": "Has a peculiar odour.", + "ids": "4283,4284" + }, + { + "examine": "Water comes out of this.", + "ids": "4285" + }, + { + "examine": "It's kind of like a barrel.", + "ids": "4286" + }, + { + "examine": "Looks pretty comfy.", + "ids": "4287" + }, + { + "examine": "I guess I could sleep in it if I was really tired.", + "ids": "4288,4289,4290,4291,4292,4293,4294,4295" + }, + { + "examine": "One of the nicest beds I have ever seen.", + "ids": "429" + }, + { + "examine": "Yup, looks like this neighbourhood is perfectly safe for travellers...", + "ids": "4296" + }, + { + "examine": "A pile of boxes for storage.", + "ids": "4297,4298,4299" + }, + { + "examine": "It seems to sparkle.", + "ids": "43" + }, + { + "examine": "Perfect for snoozing in the sun.", + "ids": "430" + }, + { + "examine": "A wooden crate for storage.", + "ids": "4300" + }, + { + "examine": "An old crate for storage.", + "ids": "4301,4302,4303" + }, + { + "examine": "A hot place for forging things in.", + "ids": "4304,4305" + }, + { + "examine": "Used for fashioning metal items.", + "ids": "4306" + }, + { + "examine": "Various implements for working with metal.", + "ids": "4307" + }, + { + "examine": "Bake your clay pots in here.", + "ids": "4308" + }, + { + "examine": "Used for spinning thread.", + "ids": "4309" + }, + { + "examine": "A comfortable seat to recline and recuperate.", + "ids": "431" + }, + { + "examine": "Used for fashioning clay items.", + "ids": "4310" + }, + { + "examine": "A wooden gate.", + "ids": "4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326,4327" + }, + { + "examine": "I don't think anyone's slept here for a long time.", + "ids": "432" + }, + { + "examine": "This tree has been cut down.", + "ids": "4328,4329,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4347" + }, + { + "examine": "Bamboo's a versatile material.", + "ids": "433,434,435" + }, + { + "examine": "Best left on the beach.", + "ids": "4348" + }, + { + "examine": "This once belonged to a sea animal.", + "ids": "4349" + }, + { + "examine": "Best left for the crabs.", + "ids": "4350" + }, + { + "examine": "I can hear the sea with this.", + "ids": "4351" + }, + { + "examine": "A half-buried bowl.", + "ids": "4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365" + }, + { + "examine": "A lump of rock.", + "ids": "436,437,438,439" + }, + { + "examine": "An empty shelf...", + "ids": "4366,4367" + }, + { + "examine": "There are some pots and pans here.", + "ids": "4368" + }, + { + "examine": "There are some tankards here.", + "ids": "4369" + }, + { + "examine": "A shelf with a bucket on it.", + "ids": "4370" + }, + { + "examine": "There are a few books on this shelf.", + "ids": "4371" + }, + { + "examine": "It's a small table.", + "ids": "4372" + }, + { + "examine": "A tray of sand.", + "ids": "4373,4374,4375,4376" + }, + { + "examine": "There should be a standard here!", + "ids": "4377,4378,4379" + }, + { + "examine": "I could climb this if I wanted.", + "ids": "4380" + }, + { + "examine": "It's a war machine.", + "ids": "4381,4382" + }, + { + "examine": "I could climb this if I wanted.", + "ids": "4383,4384" + }, + { + "examine": "The catapult is damaged.", + "ids": "4385,4386" + }, + { + "examine": "A portal to join the Saradomin team.", + "ids": "4387" + }, + { + "examine": "A portal to join the Zamorak team.", + "ids": "4388" + }, + { + "examine": "A portal to leave the Saradomin team.", + "ids": "4389" + }, + { + "examine": "A portal to leave the Zamorak team.", + "ids": "4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405" + }, + { + "examine": "I can see really big fish swimming in the water.", + "ids": "44" + }, + { + "examine": "A big slimy lump of rock.", + "ids": "440" + }, + { + "examine": "A portal to leave the Saradomin team.", + "ids": "4406" + }, + { + "examine": "A portal to leave the Zamorak team.", + "ids": "4407" + }, + { + "examine": "A portal to join a random team.", + "ids": "4408,4409,4410" + }, + { + "examine": "A slimy lump of rock.", + "ids": "441" + }, + { + "examine": "A very slippery stepping stone", + "ids": "4411" + }, + { + "examine": "I could climb this if I wanted.", + "ids": "4412,4413" + }, + { + "examine": "A rickety old staircase.", + "ids": "4414" + }, + { + "examine": "A solid stone staircase.", + "ids": "4415,4416,4417,4418,4419,4420,36480,36481,36484,36495,36521,36523,36532,36540" + }, + { + "examine": "A slippery looking rock.", + "ids": "442,443" + }, + { + "examine": "A spiky barricade.", + "ids": "4421,4422" + }, + { + "examine": "A large broken door.", + "ids": "4431,4432,4433,4434,4435,4436" + }, + { + "examine": "We could use our pickaxes to get past these...", + "ids": "4437" + }, + { + "examine": "We can almost get past these...", + "ids": "4438,4439,4440,4441,4442,4443" + }, + { + "examine": "A huge lump of rock.", + "ids": "444,445" + }, + { + "examine": "Should be long enough to scale castle walls.", + "ids": "4444,4445" + }, + { + "examine": "Maybe I could tie a rope to this...", + "ids": "4446,4447" + }, + { + "examine": "It doesn't look very stable...", + "ids": "4448" + }, + { + "examine": "A solid stone staircase.", + "ids": "4449,4450,4451,4452,4453,4454,4455,4456,4457" + }, + { + "examine": "There are some bandages on this table.", + "ids": "4458" + }, + { + "examine": "There are some toolboxes on this table.", + "ids": "4459" + }, + { + "examine": "Some small stones.", + "ids": "446,447,448" + }, + { + "examine": "There's some rock I can use with the catapult here.", + "ids": "4460" + }, + { + "examine": "There are some barricades here.", + "ids": "4461" + }, + { + "examine": "There is some rope here.", + "ids": "4462" + }, + { + "examine": "There are some explosive potions here.", + "ids": "4463" + }, + { + "examine": "There are some pickaxes on this table.", + "ids": "4464" + }, + { + "examine": "An elf-fashioned door.", + "ids": "4465,4466" + }, + { + "examine": "The door is closed.", + "ids": "4467" + }, + { + "examine": "The door is open.", + "ids": "4468" + }, + { + "examine": "Only the Saradomin team may pass.", + "ids": "4469" + }, + { + "examine": "Only the Zamorak team may pass.", + "ids": "4470" + }, + { + "examine": "I wonder what's under it?", + "ids": "4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481" + }, + { + "examine": "Water comes out of this.", + "ids": "4482" + }, + { + "examine": "It's open.", + "ids": "4483" + }, + { + "examine": "Keep track of which team has the most victories.", + "ids": "4484" + }, + { + "examine": "I could climb this if I wanted.", + "ids": "4485,4486" + }, + { + "examine": "A big wooden door.", + "ids": "4487,4488,4489,4490,4491,4492" + }, + { + "examine": "A rocky ledge.", + "ids": "449" + }, + { + "examine": "I can climb these stairs.", + "ids": "4493" + }, + { + "examine": "They go down.", + "ids": "4494" + }, + { + "examine": "I can climb these stairs.", + "ids": "4495" + }, + { + "examine": "They go down.", + "ids": "4496" + }, + { + "examine": "I can climb these stairs.", + "ids": "4497" + }, + { + "examine": "They go down.", + "ids": "4498" + }, + { + "examine": "Wow", + "ids": "4499" + }, + { + "examine": "Big Dave is fishing here.", + "ids": "45" + }, + { + "examine": "Stoney!", + "ids": "450,451,452,453" + }, + { + "examine": "This leads back to the cruel world above.", + "ids": "4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512" + }, + { + "examine": "I doubt that was built to last...", + "ids": "4513,4514,4515,4516,4517" + }, + { + "examine": "How do you make a skeleton laugh?", + "ids": "4518" + }, + { + "examine": "Tickle his funny bone.", + "ids": "4519" + }, + { + "examine": "Clearly has been relaxing too long....", + "ids": "4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538,4539,4540" + }, + { + "examine": "Solid rock from the cave floor.", + "ids": "454,455" + }, + { + "examine": "Did that thing just twitch?", + "ids": "4541,4542" + }, + { + "examine": "A strange wall with objects engraved upon it.", + "ids": "4543,4544" + }, + { + "examine": "A strange wall that appears to be hinged.", + "ids": "4545,4546,4547,4548,4549" + }, + { + "examine": "I can jump off this one.", + "ids": "4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567" + }, + { + "examine": "I can climb up these stairs.", + "ids": "4568" + }, + { + "examine": "I can climb up or go down these stairs.", + "ids": "4569,36777" + }, + { + "examine": "I can climb down these stairs.", + "ids": "4570,4571,4572,4573,4574,4575,4576" + }, + { + "examine": "Lets you walk through walls!", + "ids": "4577,4578,4579,4580,4581,4582,4583,4584,4585,4586" + }, + { + "examine": "It turns the light around.", + "ids": "4587" + }, + { + "examine": "It looks broken.", + "ids": "4588" + }, + { + "examine": "Part of the Lighthouse's lighting mechanism.", + "ids": "4589" + }, + { + "examine": "Part of the lighthouse Light mechanism.", + "ids": "4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609" + }, + { + "examine": "Joshua is fishing here.", + "ids": "46" + }, + { + "examine": "Best left on the beach.", + "ids": "4610" + }, + { + "examine": "This once belonged to a sea animal.", + "ids": "4611" + }, + { + "examine": "Best left for the crabs.", + "ids": "4612" + }, + { + "examine": "I can hear the sea with this.", + "ids": "4613" + }, + { + "examine": "It is a buried bowl.", + "ids": "4614" + }, + { + "examine": "I can jump off this one.", + "ids": "4615,4616" + }, + { + "examine": "Shelves filled with interesting books.", + "ids": "4617" + }, + { + "examine": "A nice and cosy fire.", + "ids": "4618,4619" + }, + { + "examine": "Looks slippery!", + "ids": "462,463" + }, + { + "examine": "They go down.", + "ids": "4620,4621" + }, + { + "examine": "They go up.", + "ids": "4622,4623" + }, + { + "examine": "They go down.", + "ids": "4624,4625" + }, + { + "examine": "They go up.", + "ids": "4626,4627,4628" + }, + { + "examine": "An elf-fashioned door.", + "ids": "4636,4637,4638,4639,4640" + }, + { + "examine": "Formed over many years of dripping limestone.", + "ids": "464,465,466" + }, + { + "examine": "Mmm...beer!", + "ids": "4641" + }, + { + "examine": "Has some board games news on it.", + "ids": "4642" + }, + { + "examine": "The ladder to the Runelink challenge room for experienced players.", + "ids": "4643" + }, + { + "examine": "The ladder back to the Runelink challenge room.", + "ids": "4644" + }, + { + "examine": "The ladder to the Draughts challenge room for experienced players.", + "ids": "4645" + }, + { + "examine": "The ladder back to the Draughts challenge room.", + "ids": "4646" + }, + { + "examine": "The ladder back up to the Toad and Chicken.", + "ids": "4647" + }, + { + "examine": "Leads down to the Burthorpe Games Rooms.", + "ids": "4648,4649" + }, + { + "examine": "A nice and cosy fire.", + "ids": "4650" + }, + { + "examine": "A table for board games.", + "ids": "4651" + }, + { + "examine": "A game of draughts is being played on this table.", + "ids": "4652" + }, + { + "examine": "A game of Runelink is being played on this table.", + "ids": "4653,4654,4655" + }, + { + "examine": "A comfy stool.", + "ids": "4656,4657" + }, + { + "examine": "Draughts challenge room.", + "ids": "4658" + }, + { + "examine": "Runelink challenge room.", + "ids": "4659" + }, + { + "examine": "Draughts challenge room for experienced players.", + "ids": "4660" + }, + { + "examine": "Runelink challenge room for experienced players.", + "ids": "4661,4662,4663,4664,4665,4666,4667,4668" + }, + { + "examine": "A statue of Arrav (the Fountain of Heroes has moved downstairs.)", + "ids": "4669" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "467" + }, + { + "examine": "A statue of Camorra (the Fountain of Heroes has moved downstairs.)", + "ids": "4670" + }, + { + "examine": "A good source of books!", + "ids": "4671" + }, + { + "examine": "The door into the throne room.", + "ids": "4672,4673" + }, + { + "examine": "I bet this makes good syrup!", + "ids": "4674" + }, + { + "examine": "Scented herbs.", + "ids": "4675" + }, + { + "examine": "A rocky outcrop.", + "ids": "4676" + }, + { + "examine": "Scented herbs.", + "ids": "4677,4678,4679,4680,4681,4682" + }, + { + "examine": "Limestone floor growth.", + "ids": "468" + }, + { + "examine": "A large harp.", + "ids": "4683" + }, + { + "examine": "Good for shooting fish in.", + "ids": "4684" + }, + { + "examine": "This old tree is rotting away", + "ids": "4685" + }, + { + "examine": "This old tree is rotting away.", + "ids": "4686,4687,4688,4689" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "469,470" + }, + { + "examine": "Fancy.", + "ids": "4690,4691" + }, + { + "examine": "Flag, pole...Yep, it's a flagpole.", + "ids": "4692,4693,4694,4695" + }, + { + "examine": "The door is closed.", + "ids": "4696" + }, + { + "examine": "The door is open.", + "ids": "4697,4698,4699,4700" + }, + { + "examine": "A wooden gate.", + "ids": "47,48,49,50" + }, + { + "examine": "Keeps the cold winds out.", + "ids": "4701" + }, + { + "examine": "A nicely carved wooden chair.", + "ids": "4702" + }, + { + "examine": "A wooden double bed. For sleeping in...", + "ids": "4703,4704" + }, + { + "examine": "There's something fishy about this stall.", + "ids": "4705" + }, + { + "examine": "You should eat your greens!", + "ids": "4706" + }, + { + "examine": "There's something fishy about this stall.", + "ids": "4707" + }, + { + "examine": "You should eat your greens!", + "ids": "4708,4709" + }, + { + "examine": "A deposit of rocks.", + "ids": "471,472,473,474" + }, + { + "examine": "A doorway made from bamboo.", + "ids": "4710,4711" + }, + { + "examine": "It's a trapdoor.", + "ids": "4712" + }, + { + "examine": "It's an open trapdoor.", + "ids": "4713" + }, + { + "examine": "A wooden crate for storage.", + "ids": "4714" + }, + { + "examine": "This crate is making chattering sounds.", + "ids": "4715" + }, + { + "examine": "This crate smells slightly nauseous.", + "ids": "4716" + }, + { + "examine": "This crate says 'Property of Hamab' on its side.", + "ids": "4717,4718" + }, + { + "examine": "This crate says 'Property of Ifaba' on its side.", + "ids": "4719" + }, + { + "examine": "This crate says 'Property of Daga' on its side.", + "ids": "4720,4721" + }, + { + "examine": "This crate smells slightly of banana.", + "ids": "4722,4723" + }, + { + "examine": "This crate says 'Property of Hamab' on its side.", + "ids": "4724" + }, + { + "examine": "This crate says 'Property of Ifaba' on its side.", + "ids": "4725,4726,4727" + }, + { + "examine": "I can climb up this.", + "ids": "4728" + }, + { + "examine": "A dried up bush, void of life.", + "ids": "4729,4730,4731,4732,4733,4734,4735,4736,4737,4738,4739,4740,4741,4742" + }, + { + "examine": "It's a bamboo ladder.", + "ids": "4743,4744" + }, + { + "examine": "Looks good for jumping off.", + "ids": "4745" + }, + { + "examine": "This crate is marked 'Deliver to Glough'...", + "ids": "4746,4747,4748" + }, + { + "examine": "It's a banana tree, full of bananas.", + "ids": "4749" + }, + { + "examine": "It's a banana tree, with lots of bananas.", + "ids": "4750" + }, + { + "examine": "It's a banana tree, with many bananas.", + "ids": "4751" + }, + { + "examine": "It's a banana tree, with a few bananas.", + "ids": "4752" + }, + { + "examine": "It's a banana tree, with but one banana.", + "ids": "4753" + }, + { + "examine": "It's a banana tree, with no more bananas left.", + "ids": "4754" + }, + { + "examine": "They go down.", + "ids": "4755" + }, + { + "examine": "They go up.", + "ids": "4756,4757,4758,4759,4760,4761,4762,4763" + }, + { + "examine": "It's a bamboo stool.", + "ids": "4764" + }, + { + "examine": "It's a sheer wall of fire, rising as high as you can see.", + "ids": "4765,4766" + }, + { + "examine": "The contents of this pyre are rapidly bubbling and burning.", + "ids": "4767" + }, + { + "examine": "It's a wall of bricks.", + "ids": "4768" + }, + { + "examine": "It's a pile of bricks.", + "ids": "4769,4770" + }, + { + "examine": "Looks slippery!", + "ids": "477,478" + }, + { + "examine": "A rather dapper little monkey sitting on a throne.", + "ids": "4771" + }, + { + "examine": "It's a bamboo ladder.", + "ids": "4772,4773,4774,4775,4776,4777,4778,4779,4780,4781,4782,4783,4784,4785" + }, + { + "examine": "A plant in a bamboo pot.", + "ids": "4786" + }, + { + "examine": "It's an absolutely huge bamboo gate.", + "ids": "4787,4788,4789,4790" + }, + { + "examine": "A deposit of rocks.", + "ids": "479,480,481,482,483,484,485,486" + }, + { + "examine": "It's a big comfy bed, made of strips of bamboo.", + "ids": "4791,4792,4793" + }, + { + "examine": "It's a pot made out of bamboo.", + "ids": "4794" + }, + { + "examine": "They are pots made out of bamboo.", + "ids": "4795,4796" + }, + { + "examine": "Bamboo strips have been lashed together to make this rickety desk.", + "ids": "4797" + }, + { + "examine": "It's a bookcase made out of bamboo.", + "ids": "4798" + }, + { + "examine": "It's a bamboo door with a large iron padlock.", + "ids": "4799,4800,4801,4802,4803,4804,4805,4806" + }, + { + "examine": "It's a door made from bamboo.", + "ids": "4807,4808,4809,4810,4811" + }, + { + "examine": "Looks good for hiding in...", + "ids": "4812,4813,4814" + }, + { + "examine": "Looks good for hiding in.", + "ids": "4815" + }, + { + "examine": "This is a fairly tall tree with sparse foliage.", + "ids": "4816" + }, + { + "examine": "A jungle palm with dense foliage.", + "ids": "4817" + }, + { + "examine": "This is a fairly tall tree with sparse foliage.", + "ids": "4818" + }, + { + "examine": "This was a fairly tall tree with sparse foliage.", + "ids": "4819" + }, + { + "examine": "A leafy tree.", + "ids": "4820" + }, + { + "examine": "This was a fairly tall tree with sparse foliage.", + "ids": "4821" + }, + { + "examine": "This tree has been cut down.", + "ids": "4822" + }, + { + "examine": "This flower is found only in jungle areas.", + "ids": "4823" + }, + { + "examine": "A very rare and exotic flower.", + "ids": "4824" + }, + { + "examine": "A small fernlike plant.", + "ids": "4825" + }, + { + "examine": "Leaves a nasty mark.", + "ids": "4826" + }, + { + "examine": "Looks spiky.", + "ids": "4827" + }, + { + "examine": "This has broad leaves.", + "ids": "4828" + }, + { + "examine": "A type of jungle fern.", + "ids": "4829" + }, + { + "examine": "A large waxy jungle plant.", + "ids": "4830" + }, + { + "examine": "Commonly found in these parts.", + "ids": "4831" + }, + { + "examine": "The tendrils of a creeping plant.", + "ids": "4832" + }, + { + "examine": "A jungle bush, quite common to these areas.", + "ids": "4833,4834" + }, + { + "examine": "A jungle bush that has been chopped down.", + "ids": "4835,4836,4837,4838,4839,4840,4841,4842,4843,4844" + }, + { + "examine": "A terribly tall tropical tree.", + "ids": "4845,4846,4847" + }, + { + "examine": "Leaves of a tropical tree.", + "ids": "4848" + }, + { + "examine": "This tree no doubt contains dangerous insects.", + "ids": "4849,4850,4851" + }, + { + "examine": "This old tree is rotting away.", + "ids": "4852" + }, + { + "examine": "A gnarly old tree root.", + "ids": "4853" + }, + { + "examine": "The roots of this tree are exposed.", + "ids": "4854" + }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "4855" + }, + { + "examine": "It's a small patch of glowing fungus.", + "ids": "4856" + }, + { + "examine": "It's a medium patch of glowing fungus.", + "ids": "4857" + }, + { + "examine": "It's an absolutely colossal statue of some kind of gorilla.", + "ids": "4858" + }, + { + "examine": "It's a huge statue of some kind of gorilla.", + "ids": "4859,4860" + }, + { + "examine": "It's a huge statue of some kind of monkey.", + "ids": "4861" + }, + { + "examine": "It's a large patch of glowing fungus.", + "ids": "4862" + }, + { + "examine": "Legs of this watchtower.", + "ids": "4863,4864" + }, + { + "examine": "It's the middle of this watchtower.", + "ids": "4865" + }, + { + "examine": "It's a deactivated military Gnome glider.", + "ids": "4866" + }, + { + "examine": "It's an activated military Gnome glider.", + "ids": "4867" + }, + { + "examine": "It's some kind of Gnome teleportation device.", + "ids": "4868,4869,4870" + }, + { + "examine": "This is the panel controlling the hangar reinitialisation.", + "ids": "4871" + }, + { + "examine": "There must be an exit nearby.", + "ids": "4872" + }, + { + "examine": "It's probably too risky to try pulling this.", + "ids": "4873" + }, + { + "examine": "This table has crafting paraphernalia on it.", + "ids": "4874" + }, + { + "examine": "This table has ripe bananas on show.", + "ids": "4875" + }, + { + "examine": "This table has items of a general nature on it.", + "ids": "4876" + }, + { + "examine": "This table has runes on display.", + "ids": "4877" + }, + { + "examine": "This table has a scimitar on it.", + "ids": "4878" + }, + { + "examine": "It's a trapdoor.", + "ids": "4879" + }, + { + "examine": "It's an open trapdoor.", + "ids": "4880" + }, + { + "examine": "I can climb up this.", + "ids": "4881" + }, + { + "examine": "Looks suspicious.", + "ids": "4882,4883" + }, + { + "examine": "A plank.", + "ids": "4884" + }, + { + "examine": "Formed over many years.", + "ids": "4885" + }, + { + "examine": "Human sieve creation.", + "ids": "4886" + }, + { + "examine": "Looks suspicious.", + "ids": "4887" + }, + { + "examine": "It's a trapdoor.", + "ids": "4888" + }, + { + "examine": "I can climb up this.", + "ids": "4889,4890,4891,4892,4893,4894,4895,4896,4897,4898,4899" + }, + { + "examine": "The Saradomin Team Standard.", + "ids": "4900" + }, + { + "examine": "The Zamorak Team Standard.", + "ids": "4901" + }, + { + "examine": "The Saradomin Team Standard.", + "ids": "4902" + }, + { + "examine": "The Zamorak Team Standard.", + "ids": "4903" + }, + { + "examine": "It's on fire!", + "ids": "4904,4905,4906,4907,4908,4909" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "4910" + }, + { + "examine": "I can climb down this.", + "ids": "4911,4912,36349,36365" + }, + { + "examine": "It looks cramped and dark.", + "ids": "4913,4914,4915,4916,4917" + }, + { + "examine": "This cart is blocking the way up the ridge.", + "ids": "4918" + }, + { + "examine": "These stairs seem to have been hewn out of the rock itself.", + "ids": "4919" + }, + { + "examine": "A collection of rocks over what looks like a depression.", + "ids": "492" + }, + { + "examine": "It looks cramped and dark.", + "ids": "4920,4921,4922" + }, + { + "examine": "These stairs seem to have been hewn out of the rock itself.", + "ids": "4923" + }, + { + "examine": "A valve to start and stop the flow of water.", + "ids": "4924,4925" + }, + { + "examine": "Shiny!", + "ids": "4926,4927,4928" + }, + { + "examine": "It looks cramped and dark.", + "ids": "4929" + }, + { + "examine": "It's blocking the tunnel, but it looks dark down there anyway.", + "ids": "4930,4931" + }, + { + "examine": "A bizarre fungus. It emits a pale blue light.", + "ids": "4932,4933,4934" + }, + { + "examine": "If that wasn't there I'd probably be a lot flatter now.", + "ids": "4935" + }, + { + "examine": "It prevents the wall unceremoniously squashing people.", + "ids": "4936" + }, + { + "examine": "It looks like it's water powered.", + "ids": "4937,4938,4939,4940,4941" + }, + { + "examine": "It's a little waterlogged. I hope it still works.", + "ids": "4942,4943" + }, + { + "examine": "It doesn't look very watertight. Any contents will be ruined.", + "ids": "4944" + }, + { + "examine": "A sunken mine cart, how useful.", + "ids": "4945,4946,4947,4948" + }, + { + "examine": "This displays information on how the points on this level are set.", + "ids": "4949" + }, + { + "examine": "It has the letter 'B' inscribed on the end.", + "ids": "4950" + }, + { + "examine": "It has the letter 'A' inscribed on the end.", + "ids": "4951" + }, + { + "examine": "It has the letter 'C' inscribed on the end.", + "ids": "4952" + }, + { + "examine": "It has the letter 'D' inscribed on the end.", + "ids": "4953" + }, + { + "examine": "It has the letter 'E' inscribed on the end.", + "ids": "4954" + }, + { + "examine": "It has the letter 'I' inscribed on the end.", + "ids": "4955" + }, + { + "examine": "It has the letter 'J' inscribed on the end.", + "ids": "4956" + }, + { + "examine": "It has the letter 'K' inscribed on the end.", + "ids": "4957" + }, + { + "examine": "I think this changes one of the sets of points.", + "ids": "4958" + }, + { + "examine": "It has the letter 'F' inscribed on the end.", + "ids": "4959" + }, + { + "examine": "It has the letter 'G' inscribed on the end.", + "ids": "4960" + }, + { + "examine": "It has the letter 'H' inscribed on the end.", + "ids": "4961" + }, + { + "examine": "The door is closed.", + "ids": "4962" + }, + { + "examine": "I can climb down this.", + "ids": "4965" + }, + { + "examine": "I can climb this.", + "ids": "4966" + }, + { + "examine": "I can climb down this.", + "ids": "4967" + }, + { + "examine": "I can climb this.", + "ids": "4968" + }, + { + "examine": "I can climb down this.", + "ids": "4969" + }, + { + "examine": "I can climb this.", + "ids": "4970" + }, + { + "examine": "These stairs seem to have been hewn out of the rock itself.", + "ids": "4971,4972,4973" + }, + { + "examine": "Useful for moving items around the mine.", + "ids": "4974" + }, + { + "examine": "An old crate for storage.", + "ids": "4975" + }, + { + "examine": "A distinctive layer in the rock strata.", + "ids": "4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000" + }, + { + "examine": "A powerful ranging device, unfortunately not working.", + "ids": "5" + }, + { + "examine": "This cave has been boarded up.", + "ids": "5001" + }, + { + "examine": "Useful for passing over inaccessible areas.", + "ids": "5002" + }, + { + "examine": "It's old and pretty broken.", + "ids": "5003" + }, + { + "examine": "A well used tree.", + "ids": "5004,5005" + }, + { + "examine": "These flowers only grow in one place in the mountains.", + "ids": "5006" + }, + { + "examine": "Looks like small cave.", + "ids": "5007" + }, + { + "examine": "Looks dark...", + "ids": "5008,5009,5010,5011,5012,5013,5014" + }, + { + "examine": "It's too dangerous to go down here by foot...", + "ids": "5015,5016,5017,5018,5019,5020,5021,5022,5023,5024" + }, + { + "examine": "A split in the cave wall...", + "ids": "5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036" + }, + { + "examine": "A large ice covered boulder.", + "ids": "5037" + }, + { + "examine": "An ice covered boulder.", + "ids": "5038" + }, + { + "examine": "A small ice covered boulder.", + "ids": "5039,5040,5041,5042" + }, + { + "examine": "A huge ice gate.", + "ids": "5043,5044" + }, + { + "examine": "An interesting-looking tree.", + "ids": "5045" + }, + { + "examine": "A small cave entrance.", + "ids": "5046" + }, + { + "examine": "Limestone ceiling growth.", + "ids": "5047" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "5048" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "5049" + }, + { + "examine": "Tooth shaped rock formations protruding from the floor.", + "ids": "5050" + }, + { + "examine": "A dirty little swamp boat.", + "ids": "5051" + }, + { + "examine": "If it had paint on it, you'd probably watch it dry.", + "ids": "5052,5053" + }, + { + "examine": "I can climb this.", + "ids": "5054" + }, + { + "examine": "I wonder what that's there for...", + "ids": "5055" + }, + { + "examine": "These doors look very ominous.", + "ids": "5056,5057" + }, + { + "examine": "Large doors set into the hillside.", + "ids": "5058,5059" + }, + { + "examine": "These doors seem to lead into the underground.", + "ids": "5060,5061" + }, + { + "examine": "Large doors set into the hillside.", + "ids": "5062,5063,5064,5065,5066,5067,5068,5069,5070,5071" + }, + { + "examine": "A deposit of rocks.", + "ids": "507,508,509,510" + }, + { + "examine": "A Large Pillar", + "ids": "5072,5073,5074,5075,5076,5077,5078,5079,5080,5081" + }, + { + "examine": "An overgrown dungeon entrance.", + "ids": "5082" + }, + { + "examine": "A closed overgrown dungeon entrance.", + "ids": "5083" + }, + { + "examine": "The way to go when I get scared.", + "ids": "5084,5085,5086,5087" + }, + { + "examine": "I hope I don't fall off this.", + "ids": "5088,5089,5090,5091,5092,5093" + }, + { + "examine": "These stairs seem to have been hewn out of the rock itself.", + "ids": "5094,5095,5096,5097,5098" + }, + { + "examine": "Hmmm... I wonder if it's loose enough to get through.", + "ids": "51" + }, + { + "examine": "Thick vines blocking your way.", + "ids": "5103,5104,5105,5106,5107,5108,5109" + }, + { + "examine": "A rock with a pickaxe and mining tools.", + "ids": "511" + }, + { + "examine": "I can jump from this stepping stone.", + "ids": "5110,5111" + }, + { + "examine": "Not good for eating.", + "ids": "5112" + }, + { + "examine": "I doubt that's edible.", + "ids": "5113,5114,5115" + }, + { + "examine": "A statue of a big monster.", + "ids": "5116,5117,5118,5119,5120,5121" + }, + { + "examine": "A tall wooden door.", + "ids": "5126" + }, + { + "examine": "A tall wooden door.", + "ids": "5128,5129" + }, + { + "examine": "Looks like whoever uses this has claws on their hands.", + "ids": "5130" + }, + { + "examine": "I wonder what's under it?", + "ids": "5131" + }, + { + "examine": "I wonder what's down there?", + "ids": "5132" + }, + { + "examine": "A hurdle.", + "ids": "5133,5134,5135" + }, + { + "examine": "A climbing wall made from skulls.", + "ids": "5136,5137" + }, + { + "examine": "A very slippery stepping stone.", + "ids": "5138" + }, + { + "examine": "A scary zip line for teeth??", + "ids": "5139,5140,5141,5142,5143,5144,5145" + }, + { + "examine": "A goal.", + "ids": "5146,5147,5148,5149,5150,5151" + }, + { + "examine": "This may be worth opening.", + "ids": "5156" + }, + { + "examine": "This may be worth searching.", + "ids": "5157" + }, + { + "examine": "A pile of garden canes.", + "ids": "5158" + }, + { + "examine": "There's no way to cross this bridge.", + "ids": "5159,5160" + }, + { + "examine": "A very tall column of ice.", + "ids": "516" + }, + { + "examine": "An old grandfather clock.", + "ids": "5161" + }, + { + "examine": "A closed chest.", + "ids": "5162" + }, + { + "examine": "An open chest.", + "ids": "5163" + }, + { + "examine": "There is a note pinned to the signpost.", + "ids": "5164" + }, + { + "examine": "It looks like it needs a good sweep out.", + "ids": "5165" + }, + { + "examine": "Bookish.", + "ids": "5166" + }, + { + "examine": "A large, elaborately ornamented memorial stone.", + "ids": "5167" + }, + { + "examine": "A grave, marked by an ostentatious memorial stone.", + "ids": "5168" + }, + { + "examine": "A poor grave marked by a simple, wooden cross.", + "ids": "5169" + }, + { + "examine": "A deposit of rocks.", + "ids": "517,518,519,520" + }, + { + "examine": "The entrance to an extravagantly decorated mausoleum.", + "ids": "5170,5171" + }, + { + "examine": "The door to the Tower.", + "ids": "5172,5173" + }, + { + "examine": "The door to the garden shed.", + "ids": "5174,5175" + }, + { + "examine": "At some point the lightning conductor has broken, rendering it useless.", + "ids": "5176" + }, + { + "examine": "A shocking piece of kit.", + "ids": "5177" + }, + { + "examine": "Mmm.. scented candles.", + "ids": "5178,5179" + }, + { + "examine": "I wouldn't eat there!", + "ids": "5180,5181,5182" + }, + { + "examine": "Looks wooden. Feels wooden. I wonder is it wooden?", + "ids": "5183,5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198,5199,5200,5201" + }, + { + "examine": "It's locked.", + "ids": "52,53" + }, + { + "examine": "Dangerous, someone could trip on them.", + "ids": "5202,5203,5204,5205" + }, + { + "examine": "I can climb these stairs.", + "ids": "5206" + }, + { + "examine": "They go down.", + "ids": "5207,5208,5209,5210,5211,5212,5213,5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227" + }, + { + "examine": "Formed over many years of dripping limestone.", + "ids": "5228,5229,5230" + }, + { + "examine": "Formed over many years of dripping limestone.", + "ids": "523,524,525,526,527,528" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243" + }, + { + "examine": "Looks a bit tatty...", + "ids": "5244,5245" + }, + { + "examine": "A well slept in bed.", + "ids": "5246,5247" + }, + { + "examine": "It looks like a tree made of crystal.", + "ids": "5248" + }, + { + "examine": "Looks to me like a quickly built fire.", + "ids": "5249" + }, + { + "examine": "I can climb down this.", + "ids": "5250" + }, + { + "examine": "I can climb this.", + "ids": "5251" + }, + { + "examine": "A recently extinguished fire.", + "ids": "5252" + }, + { + "examine": "I wish I could sting other people.", + "ids": "5253" + }, + { + "examine": "Probably feels like a mild bee sting.", + "ids": "5254" + }, + { + "examine": "I wouldn't like to get stung.", + "ids": "5255" + }, + { + "examine": "Dock leaves at the ready.", + "ids": "5256" + }, + { + "examine": "Nettles sting my leggies.", + "ids": "5257" + }, + { + "examine": "These may hurt.", + "ids": "5258" + }, + { + "examine": "A ghostly barrier.", + "ids": "5259,5260,5261" + }, + { + "examine": "These stairs were carved out of the rock of the cavern.", + "ids": "5262,5263" + }, + { + "examine": "I can climb this.", + "ids": "5264" + }, + { + "examine": "I can climb up here.", + "ids": "5265" + }, + { + "examine": "I can go below decks with this ladder.", + "ids": "5266" + }, + { + "examine": "I wonder what's under it.", + "ids": "5267" + }, + { + "examine": "I wonder what's down there.", + "ids": "5268" + }, + { + "examine": "I can't see a rock!", + "ids": "5269" + }, + { + "examine": "I wonder what's inside.", + "ids": "5270,5271,5272" + }, + { + "examine": "Perhaps I should search it.", + "ids": "5273" + }, + { + "examine": "High above here is a tattered flag, blowing in the wind.", + "ids": "5274" + }, + { + "examine": "An appliance for cooking with.", + "ids": "5275" + }, + { + "examine": "The bank teller will serve you from here.", + "ids": "5276" + }, + { + "examine": "This booth is closed.", + "ids": "5277" + }, + { + "examine": "The resting place of Necrovarus' mortal body.", + "ids": "5278,5279" + }, + { + "examine": "I can climb these stairs.", + "ids": "5280" + }, + { + "examine": "They go down.", + "ids": "5281" + }, + { + "examine": "A ghastly fountain filled with slime and bones, the source of Necrovarus' power.", + "ids": "5282" + }, + { + "examine": "It's a small Ectofuntus.", + "ids": "5283" + }, + { + "examine": "A big grinding thing.", + "ids": "5284" + }, + { + "examine": "The tatty gangplank of a tatty ship.", + "ids": "5285,5286" + }, + { + "examine": "Seen better days.", + "ids": "5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "529" + }, + { + "examine": "Formed over many years of dripping limestone.", + "ids": "530" + }, + { + "examine": "A wooden rowing boat.", + "ids": "5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "531" + }, + { + "examine": "Limestone floor growth.", + "ids": "532" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "533" + }, + { + "examine": "A pile of boxes for storage.", + "ids": "5330" + }, + { + "examine": "A leafy fern.", + "ids": "5331" + }, + { + "examine": "A small bushy plant.", + "ids": "5332" + }, + { + "examine": "A leafy shrub.", + "ids": "5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "534" + }, + { + "examine": "This needs dusting before I'll sit on it.", + "ids": "5346,5347" + }, + { + "examine": "Not so good for sitting on.", + "ids": "5348" + }, + { + "examine": "Generally used for putting things on.", + "ids": "5349" + }, + { + "examine": "Actually, I could do with a drink...", + "ids": "5350" + }, + { + "examine": "For leaning against...", + "ids": "5351" + }, + { + "examine": "One horsepower; wooden suspension: a beauty.", + "ids": "5352" + }, + { + "examine": "A wooden wheelbarrow.", + "ids": "5353" + }, + { + "examine": "Animal feeder.", + "ids": "5354" + }, + { + "examine": "It probably hasn't got anything interesting inside.", + "ids": "5355" + }, + { + "examine": "They probably haven't got anything interesting inside.", + "ids": "5356,5357" + }, + { + "examine": "Looks like he's been dead a while now...", + "ids": "5358" + }, + { + "examine": "I don't think he'd mind us checking for his wallet now...", + "ids": "5359" + }, + { + "examine": "I'm sure he died of natural causes. Like a massive dragon or something...", + "ids": "5360,5361,5362,5363,5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396" + }, + { + "examine": "A tooth shaped rock formation protruding from the ceiling.", + "ids": "539,540" + }, + { + "examine": "This figure brings luck to those who sail.", + "ids": "5397,5398,5399,5400,5401,5402" + }, + { + "examine": "It's a flight of stairs.", + "ids": "54,55,56,57" + }, + { + "examine": "Without this I'm going around in circles.", + "ids": "5403" + }, + { + "examine": "Holds up the sails.", + "ids": "5404,5405,5406,5407,5408,5409,5410,5411,5412,5413,5414" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "541,542" + }, + { + "examine": "Allows access to other parts of the ship.", + "ids": "5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428" + }, + { + "examine": "It's a table with candles on.", + "ids": "5429" + }, + { + "examine": "A tooth shaped rock formation protruding from the ceiling.", + "ids": "543" + }, + { + "examine": "Doesn't look too good...", + "ids": "5430" + }, + { + "examine": "Looks a bit empty.", + "ids": "5431,5432,5433,5434,5435,5436,5437,5438" + }, + { + "examine": "It's been knocked off its hinges.", + "ids": "5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "544" + }, + { + "examine": "A huge lump of rock.", + "ids": "545" + }, + { + "examine": "It's a door.", + "ids": "5453,5454,5455,5456,5457,5458,5459,5460" + }, + { + "examine": "Intimidating!", + "ids": "546,547" + }, + { + "examine": "A subterranean pool of ectoplasm.", + "ids": "5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487" + }, + { + "examine": "A deposit of rocks.", + "ids": "548,549,550,551,552,553" + }, + { + "examine": "I can climb this.", + "ids": "5488" + }, + { + "examine": "The finest rings.", + "ids": "5489" + }, + { + "examine": "I wonder what's under it?", + "ids": "5490" + }, + { + "examine": "I wonder what's down there?", + "ids": "5491,5492" + }, + { + "examine": "I can climb this.", + "ids": "5493" + }, + { + "examine": "A crude torch stuck in the ground.", + "ids": "5494" + }, + { + "examine": "I guess I could sleep in it if I was really tired.", + "ids": "5495,5496" + }, + { + "examine": "A well slept in bed.", + "ids": "5497,5498" + }, + { + "examine": "Hot!", + "ids": "5499,5500" + }, + { + "examine": "Stops people getting out.", + "ids": "5501,5502,5503,5504,5505,5506,5507" + }, + { + "examine": "It's a lectern.", + "ids": "5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537" + }, + { + "examine": "Makes you cry.", + "ids": "5538" + }, + { + "examine": "Found near the water's edge.", + "ids": "5539,5540,5541,5542" + }, + { + "examine": "Frozen water has formed an icicle here.", + "ids": "554,555,556,557" + }, + { + "examine": "Isn't Heather a girls' name?", + "ids": "5543" + }, + { + "examine": "Smells lovely!", + "ids": "5544" + }, + { + "examine": "Who is this Heather girl?", + "ids": "5545" + }, + { + "examine": "A purple haze of delight.", + "ids": "5546" + }, + { + "examine": "The colour purple on stems.", + "ids": "5547" + }, + { + "examine": "I ponder... Why is it purple?", + "ids": "5548" + }, + { + "examine": "I wonder why it's purple?", + "ids": "5549" + }, + { + "examine": "A cure for nettle stings.", + "ids": "5550" + }, + { + "examine": "A droopy tree.", + "ids": "5551" + }, + { + "examine": "These trees are found near water.", + "ids": "5552,5553" + }, + { + "examine": "This is what is left of a willow tree.", + "ids": "5554" + }, + { + "examine": "I don't know art, but I like it!", + "ids": "5555,5556,5557" + }, + { + "examine": "Aggie's cooking something, probably best not to think what...", + "ids": "5558,9736" + }, + { + "examine": "Trinkets and stuff.", + "ids": "5559" + }, + { + "examine": "Looking good!", + "ids": "5560" + }, + { + "examine": "Aggie's broomstick.", + "ids": "5561" + }, + { + "examine": "Aggie has already dyed this cloth.", + "ids": "5562,5563" + }, + { + "examine": "A thoroughly used bed.", + "ids": "5564" + }, + { + "examine": "A lovely comfy-looking big bed.", + "ids": "5565,5566,5567,5568" + }, + { + "examine": "The remains of a stone wall.", + "ids": "5569,5570" + }, + { + "examine": "An empty home for chickens.", + "ids": "5571" + }, + { + "examine": "A lovely bare chicken coop.", + "ids": "5572,5573" + }, + { + "examine": "Full of animal feed.", + "ids": "5574" + }, + { + "examine": "Animal feeder.", + "ids": "5575,5576,5577" + }, + { + "examine": "A traveller's companion.", + "ids": "5578" + }, + { + "examine": "Loaded with hay and ready to roll.", + "ids": "5579" + }, + { + "examine": "It looks cold in there.", + "ids": "558,559,560,561" + }, + { + "examine": "A wooden wheelbarrow.", + "ids": "5580" + }, + { + "examine": "Someone's been chopping logs.", + "ids": "5581,5582" + }, + { + "examine": "Baby bread.", + "ids": "5583,5584,5585,5586,5587,5588" + }, + { + "examine": "The river makes it spin.", + "ids": "5589,5590,5591,5592,5593" + }, + { + "examine": "I'd better not get my hands trapped in that.", + "ids": "5594" + }, + { + "examine": "Diango's Toy Stall.", + "ids": "5595" + }, + { + "examine": "Doesn't look too good...", + "ids": "5596" + }, + { + "examine": "Shows which way the wind blows.", + "ids": "5597" + }, + { + "examine": "A barrel for collecting rain water.", + "ids": "5598,5599,5600,5601,5602,5603" + }, + { + "examine": "A rock.", + "ids": "5604" + }, + { + "examine": "A small rock.", + "ids": "5605" + }, + { + "examine": "Some rock.", + "ids": "5606,5607" + }, + { + "examine": "This fire is already in use, I wouldn't mess with it if I were you...", + "ids": "5608" + }, + { + "examine": "They could do with a wash.", + "ids": "5609,5610,5611,5612" + }, + { + "examine": "It smells like the rats aren't washing often enough.", + "ids": "5613" + }, + { + "examine": "Sit back and relax...", + "ids": "5615" + }, + { + "examine": "This tells you which way is which.", + "ids": "5616,5617" + }, + { + "examine": "These open and close!", + "ids": "5618" + }, + { + "examine": "This may be worth searching.", + "ids": "5619,5620" + }, + { + "examine": "This person was of great importance.", + "ids": "562" + }, + { + "examine": "An elegant ceramic pot tarnished with the dirt of a hundred years.", + "ids": "5621" + }, + { + "examine": "I wonder what's inside...", + "ids": "5622" + }, + { + "examine": "It smells a bit stuffy.", + "ids": "5623" + }, + { + "examine": "It smells funny in there.", + "ids": "5624" + }, + { + "examine": "Ned is making some rope here.", + "ids": "5625,5626" + }, + { + "examine": "A blue standard.", + "ids": "5627" + }, + { + "examine": "A standard of Lumbridge Castle.", + "ids": "5628,5629,5630,36925" + }, + { + "examine": "A monarch chiselled in stone.", + "ids": "563,564" + }, + { + "examine": "Glowing embers.", + "ids": "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,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787" + }, + { + "examine": "A sculpture of a monarch.", + "ids": "565" + }, + { + "examine": "This person was of great importance.", + "ids": "566" + }, + { + "examine": "A huge carving of an ancient race.", + "ids": "567" + }, + { + "examine": "A depiction of a great dwarven miner.", + "ids": "568" + }, + { + "examine": "A depiction of a famous gnome warrior.", + "ids": "569,570" + }, + { + "examine": "The head and shoulders of a famous gnome.", + "ids": "571,572" + }, + { + "examine": "A depiction of a famous gnomeballer.", + "ids": "573" + }, + { + "examine": "A carving of a figure from the history of 2009Scape.", + "ids": "574,575,576,577,578" + }, + { + "examine": "The sort of bench you get in churches.", + "ids": "5788" + }, + { + "examine": "An expertly carved statue of a former King of Misthalin.", + "ids": "5789,5790" + }, + { + "examine": "An expertly chiselled statue.", + "ids": "579,580,581" + }, + { + "examine": "A carving of a figure from the history of 2009Scape.", + "ids": "5791,36749,36751" + }, + { + "examine": "When I've ground some flour, I'll be able to collect it here.", + "ids": "5792" + }, + { + "examine": "It's a large crack in the wall.", + "ids": "5793,5794,5795,5796,5797,5798" + }, + { + "examine": "It's a trapdoor.", + "ids": "5799,5800,5801,5802" + }, + { + "examine": "Known commonly as Red worm vine.", + "ids": "58" + }, + { + "examine": "It's an open trapdoor.", + "ids": "5803,5804,5805,5806,5807" + }, + { + "examine": "An incredibly detailed stone sculpture.", + "ids": "5808" + }, + { + "examine": "Helps the Seers predict the weather when it's working.", + "ids": "5809" + }, + { + "examine": "Helps the Seers predict the weather.", + "ids": "5810,5811" + }, + { + "examine": "I can climb this.", + "ids": "5812" + }, + { + "examine": "I can climb down this.", + "ids": "5813,5814" + }, + { + "examine": "A candle, a lens and some sort of crystal all precariously balanced together.", + "ids": "5815" + }, + { + "examine": "A beacon so gnome gliders can safely land.", + "ids": "5816,5817,5818,5819,5820,5821,5822,5823,5824" + }, + { + "examine": "An unusual symbol of ancient times.", + "ids": "582" + }, + { + "examine": "Fly Gnome Air.", + "ids": "5825" + }, + { + "examine": "This shop deals in antique swords.", + "ids": "5826,5827,5828,5829,5830" + }, + { + "examine": "An expertly crafted vase.", + "ids": "583" + }, + { + "examine": "Shop counter.", + "ids": "5831,5832,5833,5834" + }, + { + "examine": "Don't want to close this, I might not be able to get back down.", + "ids": "5835,5836,5837,5838,5839,5840,5841" + }, + { + "examine": "An expertly chiselled statue of a bird.", + "ids": "584" + }, + { + "examine": "It overlooks the path below. Let's hope it won't fall on your head.", + "ids": "5842" + }, + { + "examine": "Someone is climbing down there! Looks dangerous.", + "ids": "5843" + }, + { + "examine": "Placed in a perfect position for stumbling over.", + "ids": "5844" + }, + { + "examine": "There is a rope going over it.", + "ids": "5845" + }, + { + "examine": "Someone is climbing down the rope.", + "ids": "5846" + }, + { + "examine": "You will need to climb over it.", + "ids": "5847" + }, + { + "examine": "The tree stands tall at the edge of the pool.", + "ids": "5848" + }, + { + "examine": "You can't stand on them, but maybe something can be wedged into there.", + "ids": "5849" + }, + { + "examine": "What a good likeness!", + "ids": "585,586" + }, + { + "examine": "Something put on top of this wouldn't fall off, it's quite smooth and flat.", + "ids": "5850,5851" + }, + { + "examine": "Someone put a plank on top of the stone! Clever...", + "ids": "5852,5853,5854" + }, + { + "examine": "The pool looks very peaceful. You can also hear faint singing coming from it.", + "ids": "5855" + }, + { + "examine": "The legendary White Pearl fruit is growing on these thorny bushes!", + "ids": "5856" + }, + { + "examine": "There's a nasty stench coming from the cave.", + "ids": "5857" + }, + { + "examine": "Faint rays of daylight shine through.", + "ids": "5858" + }, + { + "examine": "It's very dark down there.", + "ids": "5859" + }, + { + "examine": "Very pointy, very sharp.", + "ids": "5860,5861" + }, + { + "examine": "This is the place where you buried Asleif.", + "ids": "5862" + }, + { + "examine": "Asleif was given a proper burial on this spot.", + "ids": "5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876,5877" + }, + { + "examine": "A depiction of the evil god Zamorak.", + "ids": "587" + }, + { + "examine": "It's a long wooden table.", + "ids": "5878" + }, + { + "examine": "Generally used for putting things on.", + "ids": "5879" + }, + { + "examine": "A base for the statue to sit on.", + "ids": "588" + }, + { + "examine": "A crude torch stuck in the ground.", + "ids": "5881,5882" + }, + { + "examine": "Sticky, dirty mud.", + "ids": "5883" + }, + { + "examine": "This stinks...", + "ids": "5884" + }, + { + "examine": "The roots go down into the mud.", + "ids": "5885" + }, + { + "examine": "Sticky, dirty roots covered in sticky, dirty mud.", + "ids": "5886" + }, + { + "examine": "It's barely a door, really.", + "ids": "5887,5888,5889,5890" + }, + { + "examine": "The scrying glass of a seer.", + "ids": "589" + }, + { + "examine": "The entry to a special tent in the camp.", + "ids": "5891,5892,5893,5894" + }, + { + "examine": "It's just a big stone, really.", + "ids": "5895" + }, + { + "examine": "Uh oh, someone is going to be in trouble!", + "ids": "5896" + }, + { + "examine": "The pool looks very peaceful. You can also hear faint singing coming from it.", + "ids": "5897,5898,5899,5900,5901" + }, + { + "examine": "A fancy hole in the wall.", + "ids": "59" + }, + { + "examine": "Banking transactions are processed here.", + "ids": "590" + }, + { + "examine": "It's only useful for hiding behind now.", + "ids": "5902,5903,5904" + }, + { + "examine": "This tree has been cut down.", + "ids": "5905,5906,5907,5908" + }, + { + "examine": "A still for making lamp oil.", + "ids": "5909" + }, + { + "examine": "Banking transactions are recorded here.", + "ids": "591" + }, + { + "examine": "The still has oil in.", + "ids": "5910,5911,5912,5913,5914,5915,5916" + }, + { + "examine": "Noxious fumes bubble up from the bowels of the earth.", + "ids": "5917,5918,5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945" + }, + { + "examine": "Contains washing items.", + "ids": "592" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "593,594,595" + }, + { + "examine": "You see a circle of light at the top.", + "ids": "5946" + }, + { + "examine": "An entrance to the dark caves.", + "ids": "5947" + }, + { + "examine": "I can jump from this stepping stone.", + "ids": "5948,5949,5950,5951,5952,5953,5954,5955,5956,5957,5958" + }, + { + "examine": "I wonder what this does...", + "ids": "5959,5960,5961,5962,5963" + }, + { + "examine": "A banquet could be eaten from this.", + "ids": "596,597" + }, + { + "examine": "Flying in mid-air!", + "ids": "5964,5965" + }, + { + "examine": "A blocked passage.", + "ids": "5966" + }, + { + "examine": "I'd better leave his expensive dwarven balls alone.", + "ids": "5967,5968,5969,5970,5971,5972" + }, + { + "examine": "A small cave entrance.", + "ids": "5973,5974" + }, + { + "examine": "A powerful ranging device that fires metal balls.", + "ids": "5975,5976" + }, + { + "examine": "It's a sheer wall of fire, rising as high as you can see.", + "ids": "5977,5978,5979,5980" + }, + { + "examine": "Useful for putting things on.", + "ids": "598" + }, + { + "examine": "Lighting for the caves.", + "ids": "5981,5982,5983,5984" + }, + { + "examine": "A rock.", + "ids": "5985" + }, + { + "examine": "A small rock.", + "ids": "5986" + }, + { + "examine": "A rock.", + "ids": "5987,5988" + }, + { + "examine": "A mineral vein that looks distinctly like gold.", + "ids": "5989,5990,5991,5992,5993,5994,5995,5996,5997" + }, + { + "examine": "At one time it was for putting things on.", + "ids": "599" + }, + { + "examine": "Functions like an open door...", + "ids": "5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008" + }, + { + "examine": "A powerful ranging device that fires metal balls.", + "ids": "6" + }, + { + "examine": "A fancy hole in the ceiling.", + "ids": "60" + }, + { + "examine": "The napkins are made from real silk.", + "ids": "600" + }, + { + "examine": "It's a tiny little blue flame. Is this the essence of the Arzinian Being?", + "ids": "6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031" + }, + { + "examine": "Useful for putting things on.", + "ids": "601,602,603,604,605,606" + }, + { + "examine": "Powers the boat.", + "ids": "6032,6033,6034,6035" + }, + { + "examine": "Allows access to other parts of the ship.", + "ids": "6036,6037" + }, + { + "examine": "Without this I'm going around in circles.", + "ids": "6038,6039,6040,6041,6042" + }, + { + "examine": "Powers the boat.", + "ids": "6043,6044" + }, + { + "examine": "You can 'cart' things around on this.", + "ids": "6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063" + }, + { + "examine": "A stand for hats.", + "ids": "6064" + }, + { + "examine": "Some dwarf clothes that are obviously too small for me!", + "ids": "6065" + }, + { + "examine": "An empty weapon rack.", + "ids": "6066" + }, + { + "examine": "A weapon rack.", + "ids": "6067,6068,6069" + }, + { + "examine": "Items for making clothes are kept on here.", + "ids": "607" + }, + { + "examine": "A method of dwarf storage", + "ids": "6070" + }, + { + "examine": "A method of dwarf storage.", + "ids": "6071,6072,6073,6074" + }, + { + "examine": "Useful for a dwarf.", + "ids": "6075" + }, + { + "examine": "A big desk, for a dwarf!", + "ids": "6076,6077,6078,6079" + }, + { + "examine": "The ideal place to study.", + "ids": "608" + }, + { + "examine": "Spins yarn.", + "ids": "6080" + }, + { + "examine": "Banking transactions are processed here.", + "ids": "6081" + }, + { + "examine": "Banking transactions are recorded here.", + "ids": "6082,37159" + }, + { + "examine": "This booth is closed.", + "ids": "6083" + }, + { + "examine": "The bank teller will serve you from here.", + "ids": "6084" + }, + { + "examine": "A way upwards.", + "ids": "6085" + }, + { + "examine": "A way down.", + "ids": "6086" + }, + { + "examine": "A way upwards.", + "ids": "6087" + }, + { + "examine": "A way down.", + "ids": "6088" + }, + { + "examine": "A way upwards.", + "ids": "6089" + }, + { + "examine": "A handy workbench for a handy person.", + "ids": "609,610" + }, + { + "examine": "A way down.", + "ids": "6090" + }, + { + "examine": "A treasure trove of knowledge.", + "ids": "6091,6092" + }, + { + "examine": "A lovely place to cook meat.", + "ids": "6093,6094,6095" + }, + { + "examine": "A great place to cook meat.", + "ids": "6096" + }, + { + "examine": "A good source of water.", + "ids": "6097,6098,6099" + }, + { + "examine": "A shrine to evil.", + "ids": "61" + }, + { + "examine": "The door is closed.", + "ids": "6100" + }, + { + "examine": "The door is open.", + "ids": "6101" + }, + { + "examine": "The door is closed.", + "ids": "6102" + }, + { + "examine": "The door is open.", + "ids": "6103" + }, + { + "examine": "The door is closed.", + "ids": "6104" + }, + { + "examine": "The door is open.", + "ids": "6105" + }, + { + "examine": "The door is closed.", + "ids": "6106" + }, + { + "examine": "The door is open.", + "ids": "6107" + }, + { + "examine": "The door is closed.", + "ids": "6108" + }, + { + "examine": "The door is open.", + "ids": "6109" + }, + { + "examine": "Sit back and enjoy the view.", + "ids": "611" + }, + { + "examine": "The door is closed.", + "ids": "6110" + }, + { + "examine": "The door is open.", + "ids": "6111" + }, + { + "examine": "The door is closed.", + "ids": "6112" + }, + { + "examine": "The door is open.", + "ids": "6113" + }, + { + "examine": "The door is closed.", + "ids": "6114" + }, + { + "examine": "The door is open.", + "ids": "6115,6116,6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132,6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,6144,6145" + }, + { + "examine": "Items are for sale here.", + "ids": "612" + }, + { + "examine": "Useful for putting things on.", + "ids": "613,614,615" + }, + { + "examine": "Useful for keeping birds.", + "ids": "6146" + }, + { + "examine": "Tower of rock.", + "ids": "6147,6148,6149" + }, + { + "examine": "Useful for making weapons.", + "ids": "6150" + }, + { + "examine": "Used for getting water.", + "ids": "6151" + }, + { + "examine": "Used for getting drunk.", + "ids": "6152,6153" + }, + { + "examine": "Used for storage.", + "ids": "6154,6155,6156,6157" + }, + { + "examine": "Not good for eating.", + "ids": "6158,6159,6160,6161" + }, + { + "examine": "A creepy looking table.", + "ids": "616" + }, + { + "examine": "Finest precious stones.", + "ids": "6162" + }, + { + "examine": "Bread and cakes are spread out over it.", + "ids": "6163" + }, + { + "examine": "Finely wrought wares of silver.", + "ids": "6164" + }, + { + "examine": "I can get clothes made up from this stall.", + "ids": "6165" + }, + { + "examine": "A whole lot of tools for crafting.", + "ids": "6166,6167,6168,6169,6170,6171,6172" + }, + { + "examine": "Items are for sale here.", + "ids": "617" + }, + { + "examine": "A fine piece of sculpting.", + "ids": "6173,6174,6175" + }, + { + "examine": "Oblong boxes. You hope there isn't anything other than salt inside.", + "ids": "6176" + }, + { + "examine": "Big mysterious crates. You wonder what could be inside.", + "ids": "6177" + }, + { + "examine": "Wooden crates, contents unknown.", + "ids": "6178" + }, + { + "examine": "They could do with a wash.", + "ids": "6179,6180" + }, + { + "examine": "There are some strange chemicals here.", + "ids": "618,619" + }, + { + "examine": "Dead animal parts dangling!", + "ids": "6181" + }, + { + "examine": "Dead meat. Dangling from the wall. Looks delicious.", + "ids": "6182" + }, + { + "examine": "It looks as hard as a rock, not very inviting.", + "ids": "6183" + }, + { + "examine": "A smelly old mattress.", + "ids": "6184" + }, + { + "examine": "Metal plating to protect the dwarf.", + "ids": "6185,6186" + }, + { + "examine": "Useful... for a dwarf!", + "ids": "6187" + }, + { + "examine": "Various implements for working with metal.", + "ids": "6188" + }, + { + "examine": "A hot place for forging things in.", + "ids": "6189,6190" + }, + { + "examine": "Being repaired.", + "ids": "6191,6192" + }, + { + "examine": "I look shorter, but not necessarily sweeter!", + "ids": "6193" + }, + { + "examine": "Used for sitting.", + "ids": "6194,6195" + }, + { + "examine": "Useful for a dwarf.", + "ids": "6196,6197,6198,6199" + }, + { + "examine": "Is there something in there?", + "ids": "62" + }, + { + "examine": "Bamboo's a versatile material.", + "ids": "620" + }, + { + "examine": "Fit for a dwarven feast!", + "ids": "6200,6201" + }, + { + "examine": "Gives out light, but then you knew that already.", + "ids": "6202,6203" + }, + { + "examine": "A simple place to sleep.", + "ids": "6204" + }, + { + "examine": "A good dwarven bed. Too small for me!", + "ids": "6205" + }, + { + "examine": "Many important meetings are held here...", + "ids": "6206" + }, + { + "examine": "Draped in cloth.", + "ids": "6207" + }, + { + "examine": "A beautiful seat, fit for a King.", + "ids": "6208,6209,6210" + }, + { + "examine": "It's a banquet table.", + "ids": "621,622" + }, + { + "examine": "A dwarf table with chopping board.", + "ids": "6211" + }, + { + "examine": "This tree has been cut down.", + "ids": "6212" + }, + { + "examine": "A barrel full of ranging equipment...", + "ids": "6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "623,624,625,626,627" + }, + { + "examine": "I suppose he wants my money.", + "ids": "6230" + }, + { + "examine": "A lovely table.", + "ids": "6231" + }, + { + "examine": "An expensive water feature.", + "ids": "6232,6233,6234,6235" + }, + { + "examine": "Stop for what, food?", + "ids": "6236" + }, + { + "examine": "Yellow blossom, lovely.", + "ids": "6237" + }, + { + "examine": "Closed.", + "ids": "6238" + }, + { + "examine": "Open.", + "ids": "6239" + }, + { + "examine": "Closed.", + "ids": "6240" + }, + { + "examine": "Open.", + "ids": "6241" + }, + { + "examine": "A stone staircase.", + "ids": "6242,6243,6244,6245" + }, + { + "examine": "Has upon it a beer, which I will not take for moral reasons.", + "ids": "6246,6247,6248" + }, + { + "examine": "No this is not a mirage!", + "ids": "6249" + }, + { + "examine": "It's a goldfish! No, wait, it's just a bed.", + "ids": "6250,6251,6252,6253,6254" + }, + { + "examine": "Animal feeder.", + "ids": "6255,6256" + }, + { + "examine": "Lovely... Fresh.", + "ids": "6257" + }, + { + "examine": "Dry dung.", + "ids": "6258" + }, + { + "examine": "Smelly.", + "ids": "6259" + }, + { + "examine": "I can climb down this.", + "ids": "6260" + }, + { + "examine": "I can climb this.", + "ids": "6261,6262" + }, + { + "examine": "Useful for pets.", + "ids": "6263" + }, + { + "examine": "Useful for keeping birds.", + "ids": "6264" + }, + { + "examine": "Bird cage.", + "ids": "6265,6266" + }, + { + "examine": "An empty cage, maybe the owner let the creature free... I do hope so.", + "ids": "6267" + }, + { + "examine": "Useful for pets.", + "ids": "6268,6269,6270,6271,6272,6273" + }, + { + "examine": "Lovely comfy looking big bed.", + "ids": "6274" + }, + { + "examine": "Laden with heaps of paper.", + "ids": "6275" + }, + { + "examine": "I don't know much about art, but I like this.", + "ids": "6276" + }, + { + "examine": "Look at the size of this cactus.", + "ids": "6277" + }, + { + "examine": "Don't you open that trapdoor!", + "ids": "6278" + }, + { + "examine": "There is a rope leading to the bottom of this smoke filled well.", + "ids": "6279" + }, + { + "examine": "Finely wrought wares of silver.", + "ids": "628" + }, + { + "examine": "I can climb this.", + "ids": "6280,6281" + }, + { + "examine": "A portal to another land?", + "ids": "6282" + }, + { + "examine": "Weird looking pillar.", + "ids": "6283,6284,6285,6286,6287,6288,6289,6290,6291" + }, + { + "examine": "Garments for the discerning.", + "ids": "629" + }, + { + "examine": "Shelves filled with interesting books.", + "ids": "6292,6293" + }, + { + "examine": "A locked display case for valuable artefacts.", + "ids": "6294" + }, + { + "examine": "One of the sculptures is missing. Oh yes, that was me!", + "ids": "6295" + }, + { + "examine": "It looks like the demon didn't survive after all.", + "ids": "6296" + }, + { + "examine": "He looks old, older than me anyway.", + "ids": "6297" + }, + { + "examine": "I don't think he's going to make it.", + "ids": "6298" + }, + { + "examine": "I don't understand, why didn't he go back to Lumby?", + "ids": "6299,6300" + }, + { + "examine": "Used for storage.", + "ids": "63,64,36351,36352" + }, + { + "examine": "Bread cakes and pastries.", + "ids": "630" + }, + { + "examine": "A throne encrusted with sparkling gems.", + "ids": "6301" + }, + { + "examine": "A throne from which you have removed the gems.", + "ids": "6302,6303,6304,6305,6306" + }, + { + "examine": "A statuette of a golem, facing right.", + "ids": "6307" + }, + { + "examine": "A statuette of a golem, facing left.", + "ids": "6308" + }, + { + "examine": "The statuette is missing from this alcove.", + "ids": "6309,6310" + }, + { + "examine": "Finest precious stones.", + "ids": "631" + }, + { + "examine": "Not good for eating.", + "ids": "6311" + }, + { + "examine": "I can climb this.", + "ids": "6312" + }, + { + "examine": "I can climb down this.", + "ids": "6313,6314,6315,6316,6317,6318,6319,6320,6321,6322" + }, + { + "examine": "These will keep you warm.", + "ids": "632" + }, + { + "examine": "A broken clay arm.", + "ids": "6323" + }, + { + "examine": "A clay foot.", + "ids": "6324" + }, + { + "examine": "Half a golem smashed and broken.", + "ids": "6325" + }, + { + "examine": "Smashed and half buried.", + "ids": "6326" + }, + { + "examine": "A device once used for making pottery.", + "ids": "6327" + }, + { + "examine": "Once upon a time this was used to make pottery.", + "ids": "6328" + }, + { + "examine": "Once upon a time this made clay hard.", + "ids": "6329" + }, + { + "examine": "The spice is right.", + "ids": "633" + }, + { + "examine": "There was a lot of pottery made here a long time ago.", + "ids": "6330" + }, + { + "examine": "A statue of a guy with a hammer.", + "ids": "6331" + }, + { + "examine": "A weathered old statue.", + "ids": "6332" + }, + { + "examine": "A reclining lady.", + "ids": "6333" + }, + { + "examine": "This statue has been tampered with.", + "ids": "6334" + }, + { + "examine": "Contains a statue of a woman.", + "ids": "6335,6336,6337" + }, + { + "examine": "Empty.", + "ids": "6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362" + }, + { + "examine": "Always a source of good bargains.", + "ids": "634" + }, + { + "examine": "Fine brews from exotic regions.", + "ids": "635" + }, + { + "examine": "Keeps mine carts from rolling away.", + "ids": "636" + }, + { + "examine": "A very heavy stone door.", + "ids": "6363" + }, + { + "examine": "A door to a demon's lair?", + "ids": "6364,6365,6366,6367,6368,6369,6370,6371" + }, + { + "examine": "You can 'cart' things around on this.", + "ids": "637" + }, + { + "examine": "A stone staircase.", + "ids": "6372,6373,6374,6375,6376,6377,6378,6379,6380" + }, + { + "examine": "A source of water for the river Elid.", + "ids": "6381" + }, + { + "examine": "Maybe I could swing on this somehow...", + "ids": "6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397,6398,6399,6400,6401" + }, + { + "examine": "A limestone ceiling growth.", + "ids": "6402" + }, + { + "examine": "A tooth shaped rock formation protruding from the floor.", + "ids": "6403" + }, + { + "examine": "An old mystical torch.", + "ids": "6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,6414,6415,6416,6417" + }, + { + "examine": "I can climb this.", + "ids": "6418,6419" + }, + { + "examine": "I wonder what's inside?", + "ids": "6420,6421,6422" + }, + { + "examine": "A magical aura seems to shimmer over the glass...", + "ids": "6423,6424,6425,6426,6427,6428,6429,6430,6431,6432,6433" + }, + { + "examine": "I wonder what that's there for...", + "ids": "6434,6435" + }, + { + "examine": "I can climb this.", + "ids": "6436" + }, + { + "examine": "An ancient looking tomb.", + "ids": "6437,6438" + }, + { + "examine": "Climb this rope to go up.", + "ids": "6439,6440" + }, + { + "examine": "Looks like a small cave.", + "ids": "6441" + }, + { + "examine": "Blocked by an icicle.", + "ids": "6442" + }, + { + "examine": "Blocked by two icicles.", + "ids": "6443" + }, + { + "examine": "Blocked by three icicles.", + "ids": "6444" + }, + { + "examine": "Blocked by four icicles.", + "ids": "6445" + }, + { + "examine": "Blocked by five icicles.", + "ids": "6446" + }, + { + "examine": "Looks like a small cave.", + "ids": "6447" + }, + { + "examine": "It looks very sturdy.", + "ids": "6448,6449" + }, + { + "examine": "I can climb this.", + "ids": "6450" + }, + { + "examine": "A wrought iron gate.", + "ids": "6451,6452,6453" + }, + { + "examine": "Even rocks could freeze in this cold!", + "ids": "6454" + }, + { + "examine": "Looks slippery.", + "ids": "6455,6456,6457,6458,6459,6460" + }, + { + "examine": "An ice gate.", + "ids": "6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471" + }, + { + "examine": "Makes you taller.", + "ids": "647,29314" + }, + { + "examine": "Chunky pieces of ice.", + "ids": "6472,6473,6474,6475,6476,6477,6478,6479,6480" + }, + { + "examine": "A bit large for a bracelet!", + "ids": "648" + }, + { + "examine": "A mysterious tunnel-like structure.", + "ids": "6481,6482" + }, + { + "examine": "You can feel a mysterious power emanating from it...", + "ids": "6483,6484,6485,6486,6487,6488,6489,6490,6491,6492,6493" + }, + { + "examine": "A macabre variation on the neck tie.", + "ids": "649,650" + }, + { + "examine": "This door is sealed by an ancient mystical power...", + "ids": "6494,6495,6496" + }, + { + "examine": "I can climb down this.", + "ids": "6497,6498,6499,6500" + }, + { + "examine": "Surprisingly sturdy looking.", + "ids": "65,66" + }, + { + "examine": "I can climb this.", + "ids": "6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511" + }, + { + "examine": "Dead and half buried.", + "ids": "651,652,653,654,655,656,657" + }, + { + "examine": "A fancy name for a coffin.", + "ids": "6512" + }, + { + "examine": "A strange thing to leave lying around...", + "ids": "6513" + }, + { + "examine": "I hope this is just ornamental...", + "ids": "6514,6515,6516,6517,6518,6519,6520" + }, + { + "examine": "Uh-oh!", + "ids": "6521,6522" + }, + { + "examine": "This leads downwards.", + "ids": "6523" + }, + { + "examine": "You can see a small fissure in the ground here.", + "ids": "6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535,6536,6537" + }, + { + "examine": "The body of a lion, the head of a man?", + "ids": "6538,6539,6540,6541,6542,6543,6544" + }, + { + "examine": "A smooth, sandstone door that is slightly warm to the touch.", + "ids": "6545,6546,6547,6548" + }, + { + "examine": "A well down into the pyramid.", + "ids": "6549,6550" + }, + { + "examine": "A portal that leads you out of the pyramid.", + "ids": "6551" + }, + { + "examine": "A mysterious ancient altar to some forgotten god...", + "ids": "6552" + }, + { + "examine": "Opens into another area.", + "ids": "6553,6554,6555,6556,6557,6558,6559,6560" + }, + { + "examine": "A ladder that's almost not there at all...", + "ids": "6561,6562,6563,6564,6565" + }, + { + "examine": "Gate like?", + "ids": "6566,6567" + }, + { + "examine": "Garments for the discerning.", + "ids": "6568" + }, + { + "examine": "Gone-off bread, cakes and pastries.", + "ids": "6569" + }, + { + "examine": "Finest precious stones.", + "ids": "6570" + }, + { + "examine": "These will keep you warm.", + "ids": "6571" + }, + { + "examine": "The spice is right.", + "ids": "6572" + }, + { + "examine": "An empty market stall.", + "ids": "6573" + }, + { + "examine": "Fine brews from exotic regions.", + "ids": "6574" + }, + { + "examine": "Best used with a horse.", + "ids": "6575,6576,6577" + }, + { + "examine": "Grows a yellow fruit.", + "ids": "6578,6579,6580" + }, + { + "examine": "Disturbing but tidy.", + "ids": "658" + }, + { + "examine": "Disturbing but tidy.", + "ids": "658,668,669,670,3969,29347" + }, + { + "examine": "A way to get in the tent.", + "ids": "6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604" + }, + { + "examine": "I don't even want to think about what did that...", + "ids": "659" + }, + { + "examine": "Now that's what I call slimline!", + "ids": "660,664,3797,3971,5122,7206,29340" + }, + { + "examine": "Wet and salty.", + "ids": "6605" + }, + { + "examine": "The focus of the suntrap.", + "ids": "6606" + }, + { + "examine": "A shiny mirror.", + "ids": "6607,6608,6609,6610,6611,6612,6613" + }, + { + "examine": "He looks very relaxed.", + "ids": "661,665,3798,3972,5123,7207,29341" + }, + { + "examine": "A large door with a hieroglyph of a cat.", + "ids": "6614" + }, + { + "examine": "Gate like?", + "ids": "6615,6616,6617,6618,6619" + }, + { + "examine": "Now he's just too thin.", + "ids": "662,666,3799,3973,5124,7208,7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,29342" + }, + { + "examine": "It looks like a hole in the wall.", + "ids": "6620,6621" + }, + { + "examine": "Rocky.", + "ids": "6622" + }, + { + "examine": "An entrance into a tunnel. I wonder where it leads to.", + "ids": "6623" + }, + { + "examine": "It looks like a door.", + "ids": "6624" + }, + { + "examine": "Hmm. A door.", + "ids": "6625" + }, + { + "examine": "A big wooden door.", + "ids": "6626,6627,6628" + }, + { + "examine": "That could hurt.", + "ids": "6629" + }, + { + "examine": "He hasn't eaten in a long time.", + "ids": "663,667,3974,5125,29343" + }, + { + "examine": "A strange thing to leave lying around...", + "ids": "6630,6631" + }, + { + "examine": "I can't see the bottom.", + "ids": "6632,6633,6634" + }, + { + "examine": "Has a lid shaped like a man. I think it contains someone's liver. Yuck.", + "ids": "6635,6636" + }, + { + "examine": "Has a lid shaped like a crocodile. Yuck, I think there are lungs inside.", + "ids": "6637,6638" + }, + { + "examine": "Has a lid shaped like a bug. Disgusting! I think there's a stomach inside.", + "ids": "6639,6640" + }, + { + "examine": "Has a lid shaped like an ape. Eeew! I think it contains someone's intestines.", + "ids": "6641" + }, + { + "examine": "Doorway*Opens into another area, come on, I know this.", + "ids": "6642" + }, + { + "examine": "Useful for putting things on.", + "ids": "6644" + }, + { + "examine": "A ladder!! Never seen one of those before.", + "ids": "6645" + }, + { + "examine": "I wonder what's inside.", + "ids": "6646" + }, + { + "examine": "Perhaps I should search it.", + "ids": "6647" + }, + { + "examine": "I wonder where this goes?", + "ids": "6648" + }, + { + "examine": "They go down.", + "ids": "6649" + }, + { + "examine": "Phew!! That's one big bridge.", + "ids": "6650" + }, + { + "examine": "A big bridge over the river.", + "ids": "6651,6652" + }, + { + "examine": "Wow. More bridge. Awesome.", + "ids": "6653,6654" + }, + { + "examine": "Oops.", + "ids": "6655" + }, + { + "examine": "Rolls of colourful cloth.", + "ids": "6656" + }, + { + "examine": "An ancient giant serpent.", + "ids": "6657" + }, + { + "examine": "A tunnel leading upwards.", + "ids": "6658" + }, + { + "examine": "A tunnel leading into the depths of the earth.", + "ids": "6659" + }, + { + "examine": "A rock wall infused with the power of Guthix.", + "ids": "6660" + }, + { + "examine": "The wall is weeping blue tears.", + "ids": "6661" + }, + { + "examine": "The wall is weeping green tears.", + "ids": "6662" + }, + { + "examine": "The wall is not weeping at the moment.", + "ids": "6663,6664" + }, + { + "examine": "The wall is weeping blue tears.", + "ids": "6665" + }, + { + "examine": "The wall is weeping green tears.", + "ids": "6666" + }, + { + "examine": "The wall is not weeping at the moment.", + "ids": "6667,6668" + }, + { + "examine": "Stone with blue veins.", + "ids": "6669,6670,6671" + }, + { + "examine": "They don't look too easy to climb.", + "ids": "6672" + }, + { + "examine": "I could climb these.", + "ids": "6673,6674,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701" + }, + { + "examine": "Used for storage.", + "ids": "67" + }, + { + "examine": "I can climb these stairs.", + "ids": "6702,6703,6704,6705,6706,6707" + }, + { + "examine": "I can climb this.", + "ids": "6708,6709,6710,6711,6712" + }, + { + "examine": "No cats were harmed in the making of this device.", + "ids": "671" + }, + { + "examine": "I wonder what is awaiting me on the other side?", + "ids": "6713,6714,6715,6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731" + }, + { + "examine": "Shattered.", + "ids": "672,673,674,675,676,677" + }, + { + "examine": "I wonder what awaits me on the other side?", + "ids": "6732,6733,6734,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761,6762,6763,6764,6765,6766" + }, + { + "examine": "A crude torch stuck in the ground.", + "ids": "6767,6768,6769,6770" + }, + { + "examine": "A large stone coffin.", + "ids": "6771,6772,6773" + }, + { + "examine": "A large stone chest.", + "ids": "6774,6775" + }, + { + "examine": "Looks like he's been dead a while now...", + "ids": "6776" + }, + { + "examine": "I don't think he'd mind us checking for his wallet now...", + "ids": "6777" + }, + { + "examine": "I'm sure he died of natural causes. Like a massive dragon or something...", + "ids": "6778,6779,6780,6781,6782,6783,6784,6785,6786,6787,6788,6789,6790" + }, + { + "examine": "I see fish.", + "ids": "678" + }, + { + "examine": "I can see fish swimming in the water.", + "ids": "679" + }, + { + "examine": "The groundskeeper's bed.", + "ids": "6791,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820" + }, + { + "examine": "Where bees live.", + "ids": "68" + }, + { + "examine": "I was always forced to play this as a child.", + "ids": "680" + }, + { + "examine": "I never learnt to play.", + "ids": "681" + }, + { + "examine": "Time for a recital?", + "ids": "682" + }, + { + "examine": "A large stone coffin.", + "ids": "6821,6822,6823" + }, + { + "examine": "It's locked.", + "ids": "6824,6825,6826" + }, + { + "examine": "It might still work...", + "ids": "6827,6828,6829,6830,6831,6832,6833,6834" + }, + { + "examine": "Tick-tock, it's a clock.", + "ids": "683" + }, + { + "examine": "Danger... weak surface beyond gate. Digging may lead to cave-ins.", + "ids": "6835" + }, + { + "examine": "Sturdy metal bars.", + "ids": "6836,6837,6838" + }, + { + "examine": "Smells pretty bad!", + "ids": "6839" + }, + { + "examine": "The noxious liquid bubbles horribly.", + "ids": "684" + }, + { + "examine": "It looks as this is where some wall fungus used to be.", + "ids": "6840" + }, + { + "examine": "Some crude stone steps.", + "ids": "6841,6842,6843" + }, + { + "examine": "An old crude tomb.", + "ids": "6844,6845" + }, + { + "examine": "It's damaged.", + "ids": "6846" + }, + { + "examine": "A note says, 'Please ring for attention.'", + "ids": "6847" + }, + { + "examine": "An old crude tomb.", + "ids": "6848" + }, + { + "examine": "'Leave da dead-uns boxes 'lone or else ya goes down da 'ole'", + "ids": "6849" + }, + { + "examine": "Shows which way the wind blows.", + "ids": "685,686,687,688" + }, + { + "examine": "An old crude tomb.", + "ids": "6850" + }, + { + "examine": "This coffin is spilt over the floor.", + "ids": "6851" + }, + { + "examine": "This coffin is open.", + "ids": "6852" + }, + { + "examine": "An old crude tomb.", + "ids": "6853" + }, + { + "examine": "This coffin is spilt over the floor.", + "ids": "6854" + }, + { + "examine": "This coffin is open.", + "ids": "6855" + }, + { + "examine": "A barricade made from skulls and bones.", + "ids": "6856,6857,6858,6859,6860,6861,6862,6863,6864" + }, + { + "examine": "Not very high.", + "ids": "6865" + }, + { + "examine": "Hot stuff.", + "ids": "6866" + }, + { + "examine": "Ogres bang these to make noise.", + "ids": "6867,6868,6869,6870" + }, + { + "examine": "A bulky door made from solid rock.", + "ids": "6871,6872,6873,6874" + }, + { + "examine": "These open and close!", + "ids": "6875" + }, + { + "examine": "This may be worth opening.", + "ids": "6876" + }, + { + "examine": "I wonder what this item contains.", + "ids": "6877,6878,6879" + }, + { + "examine": "A barricade made of skulls and bones.", + "ids": "6880" + }, + { + "examine": "A barricade made of skulls and bones which has been crushed.", + "ids": "6881,6882" + }, + { + "examine": "An old crude tomb.", + "ids": "6883,6884,6885,6886,6887" + }, + { + "examine": "A sick, frail old man.", + "ids": "6888" + }, + { + "examine": "A sick, frail old man", + "ids": "6889" + }, + { + "examine": "Looking good!", + "ids": "689" + }, + { + "examine": "An old crude tomb.", + "ids": "6890,6891,6892" + }, + { + "examine": "I don't think he'd mind us checking for his wallet now...", + "ids": "6893" + }, + { + "examine": "A good source of books!", + "ids": "6894,6895" + }, + { + "examine": "A crude torch stuck in the ground.", + "ids": "6896" + }, + { + "examine": "A strange ogre plinth, this must be where the artifacts are stored.", + "ids": "6897,6898,6899,6900,6901,6902" + }, + { + "examine": "Handy for boarding boats.", + "ids": "69,70" + }, + { + "examine": "A privacy aid!", + "ids": "690" + }, + { + "examine": "It looks like the hole in the wall has been blocked with rubble.", + "ids": "6903" + }, + { + "examine": "How exciting, some shelves.", + "ids": "6904" + }, + { + "examine": "A hole in the wall.", + "ids": "6905,6906,6907,6908" + }, + { + "examine": "A rock wall.", + "ids": "6909" + }, + { + "examine": "Use this to get changed behind and retain modesty.", + "ids": "691" + }, + { + "examine": "It has a letter 'S' on the lock.", + "ids": "6910" + }, + { + "examine": "Used for storage.", + "ids": "6911" + }, + { + "examine": "A narrow hole in the wall.", + "ids": "6912,6913,6914" + }, + { + "examine": "Rubble is blocking the passage.", + "ids": "6915" + }, + { + "examine": "A good source of books!", + "ids": "6916,6917,6918" + }, + { + "examine": "A heavy portal decorated with bone.", + "ids": "6919,6920" + }, + { + "examine": "I don't even want to think about what's cooking in that.", + "ids": "692" + }, + { + "examine": "A symbol is carved into the wall here.", + "ids": "6921,6922,6923,6924,6925,6926" + }, + { + "examine": "I don't think I can get through this way!", + "ids": "6927" + }, + { + "examine": "Looks like its no longer operational.", + "ids": "6928" + }, + { + "examine": "Big bones are being used to prop up the wall.", + "ids": "6929,6930" + }, + { + "examine": "Good for sweeping.", + "ids": "693" + }, + { + "examine": "It's a bit like walking through a giant rib cage.", + "ids": "6931,6932,6933,6934,6935,6936,6937,6938" + }, + { + "examine": "This arch is broken. I hope the ceiling doesn't come down!", + "ids": "6939,6940,6941,6942" + }, + { + "examine": "I'm guessing it's for making women's clothes.", + "ids": "694" + }, + { + "examine": "A rocky outcrop.", + "ids": "6943,6944,6945,6946,6947,6948,6949" + }, + { + "examine": "Helps make human clothing.", + "ids": "695" + }, + { + "examine": "I dont think i can get through this way.", + "ids": "6950" + }, + { + "examine": "An eye-wrenching nexus of utter negation!", + "ids": "6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968" + }, + { + "examine": "A hole.", + "ids": "696" + }, + { + "examine": "A dirty little swamp boat.", + "ids": "6969,6970" + }, + { + "examine": "Dead animal head. Lovely.", + "ids": "697,736,29344" + }, + { + "examine": "It looks dark down there.", + "ids": "6971" + }, + { + "examine": "A book sits on top.", + "ids": "6972,6973,6974" + }, + { + "examine": "The door is closed.", + "ids": "6975" + }, + { + "examine": "The door is open.", + "ids": "6976" + }, + { + "examine": "The door is closed.", + "ids": "6977" + }, + { + "examine": "The door is open.", + "ids": "6978,6979,6980,6981,6982,6983" + }, + { + "examine": "Must have been laid by a huge bird.", + "ids": "698" + }, + { + "examine": "Empty market stall.", + "ids": "6984" + }, + { + "examine": "A steam powered crushing machine.", + "ids": "6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998,6999,7000,7001" + }, + { + "examine": "Whatever it was I'm glad it's not alive.", + "ids": "699" + }, + { + "examine": "The cannon is built on here.", + "ids": "7" + }, + { + "examine": "Spooky!", + "ids": "700,701,2614,4048,4049,4050,29345" + }, + { + "examine": "A dwarven statue crumbling away from old age.", + "ids": "7002" + }, + { + "examine": "It's crumbling away from old age, it's hardly recognizable as a dwarf.", + "ids": "7003" + }, + { + "examine": "This stone dwarf must be guarding the way into a dwarven area!", + "ids": "7004,7005" + }, + { + "examine": "Big. For a dwarf!", + "ids": "7006,7007" + }, + { + "examine": "A work in progress.", + "ids": "7008,7009" + }, + { + "examine": "A work that has yet to begin.", + "ids": "7010" + }, + { + "examine": "An underground limpwurt plant.", + "ids": "7011,7012" + }, + { + "examine": "Insect eating plant.", + "ids": "7013,7014,7015" + }, + { + "examine": "A stone carved Pillar.", + "ids": "7016,7017" + }, + { + "examine": "Dwarf storage.", + "ids": "7018" + }, + { + "examine": "Used for keeping beer or glasses.", + "ids": "7019,7020,7021" + }, + { + "examine": "A dog's idea of heaven.", + "ids": "702,703,704,705,706,29346" + }, + { + "examine": "Tracks for the carts to run over.", + "ids": "7022,7023,7024,7025,7026" + }, + { + "examine": "A steam powered cart.", + "ids": "7027,7028,7029,7030,7031" + }, + { + "examine": "A fine piece of sculpting.", + "ids": "7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042,7043" + }, + { + "examine": "Keeps mine carts from rolling away.", + "ids": "7044,7045,7046,7047,7048" + }, + { + "examine": "A wooden gate.", + "ids": "7049,7050,7051,7052" + }, + { + "examine": "Lots of seeds here.", + "ids": "7053,7054,7055" + }, + { + "examine": "I can climb down these stairs.", + "ids": "7056" + }, + { + "examine": "I can climb up these stairs.", + "ids": "7057" + }, + { + "examine": "Filled to the brim with knowledge.", + "ids": "7058,7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089" + }, + { + "examine": "Ding-Dong!", + "ids": "707" + }, + { + "examine": "Some kind of pulley system.", + "ids": "708" + }, + { + "examine": "Surprisingly rope-shaped.", + "ids": "709" + }, + { + "examine": "'You are here.'", + "ids": "7090" + }, + { + "examine": "Lots of hard study has been done here.", + "ids": "7091" + }, + { + "examine": "A telescope pointing southwards...", + "ids": "7092,7093,7094,7095" + }, + { + "examine": "A map of some ancient land.", + "ids": "7096" + }, + { + "examine": "There are plenty of shelves", + "ids": "7097" + }, + { + "examine": "Maybe it shows the location of buried treasure?", + "ids": "7098" + }, + { + "examine": "Old songs, old stories...", + "ids": "7099" + }, + { + "examine": "Sturdy looking door.", + "ids": "71,72" + }, + { + "examine": "Suspiciously hole-shaped.", + "ids": "710" + }, + { + "examine": "Armour of a Saradominist warrior. Decorative, but still effective.", + "ids": "7100" + }, + { + "examine": "A cape from Saradomin, suitable for a battle-mage.", + "ids": "7101" + }, + { + "examine": "A staff as used by Saradominist magi.", + "ids": "7102" + }, + { + "examine": "There is a powerful presence about these ruins...", + "ids": "7103,7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122,7123,7124,7125,7126,7127,7128" + }, + { + "examine": "A huge chunk of shining natural crystal.", + "ids": "711,712,713" + }, + { + "examine": "A tear in the dimensional weave of the Abyss.", + "ids": "7129,7130,7131,7132,7133,7134,7135,7136,7137,7138,7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153" + }, + { + "examine": "A recently extinguished fire.", + "ids": "714,715,716,717" + }, + { + "examine": "A tunnel through the abyss.", + "ids": "7154,7155" + }, + { + "examine": "This seems to be blocking the exit...", + "ids": "7156,7157" + }, + { + "examine": "I could probably break this up with a pickaxe.", + "ids": "7158" + }, + { + "examine": "I could probably break this up with a pickaxe. Oh wait, I just did.", + "ids": "7159" + }, + { + "examine": "Pickaxe power!", + "ids": "7160" + }, + { + "examine": "They don't look that solid, an axe could help me chop them down.", + "ids": "7161,7162" + }, + { + "examine": "I cannot tell a lie. I chopped them down.", + "ids": "7163" + }, + { + "examine": "If I'm agile enough I might be able to squeeze through...", + "ids": "7164" + }, + { + "examine": "I could probably burn this away with a tinderbox.", + "ids": "7165" + }, + { + "examine": "I could probably burn this with fire.", + "ids": "7166" + }, + { + "examine": "Burnt open.", + "ids": "7167" + }, + { + "examine": "I could probably distract these with thievery.", + "ids": "7168,7169,7170" + }, + { + "examine": "An unstable portal across the dimensions...", + "ids": "7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186,7187,7188" + }, + { + "examine": "A barrel full of swords...", + "ids": "718,719,720,721,722" + }, + { + "examine": "Abyssal Tendrils.", + "ids": "7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7204,7205" + }, + { + "examine": "An opening into the crumbling wall.", + "ids": "7219,7220" + }, + { + "examine": "This leads downwards.", + "ids": "7221" + }, + { + "examine": "This way to the viewing gallery.", + "ids": "7222,7223" + }, + { + "examine": "I'll never be able to dodge that!", + "ids": "7224" + }, + { + "examine": "It's a wall...", + "ids": "7225,7226" + }, + { + "examine": "It's the floor...", + "ids": "7227" + }, + { + "examine": "It's a wall...", + "ids": "7228,7229" + }, + { + "examine": "I'm glad this isn't around anymore!", + "ids": "723" + }, + { + "examine": "It's the floor...", + "ids": "7230" + }, + { + "examine": "Not your average door...", + "ids": "7231,7232,7233,7234,7235" + }, + { + "examine": "I wonder what's inside.", + "ids": "7236,7237,7238" + }, + { + "examine": "It's a long way down...", + "ids": "7239,7240" + }, + { + "examine": "A crude torch stuck in the ground.", + "ids": "724,725,726,25200" + }, + { + "examine": "That's going to hurt if it hits me!", + "ids": "7241" + }, + { + "examine": "If only I knew Karate...", + "ids": "7242,7243,7244" + }, + { + "examine": "It's the floor...", + "ids": "7245" + }, + { + "examine": "Locked.", + "ids": "7246,7247" + }, + { + "examine": "It's a wall...", + "ids": "7248,7249" + }, + { + "examine": "It's the floor...", + "ids": "7250" + }, + { + "examine": "Bend your way through.", + "ids": "7251" + }, + { + "examine": "That could really hurt!", + "ids": "7252" + }, + { + "examine": "I must not fear, fear is the little death that brings total oblivion.", + "ids": "7253" + }, + { + "examine": "Blocking my way back.", + "ids": "7254,7255,7256" + }, + { + "examine": "I wonder where it leads.", + "ids": "7257" + }, + { + "examine": "An opening into the crumbling wall.", + "ids": "7258" + }, + { + "examine": "This way to exit.", + "ids": "7259,7260,7261,7262,7263,7264,7265,7266,7267,7268,7269,7270,7271" + }, + { + "examine": "A fissure in the cave wall.", + "ids": "727,728,729,730,731,732" + }, + { + "examine": "A mystical teleport.", + "ids": "7272,7273" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7274,7275,7276" + }, + { + "examine": "He looks hungry, but I don't think he'll do anything while I'm here.", + "ids": "7277" + }, + { + "examine": "He's no longer looks hungry, although perhaps a little guilty-looking.", + "ids": "7278,7279,7280" + }, + { + "examine": "He's looking at the grain...", + "ids": "7281,7282,7283" + }, + { + "examine": "A sack full of grain.", + "ids": "7284" + }, + { + "examine": "I think the chicken enjoyed his meal...", + "ids": "7285" + }, + { + "examine": "It looks pretty rickety...", + "ids": "7286,7287" + }, + { + "examine": "A mystical teleport.", + "ids": "7288,7289,7290,7291,7292,7293,7294,7295,7296,7297,7298,7299,7300,7301" + }, + { + "examine": "Securely fastened shut.", + "ids": "73,74" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7302" + }, + { + "examine": "A gold statue of a famous White Knight.", + "ids": "7303" + }, + { + "examine": "A silver statue of a famous White Knight.", + "ids": "7304" + }, + { + "examine": "A bronze statue of a famous White Knight.", + "ids": "7305" + }, + { + "examine": "A gold statue of an ancient warrior.", + "ids": "7306" + }, + { + "examine": "A silver statue of an ancient warrior.", + "ids": "7307" + }, + { + "examine": "A bronze statue of an ancient warrior.", + "ids": "7308" + }, + { + "examine": "A gold statue of a famous warrior.", + "ids": "7309" + }, + { + "examine": "A silver statue of a famous warrior.", + "ids": "7310" + }, + { + "examine": "A bronze statue of a famous warrior.", + "ids": "7311" + }, + { + "examine": "A gold statue of an ancient White Knight.", + "ids": "7312" + }, + { + "examine": "A silver statue of an ancient White Knight.", + "ids": "7313" + }, + { + "examine": "A bronze statue of an ancient White Knight.", + "ids": "7314" + }, + { + "examine": "A mystical teleport.", + "ids": "7315,7316" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7317" + }, + { + "examine": "A mystical teleport.", + "ids": "7318,7319" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7320" + }, + { + "examine": "A mystical teleport.", + "ids": "7321,7322" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7323" + }, + { + "examine": "A mystical teleport.", + "ids": "7324,7325" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7326" + }, + { + "examine": "Filled to the brim with knowledge.", + "ids": "7327,7328,7329,7330" + }, + { + "examine": "This huge web blocks your path.", + "ids": "733" + }, + { + "examine": "Generally used for putting things on.", + "ids": "7331" + }, + { + "examine": "A table with a Bunsen burner on it.", + "ids": "7332" + }, + { + "examine": "There are some containers of chemicals here.", + "ids": "7333,7334,7335,7336,7337,7338,7339" + }, + { + "examine": "A huge web, cruelly slashed in half.", + "ids": "734" + }, + { + "examine": "There's an empty vial here.", + "ids": "7340,7341,7342" + }, + { + "examine": "There's a small hole in the centre.", + "ids": "7343" + }, + { + "examine": "The spade is stuck in the hole.", + "ids": "7344" + }, + { + "examine": "The spade opened the door.", + "ids": "7345" + }, + { + "examine": "It's chained to the wall.", + "ids": "7346" + }, + { + "examine": "For storage.", + "ids": "7347,7348,7349" + }, + { + "examine": "The noxious liquid bubbles horribly.", + "ids": "735" + }, + { + "examine": "I wonder what's inside.", + "ids": "7350" + }, + { + "examine": "Perhaps I should search it.", + "ids": "7351" + }, + { + "examine": "A mystical teleport.", + "ids": "7352,7353" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7354" + }, + { + "examine": "Bridge will support a person carrying no more than 5 kg.", + "ids": "7355,7356,7357,7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372" + }, + { + "examine": "Whatever it was I'm glad it's not alive.", + "ids": "737,738,739,740" + }, + { + "examine": "Lets me walk through walls...", + "ids": "7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384" + }, + { + "examine": "Sit back and relax...", + "ids": "7388,7389" + }, + { + "examine": "Designed specifically to ruin your health.", + "ids": "7390" + }, + { + "examine": "A rolled up magic carpet.", + "ids": "7391" + }, + { + "examine": "A pile of rolled up magic carpets.", + "ids": "7392" + }, + { + "examine": "A little shaded area.", + "ids": "7393" + }, + { + "examine": "A magic carpet.", + "ids": "7394,7395" + }, + { + "examine": "The infamous carpet of '76.", + "ids": "7396" + }, + { + "examine": "A leafy tree.", + "ids": "7397,7398" + }, + { + "examine": "This is what is left of a willow tree.", + "ids": "7399" + }, + { + "examine": "This is what is left of a maple tree.", + "ids": "7400" + }, + { + "examine": "This is what is left of a magic tree.", + "ids": "7401" + }, + { + "examine": "This is what is left of a yew tree.", + "ids": "7402" + }, + { + "examine": "An empty barrel.", + "ids": "7403" + }, + { + "examine": "A barrel full of mushed apples.", + "ids": "7404,7405" + }, + { + "examine": "This tap runs from the apple crushing barrel.", + "ids": "7406" + }, + { + "examine": "An empty ale barrel.", + "ids": "7407" + }, + { + "examine": "A barrel of bad ale.", + "ids": "7408" + }, + { + "examine": "A barrel of unfermented liquid.", + "ids": "7409" + }, + { + "examine": "Looks a little too broken to climb.", + "ids": "741,742" + }, + { + "examine": "A barrel of bad cider.", + "ids": "7410" + }, + { + "examine": "A barrel of Dwarven Stout.", + "ids": "7411" + }, + { + "examine": "A barrel of mature Dwarven Stout.", + "ids": "7412" + }, + { + "examine": "A barrel of Asgarnian Ale.", + "ids": "7413" + }, + { + "examine": "A barrel of mature Asgarnian Ale.", + "ids": "7414" + }, + { + "examine": "A barrel of Greenmans Ale.", + "ids": "7415" + }, + { + "examine": "A barrel of mature Greenmans Ale.", + "ids": "7416" + }, + { + "examine": "A barrel of Wizards Mind Bomb.", + "ids": "7417" + }, + { + "examine": "A barrel of mature Wizards Mind Bomb.", + "ids": "7418" + }, + { + "examine": "A barrel of Dragon Bitter.", + "ids": "7419" + }, + { + "examine": "A barrel of mature Dragon Bitter.", + "ids": "7420" + }, + { + "examine": "A barrel of Moonlight Mead.", + "ids": "7421" + }, + { + "examine": "A barrel of mature Moonlight Mead.", + "ids": "7422" + }, + { + "examine": "A barrel of Axeman's Folly.", + "ids": "7423" + }, + { + "examine": "A barrel of mature Axeman's Folly.", + "ids": "7424" + }, + { + "examine": "A barrel of Chef's Delight.", + "ids": "7425" + }, + { + "examine": "A barrel of mature Chef's Delight.", + "ids": "7426" + }, + { + "examine": "A barrel of Slayer's Respite.", + "ids": "7427" + }, + { + "examine": "A barrel of mature Slayer's Respite.", + "ids": "7428" + }, + { + "examine": "A barrel of Cider.", + "ids": "7429" + }, + { + "examine": "An old-looking ladder.", + "ids": "743" + }, + { + "examine": "A barrel of mature Cider.", + "ids": "7430,7431,7432" + }, + { + "examine": "Goes up and down!", + "ids": "7433" + }, + { + "examine": "I wonder what's under it?", + "ids": "7434" + }, + { + "examine": "I wonder what's down there?", + "ids": "7435" + }, + { + "examine": "A wooden barrel.", + "ids": "7436" + }, + { + "examine": "This vat is empty.", + "ids": "7437" + }, + { + "examine": "This vat is filled with water.", + "ids": "7438" + }, + { + "examine": "This vat is filled with bad ale.", + "ids": "7439" + }, + { + "examine": "An old-looking bit of scaffolding.", + "ids": "744,745,746,747" + }, + { + "examine": "This vat is filled with bad cider.", + "ids": "7440" + }, + { + "examine": "This vat contains a mixture of water and barley malt.", + "ids": "7441" + }, + { + "examine": "This controls the flow of ale to the barrel.", + "ids": "7442,7443" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Hammerstone hops.", + "ids": "7444" + }, + { + "examine": "Dwarven Stout is fermenting in this vat.", + "ids": "7445,7446" + }, + { + "examine": "This vat is filled with Dwarven Stout.", + "ids": "7447" + }, + { + "examine": "This vat is filled with mature Dwarven Stout.", + "ids": "7448" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Asgarnian hops.", + "ids": "7449" + }, + { + "examine": "Asgarnian Ale is fermenting in this vat.", + "ids": "7450,7451" + }, + { + "examine": "This vat is filled with Asgarnian Ale.", + "ids": "7452" + }, + { + "examine": "This vat is filled with mature Asgarnian Ale.", + "ids": "7453" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Harralander.", + "ids": "7454" + }, + { + "examine": "Greenmans Ale is fermenting in this vat.", + "ids": "7455,7456" + }, + { + "examine": "This vat is filled with Greenmans Ale.", + "ids": "7457" + }, + { + "examine": "This vat is filled with mature Greenmans Ale.", + "ids": "7458" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Yanillian hops.", + "ids": "7459" + }, + { + "examine": "Wizards Mind Bomb is fermenting in this vat.", + "ids": "7460,7461" + }, + { + "examine": "This vat is filled with Wizards Mind Bomb.", + "ids": "7462" + }, + { + "examine": "This vat is filled with mature Wizards Mind Bomb.", + "ids": "7463" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Krandorian hops.", + "ids": "7464" + }, + { + "examine": "Dragon Bitter is fermenting in this vat.", + "ids": "7465,7466" + }, + { + "examine": "This vat is filled with Dragon Bitter.", + "ids": "7467" + }, + { + "examine": "This vat is filled with mature Dragon Bitter.", + "ids": "7468" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Bittercap mushrooms.", + "ids": "7469" + }, + { + "examine": "Moonlight Mead is fermenting in this vat.", + "ids": "7470,7471" + }, + { + "examine": "This vat is filled with Moonlight Mead.", + "ids": "7472" + }, + { + "examine": "This vat is filled with mature Moonlight Mead.", + "ids": "7473" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and oak roots.", + "ids": "7474" + }, + { + "examine": "Axeman's Folly is fermenting in this vat.", + "ids": "7475,7476" + }, + { + "examine": "This vat is filled with Axeman's Folly.", + "ids": "7477" + }, + { + "examine": "This vat is filled with mature Axeman's Folly.", + "ids": "7478" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and chocolate dust.", + "ids": "7479" + }, + { + "examine": "The entrance to the cave.", + "ids": "748" + }, + { + "examine": "Chef's Delight is fermenting in this vat.", + "ids": "7480,7481" + }, + { + "examine": "This vat is filled with Chef's Delight.", + "ids": "7482" + }, + { + "examine": "This vat is filled with mature Chef's Delight.", + "ids": "7483" + }, + { + "examine": "This vat contains a mixture of water, barley malt, and Wildblood hops.", + "ids": "7484" + }, + { + "examine": "Slayer's Respite is fermenting in this vat.", + "ids": "7485,7486" + }, + { + "examine": "This vat is filled with Slayer's Respite.", + "ids": "7487" + }, + { + "examine": "This vat is filled with mature Slayer's Respite.", + "ids": "7488" + }, + { + "examine": "This vat contains some apple mush.", + "ids": "7489" + }, + { + "examine": "Cider is fermenting in this vat.", + "ids": "7490,7491" + }, + { + "examine": "This vat is filled with cider.", + "ids": "7492" + }, + { + "examine": "This vat is filled with mature cider.", + "ids": "7493,7494,7495" + }, + { + "examine": "Items are for sale here.", + "ids": "7496,7497" + }, + { + "examine": "They're empty.", + "ids": "7498" + }, + { + "examine": "Farming stock is kept here.", + "ids": "7499,7500,7501,7502,7503" + }, + { + "examine": "I wonder what's inside?", + "ids": "75" + }, + { + "examine": "For sitting.", + "ids": "7504,7505" + }, + { + "examine": "They're empty.", + "ids": "7506" + }, + { + "examine": "Farming stock is kept here.", + "ids": "7507,7508,7509,7510,7511" + }, + { + "examine": "A pair of sacks.", + "ids": "7512" + }, + { + "examine": "For storage.", + "ids": "7513,7514,7515" + }, + { + "examine": "A farmer's spade and rake.", + "ids": "7516,7517,7518,7519,7520,7521,7522,7523,7524,7525,7526" + }, + { + "examine": "I can climb over the fence with this.", + "ids": "7527" + }, + { + "examine": "For fermenting beer.", + "ids": "7528,7529,7530" + }, + { + "examine": "An ale barrel.", + "ids": "7531" + }, + { + "examine": "This controls the flow of ale to the barrel.", + "ids": "7532,7533,7534,7535,7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551,7552,7553,7554,7555,7556" + }, + { + "examine": "You can grow Deadly Nightshade in this Farming patch.", + "ids": "7557,7558,7559,7560" + }, + { + "examine": "Deadly Nightshade has been sown in this farming patch.", + "ids": "7561" + }, + { + "examine": "Deadly Nightshade is growing in this farming patch.", + "ids": "7562,7563,7564" + }, + { + "examine": "I wouldn't pick this with my bare hands.", + "ids": "7565" + }, + { + "examine": "This Deadly Nightshade has become diseased.", + "ids": "7566,7567,7568" + }, + { + "examine": "This Deadly Nightshade has died.", + "ids": "7569,7570,7571,7572" + }, + { + "examine": "You can grow bushes in this Farming patch.", + "ids": "7573,7574,7575,7576,7577,7578,7579,7580" + }, + { + "examine": "A cadavaberry bush is growing in this Farming patch.", + "ids": "7581,7582,7583,7584,7585,7586" + }, + { + "examine": "A fully grown cadavaberry bush.", + "ids": "7587,7588,7589,7590,7591,7592" + }, + { + "examine": "This diseased cadavaberry bush needs pruning.", + "ids": "7593,7594,7595,7596,7597,7598" + }, + { + "examine": "This cadavaberry bush has died.", + "ids": "7599,7600,7601,7602,7603,7604" + }, + { + "examine": "It's open.", + "ids": "76" + }, + { + "examine": "A dwellberry bush is growing in this Farming patch.", + "ids": "7605,7606,7607,7608,7609,7610,7611" + }, + { + "examine": "A fully grown dwellberry bush.", + "ids": "7612,7613,7614,7615,7616,7617" + }, + { + "examine": "This diseased dwellberry bush needs pruning.", + "ids": "7618,7619,7620,7621,7622,7623,7624" + }, + { + "examine": "This dwellberry bush has died.", + "ids": "7625,7626,7627,7628,7629,7630,7631" + }, + { + "examine": "A jangerberry bush is growing in this Farming patch.", + "ids": "7632,7633,7634,7635,7636,7637,7638,7639" + }, + { + "examine": "A fully grown jangerberry bush.", + "ids": "7640,7641,7642,7643,7644,7645" + }, + { + "examine": "This diseased jangerberry bush needs pruning.", + "ids": "7646,7647,7648,7649,7650,7651,7652,7653" + }, + { + "examine": "This jangerberry bush has died.", + "ids": "7654,7655,7656,7657,7658,7659,7660,7661" + }, + { + "examine": "A Poison Ivy bush is growing in this Farming patch.", + "ids": "7662,7663,7664,7665,7666,7667,7668,7669" + }, + { + "examine": "A fully grown Poison Ivy bush.", + "ids": "7670,7671,7672,7673,7674,7675" + }, + { + "examine": "This diseased Poison Ivy bush needs pruning.", + "ids": "7676,7677,7678,7679,7680,7681,7682,7683" + }, + { + "examine": "This Poison Ivy bush has died.", + "ids": "7684,7685,7686,7687,7688,7689,7690,7691" + }, + { + "examine": "A redberry bush is growing in this Farming patch.", + "ids": "7692,7693,7694,7695,7696" + }, + { + "examine": "A fully grown redberry bush.", + "ids": "7697,7698,7699,7700,7701,7702" + }, + { + "examine": "Securely fastened shut.", + "ids": "77,78" + }, + { + "examine": "This diseased redberry bush needs pruning.", + "ids": "7703,7704,7705,7706,7707" + }, + { + "examine": "This redberry bush has died.", + "ids": "7708,7709,7710,7711,7712" + }, + { + "examine": "A map icon..", + "ids": "771" + }, + { + "examine": "A whiteberry bush is growing in this Farming patch.", + "ids": "7713,7714,7715,7716,7717,7718,7719,7720" + }, + { + "examine": "A fully grown whiteberry bush.", + "ids": "7721,7722,7723,7724,7725,7726" + }, + { + "examine": "This diseased whiteberry bush needs pruning.", + "ids": "7727,7728,7729,7730,7731,7732,7733,7734" + }, + { + "examine": "This whiteberry bush has died.", + "ids": "7735,7736,7737,7738,7739,7740,7741,7742" + }, + { + "examine": "You can grow a Cactus in this Farming patch.", + "ids": "7743,7744,7745,7746" + }, + { + "examine": "Cactus seeds have been planted in this patch.", + "ids": "7747" + }, + { + "examine": "A cactus is growing in this patch.", + "ids": "7748,7749,7750,7751,7752,7753" + }, + { + "examine": "A fully-grown cactus.", + "ids": "7754,7755,7756,7757,7758" + }, + { + "examine": "This cactus has become diseased.", + "ids": "7759,7760,7761,7762,7763,7764" + }, + { + "examine": "This cactus has died.", + "ids": "7765,7766,7767,7768,7769,7770,7771" + }, + { + "examine": "You can grow a Calquat Tree in this Farming patch.", + "ids": "7772,7773,7774,7775" + }, + { + "examine": "A Calquat tree is growing in this Farming patch.", + "ids": "7776,7777,7778,7779,7780,7781,7782,7783" + }, + { + "examine": "A fully grown Calquat tree stands in this Farming patch.", + "ids": "7784,7785,7786,7787,7788,7789,7790,7791" + }, + { + "examine": "It's a door made from bamboo.", + "ids": "779" + }, + { + "examine": "This diseased Calquat tree needs pruning.", + "ids": "7792,7793,7794,7795,7796,7797,7798" + }, + { + "examine": "This Calquat tree has died.", + "ids": "7799,7800,7801,7802,7803,7804,7805" + }, + { + "examine": "A Calquat tree stump.", + "ids": "7806,7807" + }, + { + "examine": "Turns vegetation into compost.", + "ids": "7808" + }, + { + "examine": "This compost bin contains compostable items.", + "ids": "7809" + }, + { + "examine": "This compost bin is full of compostable items.", + "ids": "7810" + }, + { + "examine": "This compost bin contains supercompostable items.", + "ids": "7811" + }, + { + "examine": "This compost bin is full of supercompostable items.", + "ids": "7812" + }, + { + "examine": "Turns vegetation into compost.", + "ids": "7813" + }, + { + "examine": "This compost bin contains compost.", + "ids": "7814" + }, + { + "examine": "This compost bin is full of compost.", + "ids": "7815" + }, + { + "examine": "This compost bin contains super compost.", + "ids": "7816" + }, + { + "examine": "This compost bin is full of super compost.", + "ids": "7817" + }, + { + "examine": "Turns vegetation into compost.", + "ids": "7818" + }, + { + "examine": "This compost bin contains compostable items.", + "ids": "7819" + }, + { + "examine": "This compost bin is full of compostable items.", + "ids": "7820" + }, + { + "examine": "This compost bin contains supercompostable items.", + "ids": "7821" + }, + { + "examine": "This compost bin is full of supercompostable items.", + "ids": "7822" + }, + { + "examine": "Turns vegetation into compost.", + "ids": "7823" + }, + { + "examine": "This compost bin contains compost.", + "ids": "7824" + }, + { + "examine": "This compost bin is full of compost.", + "ids": "7825" + }, + { + "examine": "This compost bin contains super compost.", + "ids": "7826" + }, + { + "examine": "This compost bin is full of super compost.", + "ids": "7827" + }, + { + "examine": "This compost bin contains tomatoes.", + "ids": "7828" + }, + { + "examine": "This compost bin is full of tomatoes.", + "ids": "7829" + }, + { + "examine": "This compost bin contains rotten tomatoes.", + "ids": "7830" + }, + { + "examine": "This compost bin is full of rotten tomatoes.", + "ids": "7831" + }, + { + "examine": "This compost bin contains tomatoes.", + "ids": "7832" + }, + { + "examine": "This compost bin is full of tomatoes.", + "ids": "7833" + }, + { + "examine": "This compost bin contains rotten tomatoes.", + "ids": "7834" + }, + { + "examine": "This compost bin is full of rotten tomatoes.", + "ids": "7835,7836,7837,7838,7839" + }, + { + "examine": "You can grow flowers in this Farming patch.", + "ids": "7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850" + }, + { + "examine": "A limpwurt plant is growing in this patch.", + "ids": "7851,7852,7853,7854" + }, + { + "examine": "A fully grown limpwurt plant.", + "ids": "7855" + }, + { + "examine": "A limpwurt plant is growing in this patch.", + "ids": "7856,7857,7858,7859" + }, + { + "examine": "This limpwurt plant has become diseased.", + "ids": "7860,7861,7862" + }, + { + "examine": "This limpwurt plant has died while growing.", + "ids": "7863,7864,7865" + }, + { + "examine": "This limpwurt plant has died.", + "ids": "7866" + }, + { + "examine": "A marigold is growing in this patch.", + "ids": "7867,7868,7869,7870" + }, + { + "examine": "A loom.", + "ids": "787" + }, + { + "examine": "A fully grown marigold.", + "ids": "7871" + }, + { + "examine": "A marigold is growing in this patch.", + "ids": "7872,7873,7874,7875" + }, + { + "examine": "This marigold has become diseased.", + "ids": "7876,7877,7878" + }, + { + "examine": "This marigold has died while growing.", + "ids": "7879,7880,7881" + }, + { + "examine": "You'd need a big siege engine to force that.", + "ids": "788,789" + }, + { + "examine": "This marigold has died.", + "ids": "7882" + }, + { + "examine": "A nasturtium is growing in this patch.", + "ids": "7883,7884,7885,7886" + }, + { + "examine": "A fully grown nasturtium.", + "ids": "7887" + }, + { + "examine": "A nasturtium is growing in this patch.", + "ids": "7888,7889,7890,7891" + }, + { + "examine": "This nasturtium has become diseased.", + "ids": "7892,7893,7894" + }, + { + "examine": "This nasturtium has died while growing.", + "ids": "7895,7896,7897" + }, + { + "examine": "This nasturtium has died.", + "ids": "7898" + }, + { + "examine": "A rosemary is growing in this patch.", + "ids": "7899,7900,7901,7902" + }, + { + "examine": "Looks secure.", + "ids": "79,80" + }, + { + "examine": "A fully grown rosemary.", + "ids": "7903" + }, + { + "examine": "A rosemary is growing in this patch.", + "ids": "7904,7905,7906,7907" + }, + { + "examine": "This rosemary has become diseased.", + "ids": "7908,7909,7910" + }, + { + "examine": "This rosemary has died while growing.", + "ids": "7911,7912,7913" + }, + { + "examine": "This rosemary has died.", + "ids": "7914" + }, + { + "examine": "Should scare off the birds...", + "ids": "7915,7916,7917,7918" + }, + { + "examine": "A woad plant is growing in this patch.", + "ids": "7919,7920,7921,7922" + }, + { + "examine": "A fully grown woad plant.", + "ids": "7923" + }, + { + "examine": "A woad plant is growing in this patch.", + "ids": "7924,7925,7926,7927" + }, + { + "examine": "This woad plant has become diseased.", + "ids": "7928,7929,7930" + }, + { + "examine": "This woad plant has died while growing.", + "ids": "7931,7932,7933" + }, + { + "examine": "This woad plant has died.", + "ids": "7934" + }, + { + "examine": "An apple tree sapling has been planted in this fruit tree patch.", + "ids": "7935" + }, + { + "examine": "An apple tree is growing in this fruit tree patch.", + "ids": "7936,7937,7938,7939,7940" + }, + { + "examine": "A fully grown apple tree.", + "ids": "7941" + }, + { + "examine": "There is a single apple on this apple tree.", + "ids": "7942" + }, + { + "examine": "There are two apples on this apple tree.", + "ids": "7943" + }, + { + "examine": "There are three apples on this apple tree.", + "ids": "7944" + }, + { + "examine": "There are four apples on this apple tree.", + "ids": "7945" + }, + { + "examine": "There are five apples on this apple tree.", + "ids": "7946" + }, + { + "examine": "There are six apples on this apple tree.", + "ids": "7947" + }, + { + "examine": "A fully grown apple tree.", + "ids": "7948" + }, + { + "examine": "This diseased tree looks like it needs pruning with secateurs.", + "ids": "7949,7950,7951,7952,7953,7954" + }, + { + "examine": "This apple tree has become diseased and died.", + "ids": "7955,7956,7957,7958,7959,7960" + }, + { + "examine": "This apple tree has been cut down.", + "ids": "7961,7962,7963,7964,7965" + }, + { + "examine": "A pineapple plant has been planted in this fruit tree patch.", + "ids": "7966" + }, + { + "examine": "A pineapple plant is growing in this fruit tree patch.", + "ids": "7967,7968,7969,7970,7971" + }, + { + "examine": "A fully grown pineapple plant.", + "ids": "7972" + }, + { + "examine": "There is a single pineapple on this pineapple plant.", + "ids": "7973" + }, + { + "examine": "There are two pineapples on this pineapple plant.", + "ids": "7974" + }, + { + "examine": "There are three pineapples on this pineapple plant.", + "ids": "7975" + }, + { + "examine": "There are four pineapples on this pineapple plant.", + "ids": "7976" + }, + { + "examine": "There are five pineapples on this pineapple plant.", + "ids": "7977" + }, + { + "examine": "There are six pineapples on this pineapple plant.", + "ids": "7978" + }, + { + "examine": "A fully grown pineapple plant.", + "ids": "7979" + }, + { + "examine": "This pineapple plant looks like it could do with pruning with secateurs.", + "ids": "7980,7981,7982,7983,7984,7985" + }, + { + "examine": "This pineapple plant has become diseased and died.", + "ids": "7986,7987,7988,7989,7990,7991" + }, + { + "examine": "This pineapple plant has been cut down.", + "ids": "7992" + }, + { + "examine": "A banana tree sapling has been planted in this fruit tree patch.", + "ids": "7993" + }, + { + "examine": "A banana tree is growing in this fruit tree patch.", + "ids": "7994,7995,7996,7997,7998" + }, + { + "examine": "A fully grown banana tree.", + "ids": "7999,8000" + }, + { + "examine": "The mounting for the multicannon.", + "ids": "8" + }, + { + "examine": "There is a single banana on this banana tree.", + "ids": "8001" + }, + { + "examine": "There are two bananas on this banana tree.", + "ids": "8002" + }, + { + "examine": "There are three bananas on this banana tree.", + "ids": "8003" + }, + { + "examine": "There are four bananas on this banana tree.", + "ids": "8004" + }, + { + "examine": "There are five bananas on this banana tree.", + "ids": "8005" + }, + { + "examine": "There are six bananas on this banana tree.", + "ids": "8006" + }, + { + "examine": "This banana looks like it could do with pruning with secateurs.", + "ids": "8007,8008,8009,8010,8011,8012" + }, + { + "examine": "This banana tree has become diseased and died.", + "ids": "8013,8014,8015,8016,8017,8018" + }, + { + "examine": "This banana tree has been cut down.", + "ids": "8019" + }, + { + "examine": "A curry tree sapling has been planted in this fruit tree patch.", + "ids": "8020" + }, + { + "examine": "A curry tree is growing in this fruit tree patch.", + "ids": "8021,8022,8023,8024,8025" + }, + { + "examine": "A fully grown curry tree.", + "ids": "8026" + }, + { + "examine": "There is a single curry leaf on this curry tree.", + "ids": "8027" + }, + { + "examine": "There are two curry leaves on this curry tree.", + "ids": "8028" + }, + { + "examine": "There are three curry leaves on this curry tree.", + "ids": "8029" + }, + { + "examine": "There are four curry leaves on this curry tree.", + "ids": "8030" + }, + { + "examine": "There are five curry leaves on this curry tree.", + "ids": "8031" + }, + { + "examine": "There are six curry leaves on this curry tree.", + "ids": "8032" + }, + { + "examine": "A fully grown curry tree.", + "ids": "8033" + }, + { + "examine": "This curry tree looks like it could do with pruning with secateurs.", + "ids": "8034,8035,8036,8037,8038,8039" + }, + { + "examine": "This curry tree has become diseased and died.", + "ids": "8040,8041,8042,8043,8044,8045" + }, + { + "examine": "This curry tree has been cut down.", + "ids": "8046" + }, + { + "examine": "You can grow Fruit Trees in this Farming patch.", + "ids": "8047,8048,8049,8050" + }, + { + "examine": "An orange tree sapling has been planted in this fruit tree patch.", + "ids": "8051" + }, + { + "examine": "An orange tree is growing in this fruit tree patch.", + "ids": "8052,8053,8054,8055,8056" + }, + { + "examine": "A fully grown orange tree.", + "ids": "8057" + }, + { + "examine": "There is a single orange on this orange tree.", + "ids": "8058" + }, + { + "examine": "There are two oranges on this orange tree.", + "ids": "8059" + }, + { + "examine": "There are three oranges on this orange tree.", + "ids": "8060" + }, + { + "examine": "There are four oranges on this orange tree.", + "ids": "8061" + }, + { + "examine": "There are five oranges on this orange tree.", + "ids": "8062" + }, + { + "examine": "There are six oranges on this orange tree.", + "ids": "8063" + }, + { + "examine": "A fully grown orange tree.", + "ids": "8064" + }, + { + "examine": "This orange tree looks like it could do with pruning with secateurs.", + "ids": "8065,8066,8067,8068,8069,8070" + }, + { + "examine": "This orange tree has become diseased and died.", + "ids": "8071,8072,8073,8074,8075,8076" + }, + { + "examine": "This orange tree has been cut down.", + "ids": "8077" + }, + { + "examine": "A palm tree sapling has been planted in this fruit tree patch.", + "ids": "8078" + }, + { + "examine": "A palm tree is growing in this fruit tree patch.", + "ids": "8079,8080,8081,8082,8083" + }, + { + "examine": "A fully grown palm tree.", + "ids": "8084" + }, + { + "examine": "There is a single coconut on this palm tree.", + "ids": "8085" + }, + { + "examine": "There are two coconuts on this palm tree.", + "ids": "8086" + }, + { + "examine": "There are three coconuts on this palm tree.", + "ids": "8087" + }, + { + "examine": "There are four coconuts on this palm tree.", + "ids": "8088" + }, + { + "examine": "There are five coconuts on this palm tree.", + "ids": "8089" + }, + { + "examine": "There are six coconuts on this palm tree.", + "ids": "8090" + }, + { + "examine": "A fully grown palm tree.", + "ids": "8091" + }, + { + "examine": "This palm tree looks like it could do with pruning with secateurs.", + "ids": "8092,8093,8094,8095,8096,8097" + }, + { + "examine": "This palm tree has become diseased and died.", + "ids": "8098,8099,8100,8101,8102,8103" + }, + { + "examine": "The door is closed.", + "ids": "81,82" + }, + { + "examine": "This palm tree has been cut down.", + "ids": "8104" + }, + { + "examine": "A papaya tree sapling has been planted in this fruit tree patch.", + "ids": "8105" + }, + { + "examine": "A papaya tree is growing in this fruit tree patch.", + "ids": "8106,8107,8108,8109,8110" + }, + { + "examine": "A fully grown papaya tree", + "ids": "8111" + }, + { + "examine": "There is a single papaya fruit on this tree.", + "ids": "8112" + }, + { + "examine": "There are two papaya fruits on this tree.", + "ids": "8113" + }, + { + "examine": "There are three papaya fruits on this tree.", + "ids": "8114" + }, + { + "examine": "There are four papaya fruits on this tree.", + "ids": "8115" + }, + { + "examine": "There are five papaya fruits on this tree.", + "ids": "8116" + }, + { + "examine": "There are six papaya fruits on this tree.", + "ids": "8117" + }, + { + "examine": "A fully grown papaya tree.", + "ids": "8118" + }, + { + "examine": "This papaya tree looks like it could do with pruning with secateurs.", + "ids": "8119,8120,8121,8122,8123,8124" + }, + { + "examine": "This papaya tree has become diseased and died.", + "ids": "8125,8126,8127,8128,8129,8130" + }, + { + "examine": "This papaya tree has been cut down.", + "ids": "8131" + }, + { + "examine": "You can grow herbs in this Farming patch.", + "ids": "8132,8133,8134,8135,8136,8137,8138" + }, + { + "examine": "Some herb seeds have been sown in this patch.", + "ids": "8139" + }, + { + "examine": "A herb is growing in this patch.", + "ids": "8140,8141,8142" + }, + { + "examine": "A fully grown herb.", + "ids": "8143" + }, + { + "examine": "These herbs have become diseased.", + "ids": "8144,8145,8146" + }, + { + "examine": "These herbs have become diseased and died.", + "ids": "8147,8148,8149,8150,8151,8152,8153" + }, + { + "examine": "Asgarnian hop seeds have been sown in this farming patch.", + "ids": "8154" + }, + { + "examine": "Asgarnian hops are growing in this farming patch.", + "ids": "8155,8156,8157,8158" + }, + { + "examine": "These are fully grown Asgarnian Hops.", + "ids": "8159" + }, + { + "examine": "Asgarnian hop seeds have been sown in this farming patch.", + "ids": "8160" + }, + { + "examine": "Asgarnian hops are growing in this farming patch.", + "ids": "8161,8162,8163,8164" + }, + { + "examine": "These Asgarnian Hop plants are diseased.", + "ids": "8165,8166,8167,8168" + }, + { + "examine": "These Asgarnian Hop plants have died from disease.", + "ids": "8169,8170,8171,8172,8173,8174,8175,8176" + }, + { + "examine": "I always wonder if someone's hiding inside when I see these.", + "ids": "817" + }, + { + "examine": "Hammerstone Hop seeds have been sown in this farming patch.", + "ids": "8177" + }, + { + "examine": "Hammerstone Hops are growing in this farming patch.", + "ids": "8178,8179,8180" + }, + { + "examine": "Looks kind of like a man made of metal.", + "ids": "818,819,820" + }, + { + "examine": "These are fully grown Hammerstone Hops.", + "ids": "8181" + }, + { + "examine": "Hammerstone Hop seeds have been sown in this farming patch.", + "ids": "8182" + }, + { + "examine": "Hammerstone Hops are growing in this farming patch.", + "ids": "8183,8184,8185" + }, + { + "examine": "These Hammerstone Hops are diseased.", + "ids": "8186,8187,8188" + }, + { + "examine": "These Hammerstone Hops have died from disease.", + "ids": "8189,8190,8191" + }, + { + "examine": "Barley seeds have been sown in this farming patch.", + "ids": "8192" + }, + { + "examine": "Barley is growing in this farming patch.", + "ids": "8193,8194,8195" + }, + { + "examine": "This patch is full of Barley.", + "ids": "8196" + }, + { + "examine": "Barley seeds have been sown in this farming patch.", + "ids": "8197" + }, + { + "examine": "Barley is growing in this farming patch.", + "ids": "8198,8199,8200" + }, + { + "examine": "This Barley is diseased.", + "ids": "8201,8202,8203" + }, + { + "examine": "This Barley has died from disease.", + "ids": "8204,8205,8206" + }, + { + "examine": "You can grow hops in this Farming patch.", + "ids": "8207,8208,8209,8210" + }, + { + "examine": "Not suitable for children. Aim away from face.", + "ids": "821,36780" + }, + { + "examine": "Krandorian Hop seeds have been planted in this farming patch.", + "ids": "8211" + }, + { + "examine": "Krandorian Hops are growing in this farming patch.", + "ids": "8212,8213,8214,8215,8216,8217" + }, + { + "examine": "These are fully grown Krandorian Hops.", + "ids": "8218" + }, + { + "examine": "Krandorian Hop seeds have been planted in this farming patch.", + "ids": "8219" + }, + { + "examine": "Big heavy metal balls.", + "ids": "822,36790" + }, + { + "examine": "Krandorian Hops are growing in this farming patch.", + "ids": "8220,8221,8222,8223,8224,8225" + }, + { + "examine": "These Krandorian Hops are diseased.", + "ids": "8226,8227,8228,8229,8230,8231" + }, + { + "examine": "The easiest opponent I'll ever fight.", + "ids": "823" + }, + { + "examine": "These Krandorian Hops have died from disease.", + "ids": "8232,8233,8234,8235,8236,8237" + }, + { + "examine": "Jute seeds have been sown in this farming patch.", + "ids": "8238" + }, + { + "examine": "Jute plants are growing in this farming patch.", + "ids": "8239,8240,8241,8242" + }, + { + "examine": "A wooden defensive structure.", + "ids": "824,825,826,827,828,36928" + }, + { + "examine": "These are fully grown Jute plants.", + "ids": "8243" + }, + { + "examine": "Jute seeds have been sown in this farming patch.", + "ids": "8244" + }, + { + "examine": "Jute plants are growing in this farming patch.", + "ids": "8245,8246,8247,8248" + }, + { + "examine": "These Jute plants are diseased.", + "ids": "8249,8250,8251,8252" + }, + { + "examine": "These Jute plants have died from disease.", + "ids": "8253,8254,8255,8256" + }, + { + "examine": "Wildblood hop seeds have been planted in this farming patch.", + "ids": "8257" + }, + { + "examine": "Wildblood Hops are growing in this farming patch.", + "ids": "8258,8259,8260,8261,8262,8263,8264" + }, + { + "examine": "These are fully grown Wildblood Hops.", + "ids": "8265" + }, + { + "examine": "Wildblood hop seeds have been planted in this farming patch.", + "ids": "8266" + }, + { + "examine": "Wildblood Hops are growing in this farming patch.", + "ids": "8267,8268,8269,8270,8271,8272,8273" + }, + { + "examine": "These Wildblood hops are diseased.", + "ids": "8274,8275,8276,8277,8278,8279,8280" + }, + { + "examine": "These Wildblood Hops have died from disease.", + "ids": "8281,8282,8283,8284,8285,8286,8287" + }, + { + "examine": "Yanillian Hop seeds have been sown in this farming patch.", + "ids": "8288" + }, + { + "examine": "Yanillian Hops are growing in this farming patch.", + "ids": "8289,8290,8291,8292,8293" + }, + { + "examine": "For private use only!", + "ids": "829,830,831,832,833" + }, + { + "examine": "These are fully grown Yanillian Hops.", + "ids": "8294" + }, + { + "examine": "Yanillian Hop seeds have been sown in this farming patch.", + "ids": "8295" + }, + { + "examine": "Yanillian Hops are growing in this farming patch.", + "ids": "8296,8297,8298,8299,8300" + }, + { + "examine": "These Yanillian Hops are diseased.", + "ids": "8301,8302,8303,8304,8305" + }, + { + "examine": "These Yanillian Hops have died from disease.", + "ids": "8306,8307,8308,8309,8310" + }, + { + "examine": "You can grow Bittercap mushrooms in this Farming patch.", + "ids": "8311,8312,8313,8314" + }, + { + "examine": "Bittercap mushroom spores have been sown in this farming patch.", + "ids": "8315" + }, + { + "examine": "Bittercap mushrooms are growing in this farming patch.", + "ids": "8316,8317,8318,8319,8320" + }, + { + "examine": "A patch of Bittercap Mushrooms.", + "ids": "8321,8322,8323,8324,8325,8326" + }, + { + "examine": "These Bittercap mushrooms have become diseased.", + "ids": "8327,8328,8329,8330,8331" + }, + { + "examine": "These Bittercap mushrooms have become diseased and died.", + "ids": "8332,8333,8334,8335,8336,8337,8338" + }, + { + "examine": "You can grow a Spirit Tree in this Farming patch.", + "ids": "8339,8340,8341,8342" + }, + { + "examine": "Break glass in case of emergency.", + "ids": "834,835" + }, + { + "examine": "A Spirit Tree.", + "ids": "8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356" + }, + { + "examine": "A Spirit Tree stump.", + "ids": "8357" + }, + { + "examine": "Needs pruning before it dies.", + "ids": "8358,8359,8360,8361,8362,8363,8364,8365,8366,8367,8368,8369" + }, + { + "examine": "An attractively laid out collection of ranging weapons.", + "ids": "836" + }, + { + "examine": "Break glass in case of emergency.", + "ids": "837,838" + }, + { + "examine": "Oh dear, your spirit tree has died.", + "ids": "8370,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389,8390,8391" + }, + { + "examine": "An attractively laid out collection of ranging weapons.", + "ids": "839" + }, + { + "examine": "You can grow trees in this Farming patch.", + "ids": "8392,8393,8394,8395" + }, + { + "examine": "A Magic Tree sapling has been planted in this tree patch.", + "ids": "8396" + }, + { + "examine": "A Magic Tree is growing in this tree patch.", + "ids": "8397,8398,8399,8400,8401,8402,8403,8404,8405,8406,8407" + }, + { + "examine": "A fully grown Magic Tree.", + "ids": "8408,8409" + }, + { + "examine": "You can uproot this stump with a spade.", + "ids": "8410" + }, + { + "examine": "To remove all signs of disease, prune the tree with secateurs.", + "ids": "8411,8412,8413,8414,8415,8416,8417,8418,8419,8420,8421,8422" + }, + { + "examine": "This Magic Tree has become diseased and died.", + "ids": "8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434" + }, + { + "examine": "A Maple tree sapling has been planted in this tree patch.", + "ids": "8435" + }, + { + "examine": "A Maple tree is growing in this tree patch.", + "ids": "8436,8437,8438,8439,8440,8441,8442" + }, + { + "examine": "A fully grown Maple tree.", + "ids": "8443,8444" + }, + { + "examine": "You can uproot this stump with a spade.", + "ids": "8445" + }, + { + "examine": "To remove all signs of disease, prune the tree with secateurs.", + "ids": "8446,8447,8448,8449,8450,8451,8452,8453" + }, + { + "examine": "This Maple tree has become diseased and died.", + "ids": "8454,8455,8456,8457,8458,8459,8460,8461" + }, + { + "examine": "An Oak sapling has been planted in this tree patch.", + "ids": "8462" + }, + { + "examine": "An Oak tree is growing in this tree patch.", + "ids": "8463,8464,8465" + }, + { + "examine": "A fully grown Oak tree.", + "ids": "8466,8467" + }, + { + "examine": "You can uproot this stump with a spade.", + "ids": "8468,8469,8470,8471,8472" + }, + { + "examine": "To remove all signs of disease, prune the tree with secateurs.", + "ids": "8473,8474,8475,8476" + }, + { + "examine": "This Oak tree has become diseased and died.", + "ids": "8477,8478,8479,8480" + }, + { + "examine": "A row of sharp pointy spears.", + "ids": "848" + }, + { + "examine": "A Willow sapling has been planted in this tree patch.", + "ids": "8481" + }, + { + "examine": "A Willow tree is growing in this tree patch.", + "ids": "8482,8483,8484,8485,8486" + }, + { + "examine": "A fully grown Willow tree.", + "ids": "8487,8488" + }, + { + "examine": "You can uproot this stump with a spade.", + "ids": "8489" + }, + { + "examine": "Rows of sharp pointy spears.", + "ids": "849,850" + }, + { + "examine": "To remove all signs of disease, prune the tree with secateurs.", + "ids": "8490,8491,8492,8493,8494,8495" + }, + { + "examine": "This Willow tree has become diseased and died.", + "ids": "8496,8497,8498,8499,8500,8501" + }, + { + "examine": "A Yew sapling has been planted in this tree patch.", + "ids": "8502" + }, + { + "examine": "A Yew tree is growing in this tree patch.", + "ids": "8503,8504,8505,8506,8507,8508,8509,8510,8511" + }, + { + "examine": "A tatty old standard.", + "ids": "851,852" + }, + { + "examine": "A fully grown Yew tree.", + "ids": "8512,8513" + }, + { + "examine": "You can uproot this tree stump with a spade.", + "ids": "8514" + }, + { + "examine": "To remove all signs of disease, prune the tree with secateurs.", + "ids": "8515,8516,8517,8518,8519,8520,8521,8522,8523,8524" + }, + { + "examine": "This Yew tree has become diseased and died.", + "ids": "8525,8526,8527,8528,8529,8530,8531,8532,8533,8534" + }, + { + "examine": "A standard.", + "ids": "853" + }, + { + "examine": "Cabbage seeds have been sown in this allotment.", + "ids": "8535" + }, + { + "examine": "Cabbages are growing in this allotment.", + "ids": "8536,8537,8538" + }, + { + "examine": "These cabbages could do with harvesting.", + "ids": "8539" + }, + { + "examine": "A blue standard.", + "ids": "854,855,856" + }, + { + "examine": "Cabbages are growing in this allotment.", + "ids": "8540,8541,8542,8543" + }, + { + "examine": "These cabbages have become diseased and need tending.", + "ids": "8544,8545,8546" + }, + { + "examine": "These cabbages have become diseased and died.", + "ids": "8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557" + }, + { + "examine": "Potato seeds have been planted in this allotment.", + "ids": "8558" + }, + { + "examine": "Potato plants are growing in this allotment.", + "ids": "8559,8560,8561" + }, + { + "examine": "These potato plants are fully grown.", + "ids": "8562" + }, + { + "examine": "Potato seeds have been planted in this allotment.", + "ids": "8563" + }, + { + "examine": "Potato plants are growing in this allotment.", + "ids": "8564,8565,8566" + }, + { + "examine": "These potato plants are diseased.", + "ids": "8567,8568,8569" + }, + { + "examine": "A standard of the gnome race.", + "ids": "857,858" + }, + { + "examine": "These potato plants are dead.", + "ids": "8570,8571,8572" + }, + { + "examine": "You can grow fruit and vegetables here.", + "ids": "8573,8574,8575,8576,8577,8578,8579" + }, + { + "examine": "Some onion seeds have sown in this allotment.", + "ids": "8580" + }, + { + "examine": "Some onion plants are growing in this allotment.", + "ids": "8581,8582,8583" + }, + { + "examine": "There are some fully grown onions in this allotment.", + "ids": "8584" + }, + { + "examine": "Some onion seeds have sown in this allotment.", + "ids": "8585" + }, + { + "examine": "Some onion plants are growing in this allotment.", + "ids": "8586,8587,8588" + }, + { + "examine": "These onions have become diseased.", + "ids": "8589,8590,8591" + }, + { + "examine": "The standard of the ogre race.", + "ids": "859" + }, + { + "examine": "These onions have become diseased and died.", + "ids": "8592,8593,8594" + }, + { + "examine": "Strawberry seeds have been sown in this allotment.", + "ids": "8595" + }, + { + "examine": "Strawberry plants are growing in this allotment.", + "ids": "8596,8597,8598,8599,8600" + }, + { + "examine": "Seems very old...", + "ids": "86" + }, + { + "examine": "The flag of Asgarnia.", + "ids": "860" + }, + { + "examine": "These strawberry plants are fully grown.", + "ids": "8601" + }, + { + "examine": "Strawberry seeds have been sown in this allotment.", + "ids": "8602" + }, + { + "examine": "Strawberry plants are growing in this allotment.", + "ids": "8603,8604,8605,8606,8607" + }, + { + "examine": "These strawberry plants have become diseased.", + "ids": "8608,8609,8610,8611,8612" + }, + { + "examine": "The flag of Kandarin.", + "ids": "861" + }, + { + "examine": "These strawberry plants have become diseased and died.", + "ids": "8613,8614,8615,8616,8617" + }, + { + "examine": "Some sweetcorn seeds have been sown in this allotment.", + "ids": "8618" + }, + { + "examine": "Some sweetcorn plants are growing in this allotment.", + "ids": "8619,8620,8621,8622,8623" + }, + { + "examine": "The flag of Ardougne.", + "ids": "862" + }, + { + "examine": "These sweetcorn plants are fully grown.", + "ids": "8624" + }, + { + "examine": "Some sweetcorn seeds have been sown in this allotment.", + "ids": "8625" + }, + { + "examine": "Some sweetcorn plants are growing in this allotment.", + "ids": "8626,8627,8628,8629,8630" + }, + { + "examine": "A standard of Asgarnia.", + "ids": "863" + }, + { + "examine": "These sweetcorn plants have been attacked by crows.", + "ids": "8631,8632,8633,8634,8635" + }, + { + "examine": "These sweetcorn plants have become diseased and died.", + "ids": "8636,8637,8638,8639,8640" + }, + { + "examine": "A standard of Kandarin.", + "ids": "864" + }, + { + "examine": "Tomato seeds have been sown in this allotment.", + "ids": "8641" + }, + { + "examine": "Tomato plants are growing in this allotment.", + "ids": "8642,8643,8644" + }, + { + "examine": "These tomato plants are fully grown.", + "ids": "8645" + }, + { + "examine": "Tomato seeds have been sown in this allotment.", + "ids": "8646" + }, + { + "examine": "Tomato plants are growing in this allotment.", + "ids": "8647,8648,8649" + }, + { + "examine": "A standard of Asgarnia.", + "ids": "865" + }, + { + "examine": "These tomato plants have become diseased.", + "ids": "8650,8651,8652" + }, + { + "examine": "These tomato plants have become diseased and died.", + "ids": "8653,8654,8655" + }, + { + "examine": "Watermelon seeds have been sown in this allotment.", + "ids": "8656" + }, + { + "examine": "Watermelons are growing in this allotment.", + "ids": "8657,8658,8659,8660,8661,8662,8663" + }, + { + "examine": "A standard of Kandarin.", + "ids": "866" + }, + { + "examine": "These watermelons could do with harvesting.", + "ids": "8664" + }, + { + "examine": "Watermelon seeds have been sown in this allotment.", + "ids": "8665" + }, + { + "examine": "Watermelons are growing in this allotment.", + "ids": "8666,8667,8668,8669,8670,8671,8672" + }, + { + "examine": "A standard of Misthalin.", + "ids": "867" + }, + { + "examine": "These watermelons have become diseased and need tending.", + "ids": "8673,8674,8675,8676,8677,8678,8679" + }, + { + "examine": "A standard of Varrock.", + "ids": "868" + }, + { + "examine": "These watermelon plants have become diseased and died.", + "ids": "8680" + }, + { + "examine": "These watermelons have become diseased and died.", + "ids": "8681,8682,8683,8684,8685,8686,8687" + }, + { + "examine": "The perfect accompaniment to a bedroom.", + "ids": "8688" + }, + { + "examine": "Fit for milking.", + "ids": "8689" + }, + { + "examine": "A flag flies here.", + "ids": "869" + }, + { + "examine": "Each full of milk no doubt.", + "ids": "8690,8691,8692,8693,8694" + }, + { + "examine": "The door is closed.", + "ids": "8695" + }, + { + "examine": "The door is open.", + "ids": "8696,8697,8698" + }, + { + "examine": "After working with livestock, why not wash your hands?", + "ids": "8699" + }, + { + "examine": "I guess you pull it...", + "ids": "87" + }, + { + "examine": "A sculpted trunk of wood.", + "ids": "870" + }, + { + "examine": "For putting things on.", + "ids": "8700" + }, + { + "examine": "Someone's been preparing meat.", + "ids": "8701" + }, + { + "examine": "A barrel for collecting rain water.", + "ids": "8702,8703,8704,8705,8706,8707,8708,8709,8710,8711" + }, + { + "examine": "Takes my dirty bath water away.", + "ids": "871" + }, + { + "examine": "A grand old fireplace.", + "ids": "8712" + }, + { + "examine": "I bet there's a needle in it somewhere.", + "ids": "8713,8714,8715,8716" + }, + { + "examine": "A loom.", + "ids": "8717,8718,8719,8720,8721,8722,8723,8724" + }, + { + "examine": "It smells horrible in there.", + "ids": "872" + }, + { + "examine": "A little rock.", + "ids": "8725" + }, + { + "examine": "A small chunk of rock.", + "ids": "8726" + }, + { + "examine": "They're not floating, even though it may look like they are!", + "ids": "8727" + }, + { + "examine": "A deposit of rocks.", + "ids": "8728,8729,8730,8731,8732,8733,8734,8735,8736" + }, + { + "examine": "Ideal for washing things in.", + "ids": "873,874,4063,36971" + }, + { + "examine": "This tap runs from the apple crushing barrel.", + "ids": "8737" + }, + { + "examine": "You can walk through these doors.", + "ids": "8738,8739,8740,8741" + }, + { + "examine": "An odd looking tree.", + "ids": "8742,8743" + }, + { + "examine": "I can climb this.", + "ids": "8744,8745" + }, + { + "examine": "I can climb down this.", + "ids": "8746" + }, + { + "examine": "A local water source.", + "ids": "8747" + }, + { + "examine": "Used for spinning thread.", + "ids": "8748" + }, + { + "examine": "A Shrine to the glory of Seren.", + "ids": "8749" + }, + { + "examine": "Wash here.", + "ids": "875,876,877" + }, + { + "examine": "I can cook here.", + "ids": "8750" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "8751" + }, + { + "examine": "A good source of books!", + "ids": "8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,8764,8765,8766" + }, + { + "examine": "The lamp has a glowing crystal at its core.", + "ids": "8767" + }, + { + "examine": "A table.", + "ids": "8768,8769" + }, + { + "examine": "A chair.", + "ids": "8770" + }, + { + "examine": "A painting of an elf standing in some woodland.", + "ids": "8771" + }, + { + "examine": "Sit back and relax...", + "ids": "8772" + }, + { + "examine": "A useful ranging device.", + "ids": "8773,8774" + }, + { + "examine": "Use these with bows.", + "ids": "8775" + }, + { + "examine": "Tailor-made for needlework supplies.", + "ids": "8776,8777" + }, + { + "examine": "I'm guessing it's for making elven clothes.", + "ids": "8778" + }, + { + "examine": "Helps make elf clothing.", + "ids": "8779,8780,8781,8782" + }, + { + "examine": "No this is not a mirage!", + "ids": "878" + }, + { + "examine": "I wonder what's under it?", + "ids": "8783,8784" + }, + { + "examine": "I can climb this.", + "ids": "8785" + }, + { + "examine": "The door is closed.", + "ids": "8786,8787,8788,8789" + }, + { + "examine": "Everyone needs a water feature.", + "ids": "879,28682" + }, + { + "examine": "The door is open.", + "ids": "8790,8791,8792,8793,8794" + }, + { + "examine": "Makes gnomes taller.", + "ids": "8795" + }, + { + "examine": "Makes creatures taller.", + "ids": "8796" + }, + { + "examine": "I wonder what's inside.", + "ids": "8797" + }, + { + "examine": "Perhaps I should search it.", + "ids": "8798" + }, + { + "examine": "The desk of the head mourner.", + "ids": "8799" + }, + { + "examine": "A beautiful water feature.", + "ids": "880" + }, + { + "examine": "I wonder what's inside.", + "ids": "8800" + }, + { + "examine": "A sack full of grain.", + "ids": "8801,8802,8803" + }, + { + "examine": "These have grain in them.", + "ids": "8804,8805,8806" + }, + { + "examine": "An empty barrel.", + "ids": "8807" + }, + { + "examine": "A barrel full of mushed apples.", + "ids": "8808" + }, + { + "examine": "A pile of rotten apples!", + "ids": "8809" + }, + { + "examine": "There's a cover over this manhole.", + "ids": "881" + }, + { + "examine": "A wooden gate.", + "ids": "8810,8811,8812,8813" + }, + { + "examine": "A well slept in bed.", + "ids": "8814,8815,8816,8817" + }, + { + "examine": "The door is closed.", + "ids": "8818" + }, + { + "examine": "The door is open.", + "ids": "8819" + }, + { + "examine": "How dangerous, someone has left this manhole open.", + "ids": "882" + }, + { + "examine": "Solid iron bars.", + "ids": "8820,8821,8822,8823,8824,8825,8826,8827" + }, + { + "examine": "Heavy.", + "ids": "8828,8829,8830" + }, + { + "examine": "This is supposed to stop people falling down the manhole.", + "ids": "883" + }, + { + "examine": "You can 'cart' things around on this.", + "ids": "8831,8832,8833,8834,8835,8836,8837,8838,8839" + }, + { + "examine": "Best used with a bucket.", + "ids": "884" + }, + { + "examine": "I feel the need to throw a rotten cabbage!", + "ids": "8840,8841" + }, + { + "examine": "Nothing growing on this tree at the moment...", + "ids": "8842" + }, + { + "examine": "Mmmmm, nice juicy apples!", + "ids": "8843,8844,8845,8846,8847,8848,8849,8850,8851,8852,8853,8854,8855,8856,8857,8858,8859,8860" + }, + { + "examine": "If only I had one of those at home...", + "ids": "885,886" + }, + { + "examine": "This is a special hops patch.", + "ids": "8861,8862,8863,8864" + }, + { + "examine": "Kelda Hop seeds have been sown in this farming patch.", + "ids": "8865" + }, + { + "examine": "Kelda Hops are growing in this farming patch.", + "ids": "8866,8867,8868" + }, + { + "examine": "These are fully grown Kelda Hops.", + "ids": "8869" + }, + { + "examine": "A painting of the King looking royal.", + "ids": "887" + }, + { + "examine": "A barrel of Kelda Stout.", + "ids": "8870" + }, + { + "examine": "This vat contains Kelda hops.", + "ids": "8871" + }, + { + "examine": "Kelda Stout is fermenting in this vat.", + "ids": "8872,8873" + }, + { + "examine": "This vat contains Kelda Stout.", + "ids": "8874,8875,8876,8877" + }, + { + "examine": "Some bizarre mixture between rocks and machinery. Definitely dwarven.", + "ids": "8878" + }, + { + "examine": "A strange box of some kind.", + "ids": "8879" + }, + { + "examine": "A beautiful landscape.", + "ids": "888" + }, + { + "examine": "A strange box of some kind. It's open.", + "ids": "8880" + }, + { + "examine": "A small cave entrance.", + "ids": "8881,8882,8883,8884,8885" + }, + { + "examine": "Keeps mine carts from rolling away.", + "ids": "8886" + }, + { + "examine": "Cart tracks.", + "ids": "8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898" + }, + { + "examine": "A mysterious figure stands alone in this haunting image.", + "ids": "889" + }, + { + "examine": "A support for the tracks.", + "ids": "8899,8900,8901,8902,8903,8904,8905,8906,8907,8908,8909" + }, + { + "examine": "A wrought iron gate.", + "ids": "89,90" + }, + { + "examine": "Hail to the King!", + "ids": "890" + }, + { + "examine": "I don't know much about art, but I like this.", + "ids": "891" + }, + { + "examine": "These books contain all sorts of data on the Red Axe.", + "ids": "8910" + }, + { + "examine": "Big mysterious crates. You wonder what could be inside.", + "ids": "8911" + }, + { + "examine": "Wooden crates with metal edges, contents unknown.", + "ids": "8912" + }, + { + "examine": "Big mysterious crates. There are some papers on top.", + "ids": "8913,8914,8915,8916,8917" + }, + { + "examine": "A symbol of the Red Axe.", + "ids": "8918,8919,8920,8921,8922,8923" + }, + { + "examine": "A painting of some guy standing around somewhere.", + "ids": "892" + }, + { + "examine": "A steam powered cart.", + "ids": "8924,8925" + }, + { + "examine": "A short long boat!", + "ids": "8926" + }, + { + "examine": "Best used with a bucket.", + "ids": "8927,8928" + }, + { + "examine": "A deep and terrifying cave.", + "ids": "8929" + }, + { + "examine": "A really bad portrait of the King.", + "ids": "893" + }, + { + "examine": "It looks cold in there.", + "ids": "8930,8931,8932,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,8944,8945,8946,8947,8948,8949" + }, + { + "examine": "Fine brushwork adds to the realism of this serene landscape.", + "ids": "894" + }, + { + "examine": "Pretty good painting.", + "ids": "895" + }, + { + "examine": "A big slimy lump of rock.", + "ids": "8950" + }, + { + "examine": "A slimy lump of rock.", + "ids": "8951" + }, + { + "examine": "A slippery looking rock.", + "ids": "8952,8953" + }, + { + "examine": "A tall flag blowing in the wind.", + "ids": "8954,8955" + }, + { + "examine": "I could climb this if I wanted.", + "ids": "8956,8957" + }, + { + "examine": "There must be some trick to opening this...", + "ids": "8958,8959,8960" + }, + { + "examine": "I can see the stars on this.", + "ids": "896" + }, + { + "examine": "It's opening...", + "ids": "8961" + }, + { + "examine": "Apparently there was some trick to opening it!", + "ids": "8962" + }, + { + "examine": "Not as difficult to walk through as it was a minute ago.", + "ids": "8963,8964,8965" + }, + { + "examine": "A sturdy looking door, propped shut with a support.", + "ids": "8967,8968,8969,8970,8971" + }, + { + "examine": "A picture of the stars with funny diagrams and things on.", + "ids": "897" + }, + { + "examine": "A portal back to the real world...", + "ids": "8972" + }, + { + "examine": "One of the most common trees in 2009Scape.", + "ids": "8973" + }, + { + "examine": "A commonly found tree.", + "ids": "8974,8975,8976,8977,8978,8979,8980,8981,8982,8983,8984" + }, + { + "examine": "Cross shaped.", + "ids": "898" + }, + { + "examine": "Steam seems to be condensing into the pot. Neat trick!", + "ids": "8985" + }, + { + "examine": "I can see fish swimming in the water. Well, they sort of look like fish...", + "ids": "8986" + }, + { + "examine": "A portal that leads... somewhere?", + "ids": "8987" + }, + { + "examine": "I wonder what's inside...", + "ids": "8988" + }, + { + "examine": "Someone's looking through the chest.", + "ids": "8989,8990,8991,8992,8993,8994,8995,8996,8997" + }, + { + "examine": "Mmmm decorative!", + "ids": "899" + }, + { + "examine": "An appendage for activating something.", + "ids": "8998,8999,9000,9001,9002,9003,9004,9005" + }, + { + "examine": "The barrels of the multicannon.", + "ids": "9" + }, + { + "examine": "Brightens up the room a bit.", + "ids": "900" + }, + { + "examine": "A place to cremate the dead. Needs a body.", + "ids": "9006,9007" + }, + { + "examine": "A place to cremate the dead. Needs a light.", + "ids": "9008,9009" + }, + { + "examine": "I don't know art, but I like it!", + "ids": "901" + }, + { + "examine": "This has broad leaves.", + "ids": "9010,9011,9012,9013" + }, + { + "examine": "This has been chopped away.", + "ids": "9014" + }, + { + "examine": "This has broad leaves.", + "ids": "9015,9016,9017,9018" + }, + { + "examine": "This has been chopped away.", + "ids": "9019" + }, + { + "examine": "Not bad at all.", + "ids": "902" + }, + { + "examine": "This has broad leaves.", + "ids": "9020,9021,9022,9023" + }, + { + "examine": "This has been chopped away.", + "ids": "9024" + }, + { + "examine": "A damaged wooden fence.", + "ids": "9025,9026,9027,9028" + }, + { + "examine": "A wooden fence.", + "ids": "9029" + }, + { + "examine": "Alas poor unicorn, I knew him well.", + "ids": "903" + }, + { + "examine": "Gems encrusted in stone.", + "ids": "9030,9031,9032" + }, + { + "examine": "Many rare plants such as this usually have exotic tubers.", + "ids": "9033" + }, + { + "examine": "A beautiful old mahogany tree.", + "ids": "9034" + }, + { + "examine": "This once was a beautiful tree.", + "ids": "9035" + }, + { + "examine": "A beautiful old teak tree.", + "ids": "9036" + }, + { + "examine": "This tree has been cut down.", + "ids": "9037" + }, + { + "examine": "A set of large, sturdy wooden doors.", + "ids": "9038,9039,9040,9041,9042,9043" + }, + { + "examine": "The scary-looking head of a scary-looking dragon.", + "ids": "904" + }, + { + "examine": "Some goutweed is growing in this patch.", + "ids": "9044,9045,9046,9047" + }, + { + "examine": "Some fully grown goutweed.", + "ids": "9048" + }, + { + "examine": "This goutweed has become diseased.", + "ids": "9049,9050,9051" + }, + { + "examine": "Not what I would pick as wall decoration.", + "ids": "905" + }, + { + "examine": "This goutweed has become diseased and died.", + "ids": "9052,9053,9054,9055,9056,9057" + }, + { + "examine": "He's not stocking much.", + "ids": "9058" + }, + { + "examine": "He's stocking rune caskets.", + "ids": "9059" + }, + { + "examine": "Looks like the bull lost.", + "ids": "906" + }, + { + "examine": "He's stocking blackjacks.", + "ids": "9060" + }, + { + "examine": "He's stocking fez hats.", + "ids": "9061" + }, + { + "examine": "He's stocking rune caskets.", + "ids": "9062" + }, + { + "examine": "He's stocking colourful clothes.", + "ids": "9063" + }, + { + "examine": "A wooden crate.", + "ids": "9064,9065" + }, + { + "examine": "A wooden crate containing caskets.", + "ids": "9066,9067" + }, + { + "examine": "A wooden crate containing Blackjacks.", + "ids": "9068,9069" + }, + { + "examine": "Trinkets and stuff.", + "ids": "907" + }, + { + "examine": "A wooden crate containing fez hats.", + "ids": "9070" + }, + { + "examine": "A wooden crate containing clothes.", + "ids": "9071" + }, + { + "examine": "Boxy.", + "ids": "9072,9073" + }, + { + "examine": "boxy", + "ids": "9074,9075" + }, + { + "examine": "This cloth has been dyed.", + "ids": "9076" + }, + { + "examine": "Mmmm pretty!", + "ids": "9077,9078" + }, + { + "examine": "These dyed fabrics are drying off.", + "ids": "9079,9080,9081" + }, + { + "examine": "Not everyone's idea of a nice wall decoration.", + "ids": "908" + }, + { + "examine": "Items for making clothes are kept on here.", + "ids": "9082" + }, + { + "examine": "Pots full of dye.", + "ids": "9083" + }, + { + "examine": "A way down.", + "ids": "9084" + }, + { + "examine": "There's not much coke in the stove.", + "ids": "9085" + }, + { + "examine": "There's a fair amount of coke in the stove.", + "ids": "9086" + }, + { + "examine": "There's lots of coke in the stove.", + "ids": "9087" + }, + { + "examine": "A big pile of refined coal.", + "ids": "9088" + }, + { + "examine": "You can read the furnace temperature here.", + "ids": "9089" + }, + { + "examine": "Something is written on it. I can't read what.", + "ids": "909" + }, + { + "examine": "Used to pump hot air through the furnace.", + "ids": "9090" + }, + { + "examine": "Bars come out of the blast furnace here.", + "ids": "9091,9092" + }, + { + "examine": "Your bars will come out here.", + "ids": "9093,9094" + }, + { + "examine": "The bars are glowing hot!", + "ids": "9095" + }, + { + "examine": "Your bars are ready to take.", + "ids": "9096" + }, + { + "examine": "They power the conveyor belt.", + "ids": "9097" + }, + { + "examine": "The foreman refers to it as 'Bertha'.", + "ids": "9098" + }, + { + "examine": "It shows that the furnace is working.", + "ids": "9099" + }, + { + "examine": "I don't like the look of this!", + "ids": "91" + }, + { + "examine": "Looks like an eye with huge lashes.", + "ids": "910" + }, + { + "examine": "Ore rides this into the blast furnace.", + "ids": "9100,9101" + }, + { + "examine": "It keeps the conveyor belt running.", + "ids": "9102" + }, + { + "examine": "Fix it, quick!", + "ids": "9103" + }, + { + "examine": "They keep the conveyor belt running.", + "ids": "9104" + }, + { + "examine": "Fix them, quick!", + "ids": "9105" + }, + { + "examine": "It keeps the conveyor belt turning.", + "ids": "9106,9107" + }, + { + "examine": "They keep the conveyor belt turning.", + "ids": "9108,9109,9110,9111,9112,9113,9114" + }, + { + "examine": "A tablet covered in weird looking little squiggles and pictures.", + "ids": "911" + }, + { + "examine": "It shows that the furnace is working.", + "ids": "9115" + }, + { + "examine": "Hot air circulates through these.", + "ids": "9116" + }, + { + "examine": "Quick, fix them!", + "ids": "9117,9118,9119" + }, + { + "examine": "Hot air circulates through these.", + "ids": "9120" + }, + { + "examine": "Quick, fix them!", + "ids": "9121,9122" + }, + { + "examine": "It shows the furnace is working.", + "ids": "9123,9124,9125,9126,9127,9128,9129,9130,9131,9132,9133,9134,9135,9136,9137" + }, + { + "examine": "A way upwards.", + "ids": "9138,9139" + }, + { + "examine": "The gate is closed.", + "ids": "9140,9141" + }, + { + "examine": "The gate is open.", + "ids": "9142" + }, + { + "examine": "Used for getting water.", + "ids": "9143" + }, + { + "examine": "Some kind of jewellery is dangling from this ornament.", + "ids": "9144" + }, + { + "examine": "Someone's showing off their wealth!", + "ids": "9145" + }, + { + "examine": "Filled to the brim with knowledge.", + "ids": "9146,9147" + }, + { + "examine": "A nicely carved wooden chair.", + "ids": "9148" + }, + { + "examine": "An expensive privacy aid!", + "ids": "9149" + }, + { + "examine": "A posh water bowl.", + "ids": "915" + }, + { + "examine": "An old wall.", + "ids": "9150" + }, + { + "examine": "Security breach!", + "ids": "9151" + }, + { + "examine": "A patched up hole.", + "ids": "9152,9153,9154" + }, + { + "examine": "'You are here.'", + "ids": "9155" + }, + { + "examine": "An elegant desk with a curious ornament on it.", + "ids": "9156" + }, + { + "examine": "Someone's gold-trimmed this Saradomin armour!", + "ids": "9157,9158" + }, + { + "examine": "Lit to remember the souls of the departed.", + "ids": "9159,9160,9161,9162,9163,9164,9165" + }, + { + "examine": "You can grow delphiniums in this Farming patch.", + "ids": "9166,9167,9168,9169" + }, + { + "examine": "Delphinium seeds have been sown in this farming patch.", + "ids": "9170" + }, + { + "examine": "Beautiful.", + "ids": "9171,9172" + }, + { + "examine": "These delphiniums are fully grown.", + "ids": "9173,9174,9175,9176" + }, + { + "examine": "You can grow a pink rose bush in this patch.", + "ids": "9177,9178,9179,9180" + }, + { + "examine": "You can grow a white rose bush in this patch.", + "ids": "9181,9182,9183,9184" + }, + { + "examine": "You can grow a red rose bush in this patch.", + "ids": "9185,9186,9187,9188" + }, + { + "examine": "A rose bush.", + "ids": "9189,9190,9191" + }, + { + "examine": "This white rosebush is fully grown.", + "ids": "9192" + }, + { + "examine": "A rose bush.", + "ids": "9193" + }, + { + "examine": "This red rosebush is fully grown.", + "ids": "9194" + }, + { + "examine": "A pink rosebush.", + "ids": "9195" + }, + { + "examine": "This pink rosebush is fully grown.", + "ids": "9196,9197,9198" + }, + { + "examine": "A plantpot of pink orchids.", + "ids": "9199,9200,9201" + }, + { + "examine": "The door is closed.", + "ids": "92,93" + }, + { + "examine": "These pink orchids are fully grown.", + "ids": "9202" + }, + { + "examine": "An empty plantpot.", + "ids": "9203" + }, + { + "examine": "A plantpot filled with soil.", + "ids": "9204" + }, + { + "examine": "A plantpot of yellow orchids.", + "ids": "9205,9206,9207" + }, + { + "examine": "These yellow orchids are fully grown.", + "ids": "9208,9209" + }, + { + "examine": "You can grow a White Tree in this patch.", + "ids": "9210,9211,9212,9213" + }, + { + "examine": "A White Tree sapling has been planted in this patch.", + "ids": "9214" + }, + { + "examine": "A White Tree is growing in this patch.", + "ids": "9215,9216,9217" + }, + { + "examine": "This White Tree is fully grown.", + "ids": "9218" + }, + { + "examine": "This White Tree bears a single fruit.", + "ids": "9219" + }, + { + "examine": "This White Tree bears two fruits.", + "ids": "9220" + }, + { + "examine": "This White Tree bears three fruits.", + "ids": "9221" + }, + { + "examine": "This White Tree bears four fruits.", + "ids": "9222,9223" + }, + { + "examine": "You can grow snowdrops in this Farming patch.", + "ids": "9224,9225,9226,9227" + }, + { + "examine": "A patch of snowdrops.", + "ids": "9228,9229,9230" + }, + { + "examine": "These snowdrops are fully grown.", + "ids": "9231,9232" + }, + { + "examine": "You can grow Burthorpe Vine in this patch.", + "ids": "9233" + }, + { + "examine": "You can grow Burthorpe Vines in this patch.", + "ids": "9234,9235,9236" + }, + { + "examine": "Burthorpe vines are growing in this patch.", + "ids": "9237,9238,9239" + }, + { + "examine": "These Burthorpe vines are fully grown.", + "ids": "9240" + }, + { + "examine": "An empty plinth for a statue.", + "ids": "9241,9242" + }, + { + "examine": "What a good likeness!", + "ids": "9243" + }, + { + "examine": "An empty plinth for a statue.", + "ids": "9244" + }, + { + "examine": "An expertly carved statue of a former King of Misthalin.", + "ids": "9245" + }, + { + "examine": "An empty plinth for a statue.", + "ids": "9246" + }, + { + "examine": "What a good likeness!", + "ids": "9247" + }, + { + "examine": "An empty plinth for a statue.", + "ids": "9248" + }, + { + "examine": "An expertly carved statue of a former King of Misthalin.", + "ids": "9249,9250,9251,9252,9253,9254" + }, + { + "examine": "These grapevines look much healthier now.", + "ids": "9255" + }, + { + "examine": "These grapevines are suffering from some strange disease.", + "ids": "9256" + }, + { + "examine": "Some wild-looking grass.", + "ids": "9257,9258,9259" + }, + { + "examine": "Some red roses.", + "ids": "9260" + }, + { + "examine": "Some pink roses.", + "ids": "9261" + }, + { + "examine": "Some white roses.", + "ids": "9262" + }, + { + "examine": "A White Tree.", + "ids": "9263,9264,9265,9266,9267,9268,9269,9270,9271,9272,9273,9274,9275,9276,9277,9278,9279,9280,9281,9282,9283,9284,9285,9286,9287,9288,9289,9290" + }, + { + "examine": "Sit back and enjoy the view.", + "ids": "9291" + }, + { + "examine": "An expertly carved statue of a former Queen of Misthalin.", + "ids": "9292" + }, + { + "examine": "Funny looking holes that don't look too inviting.", + "ids": "9294" + }, + { + "examine": "A rocky outcrop.", + "ids": "9296,9297,9298" + }, + { + "examine": "Ornate railing.", + "ids": "9299" + }, + { + "examine": "Wooden fencing.", + "ids": "9300" + }, + { + "examine": "A well constructed castle wall.", + "ids": "9301" + }, + { + "examine": "A rather strategically placed hole, which appears to be in the ground.", + "ids": "9302" + }, + { + "examine": "A rocky outcrop.", + "ids": "9303,9304,9305,9306" + }, + { + "examine": "A well weathered wall.", + "ids": "9307,9308" + }, + { + "examine": "A tunnel leading under the wall.", + "ids": "9309,9310" + }, + { + "examine": "An underwall tunnel.", + "ids": "9311,9312" + }, + { + "examine": "Looks suspicious.", + "ids": "9313" + }, + { + "examine": "A wall jutting out into the path.", + "ids": "9314" + }, + { + "examine": "I can jump from this stepping stone.", + "ids": "9315" + }, + { + "examine": "A rocky outcrop.", + "ids": "9316,9317,9318" + }, + { + "examine": "A chain rope", + "ids": "9319,9320" + }, + { + "examine": "A few rocks short of a wall", + "ids": "9321" + }, + { + "examine": "The foam from the river makes this log very slippy.", + "ids": "9322,9323,9324" + }, + { + "examine": "Just another crack in the wall.", + "ids": "9325" + }, + { + "examine": "Funny looking holes that don't look too inviting.", + "ids": "9326" + }, + { + "examine": "They seem to fit in the with the rocky surroundings, so as not to stick out.", + "ids": "9327" + }, + { + "examine": "A slippery well worn log.", + "ids": "9328,9329,9330" + }, + { + "examine": "A rocky outcrop.", + "ids": "9331,9332,9333" + }, + { + "examine": "Used to be ornate, now it's a little bit vandalised.", + "ids": "9334" + }, + { + "examine": "A rocky outcrop.", + "ids": "9335,9336" + }, + { + "examine": "Used to be ornate, now it's a little bit vandalised.", + "ids": "9337" + }, + { + "examine": "Should scare off the birds...", + "ids": "9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,9350,9351,9352,9353" + }, + { + "examine": "This looks like the way out.", + "ids": "9354" + }, + { + "examine": "A cave deep into the volcano.", + "ids": "9355" + }, + { + "examine": "A cave deeper into the volcano.", + "ids": "9356,9357" + }, + { + "examine": "A cave deep into the volcano.", + "ids": "9358" + }, + { + "examine": "This looks like the way out.", + "ids": "9359,9360,9361,9362,9363,9364,9365" + }, + { + "examine": "The barrier of heat is down.", + "ids": "9366" + }, + { + "examine": "A barrier of heat.", + "ids": "9367,9368,9369,9370,9371,9372,9373" + }, + { + "examine": "Hot enough to cook your breakfast on.", + "ids": "9374,9375,9376" + }, + { + "examine": "An egg incubated in the lava.", + "ids": "9377,9378,9379" + }, + { + "examine": "Who's the man?", + "ids": "9380" + }, + { + "examine": "A wooden crate.", + "ids": "9381" + }, + { + "examine": "Some wooden crates.", + "ids": "9382,9383,9384,9385,9386,9387,9388,9389" + }, + { + "examine": "A natural forge using volcanic heat.", + "ids": "9390" + }, + { + "examine": "I spy with my little eye...", + "ids": "9391,9392,9393,9394,9395,9396,9397" + }, + { + "examine": "Lets you put items into your bank.", + "ids": "9398,9399,26969,36788" + }, + { + "examine": "A wrought iron gate.", + "ids": "94,95" + }, + { + "examine": "Sparse weeds.", + "ids": "9400" + }, + { + "examine": "Weeds.", + "ids": "9401" + }, + { + "examine": "Thick weeds.", + "ids": "9402" + }, + { + "examine": "This is Unferth's patch for growing potatos.", + "ids": "9403" + }, + { + "examine": "You say Potato, I say Poh-tar-to.", + "ids": "9404,9405,9406,9407" + }, + { + "examine": "Looks like these potatoes have fully grown.", + "ids": "9408,9409,9410,9411,9412,9413,9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9424,9425,9426,9427,9428,9429" + }, + { + "examine": "A nice sturdy bare table.", + "ids": "9430" + }, + { + "examine": "A nice sturdy table.", + "ids": "9431" + }, + { + "examine": "A table with milk.", + "ids": "9432" + }, + { + "examine": "A table with cake.", + "ids": "9433" + }, + { + "examine": "A hearty meal for Unferth.", + "ids": "9434,9435" + }, + { + "examine": "Great for sleeping in.", + "ids": "9436" + }, + { + "examine": "Not so great for sleeping in, it's not made.", + "ids": "9437,9438" + }, + { + "examine": "A fire burns brightly here.", + "ids": "9439" + }, + { + "examine": "An empty fire place.", + "ids": "9440" + }, + { + "examine": "An unlit fire place.", + "ids": "9441,9442" + }, + { + "examine": "A good source of books!", + "ids": "9443,9444,9445,9446,9447,9448,9449,9450,9451,9452,9453,9454,9455,9456,9457,9458,9459,9460,9461,9462,9463,9464,9465,9466,9467,9468,9469" + }, + { + "examine": "I can climb these stairs.", + "ids": "9470" + }, + { + "examine": "They go down.", + "ids": "9471" + }, + { + "examine": "What could be down here?", + "ids": "9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497" + }, + { + "examine": "A small plant, suitable for sandy soil.", + "ids": "9498" + }, + { + "examine": "A decorative plant.", + "ids": "9499" + }, + { + "examine": "A fairly small plant.", + "ids": "9500" + }, + { + "examine": "Another plant.", + "ids": "9501,9502,9503,9504,9505,9506,9507" + }, + { + "examine": "Perfect for snoozing in the sun.", + "ids": "9508" + }, + { + "examine": "Lovely comfy-looking big bed.", + "ids": "9509" + }, + { + "examine": "East to Draynor Village :: South to Rimmington :: South-east to Port Sarim.", + "ids": "9510,9511,9512,9513" + }, + { + "examine": "Betty's chair.", + "ids": "9514" + }, + { + "examine": "I wonder what she's making?", + "ids": "9515" + }, + { + "examine": "Betty's counter.", + "ids": "9516,9517,9518" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "9519" + }, + { + "examine": "A wooden barrel containing lots of fish.", + "ids": "9520" + }, + { + "examine": "A wooden barrel for storage.", + "ids": "9521" + }, + { + "examine": "How much does this weigh?", + "ids": "9522" + }, + { + "examine": "A case. With books.", + "ids": "9523" + }, + { + "examine": "A mooring chain.", + "ids": "9524" + }, + { + "examine": "One horsepower; wooden suspension: a beauty.", + "ids": "9525" + }, + { + "examine": "Betty keeps some rune objects here.", + "ids": "9526,9527,9528,9529" + }, + { + "examine": "A sinister fungus.", + "ids": "9530" + }, + { + "examine": "Sit back and relax...", + "ids": "9531" + }, + { + "examine": "Sit back and enjoy the view.", + "ids": "9532" + }, + { + "examine": "A wooden crate.", + "ids": "9533,9534" + }, + { + "examine": "Some wooden crates.", + "ids": "9535" + }, + { + "examine": "Some wooden boxes.", + "ids": "9536" + }, + { + "examine": "It's used for loading and unloading fishing boats.", + "ids": "9537" + }, + { + "examine": "I hope it doesn't sink.", + "ids": "9538,9539,9540,9541,9542,9543,9544,9545,9546,9547" + }, + { + "examine": "For wiping muddy feet on.", + "ids": "954" + }, + { + "examine": "A bucket full of red hot coals.", + "ids": "9548,9549,9550,9551,9552,9553,9554,9555,9556,9557" + }, + { + "examine": "Disturbingly its eyes look everywhere in the room except at you.", + "ids": "955,956,957,958" + }, + { + "examine": "It leads up.", + "ids": "9558" + }, + { + "examine": "I can climb down this.", + "ids": "9559,9560" + }, + { + "examine": "I guess I could sleep in it if I were really tired.", + "ids": "9561" + }, + { + "examine": "A prison cell door.", + "ids": "9562" + }, + { + "examine": "A locked prison cell door.", + "ids": "9563" + }, + { + "examine": "A prison cell door.", + "ids": "9564" + }, + { + "examine": "Stops people getting out.", + "ids": "9565,9566" + }, + { + "examine": "Ewww!", + "ids": "9567" + }, + { + "examine": "En-suite facilities in every cell!", + "ids": "9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581" + }, + { + "examine": "I can climb up these stairs.", + "ids": "9582,9583" + }, + { + "examine": "I can climb down these stairs.", + "ids": "9584,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599,9600,9601,9602,9603" + }, + { + "examine": "What has a face and hands but is not a man?", + "ids": "959" + }, + { + "examine": "A dark flight of stairs.", + "ids": "96,98" + }, + { + "examine": "It's a time machine....sort of!", + "ids": "960" + }, + { + "examine": "A leafy plant.", + "ids": "9604" + }, + { + "examine": "A leafy fern.", + "ids": "9605" + }, + { + "examine": "A leafy shrub.", + "ids": "9606" + }, + { + "examine": "A case. With books.", + "ids": "9607,9608,9609" + }, + { + "examine": "'Ask about our low, low interest rates!'", + "ids": "961" + }, + { + "examine": "An artist's easel.", + "ids": "9610" + }, + { + "examine": "A good source of books!", + "ids": "9611" + }, + { + "examine": "The Make-over Mage's bed.", + "ids": "9612" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "9613" + }, + { + "examine": "Generally used for putting things on.", + "ids": "9614" + }, + { + "examine": "A small wooden table.", + "ids": "9615" + }, + { + "examine": "Items are for sale here.", + "ids": "9616" + }, + { + "examine": "All-purpose storage.", + "ids": "9617,9618,9619,9620" + }, + { + "examine": "Some helpful people have placed notes on here, how nice.", + "ids": "962" + }, + { + "examine": "For sitting.", + "ids": "9621" + }, + { + "examine": "Fancy.", + "ids": "9622,9623" + }, + { + "examine": "It's like a land rudder.", + "ids": "9624" + }, + { + "examine": "A wooden wheelbarrow.", + "ids": "9625,9626,9627,9628,9629,9630,9631,9632,9633,9634,9635,9636,9637,9638,9639,9640,9641,9642,9643,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9654,9655,9656,9657,9658,9659,9660" + }, + { + "examine": "Essentials for a seamstress.", + "ids": "963" + }, + { + "examine": "It really was this big!", + "ids": "964,965" + }, + { + "examine": "He doesn't look too jolly to me!", + "ids": "966" + }, + { + "examine": "This tree has been cut down.", + "ids": "9661" + }, + { + "examine": "Popular with farmers and treasure hunters.", + "ids": "9662" + }, + { + "examine": "This tree has fallen to the ground.", + "ids": "9663" + }, + { + "examine": "A gnarly old tree root.", + "ids": "9664" + }, + { + "examine": "The roots of a tree are exposed.", + "ids": "9665" + }, + { + "examine": "I hope I don't trip over any of these.", + "ids": "9666" + }, + { + "examine": "Disturbingly man-like.", + "ids": "9667" + }, + { + "examine": "Its eyes stare off into the distance...", + "ids": "9668,9669,9670,9671,9672,9673,9674,9675,9676,9677,9678,9679,9680,9681" + }, + { + "examine": "Looks like this ship was way off course!", + "ids": "967" + }, + { + "examine": "X marks the spot.", + "ids": "968" + }, + { + "examine": "Expels smoke from the range.", + "ids": "9683" + }, + { + "examine": "Wash your hands!", + "ids": "9684" + }, + { + "examine": "Generally used for putting things on.", + "ids": "9685" + }, + { + "examine": "Some fabric ready for clothing.", + "ids": "9686" + }, + { + "examine": "I'm guessing it's for making women's clothes.", + "ids": "9687,9688" + }, + { + "examine": "Looks like it's for making men's clothes.", + "ids": "9689" + }, + { + "examine": "Complete with authentic seaweed!", + "ids": "969" + }, + { + "examine": "I think this one's for making men's clothes.", + "ids": "9690,9691,9692,9693" + }, + { + "examine": "Some helpful people have placed notes on here, how nice.", + "ids": "9694" + }, + { + "examine": "This notice board is full of scrolls and charts.", + "ids": "9695" + }, + { + "examine": "I wonder what's inside ?", + "ids": "9696" + }, + { + "examine": "A nice sturdy looking table.", + "ids": "9697,9698,9699,9700,9701" + }, + { + "examine": "This mentions the monk trapped here...", + "ids": "970" + }, + { + "examine": "This clearly isn't used for dining very often.", + "ids": "9702" + }, + { + "examine": "Not as nice as some.", + "ids": "9703" + }, + { + "examine": "This needs dusting before I'll sit on it.", + "ids": "9704,9705" + }, + { + "examine": "This must let me in to the arena somehow...", + "ids": "9706" + }, + { + "examine": "This must let me out of the arena somehow...", + "ids": "9707" + }, + { + "examine": "A rocky outcrop.", + "ids": "9708,9709,9710,9711,9712,9713,9714,9715,9716,9717,9718,9719,9720,9721,9722,9723,9724,9725,9726,9727,9728,9729,9730,9731,9732,9733,9734,9735,9737" + }, + { + "examine": "A useful ranging device.", + "ids": "971" + }, + { + "examine": "Use these with bows.", + "ids": "972" + }, + { + "examine": "A powerful throwing weapon.", + "ids": "973" + }, + { + "examine": "An old crumbled pillar.", + "ids": "9738,9739,9740" + }, + { + "examine": "Improved Leather armour for archers.", + "ids": "974" + }, + { + "examine": "Smells like fish.", + "ids": "9741,9742,9743" + }, + { + "examine": "I can climb up here.", + "ids": "9744" + }, + { + "examine": "I can go below decks with this ladder.", + "ids": "9745,9746,9747" + }, + { + "examine": "A collection point.", + "ids": "9748" + }, + { + "examine": "A Lever.", + "ids": "9749,9750" + }, + { + "examine": "Leather made from the skin of a Dragon.", + "ids": "975" + }, + { + "examine": "A crystal that was broken a long time ago.", + "ids": "9751" + }, + { + "examine": "A repowered crystal.", + "ids": "9752" + }, + { + "examine": "Perhaps I should search it.", + "ids": "9753" + }, + { + "examine": "I wonder what's inside.", + "ids": "9754" + }, + { + "examine": "Perhaps I should search it.", + "ids": "9755" + }, + { + "examine": "I wonder what's inside.", + "ids": "9756" + }, + { + "examine": "Perhaps I should search it.", + "ids": "9757" + }, + { + "examine": "I wonder what's inside.", + "ids": "9758" + }, + { + "examine": "Perhaps I should search it.", + "ids": "9759" + }, + { + "examine": "I wonder what's inside.", + "ids": "9760" + }, + { + "examine": "Perhaps I should search it.", + "ids": "9761" + }, + { + "examine": "I wonder what's inside.", + "ids": "9762" + }, + { + "examine": "A recently killed guard.", + "ids": "9763,9764,9765" + }, + { + "examine": "A recently killed slave.", + "ids": "9766,9767" + }, + { + "examine": "A door of pure light!", + "ids": "9768,9769,9770,9771,9772,9773,9774,9775,9776,9777,9778,9779,9780,9781,9782,9783,9784,9785,9786,9787,9788,9789,9790,9791,9792,9793,9794,9795,9796,9797,9798,9799,9800,9801,9802,9803,9804,9805,9806,9807,9808,9809,9810,9811,9812,9813,9814,9815,9816,9817,9818,9819,9820,9821,9822,9823,9824,9825,9826,9827,9828,9829,9830,9831,9832,9833,9834,9835,9836,9837,9838,9839,9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854,9855,9856,9857,9858,9859,9860,9861,9862,9863,9864,9865,9866,9867,9868,9869,9870,9871,9872,9873,9874,9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885,9886,9887,9888,9889,9890,9891,9892,9893,9894,9895,9896,9897" + }, + { + "examine": "There are holes passing through this pillar.", + "ids": "9898,9899,9900,9901,9902,9903,9904,9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,9915,9916,9917,9918,9919,9920,9921,9922,9923,9924,9925,9926,9927,9928,9929,9930,9931,9932,9933,9934,9935,9936,9937,9938,9939,9940,9941,9942,9943,9944,9945,9946,9947,9948,9949,9950,9951,9952,9953,9954,9955,9956,9957,9958,9959,9960,9961,9962,9963,9964,9965,9966,9967,9968,9969,9970,9971,9972,9973,9974" + }, + { + "examine": "The door is closed.", + "ids": "99" + }, + { + "examine": "I can climb over the fence with this..", + "ids": "993" + }, + { + "examine": "I can climb down these.", + "ids": "9975" + }, + { + "examine": "A cave wall.", + "ids": "9976" + }, + { + "examine": "I wonder where this goes?", + "ids": "9977" + }, + { + "examine": "A ladder carved into the wall.", + "ids": "9978,9979" + }, + { + "examine": "A good place for light to merge.", + "ids": "9980" + }, + { + "examine": "A good place for a light crystal.", + "ids": "9981,9982,9983,9984,9985,9986,9987" + }, + { + "examine": "A broken pillar.", + "ids": "9988,9989,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999,10000,10001" } ] \ 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..dc6fbe429 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)", @@ -1609,5 +1546,41 @@ "animation": "426", "drop_ammo": "true", "ammunition": "14202,14203,14204,14205,14206" + }, + { + "itemId": "10149", + "name": "Swamp lizard", + "ammo_slot": "13", + "weapon_type": "0", + "animation": "5247", + "drop_ammo": "false", + "ammunition": "10142" + }, + { + "itemId": "10146", + "name": "Orange salamander", + "ammo_slot": "13", + "weapon_type": "0", + "animation": "5247", + "drop_ammo": "false", + "ammunition": "10143" + }, + { + "itemId": "10147", + "name": "Red salamander", + "ammo_slot": "13", + "weapon_type": "0", + "animation": "5247", + "drop_ammo": "false", + "ammunition": "10144" + }, + { + "itemId": "10148", + "name": "Black salamander", + "ammo_slot": "13", + "weapon_type": "0", + "animation": "5247", + "drop_ammo": "false", + "ammunition": "10145" } ] diff --git a/Server/data/configs/shops.json b/Server/data/configs/shops.json index 39f14cafa..2f285314c 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", @@ -186,7 +177,7 @@ "general_store": "true", "id": "21", "title": "Gunslik's Assorted Items", - "stock": "{1931,30,100}-{1935,30,100}-{1735,10,100}-{1925,10,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{550,10,100}-{9003,10,100}" + "stock": "{1935,10,100}-{1925,30,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{36,10,100}-{596,10,100}-{973,10,100}-{1059,10,100}-{229,300,100}-{233,10,100}-{954,10,100}" }, { "npcs": "1334", @@ -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", @@ -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..5c93c44d2 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -6,9 +6,10 @@ 1.0.0 core.Server + UTF-8 true - 1.6.21 - 1.6 + 1.8.20 + 11 5.7.0 11 11 @@ -16,7 +17,7 @@ org.jetbrains.kotlin - kotlin-stdlib-jdk8 + kotlin-stdlib ${kotlin.version} compile @@ -90,6 +91,11 @@ 2.9.0 compile + + org.java-websocket + Java-WebSocket + 1.5.7 + @@ -195,7 +201,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 deleted file mode 100644 index 3759a5951..000000000 --- a/Server/src/main/content/data/BossKillCounter.java +++ /dev/null @@ -1,177 +0,0 @@ -package content.data; - -import core.game.node.entity.player.Player; - -/** - * The BossKillcounter keeps track of the amount of bosses the player has slain. - * addtoKillcount(player, npcId) should be added in the finalizeDeath() method of the combat handler for the boss. - * @author Splinter - */ -public enum BossKillCounter { - - - /* ORDINAL BOUND */ - KING_BLACK_DRAGON(new int[] { 50 }, "King Black Dragon", 14649), - BORK(new int[] { 7133, 7134 }, "Bork", -1), - DAGANNOTH_SUPREME(new int[] { 2881 }, "Dagannoth Supreme", 14639), - DAGANNOTH_PRIME(new int[] { 2882 }, "Dagannoth Prime", 14640), - DAGANNOTH_REX(new int[] { 2883 }, "Dagannoth Rex", 14641), - CHAOS_ELEMENTAL(new int[] { 3200 }, "Chaos Elemental", 14638), - GIANT_MOLE(new int[] { 3340 }, "Giant Mole", 14642), - SARADOMIN(new int[] { 6247 }, "Commander Zilyana", 14647), - ZAMORAK(new int[] { 6203 }, "K'ril Tsutsaroth", 14648), - BANDOS(new int[] { 6260 }, "General Graardor", 14646), - ARMADYL(new int[] { 6222 }, "Kree'arra", 14645), - JAD(new int[] { 2745 }, "Tz-Tok Jad", 14828), - KALPHITE_QUEEN(new int[] { 1160 }, "Kalphite Queen", 14650), - CORPOREAL_BEAST(new int[] { 8133 }, "Corporeal Beast", 14653), - TORMENTED_DEMONS(new int[] { - 8349, 8350, 8351, 8352, 8353, 8354, - 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), - - - ; - - /** - * The npcs that can increase the killcounter - */ - private final int[] npc; - - /** - * The name of the NPC, to be displayed as a sendMessage - */ - private final String name; - - /** - * The item ID of the pet relating to the boss. - */ - private final int petId; - - /** - * Constructs a new {@code BossKillCounter} {@code Object}. - * @param npc the npc. - * @param name the npc's string name - */ - BossKillCounter(final int[] npc, final String name, final int petId) { - this.npc = npc; - this.name = name; - this.petId = petId; - } - - /** - * Gets the npc. - * @return The npc. - */ - public int[] getNpc() { - return npc; - } - - /** - * Gets the NPC's name - * @return their name - */ - public String getName() { - return name; - } - - /** - * Gets the petId - * @return The petId - */ - public int getPetId() { - return petId; - } - - /** - * Gets the type for the npc. - * @param npc the npc. - * @return the BossKillcounter - */ - public static BossKillCounter forNPC(final int npc) { - for (BossKillCounter kc : BossKillCounter.values()) { - for (int i : kc.getNpc()) { - if (npc == i) { - return kc; - } - } - } - return null; - } - - /** - * Adds to the player's killcount for that particular boss. - * @param killer The player who killed the npc - * @param npcid the ID of the npc that just died - */ - public static void addtoKillcount(Player killer, int npcid) { - if (killer == null) { - return; - } - BossKillCounter boss = BossKillCounter.forNPC(npcid); - if (boss == null) { - return; - } - killer.getSavedData().getGlobalData().getBossCounters()[boss.ordinal()]++; - killer.getPacketDispatch().sendMessage("Your " + boss.getName() + " killcount is now: " + killer.getSavedData().getGlobalData().getBossCounters()[boss.ordinal()] + "."); -// addBossPet(killer, npcid, boss); - } - -// /** -// * Gives the player the pet if they killed a certain boss. -// * The chance by default is 1/5000. This rate lowers to 1/2200 if the for Boss Pets is active. -// * Note: Not all bosses have pet versions of themselves. -// */ -// private static void addBossPet(Player killer, int npcid, BossKillCounter boss){ -// if(boss.getPetId() == -1){ //The boss does not have a pet version. -// return; -// } -// int number = 5000; -// if (npcid == 2745) { -// number = 200; -// if (killer.getSlayer().getTask() == Tasks.JAD) { -// number = 100; -// } -// } else if (npcid == 3200) { -// number = 300; -// } -// int rand = number; -// if(rand == 10){ -// for (int i = 0; i < killer.getFamiliarManager().getInsuredPets().size(); i++) { -// if (killer.getFamiliarManager().getInsuredPets().get(i).getBabyItemId() == boss.getPetId()) { -// return; -// } -// } -// if(killer.getFamiliarManager().hasFamiliar() && killer.getInventory().freeSlots() < 1){ -// return; -// } -// if(!killer.getFamiliarManager().hasFamiliar()){ -// killer.getFamiliarManager().summon(new Item(boss.getPetId()), true); -// killer.sendNotificationMessage("You have a funny feeling like you're being followed."); -// } else if (killer.getInventory().freeSlots() > 0){ -// killer.getInventory().add(new Item(boss.getPetId(), 1)); -// killer.sendNotificationMessage("You feel something weird sneaking into your backpack."); -// } -// Repository.sendNews(killer.getUsername()+" now commands a miniature "+(boss.equals(CORPOREAL_BEAST) ? "Dark core" : boss.getName())+"!"); -// } -// } - - /** - * Increments the player's Barrows chest counter. - * @param player the player - */ - public static void addtoBarrowsCount(Player player) { - if (player == null) { - return; - } - player.getSavedData().getGlobalData().setBarrowsLoots(player.getSavedData().getGlobalData().getBarrowsLoots() + 1); - player.getPacketDispatch().sendMessage("Your Barrows chest count is: " + player.getSavedData().getGlobalData().getBarrowsLoots() + "."); - } - -} diff --git a/Server/src/main/content/data/BossKillCounter.kt b/Server/src/main/content/data/BossKillCounter.kt new file mode 100644 index 000000000..cea7c687d --- /dev/null +++ b/Server/src/main/content/data/BossKillCounter.kt @@ -0,0 +1,72 @@ +package content.data + +import core.api.sendMessage +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +/** + * Enumerates the bosses that have their kill counts tracked in a player's statistics (::stats). + * addToBossKillCount(player, npcId) should be added in the finalizeDeath() method of the combat handler for the boss. + * ORDINAL BOUND + * @author Bishop + */ + +enum class BossKillCounter(val bossId: IntArray, val bossName: String) { + + KING_BLACK_DRAGON(intArrayOf(NPCs.KING_BLACK_DRAGON_50), "King Black Dragon"), + BORK(intArrayOf(NPCs.BORK_7133, NPCs.BORK_7134), "Bork"), + DAGANNOTH_SUPREME(intArrayOf(NPCs.DAGANNOTH_SUPREME_2881), "Dagannoth Supreme"), + DAGANNOTH_PRIME(intArrayOf(NPCs.DAGANNOTH_PRIME_2882), "Dagannoth Prime"), + DAGANNOTH_REX(intArrayOf(NPCs.DAGANNOTH_REX_2883), "Dagannoth Rex"), + CHAOS_ELEMENTAL(intArrayOf(NPCs.CHAOS_ELEMENTAL_3200), "Chaos Elemental"), + GIANT_MOLE(intArrayOf(NPCs.GIANT_MOLE_3340), "Giant Mole"), + SARADOMIN(intArrayOf(NPCs.COMMANDER_ZILYANA_6247), "Commander Zilyana"), + ZAMORAK(intArrayOf(NPCs.KRIL_TSUTSAROTH_6203), "K'ril Tsutsaroth"), + BANDOS(intArrayOf(NPCs.GENERAL_GRAARDOR_6260), "General Graardor"), + ARMADYL(intArrayOf(NPCs.KREEARRA_6222), "Kree'arra"), + JAD(intArrayOf(NPCs.TZTOK_JAD_2745), "Tz-Tok Jad"), + KALPHITE_QUEEN(intArrayOf(NPCs.KALPHITE_QUEEN_1160), "Kalphite Queen"), + CORPOREAL_BEAST(intArrayOf(NPCs.CORPOREAL_BEAST_8133), "Corporeal Beast"), + TORMENTED_DEMONS( + intArrayOf( + NPCs.TORMENTED_DEMON_8349, NPCs.TORMENTED_DEMON_8350, NPCs.TORMENTED_DEMON_8351, + NPCs.TORMENTED_DEMON_8352, NPCs.TORMENTED_DEMON_8353, NPCs.TORMENTED_DEMON_8354, + NPCs.TORMENTED_DEMON_8355, NPCs.TORMENTED_DEMON_8356, NPCs.TORMENTED_DEMON_8357, + NPCs.TORMENTED_DEMON_8358, NPCs.TORMENTED_DEMON_8359, NPCs.TORMENTED_DEMON_8360, + NPCs.TORMENTED_DEMON_8361, NPCs.TORMENTED_DEMON_8362, NPCs.TORMENTED_DEMON_8363, + NPCs.TORMENTED_DEMON_8364, NPCs.TORMENTED_DEMON_8365, NPCs.TORMENTED_DEMON_8366), + "Tormented demon" + ), + PENANCE_QUEEN(intArrayOf(NPCs.PENANCE_QUEEN_5247), "Penance Queen"); + + companion object { + private fun forBossId(npc: Int): BossKillCounter? { + for (kc in values()) { + for (i in kc.bossId) { + if (npc == i) { + return kc + } + } + } + return null + } + + @JvmStatic + fun addToBossKillCount(killer: Player?, bossId: Int) { + if (killer == null) { + return + } + val boss: BossKillCounter = forBossId(bossId) ?: return + killer.getSavedData().globalData.bossCounters[boss.ordinal]++ + sendMessage(killer, "Your ${boss.bossName} killcount is now: ${killer.getSavedData().globalData.bossCounters[boss.ordinal]}.") + } + + fun addToBarrowsChestCount(player: Player?) { + if (player == null) { + return + } + player.getSavedData().globalData.barrowsLoots++ + sendMessage(player, "Your Barrows chest count is: ${player.getSavedData().globalData.barrowsLoots}.") + } + } +} 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..251bfa6c1 100644 --- a/Server/src/main/content/data/EnchantedJewellery.kt +++ b/Server/src/main/content/data/EnchantedJewellery.kt @@ -2,23 +2,27 @@ 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.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.tools.Log 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 + * Represents a piece of enchanted jewellery. + * @author Vexia, downthecrop, Player Name */ enum class EnchantedJewellery( @@ -73,14 +77,14 @@ enum class EnchantedJewellery( "Edgeville", "Karamja", "Draynor Village", - "Al-Kharid", - "Nowhere." + "Al Kharid", + "Nowhere" ), arrayOf( Location.create(3087, 3495, 0), Location.create(2919, 3175, 0), Location.create(3104, 3249, 0), - Location.create(3304, 3124, 0) + Location.create(3292, 3175, 0) ), Items.AMULET_OF_GLORY4_1712, Items.AMULET_OF_GLORY3_1710, @@ -103,7 +107,7 @@ enum class EnchantedJewellery( "Barbarian Outpost.", "Clan Wars.", "Wilderness Volcano.", - "Corporeal Beast" + "Corporeal Beast." ), arrayOf( Location.create(2899, 3563, 0), @@ -135,10 +139,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 +159,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 +179,7 @@ enum class EnchantedJewellery( ), RING_OF_WEALTH( arrayOf( - "Grand Exchange", + "Grand Exchange.", "Nowhere." ), arrayOf( @@ -186,6 +190,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 +210,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 clicks one of the options in the jewellery piece's options dialog. * @param player the player. * @param item the used jewellery item. * @param buttonID the button id. @@ -212,38 +223,92 @@ enum class EnchantedJewellery( } return } + attemptTeleport(player, item, buttonID, true, 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 replace Whether to attempt replacing the item with its lower-charge version. Always true unless you generated a fake item in code. + * @param isEquipped If the player is operating. + */ + fun attemptTeleport(player: Player, item: Item, buttonID: Int, replace: Boolean, 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 } + val location = getLocation(buttonID) + player.scripts.delay = GameWorld.ticks + 4 + queueScript(player, 0, QueueStrength.SOFT) { stage -> + when (stage) { + 0 -> { + lock(player, 4) + // This is an intentional teleport animation & graphics difference as these two jewellery + // animations are authentic to 2009 while others are currently unknown. + if (this == RING_OF_DUELING || this == GAMES_NECKLACE) { + visualize(player, JEWELLERY_ANIMATION, JEWELLERY_GRAPHICS) + } else { + visualize(player, ANIMATION, GRAPHICS) + } + playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + player.impactHandler.disabledTicks = 4 + closeInterface(player) + return@queueScript delayScript(player, 3) + } + 1 -> { + teleport(player, location) + resetAnimator(player) + if (this == RING_OF_DUELING || this == GAMES_NECKLACE) { + player.graphics(JEWELLERY_GRAPHICS) + } + unlock(player) + player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.JEWELRY, item, location)) + + if (DIGSITE_PENDANT.ids.contains(item.id)) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 10) + } + + if (!replace) { + return@queueScript stopExecuting(player) + } + if (isLastItemIndex(itemIndex)) { + if (isSlayerRing(item)) { + if (isEquipped) { + // Remove it now, but only addItemOrDrop the gem after the teleport has procced + if (removeItem(player, item, Container.EQUIPMENT)) { + return@queueScript delayScript(player, 1) + } + log(this.javaClass, Log.ERR, "Error replacing slayer ring with enchanted gem (removeItem from equipment)") + } + // Not equipped -> can replace fluently + if (replaceSlot(player, item.slot, Item(Items.ENCHANTED_GEM_4155), item) == item) { + return@queueScript stopExecuting(player) + } + log(this.javaClass, Log.ERR, "Error replacing slayer ring with enchanted gem (replaceSlot)") + } + // Not slayer ring + if (isCrumble) { + val removeFrom = if (isEquipped) Container.EQUIPMENT else Container.INVENTORY + if (removeItem(player, item, removeFrom)) { + return@queueScript stopExecuting(player) + } + log(this.javaClass, Log.ERR, "Error crumbling jewelry") + } + } else { + replaceJewellery(player, item, nextJewellery, isEquipped) + } + } + 2 -> { + addItemOrDrop(player, Items.ENCHANTED_GEM_4155) + sendMessage(player, "Your Ring of Slaying reverts back into a regular enchanted gem.") + } + } + return@queueScript stopExecuting(player) + } + return true } private fun replaceJewellery(player: Player, item: Item, nextJewellery: Item, isEquipped: Boolean) { @@ -254,18 +319,6 @@ enum class EnchantedJewellery( } } - private fun crumbleJewellery(player: Player, item: Item, isEquipped: Boolean) { - if (isEquipped) { - removeItem(player, item, Container.EQUIPMENT) - } else { - 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.") - } - } - private fun isSlayerRing(item: Item): Boolean { return (item.id in RING_OF_SLAYING.ids) } @@ -274,14 +327,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,8 +378,9 @@ enum class EnchantedJewellery( companion object { private val ANIMATION = Animation(714) - private val AUDIO = Audio(200) private val GRAPHICS = Graphics(308, 100, 50) + private val JEWELLERY_ANIMATION = Animation(9603) + private val JEWELLERY_GRAPHICS = Graphics(1684) val idMap = HashMap() init { @@ -335,4 +389,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/RepairItem.java b/Server/src/main/content/data/RepairItem.java deleted file mode 100644 index 3ab25a4d5..000000000 --- a/Server/src/main/content/data/RepairItem.java +++ /dev/null @@ -1,87 +0,0 @@ -package content.data; - -import core.game.node.item.Item; - -/** - * Represents the repair item type. - * @author Vexia - */ -public enum RepairItem { - BRONZE_HATCHET(new Item(494, 1), new Item(1351, 1), 0), - BRONZE_PICKAXE(new Item(468, 1), new Item(1265, 1), 0), - IRON_HATCHET(new Item(496, 1), new Item(1349, 1), 0), - IRON_PICKAXE(new Item(470, 1), new Item(1267, 1), 0), - STEEL_HATCHET(new Item(498, 1), new Item(1353, 1), 0), - STEEL_PICKAXE(new Item(472, 1), new Item(1269, 1), 14), - BLACK_HATCHET(new Item(500, 1), new Item(1361, 1), 10), - MITHRIL_HATCHET(new Item(502, 1), new Item(1355, 1), 18), - MITHRIL_PICKAXE(new Item(474, 1), new Item(1273, 1), 43), - ADAMANT_HATCHET(new Item(504, 1), new Item(1357, 1), 43), - ADAMANT_PICKAXE(new Item(476, 1), new Item(1271, 1), 107), - RUNE_HATCHET(new Item(506, 1), new Item(1359, 1), 427), - RUNE_PICKAXE(new Item(478, 1), new Item(1275, 1), 1100), - DRAGON_HATCHET(new Item(6741, 1), new Item(6739, 1), 1800); - - /** - * The item id. - */ - private final Item item; - - /** - * The product item. - */ - private final Item product; - - /** - * The cost of the money to repair. - */ - private final int cost; - - /** - * Constructs a new {@code BobRepairItem} {@code Object}. - * @param item the item. - * @param product the product. - * @param cost the cost. - */ - RepairItem(Item item, Item product, int cost) { - this.item = item; - this.product = product; - this.cost = cost; - } - - /** - * Gets the item. - * @return The item. - */ - public Item getItem() { - return item; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the cost. - * @return The cost. - */ - public int getCost() { - return cost; - } - - /** - * Gets the reapir item by the id. - * @param id the id. - * @return the repair item. - */ - public static RepairItem forId(int id) { - for (RepairItem item : RepairItem.values()) - if (item.item.getId() == id) - return item; - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/content/data/RepairItem.kt b/Server/src/main/content/data/RepairItem.kt new file mode 100644 index 000000000..4d89a7194 --- /dev/null +++ b/Server/src/main/content/data/RepairItem.kt @@ -0,0 +1,43 @@ +package content.data + +import core.game.node.item.Item + +/** + * Represents the repair item type. + * @author Vexia + * @author Damighty - Kotlin conversion + */ +enum class RepairItem( + val item: Item, + val product: Item, + val cost: Int +) { + BRONZE_HATCHET(Item(494, 1), Item(1351, 1), 0), + BRONZE_PICKAXE(Item(468, 1), Item(1265, 1), 0), + IRON_HATCHET(Item(496, 1), Item(1349, 1), 0), + IRON_PICKAXE(Item(470, 1), Item(1267, 1), 0), + STEEL_HATCHET(Item(498, 1), Item(1353, 1), 0), + STEEL_PICKAXE(Item(472, 1), Item(1269, 1), 14), + BLACK_HATCHET(Item(500, 1), Item(1361, 1), 10), + MITHRIL_HATCHET(Item(502, 1), Item(1355, 1), 18), + MITHRIL_PICKAXE(Item(474, 1), Item(1273, 1), 43), + ADAMANT_HATCHET(Item(504, 1), Item(1357, 1), 43), + ADAMANT_PICKAXE(Item(476, 1), Item(1271, 1), 107), + RUNE_HATCHET(Item(506, 1), Item(1359, 1), 427), + RUNE_PICKAXE(Item(478, 1), Item(1275, 1), 1100), + DRAGON_HATCHET(Item(6741, 1), Item(6739, 1), 1800); + + companion object { + /** + * List of all repairable item IDs. + */ + @JvmStatic + val repairableItemIds: List = values().map { it.item.id } + + /** + * Gets the repair item by the broken items ID. + */ + @JvmStatic + fun forId(id: Int): RepairItem? = values().firstOrNull { it.item.id == id } + } +} diff --git a/Server/src/main/content/data/consumables/Consumables.java b/Server/src/main/content/data/consumables/Consumables.java index 40c52e87e..67791684b 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,14 +110,16 @@ 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))), EGG_AND_TOMATO(new Food(new int[] {7064, 1923}, new HealingEffect(8))), SWEET_CORN(new Food(new int[] {5988}, new MultiEffect(new HealingEffect(1), new PercentageHealthEffect(10)))), SWEETCORN_BOWL(new Food(new int[] {7088, 1923}, new MultiEffect(new HealingEffect(1), new PercentageHealthEffect(10)))), + CHOPPED_TUNA(new Food(new int[] {7086, 1923}, new HealingEffect(10))), + CHOPPED_ONION(new Food(new int[] {1871, 1923}, new HealingEffect(1))), POTATO_WITH_BUTTER(new Food(new int[] {6703}, new HealingEffect(7))), CHILLI_POTATO(new Food(new int[] {7054}, new HealingEffect(14))), FRIED_ONIONS(new Food(new int[] {7084, 1923}, new HealingEffect(5))), @@ -126,8 +130,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 +149,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 +168,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))), @@ -199,92 +203,111 @@ public enum Consumables { /** Ales */ ASGOLDIAN_ALE(new FakeConsumable(7508, new String[] {"I don't think I'd like gold in beer thanks. Leave it for the dwarves."})), ASGARNIAN_ALE(new Drink(new int[] {1905, 1919}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")), + ASGARNIAN_ALE_POH(new Drink(new int[] {7744, 7742}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")), ASGARNIAN_ALE_KEG(new Drink(new int[] {5785, 5783, 5781, 5779, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")), - ASGARNIAN_ALE_M(new Drink(new int[] {5739, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))), - ASGARNIAN_ALE_M_KEG(new Drink(new int[] {5865, 5863, 5861, 5859, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))), + ASGARNIAN_ALE_M(new Drink(new int[] {5739, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")), + ASGARNIAN_ALE_M_KEG(new Drink(new int[] {5865, 5863, 5861, 5859, 5769}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289), "You drink the ale. You feel slightly reinvigorated...", "...and slightly dizzy too.")), AXEMANS_FOLLY(new Drink(new int[] {5751, 1919}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 1, 0), new HealingEffect(1), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.ATTACK, -3, 0)))), AXEMANS_FOLLY_KEG(new Drink(new int[] {5825, 5823, 5821, 5819, 5769}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 1, 0), new HealingEffect(1), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.ATTACK, -3, 0)), new Animation(2289))), AXEMANS_FOLLY_M(new Drink(new int[] {5753, 1919}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 2, 0), new HealingEffect(2), new SkillEffect(Skills.STRENGTH, -4, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), AXEMANS_FOLLY_M_KEG(new Drink(new int[] {5905, 5903, 5901, 5899, 5769}, new MultiEffect(new SkillEffect(Skills.WOODCUTTING, 2, 0), new HealingEffect(2), new SkillEffect(Skills.STRENGTH, -4, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289))), BANDITS_BREW(new Drink(new int[] {4627, 1919}, new MultiEffect(new SkillEffect(Skills.THIEVING, 1, 0), new SkillEffect(Skills.ATTACK, 1, 0), new SkillEffect(Skills.STRENGTH, -1, 0), new SkillEffect(Skills.DEFENCE, -6, 0), new HealingEffect(1)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")), BEER(new Drink(new int[] {1917, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 0, 0.04), new SkillEffect(Skills.ATTACK, 0, -0.07)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")), + BEER_POH(new Drink(new int[] {7740, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 0, 0.04), new SkillEffect(Skills.ATTACK, 0, -0.07)), "You drink the beer. You feel slightly reinvigorated...", "...and slightly dizzy too.")), BEER_TANKARD(new Drink(new int[] {3803, 3805}, new MultiEffect(new SkillEffect(Skills.ATTACK, -9, 0), new SkillEffect(Skills.STRENGTH, 4, 0)), "You quaff the beer. You feel slightly reinvigorated...", "...but very dizzy too.")), KEG_OF_BEER(new Drink(new int[] {3801}, new KegOfBeerEffect(), new Animation(1330), "You chug the keg. You feel reinvigorated...", "...but extremely drunk too.")), CHEFS_DELIGHT(new Drink(new int[] {5755, 1919}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))), + CHEFS_DELIGHT_POH(new Drink(new int[] {7754, 7742}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))), CHEFS_DELIGHT_KEG(new Drink(new int[] {5833, 5831, 5829, 5827, 5769}, new MultiEffect(new SkillEffect(Skills.COOKING, 1, 0.05), new HealingEffect(1), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))), CHEFS_DELIGHT_M(new Drink(new int[] {5757, 1919}, new MultiEffect(new SkillEffect(Skills.COOKING, 2, 0.05), new HealingEffect(2), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0)))), CHEFS_DELIGHT_M_KEG(new Drink(new int[] {5913, 5911, 5909, 5907, 5769}, new MultiEffect(new SkillEffect(Skills.COOKING, 2, 0.05), new HealingEffect(2), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0)), new Animation(2289))), CIDER(new Drink(new int[] {5763, 1919}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))))), + CIDER_POH(new Drink(new int[] {7752, 7742}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))))), CIDER_KEG(new Drink(new int[] {5849, 5847, 5845, 5843, 5769}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 1, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0))), new Animation(2289))), MATURE_CIDER(new Drink(new int[] {5765, 1919}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 2, 0), new SkillEffect(Skills.ATTACK, -5, 0), new SkillEffect(Skills.STRENGTH, -5, 0))))), CIDER_M_KEG(new Drink(new int[] {5929, 5927, 5925, 5923, 5769}, new MultiEffect(new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.FARMING, 2, 0), new SkillEffect(Skills.ATTACK, -5, 0), new SkillEffect(Skills.STRENGTH, -5, 0))), new Animation(2289))), DRAGON_BITTER(new Drink(new int[] {1911, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), + DRAGON_BITTER_POH(new Drink(new int[] {7748, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), DRAGON_BITTER_KEG(new Drink(new int[] {5809, 5807, 5805, 5803, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.STRENGTH, 2, 0), new SkillEffect(Skills.ATTACK, -4, 0)), new Animation(2289))), DRAGON_BITTER_M(new Drink(new int[] {5745, 1919}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))), DRAGON_BITTER_M_KEG(new Drink(new int[] {5889, 5887, 5885, 5883, 5769}, new MultiEffect(new HealingEffect(2), new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)), new Animation(2289))), DWARVEN_STOUT(new Drink(new int[] {1913, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 1, 0), new SkillEffect(Skills.SMITHING, 1 ,0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0), new SkillEffect(Skills.DEFENCE, -2, 0), new HealingEffect(1)), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")), DWARVEN_STOUT_KEG(new Drink(new int[] {5777, 5775, 5773, 5771, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 1, 0), new SkillEffect(Skills.SMITHING, 1 ,0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0), new SkillEffect(Skills.DEFENCE, -2, 0), new HealingEffect(1)), new Animation(2289), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")), - DWARVEN_STOUT_M(new Drink(new int[] {5747, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)))), - DWARVEN_STOUT_M_KEG(new Drink(new int[] {5857, 5855, 5853, 5851, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), new Animation(2289))), + DWARVEN_STOUT_M(new Drink(new int[] {5747, 1919}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")), + DWARVEN_STOUT_M_KEG(new Drink(new int[] {5857, 5855, 5853, 5851, 5769}, new MultiEffect(new SkillEffect(Skills.MINING, 2, 0), new SkillEffect(Skills.SMITHING, 2 ,0), new SkillEffect(Skills.ATTACK, -7, 0), new SkillEffect(Skills.STRENGTH, -7, 0), new SkillEffect(Skills.DEFENCE, -7, 0), new HealingEffect(1)), new Animation(2289), "You drink the Dwarven Stout. It tastes foul.", "It tastes pretty strong too.")), GREENMANS_ALE(new Drink(new int[] {1909, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))), + GREENMANS_ALE_POH(new Drink(new int[] {7746, 7742}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)))), GREENMANS_ALE_KEG(new Drink(new int[] {5793, 5791, 5789, 5787, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 1, 0), new SkillEffect(Skills.ATTACK, -3, 0), new SkillEffect(Skills.STRENGTH, -3, 0), new SkillEffect(Skills.DEFENCE, -3, 0)), new Animation(2289))), GREENMANS_ALE_M(new Drink(new int[] {5743, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))), GREENMANS_ALE_M_KEG(new Drink(new int[] {5873, 5871, 5869, 5867, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.HERBLORE, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))), GROG(new Drink(new int[] {1915, 1919}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0), new SkillEffect(Skills.ATTACK, -6, 0)))), - MOONLIGHT_MEAD(new Drink(new int[] {2955, 1919}, new HealingEffect(4), "It tastes like something just died in your mouth.")), - MOONLIGHT_MEAD_KEG(new Drink(new int[] {5817, 5815, 5813, 5811, 5769}, new HealingEffect(4), new Animation(2289), "It tastes like something just died in your mouth.")), - MOONLIGHT_MEAD_M(new Drink(new int[] {5749, 1919}, new HealingEffect(6))), - MOONLIGHT_MEAD_M_KEG(new Drink(new int[] {5897, 5895, 5893, 5891, 5769}, new HealingEffect(6), new Animation(2289))), + MOONLIGHT_MEAD(new Drink(new int[] {2955, 1919}, new HealingEffect(5), "It tastes like something just died in your mouth.")), + MOONLIGHT_MEAD_KEG(new Drink(new int[] {5817, 5815, 5813, 5811, 5769}, new HealingEffect(5), new Animation(2289), "It tastes like something just died in your mouth.")), + MOONLIGHT_MEAD_M(new Drink(new int[] {5749, 1919}, new HealingEffect(4), "It tastes like something just died in your mouth.")), + MOONLIGHT_MEAD_M_KEG(new Drink(new int[] {5897, 5895, 5893, 5891, 5769}, new HealingEffect(4), new Animation(2289), "It tastes like something just died in your mouth.")), SLAYERS_RESPITE(new Drink(new int[] {5759, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))), SLAYERS_RESPITE_KEG(new Drink(new int[] {5841, 5839, 5837, 5835, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 2, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))), SLAYERS_RESPITE_M(new Drink(new int[] {5761, 1919}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 4, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)))), SLAYERS_RESPITE_M_KEG(new Drink(new int[] {5841, 5839, 5837, 5835, 5769}, new MultiEffect(new HealingEffect(1), new SkillEffect(Skills.SLAYER, 4, 0), new SkillEffect(Skills.ATTACK, -2, 0), new SkillEffect(Skills.STRENGTH, -2, 0)), new Animation(2289))), WIZARDS_MIND_BOMB(new Drink(new int[] {1907, 1919}, new WizardsMindBombEffect(), "You drink the Wizard's Mind Bomb.", "You feel very strange.")), - MATURE_WMB(new Drink(new int[] {5741, 1919}, new MatureWmbEffect())), + WIZARDS_MIND_BOMB_KEG(new Drink(new int[] {5801, 5799, 5797, 5795, 5769}, new WizardsMindBombEffect(), new Animation(2289), "You drink the Wizard's Mind Bomb.", "You feel very strange.")), + MATURE_WMB(new Drink(new int[] {5741, 1919}, new MatureWmbEffect(), "You drink the mature Wizard's Mind Bomb.", "You feel very strange.")), + MATURE_WMB_KEG(new Drink(new int[] {5881, 5879, 5877, 5875, 5769}, new MatureWmbEffect(), new Animation(2289), "You drink the mature Wizard's Mind Bomb.", "You feel very strange.")), /** Cocktails */ FRUIT_BLAST(new Drink(new int[] {2084, 2026}, new HealingEffect(9))), - 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_MILKY(new Drink(new int[] {4240, 1980}, new NettleTeaEffect())), + NETTLE_WATER(new Drink(new int[] {Items.NETTLE_WATER_4237, Items.BOWL_1923}, new HealingEffect((1)))), + CUP_OF_TEA_NETTLE(new Drink(new int[] {4242, 1980}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), + CUP_OF_TEA_MILKY_NETTLE(new Drink(new int[] {4243, 1980}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), + CUP_OF_TEA_NETTLE_PORCELAIN(new Drink(new int[] {4245, 4244}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), + CUP_OF_TEA_MILKY_NETTLE_PORCELAIN(new Drink(new int[] {4246, 4244}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), + NETTLE_TEA(new Drink(new int[] {4239, 1923}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), + NETTLE_TEA_MILKY(new Drink(new int[] {4240, 1923}, new MultiEffect(new HealingEffect(3), new EnergyEffect(10)))), CUP_OF_TEA_CLAY(new Drink(new int[] {7730, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0), "You feel refreshed and ready for more building.")), - CUP_OF_TEA_CLAY_MILKY(new Drink(new int[] {7731, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0))), + CUP_OF_TEA_CLAY_MILKY(new Drink(new int[] {7731, 7728}, new SkillEffect(Skills.CONSTRUCTION, 1, 0), "You feel refreshed and ready for more building.")), CUP_OF_TEA_WHITE(new Drink(new int[] {7733, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0), "You feel refreshed and ready for more building.")), - CUP_OF_TEA_WHITE_MILKY(new Drink(new int[] {7734, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0))), + CUP_OF_TEA_WHITE_MILKY(new Drink(new int[] {7734, 7732}, new SkillEffect(Skills.CONSTRUCTION, 2, 0), "You feel refreshed and ready for more building.")), CUP_OF_TEA_GOLD(new Drink(new int[] {7736, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0), "You feel refreshed and ready for more building.")), - CUP_OF_TEA_GOLD_MILKY(new Drink(new int[] {7737, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0))), + CUP_OF_TEA_GOLD_MILKY(new Drink(new int[] {7737, 7735}, new SkillEffect(Skills.CONSTRUCTION, 3, 0), "You feel refreshed and ready for more building.")), /** Miscellaneous */ CHOCOLATE_BAR(new Food(new int[] {1973}, new HealingEffect(3))), @@ -296,9 +319,11 @@ public enum Consumables { TCHIKI_MONKEY_PASTE(new Food(new int[] {7575}, new HealingEffect(5), "You eat the Tchiki monkey nut paste. It sticks to the roof of your mouth.")), 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))), + CAVIAR(new Food(new int[]{11326}, new HealingEffect(5))), 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 +333,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)))), 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)))), - 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")))), + 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, 2), new SkillEffect(Skills.ATTACK, 2, 0.20), new SkillEffect(Skills.STRENGTH, 2, 0.12), new SkillEffect(Skills.DEFENCE, -2, -0.10), new PrayerEffect(0.0, 0.10, true)))), + 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[] {9021, 9022, 9023, 9024}, new SkillEffect(Skills.MAGIC,3,0))), + SANFEW(new Potion(new int[] {10925, 10927, 10929, 10931}, new MultiEffect(new RestoreEffect(8,0.25, true), new AddTimerEffect("poison:immunity", secondsToTicks(90)), new RemoveTimerEffect("disease")))), SUPER_ENERGY(new Potion(new int[] {3016, 3018, 3020, 3022}, new EnergyEffect(20))), BLAMISH_OIL(new FakeConsumable(1582, new String[] {"You know... I'd really rather not."})), /** Barbarian Mixes */ PRAYERMIX(new BarbarianMix(new int[] {11465, 11467}, new MultiEffect(new PrayerEffect(7, 0.25), new HealingEffect(6)))), - ZAMMY_MIX(new BarbarianMix(new int[] {11521, 11523}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1), new RandomPrayerEffect(0, 10)))), + ZAMMY_MIX(new BarbarianMix(new int[]{11521, 11523}, new MultiEffect(new ZamorakMixEffect(), new SkillEffect(Skills.ATTACK, 2, 0.20), new SkillEffect(Skills.STRENGTH, 2, 0.12), new SkillEffect(Skills.DEFENCE, -2, -0.10)))), 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 +379,13 @@ public enum Consumables { SUPER_ENERGY_MIX(new BarbarianMix(new int[] {11481, 11483}, new MultiEffect(new EnergyEffect(20), new HealingEffect(6)))), HUNTING_MIX(new BarbarianMix(new int[] {11517, 11519}, new MultiEffect(new SkillEffect(Skills.HUNTER, 3, 0), new HealingEffect(6)))), SUPER_STR_MIX(new BarbarianMix(new int[] {11485, 11487}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 5, 0.15), new HealingEffect(6)))), + ANTIDOTE_PLUS_MIX(new BarbarianMix(new int[] {11501, 11503}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(9)), new HealingEffect(6)))), + ANTIP_SUPERMIX(new BarbarianMix(new int[] {11473, 11475}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(6)), new HealingEffect(6)))), + ANTIFIRE_MIX(new BarbarianMix(new int[] {11505, 11507}, new MultiEffect(new AddTimerEffect("dragonfire:immunity", 600, true), new HealingEffect(6)))), + MAGIC_ESS_MIX(new BarbarianMix(new int[] {11489, 11491}, new MultiEffect(new SkillEffect(Skills.MAGIC, 3, 0), new HealingEffect(6)))), + SUPER_DEF_MIX(new BarbarianMix(new int[] {11497, 11499}, new MultiEffect(new SkillEffect(Skills.DEFENCE, 5, 0.15), new HealingEffect(6)))), + RANGING_MIX(new BarbarianMix(new int[] {11509, 11511}, new MultiEffect(new SkillEffect(Skills.RANGE, 4, 0.1), new HealingEffect(6)))), + MAGIC_MIX(new BarbarianMix(new int[] {11513, 11515}, new MultiEffect(new SkillEffect(Skills.MAGIC, 4, 0), new HealingEffect(6)))), /** Stealing creation potions */ SC_PRAYER(new Potion(new int[] {14207, 14209, 14211, 14213, 14215}, new PrayerEffect(7, 0.25))), @@ -364,24 +397,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 +428,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/DamageEffect.java b/Server/src/main/content/data/consumables/effects/DamageEffect.java index c4573306b..1b5e4778c 100644 --- a/Server/src/main/content/data/consumables/effects/DamageEffect.java +++ b/Server/src/main/content/data/consumables/effects/DamageEffect.java @@ -7,15 +7,21 @@ import core.game.node.entity.player.Player; public class DamageEffect extends ConsumableEffect { final double amt; final boolean isPercent; + final int baseAmount; - public DamageEffect(double amt,boolean isPercent){ + public DamageEffect(double amt, boolean isPercent) { + this(amt, isPercent, 0); + } + + public DamageEffect(double amt, boolean isPercent, int baseAmount) { this.amt = amt; this.isPercent = isPercent; + this.baseAmount = baseAmount; } @Override public void activate(Player p) { - p.getImpactHandler().manualHit(p,-getHealthEffectValue(p), ImpactHandler.HitsplatType.NORMAL); + p.getImpactHandler().manualHit(p, -getHealthEffectValue(p), ImpactHandler.HitsplatType.NORMAL); } @Override @@ -23,8 +29,8 @@ public class DamageEffect extends ConsumableEffect { double amount = amt; if (isPercent) { amount /= 100; - return (int) -(amount * player.getSkills().getLifepoints()); + return (int) -(amount * player.getSkills().getLifepoints() + baseAmount); } - return (int) -amt; + return (int) -(amt + baseAmount); } } diff --git a/Server/src/main/content/data/consumables/effects/NettleTeaEffect.java b/Server/src/main/content/data/consumables/effects/NettleTeaEffect.java deleted file mode 100644 index 8f20e8c33..000000000 --- a/Server/src/main/content/data/consumables/effects/NettleTeaEffect.java +++ /dev/null @@ -1,20 +0,0 @@ -package content.data.consumables.effects; - -import core.game.consumable.ConsumableEffect; -import core.game.node.entity.player.Player; - -public class NettleTeaEffect extends ConsumableEffect { - - private final static int healing = 3; - - @Override - public void activate(Player p) { - final ConsumableEffect effect = p.getSkills().getLifepoints() < p.getSkills().getMaximumLifepoints() ? new MultiEffect(new HealingEffect(3), new EnergyEffect(5)) : new HealingEffect(3); - effect.activate(p); - } - - @Override - public int getHealthEffectValue(Player player) { - return healing; - } -} 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..5edaff7dc --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/PrayerEffect.kt @@ -0,0 +1,22 @@ +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.ceil +import kotlin.math.floor + +class PrayerEffect @JvmOverloads constructor(var base: Double, var bonus: Double, private val roundUp: Boolean = false) : 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 rawAmount = base + (level * b) + val amount = if (roundUp) ceil(rawAmount) else floor(rawAmount) + 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/consumables/effects/ZamorakMixEffect.java b/Server/src/main/content/data/consumables/effects/ZamorakMixEffect.java new file mode 100644 index 000000000..688da0961 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/ZamorakMixEffect.java @@ -0,0 +1,25 @@ +package content.data.consumables.effects; + +import core.game.consumable.ConsumableEffect; +import core.game.node.entity.combat.ImpactHandler; +import core.game.node.entity.player.Player; + +/** + * The Zamorak mix shows the regular Zamorak brew hitsplat (10% of pre-drink lifepoints + 2, rounded down) + * but also heals 6 lifepoints. The heal must be applied before the hit so the net change can never kill + * the player, while the hitsplat still reflects the pre-heal lifepoints. + */ +public class ZamorakMixEffect extends ConsumableEffect { + + @Override + public void activate(Player p) { + int damage = (int) (p.getSkills().getLifepoints() * 0.10 + 2); + p.getSkills().heal(6); + p.getImpactHandler().manualHit(p, damage, ImpactHandler.HitsplatType.NORMAL); + } + + @Override + public int getHealthEffectValue(Player player) { + return 6 - (int) (player.getSkills().getLifepoints() * 0.10 + 2); + } +} diff --git a/Server/src/main/content/data/skill/SkillingPets.java b/Server/src/main/content/data/skill/SkillingPets.java deleted file mode 100644 index 4ea03edd9..000000000 --- a/Server/src/main/content/data/skill/SkillingPets.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.data.skill; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.repository.Repository; - -/** - * Represents the skilling pets obtained randomly. - * @author Empathy - * - */ -public enum SkillingPets { - - BABY_RED_CHINCHOMPA(new Item(14823), "Baby Chinchompa", Skills.HUNTER), - BABY_GREY_CHINCHOMPA(new Item(14824), "Baby Chinchompa", Skills.HUNTER), - BEAVER(new Item(14821), "Beaver", Skills.WOODCUTTING), - GOLEM(new Item(14822), "Rock Golem", Skills.MINING), - HERON(new Item(14827), "Heron", Skills.FISHING); - - /** - * The pet item drop. - */ - private final Item pet; - - /** - * The name. - */ - private final String name; - - /** - * The skill. - */ - private final int skill; - - /** - * Constructs a new {@code SkillingPets} object. - * @param skill The skill id. - * @param pet The pet item. - */ - SkillingPets(Item pet, String name, int skill) { - this.pet = pet; - this.name = name; - this.skill = skill; - } - - /** - * Checks the pet drop. - * @param player The player. - * @param pet The pet drop to check. - */ - public static void checkPetDrop(Player player, SkillingPets pet) { - if (pet == null) { - return; - } - int defaultChance = 15000; - int newChance = (defaultChance / player.getSkills().getStaticLevel(pet.getSkill()) * 55); - int outOf = (newChance > defaultChance ? defaultChance : newChance); - int getChance = outOf; - if (getChance != 1) { - return; - } - if (player.hasItem(pet.getPet())) { - return; - } - if (player.getFamiliarManager().hasFamiliar() && player.getInventory().isFull()) { - return; - } - if (player.getFamiliarManager().hasFamiliar()) { - if (player.getFamiliarManager().getFamiliar().getName().equalsIgnoreCase(pet.getName())) { - return; - } - player.getInventory().add(pet.getPet()); - player.sendNotificationMessage("You feel something weird sneaking into your backpack."); - } else { - player.getFamiliarManager().summon(pet.getPet(), true); - player.sendNotificationMessage("You have a funny feeling like you're being followed."); - } - Repository.sendNews(player.getUsername() + " has found a " + pet.getPet().getName() + "!"); - } - - - /** - * @return the pet - */ - public Item getPet() { - return pet; - } - - /** - * @return the pet name. - */ - public String getName() { - return name; - } - - /** - * @return the skill. - */ - public int getSkill() { - return skill; - } -} diff --git a/Server/src/main/content/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..1b9147172 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt @@ -0,0 +1,230 @@ +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 Champions' Guild.", + "", + "Champion of Imps" + ) + + private val GOBLIN_SCROLL_TEXT = arrayOf( + "Fight me if you think you can human, I'll wait", + "for you in the Champions' 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 Champions' Guild and fight me if you", + "dare.", + "", + "Champion of Earth Warriors" + ) + + private val JOGRE_SCROLL_TEXT = arrayOf( + "You think you can defeat me? Come to the", + "Champions' Guild and prove it!", + "", + "Champion of Jogres" + ) + + private val LESSER_DEMON_SCROLL_TEXT = arrayOf( + "Come to the Champions' 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..bfbe78a76 --- /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_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..3157b6b50 --- /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 any Prayers. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6799) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to take Weapons, no other items are allowed. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6800) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Melee attacks, no Ranged or Magic. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6801) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Magic attacks, no Melee or Ranged. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6802) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Melee attacks. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6803) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Special Attacks. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6804) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Ranged attacks. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6805) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed any Weapons or Armour. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6806) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're only allowed to use Ranged attacks, no Melee or Magic. Do you still want to proceed?").also { stage = 1 } + inInventory(player!!,Items.CHAMPION_SCROLL_6807) -> npcl("So you want to accept the challenge huh? Well there are some specific rules for these Champion fights. For this fight you're not allowed to use any Magic attacks. Do you still want to proceed?").also { stage = 1 } + else -> { + sendMessage(player!!, "Nothing interesting happens.").also { stage = END_DIALOGUE } } + } + } + 1 -> options("Yes, let me at him!", "No thanks, I'll pass.").also { stage = 2 } + 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. If you find a challenge scroll in your travels, bring it here to face your challenger.").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..1c131efc9 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt @@ -0,0 +1,92 @@ +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.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) { + + //somehow the maximumHit is determined to be zero by this point if you're using a melee special attack. + if (state.maximumHit == 0) { + state.neutralizeHits() + sendMessage(player, "Larxus said you couldn't use special attacks in this duel.") + } + } + } + + 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..e41fd5d72 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/LesserDemonChampionNPC.kt @@ -0,0 +1,99 @@ +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.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.isEmpty) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } else { + sendMessage(player, "You cannot wear any equipment 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..bab84b5b5 --- /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.neutralizeHits() + state.estimatedHit = state.maximumHit + } + 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..48cdc7dec 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{ @@ -28,10 +30,10 @@ class PenguinManager{ return tagMapping[Penguin.forLocation(location)?.ordinal]?.contains(player.username.toLowerCase()) ?: false } - private fun updateStoreFile(){ + 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/PenguinSpawner.kt b/Server/src/main/content/global/activity/penguinhns/PenguinSpawner.kt index d19b9a82a..2d6abe7bb 100644 --- a/Server/src/main/content/global/activity/penguinhns/PenguinSpawner.kt +++ b/Server/src/main/content/global/activity/penguinhns/PenguinSpawner.kt @@ -44,18 +44,65 @@ enum class Penguin(val id: Int, val hint: String, val location: Location){ BUSH_8(NPCs.BUSH_8105,"located in the kingdom of Asgarnia.",Location.create(2951, 3511, 0)), BUSH_9(NPCs.BUSH_8105,"located in the northern desert.",Location.create(3350, 3311, 0)), BUSH_10(NPCs.BUSH_8105,"located somewhere in the kingdom of Kandarin.",Location.create(2633, 3501, 0)), - BUSH_11(NPCs.BUSH_8105,"located south of Ardougne.",Location.create(2440, 3206, 0)), - BUSH_12(NPCs.BUSH_8105,"located where wizards study.",Location.create(3112, 3149, 0)), + BUSH_11(NPCs.BUSH_8105,"located where wizards study.",Location.create(3112, 3149, 0)), ROCK_1(NPCs.ROCK_8109,"located where the Imperial Guard train.",Location.create(2852, 3578, 0)), ROCK_2(NPCs.ROCK_8109,"located in the kingdom of Misthalin.",Location.create(3356, 3416, 0)), - ROCK_3(NPCs.ROCK_8109,"located near some ogres.",Location.create(2631, 2980, 0)), + ROCK_3(NPCs.ROCK_8109,"located near some ogres.",Location.create(2631, 2980, 0)), //potentially incorrect location ROCK_4(NPCs.ROCK_8109,"located in the Kingdom of Asgarnia.",Location.create(3013, 3501, 0)), ROCK_5(NPCs.ROCK_8109,"located between Fremennik and barbarians.",Location.create(2532, 3630, 0)), CRATE_1(NPCs.CRATE_8108,"located in the kingdom of Misthalin.",Location.create(3112, 3332, 0)), CRATE_2(NPCs.CRATE_8108,"located in the Kingdom of Misthalin.",Location.create(3305, 3508, 0)), - BARREL_1(NPCs.CRATE_8108,"located where no weapons may go.",Location.create(2806, 3383, 0)), + CRATE_3(NPCs.CRATE_8108,"located south of Ardougne.",Location.create(2440, 3206, 0)), + BARREL_1(NPCs.BARREL_8104,"located where no weapons may go.",Location.create(2806, 3383, 0)), TOADSTOOL_1(NPCs.TOADSTOOL_8110,"located in the kingdom of Misthalin.",Location.create(3156, 3178, 0)), - TOADSTOOL_2(NPCs.TOADSTOOL_8110,"located in the fairy realm.",Location.create(2409, 4462, 0)); + TOADSTOOL_2(NPCs.TOADSTOOL_8110,"located in the fairy realm.",Location.create(2409, 4462, 0)), //potentially a spawn from 2010 + + BUSH_12(NPCs.BUSH_8105,"located on an island.",Location.create(2534, 3871, 0)), + BARREL_2(NPCs.BARREL_8104,"located near the ghost town.",Location.create(3654, 3491, 0)), + BUSH_13(NPCs.BUSH_8105,"located where bloodsuckers rule.",Location.create(3600, 3487, 0)), + BUSH_14(NPCs.BUSH_8105,"located on islands where brothers quarrel.",Location.create(2355, 3848, 0)), + ROCK_6(NPCs.ROCK_8109,"located on a large crescent island.",Location.create(2118, 3943, 0)), + ROCK_7(NPCs.ROCK_8109,"located on islands where brothers quarrel.",Location.create(2357, 3797, 0)), + ROCK_8(NPCs.ROCK_8109,"located in the Wilderness.",Location.create(3169, 3650, 0)), + BARREL_3(NPCs.BARREL_8104,"located where pirates feel mostly harmless.",Location.create(3738, 3001, 0)), + CACTUS_3(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3276, 2797, 0)), + TOADSTOOL_3(NPCs.TOADSTOOL_8110,"located near the pointy-eared ones.",Location.create(2314, 3174, 0)), + BUSH_15(NPCs.BUSH_8105,"located deep in the jungle.",Location.create(2938, 2978, 0)), + TOADSTOOL_4(NPCs.TOADSTOOL_8110,"located near the pointy-eared ones.",Location.create(2219, 3227, 0)), + BARREL_4(NPCs.BARREL_8104,"located south of Ardougne.",Location.create(2662, 3152, 0)), + //BARREL_5(NPCs.BARREL_8104,"located where monkeys rule.",Location.create(2751, 2700, 0)), + //currently not well accessible, will need to add when Ape Atoll is sorted. + //BUSH_16(NPCs.BUSH_8105,"located on islands where brothers quarrel.",Location.create(2353, 3834, 0)), + //currently not well accessible, will need to add when Neitiznot bridges are fixed. + ROCK_9(NPCs.ROCK_8109,"located near a mountain of wolves.",Location.create(2852, 3504, 0)), + ROCK_10(NPCs.ROCK_8109,"located on islands where brothers quarrel.",Location.create(2413, 3846, 0)), + + BUSH_17(NPCs.BUSH_8105,"located near some ogres.",Location.create(2578, 2909, 0)), + CRATE_4(NPCs.CRATE_8108,"located where banana smugglers dwell.",Location.create(2869, 3157, 0)), + //CRATE_5(NPCs.CRATE_8108,"located near the island of Dragontooth.",Location.create(3824, 3562, 0)), + //current not accessible, will need to add when Dragontooth island is sorted. + ROCK_11(NPCs.ROCK_8109,"located where bloodsuckers rule.",Location.create(3550, 3439, 0)), + TOADSTOOL_5(NPCs.TOADSTOOL_8110,"located near the pointy-eared ones.",Location.create(2181, 3172, 0)), + BUSH_18(NPCs.BUSH_8105,"located where bloodsuckers rule.",Location.create(3472, 3392, 0)), + BUSH_19(NPCs.BUSH_8105,"located near Port Sarim.",Location.create(2989, 3121, 0)), + CRATE_6(NPCs.CRATE_8108,"located where fishers colonise.",Location.create(2322, 3658, 0)), + ROCK_12(NPCs.ROCK_8109,"located between Fremennik and barbarians.",Location.create(2675, 3717, 0)), + ROCK_13(NPCs.ROCK_8109,"located near the pointy-eared ones.",Location.create(2296, 3270, 0)), + CRATE_7(NPCs.CRATE_8108,"located where bloodsuckers rule.",Location.create(3637, 3486, 0)), + TOADSTOOL_6(NPCs.TOADSTOOL_8110,"located where bloodsuckers rule.",Location.create(3416, 3437, 0)), + BUSH_20(NPCs.BUSH_8105,"located where monkeys rule.",Location.create(2802, 2806, 0)), + + ROCK_14(NPCs.ROCK_8109,"located near some ogres.",Location.create(2438, 3050, 0)), + CACTUS_4(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3252, 2963, 0)), + ROCK_15(NPCs.ROCK_8109,"located near some ogres.",Location.create(2340, 3064, 0)), + ROCK_16(NPCs.ROCK_8109,"located in the Wilderness.",Location.create(3108, 3837, 0)), + CACTUS_5(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3433, 3000, 0)), + CACTUS_6(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3274, 2813, 0)), + ROCK_17(NPCs.ROCK_8109,"located in the Wilderness.",Location.create(3019, 3866, 0)), + ROCK_18(NPCs.ROCK_8109,"located in the Wilderness.",Location.create(2991, 3824, 0)), + BUSH_21(NPCs.BUSH_8105,"located north of Ardougne.",Location.create(2398, 3361, 0)), + ROCK_19(NPCs.ROCK_8109,"located near the coast.",Location.create(2733, 3283, 0)), + ROCK_20(NPCs.ROCK_8109,"located in the Wilderness.",Location.create(3236, 3927, 0)); companion object { private val locationMap = values().map { it.location.toString() to it }.toMap() 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..074ab27a6 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..5c5ecf033 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.skill.SkillPulse import core.game.node.entity.skill.Skills import content.data.skill.SkillingTool +import core.ServerConstants import core.game.node.item.Item import core.tools.RandomFunction import org.rs09.consts.Items @@ -13,6 +14,9 @@ import core.game.world.GameWorld import core.game.world.repository.Repository import core.tools.colorize +// TODO: Shooting stars should roll for bonus gems while mining +// See: https://youtu.be/6OqZ2TGc6fM?si=U8nB5IDQREhWXApD + /** * The pulse used to handle mining shooting stars. */ @@ -46,14 +50,14 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin //checks if the star has been discovered and if not, awards the bonus xp. Xp can be awarded regardless of mining level as per the wiki. if (!star.isDiscovered && !player.isArtificial) { val bonusXp = 75 * player.skills.getStaticLevel(Skills.MINING) - player.incrementAttribute("/save:shooting-star:bonus-xp", bonusXp) + rewardXP(player, Skills.MINING, bonusXp.toDouble()) Repository.sendNews(player.username + " is the discoverer of the crashed star near " + star.location + "!") - player.sendMessage("You have ${player.skills.experienceMutiplier * player.getAttribute("shooting-star:bonus-xp", 0).toDouble()} bonus xp towards mining stardust.") ShootingStarPlugin.submitScoreBoard(player) star.isDiscovered = true + ShootingStarPlugin.getStoreFile()["isDiscovered"] = star.isDiscovered return player.skills.getLevel(Skills.MINING) >= star.miningLevel } - + if (player.skills.getLevel(Skills.MINING) < star.miningLevel) { player.dialogueInterpreter.sendDialogue("You need a Mining level of at least " + star.miningLevel + " in order to mine this layer.") return false @@ -83,14 +87,14 @@ 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) { + // legacy: shooting star bonus xp used to be handed out in an inauthentic way; see GL !2092 val delta = Math.min(bonusXp, xp) player.incrementAttribute("/save:shooting-star:bonus-xp", (-delta).toInt()) - xp += delta; + xp += delta if(player.getAttribute("shooting-star:bonus-xp", 0) <= 0) { player.sendMessage("You have obtained all of your bonus xp from the star.") } @@ -100,9 +104,11 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin if (ShootingStarPlugin.getStarDust(player) < 200) { player.inventory.add(Item(ShootingStarPlugin.STAR_DUST, 1)) } - if(!inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !inBank(player, Items.ANCIENT_BLUEPRINT_14651)){ + if (ServerConstants.SHOOTING_STAR_RING && hasAnItem(player, Items.ANCIENT_BLUEPRINT_14651).container == null) { rollBlueprint(player) } + + star.decDust() return false } @@ -128,7 +134,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin override fun message(type: Int) { when (type) { - 0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock...") + 0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock.") } } diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt index fb8e1df9f..7368b7689 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,77 +82,77 @@ 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)} + "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 -> 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 } } override fun defineCommands() { - define("tostar", Privilege.ADMIN) { player, _ -> + define("tostar", Privilege.ADMIN, description = "Teleports you adjacent to the currently active star.") { player, _ -> teleport(player, star.starObject.location.transform(1,1,0)) } - define("submit", Privilege.ADMIN) { _, _ -> + define("submit", Privilege.ADMIN, description = "Rebuilds and fires the shooting star event immediately.") { _, _ -> + star.rebuildVars() star.fire() } - define("resetsprite", Privilege.ADMIN) { player, _ -> + define("resetsprite", Privilege.ADMIN, description = "Resets your star sprite reward cooldown.") { player, _ -> player.savedData.globalData.starSpriteDelay = 0L } } 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..13efad3c6 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())); 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..39406108f --- /dev/null +++ b/Server/src/main/content/global/ame/KidnapHelper.kt @@ -0,0 +1,59 @@ +package content.global.ame + +import core.ServerConstants +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager.TeleportType +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Sounds + +fun kidnapPlayer(npc: NPC, player: Player, dest: Location, playerLine: String? = null, callback: (player: Player, npc: NPC) -> Unit) { + clearScripts(player) + val lockDuration = if (playerLine != null) 4 else 6 + lock(player, lockDuration) + queueScript(player, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + if (playerLine != null) { + sendChat(player, playerLine) + return@queueScript delayScript(player, 2) + } + return@queueScript delayScript(player, 0) + } + 1 -> { + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 2 -> { + setAttribute(player, "kidnapped-by-random", true) + if (getAttribute(player, "/save:original-loc", null) == null) { + setAttribute(player, "/save:original-loc", player.location) + } + teleport(player, dest, TeleportType.INSTANT) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player, 8941) + resetAnimator(player) + callback(player, npc) + return@queueScript delayScript(player, 2) + } + 3 -> { + removeAttribute(player, "kidnapped-by-random") //this is not needed at this point anymore and will reenable the original-loc sanity check tick action + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } +} + +fun returnPlayer(player: Player) { + player.locks.unlockTeleport() + val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION) + teleport(player, destination!!) + unlock(player) + removeAttributes(player, "/save:original-loc", "kidnapped-by-random") +} diff --git a/Server/src/main/content/global/ame/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..a4f3072f7 100644 --- a/Server/src/main/content/global/ame/RandomEventNPC.kt +++ b/Server/src/main/content/global/ame/RandomEventNPC.kt @@ -1,10 +1,20 @@ package content.global.ame -import content.global.ame.events.MysteriousOldManNPC +import content.global.ame.events.surpriseexam.MysteriousOldManNPC +import core.api.getAttribute +import core.api.playGlobalAudio import core.api.poofClear +import core.api.removeAttribute +import core.api.sendMessage +import core.api.setAttribute +import core.api.utils.WeightBasedTable import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle import core.game.node.entity.impl.PulseType import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.agg.AggressiveBehavior +import core.game.node.entity.npc.agg.AggressiveHandler import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.world.map.Location @@ -12,8 +22,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.RandomFunction import core.tools.secondsToTicks +import core.tools.ticksToCycles +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds import kotlin.random.Random import kotlin.reflect.full.createInstance @@ -24,8 +37,8 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { val SMOKE_GRAPHICS = Graphics(86) var initialized = false var finalized = false - var timerPaused = false var ticksLeft = secondsToTicks(180) + private val combatBracket = intArrayOf(10, 20, 40, 60, 90) open fun create(player: Player, loot: WeightBasedTable? = null, type: String = ""): RandomEventNPC { val event = this::class.createInstance() @@ -33,16 +46,18 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { event.loot = loot event.player = player event.spawnLocation = RegionManager.getSpawnLocation(player, this) + setAttribute(event, "spawned-by-ame", true) return event } 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)) } + removeAttribute(player, "random:pause") + finalized = true } open fun follow() { @@ -63,7 +78,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { if (!player.getAttribute("random:pause", false)) { ticksLeft-- } - if (!pulseManager.hasPulseRunning() && !finalized) { follow() } @@ -79,19 +93,23 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { override fun init() { initialized = true finalized = false - timerPaused = false spawnLocation ?: terminate() location = spawnLocation player.setAttribute("re-npc", this) super.init() + super.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun canSelectTarget(entity: Entity, target: Entity): Boolean { + return target == player + } + }) } open fun onTimeUp() { - noteAndTeleport() terminate() } fun noteAndTeleport() { + player.pulseManager.clear() for (item in player.inventory.toArray()) { if (item == null) continue if (item.definition.isUnnoted) { @@ -114,4 +132,39 @@ 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 = combatBracket.indexOfFirst { player.properties.currentCombatLevel <= it }.takeIf { it != -1 }?: ids.lastIndex + return ids[index] + } + + fun sayLine(npc: NPC, phrases: Array, hasOpeningPhrase: Boolean, hasOverTimePhrase: Boolean) { + val inInteraction = getAttribute(player, "random:pause", false) //used by Certer + if ((ticksLeft % 20 == 0 || ticksLeft <= 2) && !inInteraction) { //speak every 20 ticks, or in the 2nd-to-last tick before attack/note-&-teleport + var playDwarfWhistle = true + if (ticksLeft == secondsToTicks(180) && hasOpeningPhrase) { + sendChat(phrases[0]) + } else if (ticksLeft <= 2 && hasOverTimePhrase) { + sendChat(phrases[phrases.size - 1]) + playDwarfWhistle = false + } else { + val start = if (hasOpeningPhrase) 0 else 1 + val end = if (hasOverTimePhrase) phrases.size - 2 else phrases.size - 1 + sendChat(phrases[RandomFunction.random(start, end + 1)]) + } + if (npc.id == NPCs.DRUNKEN_DWARF_956 && playDwarfWhistle) { + playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) + } + } + } +} diff --git a/Server/src/main/content/global/ame/RandomEvents.kt b/Server/src/main/content/global/ame/RandomEvents.kt index 7c586596d..db5ad9b2e 100644 --- a/Server/src/main/content/global/ame/RandomEvents.kt +++ b/Server/src/main/content/global/ame/RandomEvents.kt @@ -1,25 +1,38 @@ package content.global.ame import org.rs09.consts.Items -import content.global.ame.events.MysteriousOldManNPC +import content.global.ame.events.surpriseexam.MysteriousOldManNPC import content.global.ame.events.certer.CerterNPC -import content.global.ame.events.drilldemon.SeargentDamienNPC +import content.global.ame.events.drilldemon.SergeantDamienNPC +import content.global.ame.events.drunkendwarf.DrunkenDwarfNPC +import content.global.ame.events.evilbob.EvilBobNPC import content.global.ame.events.evilchicken.EvilChickenNPC +import content.global.ame.events.freakyforester.FreakyForesterNPC +import content.global.ame.events.maze.MazeNPC import content.global.ame.events.genie.GenieNPC +import content.global.ame.events.candlelight.PiousPeteNPC +import content.global.ame.events.pillory.PilloryNPC +import content.global.ame.events.rickturpentine.RickTurpentineNPC import content.global.ame.events.rivertroll.RiverTrollRENPC import content.global.ame.events.rockgolem.RockGolemRENPC +import content.global.ame.events.quizmaster.QuizMasterNPC import content.global.ame.events.sandwichlady.SandwichLadyRENPC import content.global.ame.events.shade.ShadeRENPC +import content.global.ame.events.strangeplant.StrangePlantNPC +import content.global.ame.events.swarm.SwarmNPC import content.global.ame.events.treespirit.TreeSpiritRENPC import content.global.ame.events.zombie.ZombieRENPC +import core.ServerConstants import core.api.utils.WeightBasedTable import core.api.utils.WeightedItem +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()), + CANDLELIGHT(npc = PiousPeteNPC(), skillIds = intArrayOf(Skills.PRAYER)), + CERTER(npc = CerterNPC(), loot = WeightBasedTable.create( WeightedItem(Items.UNCUT_SAPPHIRE_1623,1,1,3.4), WeightedItem(Items.KEBAB_1971,1,1,1.7), WeightedItem(Items.UNCUT_EMERALD_1621,1,1,1.7), @@ -36,25 +49,52 @@ 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() -} \ No newline at end of file + init { populateMappings() } + + fun getSkillBasedRandomEvent (skill: Int) : RandomEvents? { + return skillMap[skill]?.random() + } + + fun getNonSkillRandom() : RandomEvents { + return nonSkillList.random() + } + + private fun populateMappings() { + for (event in values()) { + if (!ServerConstants.INAUTHENTIC_CANDLELIGHT_RANDOM && event == CANDLELIGHT) { + continue + } + for (id in event.skillIds) { + val list = skillMap[id] ?: ArrayList().also { skillMap[id] = it } + list.add (event) + } + if (event.skillIds.isEmpty()) + nonSkillList.add (event) + } + } + } +} diff --git a/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt new file mode 100644 index 000000000..952bd0289 --- /dev/null +++ b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt @@ -0,0 +1,20 @@ +package content.global.ame.events + +import core.api.getAttribute +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class HostileRandomEventBehavior : NPCBehavior( + NPCs.EVIL_CHICKEN_2463, NPCs.EVIL_CHICKEN_2464, NPCs.EVIL_CHICKEN_2465, NPCs.EVIL_CHICKEN_2466, NPCs.EVIL_CHICKEN_2467, NPCs.EVIL_CHICKEN_2468, + NPCs.RIVER_TROLL_391, NPCs.RIVER_TROLL_392, NPCs.RIVER_TROLL_393, NPCs.RIVER_TROLL_394, NPCs.RIVER_TROLL_395, NPCs.RIVER_TROLL_396, + NPCs.SHADE_425, NPCs.SHADE_426, NPCs.SHADE_427, NPCs.SHADE_428, NPCs.SHADE_429, NPCs.SHADE_430, NPCs.SHADE_431, + NPCs.TREE_SPIRIT_438, NPCs.TREE_SPIRIT_439, NPCs.TREE_SPIRIT_440, NPCs.TREE_SPIRIT_441, NPCs.TREE_SPIRIT_442, NPCs.TREE_SPIRIT_443, + NPCs.ZOMBIE_419, NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424 +) { + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + val xprate = super.getXpMultiplier(self, attacker) + return if (getAttribute(self, "spawned-by-ame", false)) xprate / 16.0 else xprate + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt b/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt deleted file mode 100644 index 5366ee17b..000000000 --- a/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt +++ /dev/null @@ -1,40 +0,0 @@ -package content.global.ame.events - -import core.game.node.entity.player.Player -import content.global.ame.events.supriseexam.SurpriseExamUtils -import core.game.dialogue.DialogueFile - -class MysteriousOldManDialogue(val type: String) : DialogueFile() { - - val CHOICE_STAGE = 50000 - - override fun handle(componentID: Int, buttonID: Int) { - - if(type == "sexam" && stage < CHOICE_STAGE){ - npc("Would you like to come do a surprise exam?") - stage = CHOICE_STAGE - } - - else if(stage >= CHOICE_STAGE){ - when(stage) { - CHOICE_STAGE -> options("Yeah, sure!", "No, thanks.").also { stage++ } - CHOICE_STAGE.substage(1) -> when(buttonID){ - 1 -> { - end() - teleport(player!!,type) - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() - } - 2 -> { - end() - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() - } - } - } - } - } - fun teleport(player: Player,type: String){ - when(type){ - "sexam" -> SurpriseExamUtils.teleport(player) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt deleted file mode 100644 index 3ed7289b1..000000000 --- a/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt +++ /dev/null @@ -1,31 +0,0 @@ -package content.global.ame.events - -import content.global.ame.RandomEventNPC -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import core.api.utils.WeightBasedTable - -class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { - override fun init() { - super.init() - sayLine() - } - - override fun tick() { - super.tick() - if(RandomFunction.random(1,10) == 5) sayLine() - } - - fun sayLine() { - when(type){ - "sexam" -> sendChat("Surprise exam, ${player.username.capitalize()}!") - } - } - - override fun talkTo(npc: NPC) { - when(type){ - "sexam" -> player.dialogueInterpreter.open(MysteriousOldManDialogue("sexam"),this.asNpc()) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt b/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt new file mode 100644 index 000000000..249f9f194 --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/CandlelightInterface.kt @@ -0,0 +1,164 @@ +package content.global.ame.events.candlelight + +import core.api.* +import core.api.utils.PlayerCamera +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.Scenery + +/** + * Candlelight Interface CANDLELIGHT_178 + * + */ +class CandlelightInterface : InterfaceListener, InteractionListener, MapArea { + companion object { + const val CANDLELIGHT_INTERFACE = 178 + const val CANDLELIGHT_RETURN_LOC = "/save:original-loc" + const val CANDLELIGHT_CANDLE_ARRAY = "/save:candlelight:candle-array" + const val CANDLELIGHT_CAMERA_AT = "candlelight:camera-at" + + val CANDLE_LOC_ARRAY = arrayOf( + Location(1967, 4997), + Location(1968, 4998), + Location(1967, 4999), + Location(1968, 5000), + Location(1967, 5001), + Location(1968, 5002), + Location(1967, 5003), + Location(1968, 5004), + Location(1967, 5005), + Location(1968, 5006), + Location(1967, 5007), + ) + + fun initCandlelight(player: Player) { + val candleArray = intArrayOf(0,0,0,0,0,0,2,2,2,2,2) + candleArray.shuffle() + setAttribute(player, CANDLELIGHT_CANDLE_ARRAY, candleArray) + for (candleIndex in 0..10) { + setVarbit(player, 1771 + candleIndex, candleArray[candleIndex]) + } + } + + fun areCandlesLit(player: Player): Boolean { + val candleArray = getAttribute(player, CANDLELIGHT_CANDLE_ARRAY, intArrayOf(0,0,0,0,0,0,0,0,0,0,0)) + for (candle in candleArray) { + if (candle == 0) { + return false + } + } + return true + } + + fun lightCandle(player: Player) { + var currentCamLoc = getAttribute(player, CANDLELIGHT_CAMERA_AT, Location(1968, 5002)) + val candleIndex = CANDLE_LOC_ARRAY.indexOf(currentCamLoc) + val varbit = candleIndex + 1771 // Essentially all varbits are 1771 .. 1881 + + if (candleIndex != -1) { + val candleArray = getAttribute(player, CANDLELIGHT_CANDLE_ARRAY, intArrayOf(0,0,0,0,0,0,0,0,0,0,0)) + if (candleArray[candleIndex] == 0) { + candleArray[candleIndex] = 1 + setAttribute(player, CANDLELIGHT_CANDLE_ARRAY, candleArray) + setVarbit(player, varbit, 1) + sendMessage(player, "You light the candle.") + } else if (candleArray[candleIndex] == 1) { + sendMessage(player, "This candle is already lit.") + } else { + sendMessage(player, "This candle is too short to light.") + } + } else { + sendMessage(player, "There is nothing to light here.") + } + } + + fun moveCamera(player: Player, direction: Direction, firstTime: Boolean = false) { + var currentCamLoc = getAttribute(player, CANDLELIGHT_CAMERA_AT, Location(1968, 5002)) + when(direction) { + Direction.NORTH -> currentCamLoc = currentCamLoc.transform(Direction.NORTH) + Direction.SOUTH -> currentCamLoc = currentCamLoc.transform(Direction.SOUTH) + Direction.EAST -> currentCamLoc = currentCamLoc.transform(Direction.EAST) + Direction.WEST -> currentCamLoc = currentCamLoc.transform(Direction.WEST) + else -> {} + } + if (currentCamLoc.x < 1967) { currentCamLoc.x = 1967 } + if (currentCamLoc.x > 1968) { currentCamLoc.x = 1968 } + if (currentCamLoc.y < 4997) { currentCamLoc.y = 4997 } + if (currentCamLoc.y > 5007) { currentCamLoc.y = 5007 } + setAttribute(player, CANDLELIGHT_CAMERA_AT, currentCamLoc) + PlayerCamera(player).rotateTo(currentCamLoc.x - 30, currentCamLoc.y,0,200) // height is kind of a relative value? + PlayerCamera(player).panTo(currentCamLoc.x + 2, currentCamLoc.y,350, if(firstTime) 400 else 10) + } + } + + override fun defineInterfaceListeners() { + on(CANDLELIGHT_INTERFACE){ player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 1 -> moveCamera(player, Direction.WEST) + 2 -> moveCamera(player, Direction.EAST) + 3 -> lightCandle(player)/* Light */ + 4 -> moveCamera(player, Direction.SOUTH) + 5 -> moveCamera(player, Direction.NORTH) + 9 -> closeInterface(player) + } + return@on true + } + + onOpen(CANDLELIGHT_INTERFACE){ player, component -> + // Move camera + return@onOpen true + } + + onClose(CANDLELIGHT_INTERFACE){ player, component -> + PlayerCamera(player).reset() + // Reset camera + return@onClose true + } + } + + + override fun defineListeners() { + on((11364 .. 11394).toIntArray(), SCENERY, "light") { player, node -> + setAttribute(player, CANDLELIGHT_CAMERA_AT, Location(node.location.x, node.location.y)) + moveCamera(player, Direction.NORTH_WEST, true) + openInterface(player, CANDLELIGHT_INTERFACE) + return@on true + } + } + + override fun defineDestinationOverrides() { + setDest(SCENERY, (11364 .. 11394).toIntArray(),"light"){ player, node -> + return@setDest Location(1970, node.location.y) + } + } + + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders.forRegion(7758)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + initCandlelight(entity) + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt b/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt new file mode 100644 index 000000000..9eb9b7ede --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/PiousPeteDialogue.kt @@ -0,0 +1,94 @@ +package content.global.ame.events.candlelight + +import content.global.ame.RandomEvents +import content.global.ame.events.pillory.PilloryInterface +import content.global.ame.returnPlayer +import core.api.* +import core.game.dialogue.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +// iface 178 + +@Initializable +class PiousPeteDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return PiousPeteDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PiousPeteDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PIOUS_PETE_3207, NPCs._6564) + } +} + +class PiousPeteDialogueFile : DialogueLabeller() { + + override fun addConversation() { + npc(ChatAnim.THINKING, "Have you lit all the tall candles?") + exec { player, npc -> + if (CandlelightInterface.areCandlesLit(player)) { + loadLabel(player, "yeslit") + } else { + loadLabel(player, "nolit") + } + } + + label("nolit") + player(ChatAnim.HALF_GUILTY, "Sorry, not yet.") + npc(ChatAnim.SAD, "Please help me in lighting the candles. I just need you to light all the tall candles, but not the short ones.") + line("Click on the pillars to open an interface", "to move around and light the candles.") + + label("yeslit") + player(ChatAnim.FRIENDLY, "Yes, the tall ones are all lit.") + npc(ChatAnim.HAPPY, "Thank you my brother! I will now return you where you came from with a parting gift.") + npc(ChatAnim.FRIENDLY, "Take care brother!") + exec { player, npc -> + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 1 -> { + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeInterface(player) + return@queueScript delayScript(player, 3) + } + 2 -> { + val loot = RandomEvents.CERTER.loot!!.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + } +} +class PiousPeteStartingDialogueFile : DialogueLabeller() { + + override fun addConversation() { + npc("I'm sorry to drag you away from your tasks, but I need a little help with something.") + player(ChatAnim.THINKING,"How can I help?") + npc("This is a chapel dedicated to our lord, Saradomin, and I'm tasked with maintaining this chapel.") + npc(ChatAnim.SAD,"My task is to light the chapel candles, but I couldn't reach them myself and I kept getting dazzled by the light whenever I tried.") + npc("So I need your help in lighting the candles. I need you to light all the tall candles, but not the short ones.") + npc(ChatAnim.FRIENDLY, "Once all the tall candles are all lit, come back and see me, and I will reward you for your work.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt new file mode 100644 index 000000000..5231b6f9c --- /dev/null +++ b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt @@ -0,0 +1,26 @@ +package content.global.ame.events.candlelight + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class PiousPeteNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PRIEST_3206) { + override fun init() { + super.init() + // Supposed to be "I'm sorry to drag you away from your tasks, but I need a little help with something." but it's too goddamn long. + sendChat("${player.username}! I need a little help with something.") + face(player) + kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ -> + CandlelightInterface.initCandlelight(player) + openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207)) + } + } + + override fun talkTo(npc: NPC) { + player.dialogueInterpreter.open(PiousPeteDialogueFile(),npc) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/certer/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/certer/CerterNPC.kt b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt index daf3bc822..dee88ed3a 100644 --- a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt +++ b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt @@ -2,29 +2,20 @@ package content.global.ame.events.certer import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.emote.Emotes -import core.tools.RandomFunction import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC import core.api.animate +import core.api.lock +import core.api.setAttribute import core.api.utils.WeightBasedTable class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) { - lateinit var pName: String lateinit var phrases: Array override fun tick() { - // Don't speak if we have the interface opened - if (!timerPaused) { - // Over allotted time phrase - if (ticksLeft <= 2) { - player.lock(2) - sendChat(phrases[4]) - - // Say a phrase every 20 ticks starting at 280 ticks - // as to not interfere with the init chat phrase - } else if (ticksLeft <= 280 && ticksLeft % 20 == 0) { - sendChat(phrases[RandomFunction.random(1, 3)]) - } + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } super.tick() } @@ -36,15 +27,19 @@ class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NP override fun init() { super.init() - pName = player.username.capitalize() - phrases = arrayOf("Greetings $pName, I need your help.", - "ehem... Hello $pName, please talk to me!", - "Hello, are you there $pName?", - "It's really rude to ignore someone, $pName!", - "No-one ignores me!") - player.setAttribute("random:pause", false) - player.setAttribute("certer:reward", false) - sendChat(phrases[0]) + phrases = arrayOf( + "Greetings ${player.username}, I need your help.", + "ehem... Hello ${player.username}, please talk to me!", + "Hello, are you there ${player.username}?", + "It's really rude to ignore someone, ${player.username}!", + "No-one ignores me!" + ) + setAttribute(player, "certer:reward", false) animate(this, Emotes.BOW.animation, true) } -} \ No newline at end of file + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } +} 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..8c90bd77f 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt @@ -1,97 +1,68 @@ package content.global.ame.events.drilldemon +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.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) - 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) - } - - 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 +71,18 @@ 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..2a3ec7f51 --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt @@ -0,0 +1,28 @@ +package content.global.ame.events.drilldemon + +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable + +import core.game.world.map.Location + +class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { + override fun init() { + super.init() + sendChat("${player.username}! Drop and give me 20!") + face(player) + kidnapPlayer(this, player, Location(3163, 4819, 0)) { player, _ -> + player.interfaceManager.closeDefaultTabs() + setComponentVisibility(player, 548, 69, true) + setComponentVisibility(player, 746, 12, true) + openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790) + } + } + + override fun talkTo(npc: NPC) { + openDialogue(player, SeargentDamienDialogue(), npc) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drunkendwarf/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..f4a6842ff --- /dev/null +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt @@ -0,0 +1,48 @@ +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) { + lateinit var phrases: Array + private var attackDelay = 0 + + override fun init() { + super.init() + phrases = arrayOf( + "'Ello der ${player.username}! *hic*", + "Oi, are you der ${player.username}!", + "Dunt ignore your matey!", + "Aww comeon, talk to ikle me ${player.username}!", + "I hates you, ${player.username}!" + ) + } + + override fun tick() { + sayLine(this, phrases, true, true) + 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, DrunkenDwarfDialogue(), this.asNpc()) + } + + override fun onTimeUp() { + if (attackDelay <= getWorldTicks()) { + this.attack(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt 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..4995b6b41 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt @@ -0,0 +1,26 @@ +package content.global.ame.events.evilbob + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) { + override fun init() { + super.init() + sendChat("meow") + face(player) + kidnapPlayer(this, player, Location(3419, 4776, 0), "No... what? Nooooooooooooo!") { player, _ -> + EvilBobUtils.giveEventFishingSpot(player) + sendMessage(player, "Welcome to Scape2009.") + openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479) + } + } + + 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..58f8e5aee --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt @@ -0,0 +1,82 @@ +package content.global.ame.events.evilbob + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.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 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..4dfc72698 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt @@ -0,0 +1,182 @@ +package content.global.ame.events.evilbob + +import core.api.* +import core.game.activity.Cutscene +import core.game.node.entity.player.Player +import core.game.world.map.Direction + +class ServantCutsceneN(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob + addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + teleport(player, 30, 41) + moveCamera(30, 37, 400, 255) + rotateCamera(30, 50, 400, 255) + openInterface(player, 186) + timedUpdate(0) + } + 1 -> { + timedUpdate(0) + } + 2 -> { // Slow start + moveCamera(30, 49, 400, 2) + timedUpdate(2) + } + 3 -> { // Fast middle + moveCamera(30, 44, 380, 4) + timedUpdate(6) + } + 4 -> { // Slow end + moveCamera(30, 45, 340, 2) + timedUpdate(2) + } + 5 -> { + timedUpdate(2) + } + 6 -> { + closeInterface(player) + end(fade = false) { player.locks.lockTeleport(1000000) } + } + } + } +} + +class ServantCutsceneS(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob + addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + teleport(player, 30, 41) + moveCamera(31, 46, 365, 255) + rotateCamera(29, 30, 365, 255) + openInterface(player, 186) + timedUpdate(0) + } + 1 -> { + timedUpdate(0) + } + 2 -> { + moveCamera(31, 43, 480, 2) + timedUpdate(2) + } + 3 -> { + moveCamera(31, 37, 455, 4) + timedUpdate(8) + } + 4 -> { + moveCamera(31, 36, 395, 2) + timedUpdate(2) + } + 5 -> { + timedUpdate(3) + } + 6 -> { + closeInterface(player) + end(fade = false) { player.locks.lockTeleport(1000000) } + } + } + } +} + +class ServantCutsceneE(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob + addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + teleport(player, 30, 41) + moveCamera(25, 41, 440, 255) + rotateCamera(42, 41, 440, 255) + openInterface(player, 186) + timedUpdate(0) + } + 1 -> { + timedUpdate(0) + } + 2 -> { // Slow start + moveCamera(28, 41, 500, 3) + timedUpdate(2) + } + 3 -> { // Fast middle + moveCamera(34, 41, 390, 5) + timedUpdate(6) + } + 4 -> { // Slow end + moveCamera(36, 41, 340, 2) + timedUpdate(2) + } + 5 -> { + timedUpdate(4) + } + 6 -> { + closeInterface(player) + end(fade = false) { player.locks.lockTeleport(1000000) } + } + } + } +} + +class ServantCutsceneW(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + addNPC(2479, 28, 41, Direction.SOUTH) // Evil Bob + addNPC(2481, 31, 41, Direction.SOUTH_WEST) // Servant + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + teleport(player, 30, 41) + moveCamera(34, 41, 325, 255) + rotateCamera(16, 40, 300, 255) + openInterface(player, 186) + timedUpdate(0) + } + 1 -> { + timedUpdate(0) + } + 2 -> { // Slow start + moveCamera(31, 41, 440, 3) + timedUpdate(2) + } + 3 -> { // Fast middle + moveCamera(24, 41, 330, 5) + timedUpdate(7) + } + 4 -> { // Slow end + moveCamera(23, 41, 300, 2) + timedUpdate(2) + } + 5 -> { + timedUpdate(3) + } + 6 -> { + closeInterface(player) + end(fade = false) { player.locks.lockTeleport(1000000) } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/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..84e491f89 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt @@ -0,0 +1,52 @@ +package content.global.ame.events.freakyforester + +import content.global.ame.returnPlayer +import core.api.* +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.node.entity.player.Player +import core.game.world.map.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 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..1ef52dde6 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt @@ -0,0 +1,25 @@ +package content.global.ame.events.freakyforester + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import org.rs09.consts.NPCs +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location + +class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) { + override fun init() { + super.init() + sendChat("Ah, ${player.username}, just the person I need!") + face(player) + kidnapPlayer(this, player, Location(2599, 4777, 0)) { player, _ -> + FreakUtils.giveFreakTask(player) + openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc) + } + } + + 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..b4700a9a5 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,42 @@ 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.lock +import core.api.playAudio import core.api.utils.WeightBasedTable +import org.rs09.consts.Sounds class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { - val phrases = arrayOf("Greetings, @name!","Ehem... Master @name?","Are you there, Master @name?","No one ignores me!") - var assigned_item = 0 - val items = arrayOf(Items.LAMP_2528) + lateinit var phrases: Array override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } 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) + val honorific = if (player.isMale) "Master" else "Mistress" + phrases = arrayOf( + "Greetings, ${player.username}!", + "Ehem... $honorific ${player.username}?", + "Are you there, $honorific ${player.username}?", + "No one ignores me!" + ) + playAudio(player, Sounds.GENIE_APPEAR_2301) } override fun talkTo(npc: NPC) { player.dialogueInterpreter.open(GenieDialogue(),npc) } -} \ No newline at end of file + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } +} diff --git a/Server/src/main/content/global/ame/events/maze/MazeInterface.kt b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt new file mode 100644 index 000000000..c3c1ab7f4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt @@ -0,0 +1,305 @@ +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), + ) + + private val ONE_WAY_WALL = mapOf( + Location.create(2904, 4573, 0) to setOf( // Diagonal entries + Location.create(2904, 4573, 0), + Location.create(2904, 4572, 0), + Location.create(2904, 4574, 0), + ), + Location.create(2906, 4586, 0) to setOf( // Diagonal entries + Location.create(2906, 4586, 0), + Location.create(2907, 4586, 0), + ), + Location.create(2902, 4575, 0) to setOf(Location.create(2903, 4575, 0)), + Location.create(2924, 4583, 0) to setOf(Location.create(2923, 4583, 0)), + ) + + fun initMaze(player: Player) { + setAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 300) + setVarp(player, MAZE_TIMER_VARP, (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3),false) + openOverlay(player, MAZE_TIMER_INTERFACE) + sendMessage(player, "You need to reach the maze center, then you'll be returned to where you were.") + sendNPCDialogue(player, NPCs.MYSTERIOUS_OLD_MAN_410, "You need to reach the maze center, then you'll be returned to where you were.") + } + + fun calculateLoot(player: Player) { + val randomNumber = (0..8).random() + val totalLevel = player.getSkills().totalLevel.toDouble() + val rewardPotential = getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0).toDouble() / 300.0 + val itemDivisor = ITEM_DIVISOR[randomNumber] + val itemQuantity = (totalLevel * rewardPotential * 3.33) / itemDivisor + // sendMessage(player, "Maze reward calculation: $totalLevel * $rewardPotential * 3.33 / $itemDivisor = $itemQuantity") + if (itemQuantity.toInt() > 0) { + addItemOrDrop(player, REWARD_ITEM[randomNumber], itemQuantity.toInt()) + } + } + + /** + * Chest Location to rotation mapping. + * This is needed as it is impossible to obtain the underlying chest scenery for the rotation. + * 0: Facing North, 1: Facing East, 2: Facing South, 3: Facing West + */ + val chestLocationRotationMap = mapOf( + Location(2930, 4595, 0).toString() to 2, + Location(2924, 4572, 0).toString() to 2, + Location(2925, 4573, 0).toString() to 0, + Location(2900, 4578, 0).toString() to 2, + Location(2901, 4560, 0).toString() to 1, + Location(2890, 4599, 0).toString() to 2, + Location(2896, 4591, 0).toString() to 2, + Location(2895, 4592, 0).toString() to 1, + Location(2901, 4560, 0).toString() to 3, + Location(2918, 4590, 0).toString() to 1, + Location(2917, 4590, 0).toString() to 3, + ) + + /** + * Chest Interaction workaround + * + * The issue here is that the walls(3626) of the Maze are overlapping some(not all) chest sceneries. + * + * The types for the wallScenery: + * Type 0 - flat panel | + * Type 2 - right angle panel with rotation 0:r 1:7 2:> 3:L + * Type 3 - corner post . for angle edges of walls + */ + fun overrideScenery(wallScenery: core.game.node.scenery.Scenery, chestSceneryId: Int): core.game.node.scenery.Scenery { + if (wallScenery.id == chestSceneryId) { + replaceScenery(wallScenery, Scenery.CHEST_3636, 30) + wallScenery.isActive = true + return wallScenery // Return the chest scenery as the wallScenery isn't there. + } + + addScenery(Scenery.CHEST_3636, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) + addScenery(wallScenery) + // replaceScenery(newChestScenery, Scenery.CHEST_3636, 3) // didn't work for an underlying scenery + // I did a world pulse since everyone will get to see the chest open. + Pulser.submit(object : Pulse(30) { + override fun pulse(): Boolean { + addScenery(Scenery.CHEST_3635, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) + addScenery(wallScenery) + return true + } + }) + // Return the chest scenery to replace PacketProcessor so that MISMATCH will not happen. + return core.game.node.scenery.Scenery( + chestSceneryId, + wallScenery.location, + chestLocationRotationMap[wallScenery.location.toString()] ?: 0 + ) + } + } + + override fun defineListeners() { + + // This somehow doesn't trigger as the scenery.id != objId (3626 != 3635) + on(Scenery.CHEST_3635, IntType.SCENERY, "open") { player, node -> + if (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0 && getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0) < 10) { + animate(player, 536) + // val actualScenery = RegionManager.getObject(node.location.z, node.location.x, node.location.y, 3626) + val tableRoll = CHEST_REWARDS.roll() + addItemOrBank(player, tableRoll[0].id, tableRoll[0].amount) + when (tableRoll[0].id){ + Items.AIR_RUNE_556 -> sendItemDialogue(player, Items.AIR_RUNE_556, "You've found some air runes!") + Items.WATER_RUNE_555 -> sendItemDialogue(player, Items.WATER_RUNE_555, "You've found some water runes!") + Items.EARTH_RUNE_557 -> sendItemDialogue(player, Items.EARTH_RUNE_557, "You've found some earth runes!") + Items.FIRE_RUNE_554 -> sendItemDialogue(player, Items.FIRE_RUNE_554, "You've found some fire runes!") + Items.BRONZE_ARROW_882 -> sendItemDialogue(player, Items.BRONZE_ARROW_882, "You've found some bronze arrows!") + Items.BRONZE_BOLTS_877 -> sendItemDialogue(player, Items.BRONZE_BOLTS_877, "You've found some bronze bolts!") + Items.IRON_ARROW_884 -> sendItemDialogue(player, Items.IRON_ARROW_884, "You've found some iron arrows!") + Items.ATTACK_POTION2_123 -> sendItemDialogue(player, Items.ATTACK_POTION2_123, "You've found an attack potion!") + Items.STRENGTH_POTION2_117 -> sendItemDialogue(player, Items.STRENGTH_POTION2_117, "You've found a strength potion!") + Items.DEFENCE_POTION2_135 -> sendItemDialogue(player, Items.DEFENCE_POTION2_135, "You've found a defence potion!") + } + setAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0)) + } else { + sendMessage(player,"You find nothing of interest.") + } + return@on true + } + + on(Scenery.CHEST_3636, SCENERY, "search") { player, node -> + sendMessage(player,"You find nothing of interest.") + return@on true + } + + on(Scenery.WALL_3626, IntType.SCENERY, "open") { player, node -> + sendMessage(player, "That bit doesn't open.") // 0xBrLo9woIY + return@on true + } + + on( + intArrayOf( + Scenery.WALL_3628, + Scenery.WALL_3629, + Scenery.WALL_3630, + Scenery.WALL_3631, + Scenery.WALL_3632, + ), + IntType.SCENERY, + "open" + ) { player, node -> + val scenery = node as core.game.node.scenery.Scenery + + if (ONE_WAY_WALL[scenery.location]?.contains(player.location) == true) { + sendDialogue(player, "I don't think that's the right way.") + } else { + DoorActionHandler.handleAutowalkDoor(player, scenery) + } + + return@on true + } + + on(Scenery.STRANGE_SHRINE_3634, IntType.SCENERY, "touch") { player, node -> + player.unhook(this) + lock(player, 12) + closeOverlay(player) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendGraphics(Graphics(86, 0, 3), player.location) + animate(player,862) + return@queueScript delayScript(player, 6) + } + 1 -> { + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 2 -> { + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeOverlay(player) + return@queueScript delayScript(player, 1) + } + 3 -> { + calculateLoot(player) + removeAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT) + removeAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + return@on true + } + } + + override fun process(entity: Entity, event: TickEvent) { + if (entity is Player) { + if (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0) { + setAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) - 1) + } + setVarp(entity, MAZE_TIMER_VARP, (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3), false) + } + } + + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(11591)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + sendMessage(entity, "Head for the center of the maze.") + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + openOverlay(entity, MAZE_TIMER_INTERFACE) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + closeOverlay(entity) + entity.unhook(this) + } + } + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + entity.hook(Event.Tick, this) + } + } + +} diff --git a/Server/src/main/content/global/ame/events/maze/MazeNPC.kt b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt new file mode 100644 index 000000000..a51f0b044 --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt @@ -0,0 +1,32 @@ +package content.global.ame.events.maze + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { + override fun init() { + super.init() + sendChat("Aha, you'll do ${player.username}!") + face(player) + // Note: This event is NOT instanced: + // Sources: + // https://youtu.be/2gpzn9oNdy0 (2007) + // https://youtu.be/Tni1HURgnxg (2008) + // https://youtu.be/igdwDZOv9LU (2008) + // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for." + // https://youtu.be/FMuKZm-Ikgs (2011) + // val region = DynamicRegion.create(11591) + kidnapPlayer(this, player, MazeInterface.STARTING_POINTS.random()) { player, _ -> + MazeInterface.initMaze(player) + removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN) + } + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "He isn't interested in talking to you.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt b/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt new file mode 100644 index 000000000..f48fc76f4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/pillory/PilloryInterface.kt @@ -0,0 +1,219 @@ +package content.global.ame.events.pillory + +import content.global.ame.RandomEvents +import content.global.ame.returnPlayer +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.interaction.InterfaceListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +/** + * Pillory Unlocking Interface PILLORY_189 + * + * https://www.youtube.com/watch?v=caWn7pE2mkE + * https://www.youtube.com/watch?v=TMVR5cZZwZ0 + * https://www.youtube.com/watch?v=Ym9LCDP-Q74 + * https://www.youtube.com/watch?v=_vn0QZTtI6U (Failure) + * https://www.youtube.com/watch?v=zmXDikQIua4 + * + * Child IDs + * 4 - Rotating Lock Model + * 5 6 7 - Swinging Keys Models + * 8 9 10 - Buttons for the Swinging Keys Models + * 11 12 13 14 15 16 - Padlocks at the Top + * 17 18 19 20 21 22 - Padlocks stars? Model 15272, Anim 4135 + * + * Model IDs + * Using the amazeballs ::listifmodels + * 9749, 9750, 9751, 9752 - Swinging Keys Models + * 9753, 9754, 9755, 9756 - Rotating Lock Models + * 9757 9758 locked unlock + */ +class PilloryInterface : InterfaceListener, InteractionListener, MapArea { + companion object { + const val PILLORY_LOCK_INTERFACE = 189 + const val PILLORY_ATTRIBUTE_EVENT_KEYS = "pillory:event-keys" + const val PILLORY_ATTRIBUTE_EVENT_LOCK = "pillory:event-lock" + const val PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT = "/save:pillory:target-correct" + const val PILLORY_ATTRIBUTE_CORRECT_COUNTER = "/save:pillory:num-correct" + + val LOCATIONS = arrayOf( + // Varrock Cages + Location(3226, 3407, 0), + Location(3228, 3407, 0), + Location(3230, 3407, 0), + // Seers Village Cages + Location(2681, 3489, 0), + Location(2683, 3489, 0), + Location(2685, 3489, 0), + // Yannile Cages + Location(2604, 3105, 0), + Location(2606, 3105, 0), + Location(2608, 3105, 0), + ) + + fun initPillory(player: Player) { + setAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") + } + + fun randomPillory(player: Player) { + // Shuffle all 4 kinds of keys in, pick 3 for the keys, pick 1 from the 3 as the lock. + val keys = (0..3).toIntArray().let{ keys -> keys.shuffle(); return@let keys } + val lock = intArrayOf(keys[1], keys[2], keys[3]).random() // Last 3 as there are 4 keys. key[0] is fallback. + + setAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, keys) + setAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, lock) + + player.packetDispatch.sendModelOnInterface(9753 + lock, PILLORY_LOCK_INTERFACE, 4, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[1], PILLORY_LOCK_INTERFACE, 5, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[2], PILLORY_LOCK_INTERFACE, 6, 0) + player.packetDispatch.sendModelOnInterface(9749 + keys[3], PILLORY_LOCK_INTERFACE, 7, 0) + + val numberToGetCorrect = getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) + val correctCount = getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + for (i in 1.. 6) { + // Set if lock is red or green. + if (i <= correctCount) { + player.packetDispatch.sendModelOnInterface(9758, PILLORY_LOCK_INTERFACE, 10 + i, 0) + } else { + player.packetDispatch.sendModelOnInterface(9757, PILLORY_LOCK_INTERFACE, 10 + i, 0) + } + // Set if hide or show lock. + player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 10 + i, i > numberToGetCorrect) + } + } + + fun selectedKey(player: Player, buttonID: Int) { + val keys = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, intArrayOf(0, 0, 0)) + val lock = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, -1) + if (keys[buttonID] == lock) { + // CORRECT ANSWER + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + 1) + if (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) <= getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, -1)) { + player.dialogueInterpreter.sendPlainMessage(true, "", "You've escaped!") + sendMessage(player, "You've escaped!") + removeAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT) + removeAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER) + closeInterface(player) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 1 -> { + val loot = RandomEvents.CERTER.loot!!.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + returnPlayer(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + closeInterface(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return + } + randomPillory(player) + player.dialogueInterpreter.sendPlainMessage( + true, + "", + "Correct!", + "" + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + " down, " + + (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 3) - getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0)) + " to go!") + // Animation for the star, but it doesn't work. + player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 1), false) + sendAnimationOnInterface(player, 4135, PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 1)) + } else { + // WRONG ANSWER + player.dialogueInterpreter.close() + player.dialogueInterpreter.sendDialogues(NPCs.TRAMP_2794 , FacialExpression.OLD_ANGRY1, "Bah, that's not right.","Use the key that matches the hole", "in the spinning lock.") + if (getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 0) < 6) { + setAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, getAttribute(player, PILLORY_ATTRIBUTE_NEEDED_TO_GET_CORRECT, 0) + 1) + } + setAttribute(player, PILLORY_ATTRIBUTE_CORRECT_COUNTER, 0) + closeInterface(player) + } + } + } + + override fun defineInterfaceListeners() { + on(PILLORY_LOCK_INTERFACE){ player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 8 -> selectedKey(player, 1) + 9 -> selectedKey(player, 2) + 10 -> selectedKey(player, 3) + } + return@on true + } + + onOpen(PILLORY_LOCK_INTERFACE){ player, component -> + return@onOpen true + } + } + + override fun defineListeners() { + on(Scenery.CAGE_6836, IntType.SCENERY, "unlock") { player, node -> + if (player.location in LOCATIONS) { // When you aren't inside. + randomPillory(player) + openInterface(player, PILLORY_LOCK_INTERFACE) + player.dialogueInterpreter.sendPlainMessage(true, "", "Pick the swinging key that matches the", "hole in the spinning lock.") + } else { + sendMessage(player, "You can't unlock the pillory, you'll let all the prisoners out!") + } + return@on true + } + } + + override fun defineAreaBorders(): Array { + return arrayOf( + // Varrock Cages + ZoneBorders(3226, 3407, 3226, 3407), + ZoneBorders(3228, 3407, 3228, 3407), + ZoneBorders(3230, 3407, 3230, 3407), + // Seers Village Cages + ZoneBorders(2681, 3489, 2681, 3489), + ZoneBorders(2683, 3489, 2683, 3489), + ZoneBorders(2685, 3489, 2685, 3489), + // Yannile Cages + ZoneBorders(2604, 3105, 2604, 3105), + ZoneBorders(2606, 3105, 2606, 3105), + ZoneBorders(2608, 3105, 2608, 3105), + ) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt new file mode 100644 index 000000000..3917b035c --- /dev/null +++ b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt @@ -0,0 +1,25 @@ +package content.global.ame.events.pillory + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.component.Component.setUnclosable +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) { + override fun init() { + super.init() + sendChat("${player.username}, you're under arrest!") + face(player) + kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ -> + PilloryInterface.initPillory(player) + setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")) + } + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "He isn't interested in talking to you.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt new file mode 100644 index 000000000..860d4d7dc --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterBorders.kt @@ -0,0 +1,37 @@ +package content.global.ame.events.quizmaster + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.NPCs + +class QuizMasterBorders : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(7754)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12) + face(entity, Location(1952, 4768, 1)) + animate(entity,2378) + openDialogue(entity, QuizMasterDialogueFile(), NPC(NPCs.QUIZ_MASTER_2477)) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.interfaceManager.restoreTabs() + //closeOverlay(entity) + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt new file mode 100644 index 000000000..4bb2ff79d --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt @@ -0,0 +1,152 @@ +package content.global.ame.events.quizmaster + +import content.global.ame.returnPlayer +import core.ServerConstants +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.component.Component +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class QuizMasterDialogueFile : DialogueLabeller() { + companion object { + const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191 + const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct" + const val QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER = "quizmaster:random-answer" + + /* + // Golden Models: + 8828 ADAMANT_BATTLEAXE_1371 + 8829 SALMON_329 + 8830 TROUT_333 + 8831 NECKLACE + 8832 WOODEN_SHIELD_1171 + 8833 BRONZE_MED_HELM_1139 + 8834 RING + 8835 SECATEURS_5329 + 8836 BRONZE_SWORD_1277 + 8837 GARDENING_TROWEL_5325 + */ + val sets = arrayOf( + intArrayOf(8828, 8829, 8829), + intArrayOf(8831, 8837, 8835), + intArrayOf(8830, 8832, 8833), + intArrayOf(8835, 8834, 8831), + intArrayOf(8837, 8836, 8828), + ) + + fun randomQuestion(player: Player): Int { + val randomSet = intArrayOf(*sets.random()) + val answer = intArrayOf(*randomSet)[0] + randomSet.shuffle() + val correctButton = randomSet.indexOf(answer) + 2 // buttons are 3,4,5 + + player.packetDispatch.sendModelOnInterface(randomSet[0], QUIZMASTER_INTERFACE, 6, 512) + player.packetDispatch.sendModelOnInterface(randomSet[1], QUIZMASTER_INTERFACE, 7, 512) + player.packetDispatch.sendModelOnInterface(randomSet[2], QUIZMASTER_INTERFACE, 8, 512) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 6, 512,0,0) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 7, 512,0,0) + player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 8, 512,0,0) + + return correctButton + } + + // Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. + val tableRoll = WeightBasedTable.create( + WeightedItem(Items.LAMP_2528, 1, 1, 1.0, false), + WeightedItem(Items.CABBAGE_1965, 1, 1, 1.0, false), + WeightedItem(Items.DIAMOND_1601, 1, 1, 1.0, false), + WeightedItem(Items.BUCKET_1925, 1, 1, 1.0, false), + WeightedItem(Items.FLIER_956, 1, 1, 1.0, false), + WeightedItem(Items.OLD_BOOT_685, 1, 1, 1.0, false), + WeightedItem(Items.BODY_RUNE_559, 1, 1, 1.0, false), + WeightedItem(Items.ONION_1957, 1, 1, 1.0, false), + WeightedItem(Items.MITHRIL_SCIMITAR_1329, 1, 1, 1.0, false), + WeightedItem(Items.CASKET_405, 1, 1, 1.0, false), + WeightedItem(Items.STEEL_PLATEBODY_1119, 1, 1, 1.0, false), + WeightedItem(Items.NATURE_RUNE_561, 20, 20, 1.0, false), + ) + } + + + override fun addConversation() { + assignToIds(NPCs.QUIZ_MASTER_2477) + afterClose { player -> + loadLabel(player, "question") + } + + npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "O D D O N E O U T", unclosable = true) + player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...", unclosable = true) + npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "${player?.username}!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!", unclosable = true) + goto("question") + + label("question") + manual(unclosable = true) { player, _ -> + setAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player)) + val comp = Component(QUIZMASTER_INTERFACE) + player.interfaceManager.openChatbox(comp) + return@manual comp + } + exec { player, _ -> + if (buttonID == getAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { + // Correct Answer + setAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) + if (getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { + goto("winner") + } else { + goto("right") + } + } else { + goto("wrong") + } + } + + label("right") + npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!", unclosable = true) + goto("question") + + label("wrong") + npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!", unclosable = true) + goto("question") + + label("winner") + npc(FacialExpression.FRIENDLY,"CONGRATULATIONS!", "You are a WINNER!", "Please choose your PRIZE!", unclosable = true) + options( + DialogueOption("money", "1000 Coins", skipPlayer = true), + DialogueOption("item", "Random Item", skipPlayer = true) + ) + + label("money") + exec { player, _ -> + queueScript(player, 0, QueueStrength.SOFT) { _ -> + addItemOrDrop(player, Items.COINS_995, 1000) + return@queueScript stopExecuting(player) + } + } + goto("cleanup") + + label("item") + exec { player, _ -> + queueScript(player, 0, QueueStrength.SOFT) { _ -> + addItemOrDrop(player, tableRoll.roll()[0].id) + return@queueScript stopExecuting(player) + } + } + goto("cleanup") + + label("cleanup") + exec { player, _ -> + resetAnimator(player) + returnPlayer(player) + removeAttributes(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER) + } + goto("nowhere") + } +} diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt new file mode 100644 index 000000000..cccc4d0eb --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt @@ -0,0 +1,39 @@ +package content.global.ame.events.quizmaster + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs + +/** + * Quiz Master NPC: + * + * https://www.youtube.com/watch?v=EFAWSiPTfcM + * https://www.youtube.com/watch?v=caWn7pE2mkE + * https://www.youtube.com/watch?v=Bc1gAov2o4w + * https://www.youtube.com/watch?v=oHU8-MUarxE + * https://www.youtube.com/watch?v=wvjYiF4v9tI + * https://www.youtube.com/watch?v=dC6rlSnXEfw + */ +class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.QUIZ_MASTER_2477) { + override fun init() { + super.init() + sendChat("Hey ${player.username}! It's your lucky day!") + face(player) + kidnapPlayer(this, player, Location(1952, 4764, 1)) { player, _ -> + setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.") + sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case. + face(player, Location(1952, 4768, 1)) + animate(player,2378) + // Quiz dialogue gets opened automatically on zone entry. + } + } + + override fun talkTo(npc: NPC) { + openDialogue(player, QuizMasterDialogueFile(), this.asNpc()) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/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/RockGolemBehavior.kt b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt new file mode 100644 index 000000000..58852ec18 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt @@ -0,0 +1,86 @@ +package content.global.ame.events.rockgolem + +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.InteractionType +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.update.flag.context.Animation +import org.rs09.consts.NPCs + +class RockGolemBehavior() : NPCBehavior(NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418) { + private val swingHandler = RockGolemSwingHandler() + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return swingHandler + } + + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return super.getXpMultiplier(self, attacker) / 16.0 + } +} + +private class RockGolemSwingHandler : CombatSwingHandler(CombatStyle.RANGE) { + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + return type?.swingHandler?.canSwing(entity, victim) + } + + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + if (entity == null || victim == null) return -1 + + val prayingRange = victim.hasProtectionPrayer(CombatStyle.RANGE) + + type = if (prayingRange) { + CombatStyle.MELEE + } else { + CombatStyle.RANGE + } + + if (type == CombatStyle.MELEE && entity.location.getDistance(victim.location) > 1) return -1 + + return type?.swingHandler?.swing(entity, victim, state) ?: 0 + } + + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + type?.swingHandler?.impact(entity, victim, state) + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + type?.swingHandler?.visualizeImpact(entity, victim, state) + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + when (type) { + CombatStyle.MELEE -> { + entity.animate(Animation(153)) + } + + CombatStyle.RANGE -> { + val speed = (100 + (Projectile.getLocation(entity).getDistance(victim!!.getLocation()) * 5)).toInt() + Projectile.create(entity, victim, 968, 48, 36, 80, speed, 5, entity.size() shl 5).send() + entity.animate(Animation(5347)) + } + + else -> {} + } + } + + override fun calculateAccuracy(entity: Entity?): Int { + return type?.swingHandler?.calculateAccuracy(entity) ?: 0 + } + + override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { + return type?.swingHandler?.calculateHit(entity, victim, modifier) ?: 0 + } + + override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { + return type?.swingHandler?.calculateDefence(victim, attacker) ?: 0 + } + + override fun getSetMultiplier(e: Entity?, skillId: Int): Double { + return type?.swingHandler?.getSetMultiplier(e, skillId) ?: 0.0 + } +} 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..99e0a0e68 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt @@ -5,32 +5,49 @@ import core.tools.RandomFunction import org.rs09.consts.Items import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC +import core.api.lock import core.api.utils.WeightBasedTable class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) { - val phrases = arrayOf("Hello, @name, can you hear me?","Sandwiches, @name!","Are you ignoring me @name??","Yoohoo! Sandwiches, @name!","Hello, @name?", "Come get your sandwiches @name!", "How could you ignore me like this @name?!", "Do you even want your sandwiches, @name?") + lateinit var phrases: Array var assigned_item = 0 - val items = arrayOf(Items.BAGUETTE_6961,Items.TRIANGLE_SANDWICH_6962,Items.SQUARE_SANDWICH_6965,Items.ROLL_6963,Items.MEAT_PIE_2327,Items.KEBAB_1971,Items.CHOCOLATE_BAR_1973) + val items = arrayOf(Items.BAGUETTE_6961, Items.TRIANGLE_SANDWICH_6962, Items.SQUARE_SANDWICH_6965, Items.ROLL_6963, Items.MEAT_PIE_2327, Items.KEBAB_1971, Items.CHOCOLATE_BAR_1973) override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } super.tick() } override fun init() { super.init() + phrases = arrayOf( + // https://www.youtube.com/watch?v=ek8r3ZS929E + // She always starts with "Sandwiches, ${player.username}!" but she ALSO picks that at random, hence duplicate it with hasOpeningPhrase = true + "Sandwiches, ${player.username}!", + "Sandwiches, ${player.username}!", + "Come on ${player.username}, I made these specially!!", + "All types of sandwiches, ${player.username}.", + "Did you hear me ${player.username}?", + "You think I made these just for fun?!!?", + "How could you ignore me like this, ${player.username}?!" //unknown if authentic but it was already here + ) assignItem() - sendChat(phrases.random().replace("@name",player.name.capitalize())) + } + + override fun onTimeUp() { + noteAndTeleport() + terminate() } fun assignItem(){ assigned_item = items.random() - player.setAttribute("sandwich-lady:item",assigned_item) + player.setAttribute("sandwich-lady:item", assigned_item) } override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) + player.dialogueInterpreter.open(SandwichLadyDialogue(false), npc) } -} \ 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/surpriseexam/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt new file mode 100644 index 000000000..2a3896836 --- /dev/null +++ b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt @@ -0,0 +1,24 @@ +package content.global.ame.events.surpriseexam + +import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer +import core.api.* +import org.rs09.consts.NPCs +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.game.world.map.Location + +class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { + override fun init() { + super.init() + sendChat("Surprise exam, ${player.username}!") + face(player) + kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ -> + /* nothing needed */ + } + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "He isn't interested in talking to you.") + } +} diff --git a/Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt similarity index 90% rename from Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt index 3fffea71b..221f4b4fe 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SEDoorDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt @@ -1,4 +1,4 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE diff --git a/Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt similarity index 96% rename from Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt index e445c5145..3ba12a8c0 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SEPatternInterface.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt @@ -1,4 +1,4 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.node.entity.npc.NPC import org.rs09.consts.Components diff --git a/Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt similarity index 58% rename from Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt index 6629982fa..ad19ffbda 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt @@ -1,28 +1,29 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.component.Component import core.game.node.entity.player.Player -import core.game.node.item.Item import core.game.world.map.Location import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType import content.global.handlers.iface.ExperienceInterface +import core.api.MapArea +import core.api.removeItem +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction -class SupriseExamListeners : InteractionListener { - val MORDAUT = NPCs.MR_MORDAUT_6117 - val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640 +class SupriseExamListeners : InteractionListener, MapArea { override fun defineListeners() { - on(MORDAUT, IntType.NPC, "talk-to"){ player, node -> + on(NPCs.MR_MORDAUT_6117, IntType.NPC, "talk-to") { player, node -> player.faceLocation(Location.create(1886, 5024, 0)) - val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3 - player.dialogueInterpreter.open(MordautDialogue(examComplete),node.asNpc()) + val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT, 0) == 3 + player.dialogueInterpreter.open(MordautDialogue(examComplete), node.asNpc()) return@on true } - on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open"){ player, node -> + on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open") { player, node -> val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) if(correctDoor == -1){ @@ -39,9 +40,9 @@ class SupriseExamListeners : InteractionListener { return@on true } - on(BOOK_OF_KNOWLEDGE, IntType.ITEM, "read"){ player, _ -> - player.setAttribute("caller"){skill: Int,p: Player -> - if(p.inventory.remove(Item(BOOK_OF_KNOWLEDGE))) { + on(Items.BOOK_OF_KNOWLEDGE_11640, IntType.ITEM, "read") { player, _ -> + player.setAttribute("caller") { skill: Int, p: Player -> + if (removeItem(p, Items.BOOK_OF_KNOWLEDGE_11640)) { val level = p.skills.getStaticLevel(skill) val experience = level * 15.0 p.skills.addExperience(skill, experience) @@ -54,8 +55,16 @@ class SupriseExamListeners : InteractionListener { } override fun defineDestinationOverrides() { - setDest(IntType.NPC,MORDAUT){ _, _ -> + setDest(IntType.NPC, NPCs.MR_MORDAUT_6117) { _, _ -> return@setDest Location.create(1886, 5025, 0) } } -} \ No newline at end of file + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders.forRegion(7502)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) + } +} diff --git a/Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt similarity index 69% 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..e1cbdd5a8 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt @@ -1,20 +1,15 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam +import content.global.ame.returnPlayer import core.api.* import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player -import core.game.node.item.GroundItemManager -import core.game.node.item.Item import core.game.system.task.Pulse -import core.game.world.map.Location import org.rs09.consts.Components import org.rs09.consts.Items 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 +23,12 @@ object SurpriseExamUtils { intArrayOf(Items.FLY_FISHING_ROD_309,Items.BARBARIAN_ROD_11323,Items.SMALL_FISHING_NET_303,Items.HARPOON_311) ) - fun teleport(player: Player){ - player.setAttribute(SE_KEY_LOC,player.location) - - registerLogoutListener(player, SE_LOGOUT_KEY){p -> - teleport(p, p.getAttribute(SE_KEY_LOC, p.location)) - } - - player.properties.teleportLocation = Location.create(1886, 5025, 0) - } - fun cleanup(player: Player){ - player.properties.teleportLocation = player.getAttribute(SE_KEY_LOC,null) - clearLogoutListener(player, SE_LOGOUT_KEY) - player.removeAttribute(SE_KEY_LOC) - player.removeAttribute(SE_KEY_INDEX) - player.removeAttribute(SE_KEY_CORRECT) + returnPlayer(player) + removeAttributes(player, SE_KEY_INDEX, SE_KEY_CORRECT) player.pulseManager.run(object : Pulse(2){ override fun pulse(): Boolean { - val reward = Item(Items.BOOK_OF_KNOWLEDGE_11640) - if(!player.inventory.add(reward)){ - GroundItemManager.create(reward,player) - } + addItemOrDrop(player, Items.BOOK_OF_KNOWLEDGE_11640) return true } }, PulseType.CUSTOM_1) @@ -81,4 +60,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/FletchingBankstander.kt b/Server/src/main/content/global/bots/FletchingBankstander.kt index 201dd6f00..dd60a4e3c 100644 --- a/Server/src/main/content/global/bots/FletchingBankstander.kt +++ b/Server/src/main/content/global/bots/FletchingBankstander.kt @@ -1,12 +1,12 @@ package content.global.bots +import content.global.skill.fletching.log.LogCraftInfo +import content.global.skill.fletching.log.CraftItemWithLogScript +import core.game.bots.Script +import core.game.bots.SkillingBotAssembler import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching -import content.global.skill.fletching.FletchingPulse import core.game.node.item.Item import org.rs09.consts.Items -import core.game.bots.SkillingBotAssembler -import core.game.bots.Script class FletchingBankstander : Script(){ var state = State.FLETCHING @@ -17,7 +17,7 @@ class FletchingBankstander : Script(){ State.FLETCHING -> { bot.inventory.add(Item(Items.KNIFE_946)) bot.inventory.add(Item(Items.LOGS_1511,27)) - bot.pulseManager.run(FletchingPulse(bot, Item(Items.LOGS_1511),27, Fletching.FletchingItems.ARROW_SHAFT)) + CraftItemWithLogScript(bot, LogCraftInfo.ARROW_SHAFT, 27).invoke() State.BANKING } diff --git a/Server/src/main/content/global/bots/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/ManThiever.kt b/Server/src/main/content/global/bots/ManThiever.kt index 25a2638c2..75e0a6aac 100644 --- a/Server/src/main/content/global/bots/ManThiever.kt +++ b/Server/src/main/content/global/bots/ManThiever.kt @@ -5,6 +5,7 @@ import core.game.bots.SkillingBotAssembler import core.game.bots.Script import core.game.interaction.IntType import core.game.interaction.InteractionListeners +import core.game.node.entity.skill.Skills import java.util.* class ManThiever : Script() { @@ -22,6 +23,6 @@ class ManThiever : Script() { } init { - equipment.addAll(Arrays.asList(Item(1103), Item(1139), Item(1265))) + skills[Skills.THIEVING] = 20 } } \ 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..f8dcb56ab 100644 --- a/Server/src/main/content/global/bots/SeersFlax.kt +++ b/Server/src/main/content/global/bots/SeersFlax.kt @@ -1,31 +1,37 @@ package content.global.bots +import content.global.skill.crafting.spinning.SpinningItem +import content.global.skill.crafting.spinning.SpinningPulse +import core.game.bots.* import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.skill.Skills -import content.global.skill.crafting.spinning.SpinningItem -import content.global.skill.crafting.spinning.SpinningPulse import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.path.Pathfinder import org.rs09.consts.Items -import core.game.bots.SkillingBotAssembler -import core.game.bots.Script +@PlayerCompatible +@ScriptName("Seers' Village Flax Spinning") +@ScriptDescription("Pick flax and spin it into string. Start in the flax field near Seers' Village.") +@ScriptIdentifier("seers_flax") class SeersFlax : Script(){ + val waypoint1 = Location(2730, 3460, 0) // South of Seers' Village + val waypoint2 = Location(2737, 3441, 0) // Outside flax field var state = State.PICKING var stage = 0 var doorOpen = false override fun tick() { when(state){ - State.PICKING -> { - val flax = scriptAPI.getNearestNode(2646,true) - flax?.interaction?.handle(bot,flax.interaction[1]) - if(bot.inventory.getAmount(Items.FLAX_1779) > 25){ + if (bot.inventory.freeSlots() == 0) { state = State.TO_SPINNER + return } + + val flax = scriptAPI.getNearestNode("Flax", true) + scriptAPI.interact(bot, flax, "Pick") } State.TO_SPINNER -> { @@ -65,8 +71,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) -> { @@ -101,14 +107,13 @@ class SeersFlax : Script(){ } State.RETURN_TO_FLAX -> { - if(bot.location == Location.create(2756, 3478, 0)) - Pathfinder.find(bot,Location.create(2726, 3486, 0)).walk(bot) - if(stage == 0) - Pathfinder.find(bot,Location.create(2726, 3486, 0)).walk(bot).also { stage++ } - when(bot.location){ - Location.create(2726, 3486, 0) -> Pathfinder.find(bot,Location.create(2729, 3469, 0)).walk(bot) - Location.create(2729, 3469, 0) -> Pathfinder.find(bot,Location.create(2734, 3447, 0)).walk(bot) - Location.create(2734, 3447, 0) -> state = State.PICKING.also { stage = 0 } + val flax = scriptAPI.getNearestNode("Flax", true) + if (flax != null) { + state = State.PICKING + } else if (bot.location.withinMaxnormDistance(waypoint1, 5)) { + Pathfinder.find(bot, waypoint2).walk(bot) + } else { + Pathfinder.find(bot, waypoint1).walk(bot) } } @@ -124,7 +129,6 @@ class SeersFlax : Script(){ State.TELE_CAMELOT -> { scriptAPI.teleport(Location.create(2756, 3478, 0)) - stage = 0 state = State.RETURN_TO_FLAX } diff --git a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt index 3dafd687f..38a9e6b5a 100644 --- a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt +++ b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt @@ -1,13 +1,16 @@ package content.global.bots +import content.data.Quests import core.game.bots.* import core.game.interaction.DestinationFlag +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners import core.game.interaction.MovementPulse +import core.game.node.entity.skill.Skills +import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -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,11 +21,17 @@ 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){ State.TO_ESSENCE -> { bot.interfaceManager.close() + if (bot.bank.getAmount(Items.PURE_ESSENCE_7936) > 500) { + state = State.TELE_GE + return + } + if(!auburyZone.insideBorder(bot)) scriptAPI.walkTo(auburyZone.randomLoc) else { @@ -49,10 +58,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 -> { @@ -95,7 +107,6 @@ class VarrockEssenceMiner : Script(){ scriptAPI.sellOnGE(Items.PURE_ESSENCE_7936) state = State.TO_ESSENCE } - } } @@ -114,4 +125,10 @@ class VarrockEssenceMiner : Script(){ script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) return script } -} \ No newline at end of file + + init { + quests.add(Quests.RUNE_MYSTERIES) + inventory.add(Item(Items.ADAMANT_PICKAXE_1271)) + skills[Skills.MINING] = 31 + } +} diff --git a/Server/src/main/content/global/dialogue/BankerDialogue.kt b/Server/src/main/content/global/dialogue/BankerDialogue.kt deleted file mode 100644 index e913ce4f0..000000000 --- a/Server/src/main/content/global/dialogue/BankerDialogue.kt +++ /dev/null @@ -1,181 +0,0 @@ -package content.global.dialogue - -import core.api.* -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import content.global.handlers.npc.BankerNPC -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -@Initializable -class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> when { - hasIronmanRestriction(player, IronmanMode.ULTIMATE) -> { - npcl( - core.game.dialogue.FacialExpression.ANNOYED, - "My apologies, dear ${if (player.isMale) "sir" else "madam"}, " + - "our services are not available for Ultimate ${if (player.isMale) "Ironmen" else "Ironwomen"}" - ).also { stage = END_DIALOGUE } - } - - else -> { - npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Good day, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - } - - 1 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Before we go any further, I should inform you that you " + - "have items ready for collection from the Grand Exchange." - ).also { stage++ } - - 2 -> showTopics( - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to access my bank account, please.", 10), - IfTopic( - core.game.dialogue.FacialExpression.FRIENDLY, - "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", - 13, - hasActivatedSecondaryBankAccount(player) - ), - IfTopic( - core.game.dialogue.FacialExpression.FRIENDLY, - "I'd like to open a secondary bank account.", - 20, - !hasActivatedSecondaryBankAccount(player) - ), - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to check my PIN settings.", 11), - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to collect items.", 12), - Topic(core.game.dialogue.FacialExpression.ASKING, "What is this place?", 3), - ) - - 3 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "This is a branch of the Bank of Gielinor. We have branches in many towns." - ).also { stage++ } - - 4 -> playerl( - core.game.dialogue.FacialExpression.ASKING, - "And what do you do?" - ).also { stage++ } - - 5 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "We will look after your items and money for you. " + - "Leave your valuables with us if you want to keep them safe." - ).also { stage = END_DIALOGUE } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - openBankPinSettings(player) - end() - } - - 12 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 13 -> { - toggleBankAccount(player) - - npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Your active bank account has been switched. " + - "You can now access your ${getBankAccountName(player)} account." - ).also { stage = 2 } - } - - 20 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Certainly. We offer secondary accounts to all our customers." - ).also { stage++ } - - 21 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "The secondary account comes with a standard fee of 5,000,000 coins. The fee is non-refundable " + - "and account activation is permanent." - ).also { stage++ } - - 22 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "If your inventory does not contain enough money to cover the costs, we will complement " + - "the amount with the money inside your primary bank account." - ).also { stage++ } - - 23 -> npcl( - core.game.dialogue.FacialExpression.ASKING, - "Knowing all this, would you like to proceed with opening your secondary bank account?" - ).also { stage++ } - - 24 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, I am still interested.", 25), - Topic(core.game.dialogue.FacialExpression.ANNOYED, "Actually, I've changed my mind.", 26) - ) - - 25 -> { - when (activateSecondaryBankAccount(player)) { - SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> { - npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Your bank account was already activated, there is no need to pay twice." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.INTERNAL_FAILURE -> { - npcl( - core.game.dialogue.FacialExpression.ANNOYED, - "I must apologize, the transaction was not successful. Please check your " + - "primary bank account and your inventory - if there's money missing, please " + - "screenshot your chat box and contact the game developers." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> { - npcl( - core.game.dialogue.FacialExpression.ANNOYED, - "It appears that you do not have the money necessary to cover the costs " + - "associated with opening a secondary bank account. I will be waiting here " + - "until you do." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.SUCCESS -> { - npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Your secondary bank account has been opened and can be accessed through any " + - "of the Bank of Gielinor's employees. Thank you for choosing our services." - ).also { stage = END_DIALOGUE } - } - } - } - - 26 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Very well. Should you decide a secondary bank account is needed, do not hesitate to " + - "contact any of the Bank of Gielinor's stationary employees. We will be happy to help." - ).also { stage = END_DIALOGUE } - } - - return true - } - - override fun getIds(): IntArray = BankerNPC.NPC_IDS -} diff --git a/Server/src/main/content/global/dialogue/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..f330bda86 100644 --- a/Server/src/main/content/global/dialogue/TownCrierDialogue.java +++ b/Server/src/main/content/global/dialogue/TownCrierDialogue.java @@ -1,6 +1,8 @@ package content.global.dialogue; +import content.global.handlers.item.book.GeneralRuleBook; import core.game.dialogue.DialoguePlugin; +import core.game.diary.DiaryLevel; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; @@ -9,6 +11,10 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.NPCs; + +import static content.region.misthalin.lumbridge.diary.LumbridgeAchivementDiary.Companion.BeginnerTasks.DRAYNOR_TALK_TO_TOWNCRIER_ABOUT_RULES; +import static core.api.ContentAPIKt.finishTask; /** * Represents the town crier dialogue plugin. @@ -122,13 +128,12 @@ 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; } }); - // Find out about the Rules of Conduct from the Draynor

Town Crier - if (npc.getId() == 6136) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 10); + if (npc.getId() == NPCs.TOWN_CRIER_6136) { + finishTask(player, DiaryType.LUMBRIDGE, DiaryLevel.BEGINNER, DRAYNOR_TALK_TO_TOWNCRIER_ABOUT_RULES); } stage = 71; break; diff --git a/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java index a010c7bb5..9213cf0f6 100644 --- a/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java +++ b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java @@ -30,6 +30,17 @@ public final class AutocastSelectPlugin extends ComponentPlugin { if (!player.getAttribute("autocast_select", false)) { return true; } + if (player.timers.getTimer("spellbook:swap") != null) { + player.getPacketDispatch().sendMessage("You cannot autocast while using Spellbook Swap."); + player.removeAttribute("autocast_select"); + player.removeAttribute("autocast_component"); + final WeaponInterface w = player.getExtension(WeaponInterface.class); + if (w != null) { + w.setAttackStyle(3); + player.getInterfaceManager().openTab(w); + } + return true; + } player.removeAttribute("autocast_select"); final WeaponInterface w = player.getExtension(WeaponInterface.class); if (w != null) { 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..508b8b16d --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/BookInterface.kt @@ -0,0 +1,247 @@ +package content.global.handlers.iface + +import content.global.handlers.iface.BookInterface.Companion.FANCY_BOOK_2_27 +import core.api.closeInterface +import core.api.getAttribute +import core.api.openInterface +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) + } + } + + /** Sets item on lineId of pageSet (0 index). Call this in the display function after pageSetup. */ + fun setItemOnPage(player: Player, pageSet: Int, itemId: Int, componentId: Int, enableLineId: Int, drawLineId: Int, zoom: Int, pitch: Int, yaw: Int) { + if (pageSet == getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0)) { + player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, false) + player.packetDispatch.sendItemOnInterface(itemId, 1, componentId, drawLineId) + } + } + + /** Clears models(pictures) on lineId of pageSet (0 index). Call this in the display function after pageSetup. */ + fun clearModelsOnPage(player: Player, componentId: Int) { + BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS.forEach { drawId -> + player.packetDispatch.sendInterfaceConfig(componentId, drawId, true); + } + } + + /** 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 deleted file mode 100644 index 1ad3857e8..000000000 --- a/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java +++ /dev/null @@ -1,155 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.game.system.communication.ClanRank; -import core.game.system.communication.ClanRepository; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.StringUtils; -import kotlin.Unit; - -import static core.api.ContentAPIKt.sendInputDialogue; -import static core.api.ContentAPIKt.setInterfaceText; - -/** - * Represents the plugin used to handle the clan interfaces. - * @author Vexia - */ -@Initializable -public final class ClanInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(590, this); - ComponentDefinition.put(589, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 589: - switch (button) { - case 9: - if (player.getInterfaceManager().getComponent(590) != null) { - player.getPacketDispatch().sendMessage("Please close the interface you have open before using 'Clan Setup'"); - return true; - } - ClanRepository.openSettings(player); - return true; - case 14: - player.getDetails().getCommunication().toggleLootshare(player); - return true; - } - break; - case 590: - final ClanRepository clan = ClanRepository.get(player.getName(), true); - switch (button) { - case 23: - if (opcode == 155) { - clan.setJoinRequirement(ClanRank.NONE); - } else { - clan.setJoinRequirement(getRank(opcode)); - } - player.getDetails().getCommunication().setJoinRequirement(clan.getJoinRequirement()); - MSPacketRepository.setClanSetting(player, 0, clan.getJoinRequirement()); - player.getPacketDispatch().sendString(clan.getJoinRequirement().getInfo(), 590, 23); - break; - case 24: - clan.setMessageRequirement(getRank(opcode)); - player.getDetails().getCommunication().setMessageRequirement(clan.getMessageRequirement()); - MSPacketRepository.setClanSetting(player, 1, clan.getMessageRequirement()); - player.getPacketDispatch().sendString(clan.getMessageRequirement().getInfo(), 590, 24); - break; - case 25: - clan.setKickRequirement(getRank(opcode)); - player.getDetails().getCommunication().setKickRequirement(clan.getKickRequirement()); - MSPacketRepository.setClanSetting(player, 2, clan.getKickRequirement()); - player.getPacketDispatch().sendString(clan.getKickRequirement().getInfo(), 590, 25); - clan.update(); - break; - case 26: - if (opcode == 230) { - clan.setLootRequirement(ClanRank.ADMINISTRATOR); - } else { - clan.setLootRequirement(getRank(opcode)); - } - player.getDetails().getCommunication().setLootRequirement(clan.getLootRequirement()); - MSPacketRepository.setClanSetting(player, 3, clan.getLootRequirement()); - player.getPacketDispatch().sendString(clan.getLootRequirement().getInfo(), 590, 26); - break; - case 22: - switch (opcode) { - case 196: - clan.setName("Chat disabled"); - player.getCommunication().setClanName(""); - player.getPacketDispatch().sendString(clan.getName(), 590, 22); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendClanRename(player, ""); - break; - } - clan.clean(true); - break; - default: - sendInputDialogue(player, false, "Enter clan prefix:", (value) -> { - String name = StringUtils.formatDisplayName((String) value); - setInterfaceText(player, name, 590, 22); - if(WorldCommunicator.isEnabled()){ - MSPacketRepository.sendClanRename(player, name); - clan.setName(name); - return Unit.INSTANCE; - } - if (clan.getName().equals("Chat disabled")) { - player.getPacketDispatch().sendMessage("Your clan channel has now been enabled!"); - player.getPacketDispatch().sendMessage("Join your channel by clicking 'Join Chat' and typing: " + player.getUsername()); - } - clan.setName(name); - player.getCommunication().setClanName(name); - clan.update(); - return Unit.INSTANCE; - }); - break; - } - break; - } - break; - } - return true; - } - - /** - * Gets the value to set. - * @param opcode the opcode. - * @return the value. - */ - public static ClanRank getRank(int opcode) { - switch (opcode) { - case 155: - return ClanRank.NONE; - case 196: - return ClanRank.FRIEND; - case 124: - return ClanRank.RECRUIT; - case 199: - return ClanRank.CORPORAL; - case 234: - return ClanRank.SERGEANT; - case 168: - return ClanRank.LIEUTENANT; - case 166: - return ClanRank.CAPTAIN; - case 64: - return ClanRank.GENERAL; - case 53: - return ClanRank.OWNER; - } - return ClanRank.NONE; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java b/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java deleted file mode 100644 index 852eb4997..000000000 --- a/Server/src/main/content/global/handlers/iface/CrystalKeyChestPlugin.java +++ /dev/null @@ -1,50 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class CrystalKeyChestPlugin extends ComponentPlugin { - - private final Integer CHEST_INTERFACE = 501; - - private Player player; - - public CrystalKeyChestPlugin() { - /* - * Empty - */ - } - - public CrystalKeyChestPlugin(Player player) { - this.player = player; - } - - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(CHEST_INTERFACE, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - return false; - } - - public void constructInterface(Player player) { - Integer[] hiddenChildren = new Integer[] { 3, 4, 5, 6, 7}; - Component component = new Component(CHEST_INTERFACE); - for (int i = 3; i < hiddenChildren.length; i++) { - player.getPacketDispatch().sendInterfaceConfig(CHEST_INTERFACE, i, true); - } - player.getPacketDispatch().sendItemOnInterface(989, 1, CHEST_INTERFACE, hiddenChildren[2]); - player.getInterfaceManager().open(component); - this.player = player; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/DeathInterface.kt b/Server/src/main/content/global/handlers/iface/DeathInterface.kt new file mode 100644 index 000000000..3b0dae31f --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/DeathInterface.kt @@ -0,0 +1,17 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components + +class DeathInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.AIDE_DEATH_153) { player, _, _, buttonID, _, _ -> + if (buttonID == 1) { + player.savedData.globalData.setDisableDeathScreen(true) + closeInterface(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java deleted file mode 100644 index 5b57c3078..000000000 --- a/Server/src/main/content/global/handlers/iface/DeathInterfacePlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the death interface. - * @author Vexia - */ -@Initializable -public final class DeathInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(153).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (button == 1) { - player.getSavedData().getGlobalData().setDisableDeathScreen(true); - player.getInterfaceManager().close(); - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/EllisDialogue.java b/Server/src/main/content/global/handlers/iface/EllisDialogue.java deleted file mode 100644 index 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/EmoteTabInterface.java b/Server/src/main/content/global/handlers/iface/EmoteTabInterface.java deleted file mode 100644 index 7506f5fcb..000000000 --- a/Server/src/main/content/global/handlers/iface/EmoteTabInterface.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.emote.Emotes; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the emote tab interface. - * @author Vexia - * - */ -@Initializable -public final class EmoteTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(464, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - Emotes.handle(player, button); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/EquipmentInterface.java b/Server/src/main/content/global/handlers/iface/EquipmentInterface.java deleted file mode 100644 index 3461a0236..000000000 --- a/Server/src/main/content/global/handlers/iface/EquipmentInterface.java +++ /dev/null @@ -1,201 +0,0 @@ -package content.global.handlers.iface; - -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.container.Container; -import core.game.container.ContainerEvent; -import core.game.container.ContainerListener; -import core.game.container.access.InterfaceContainer; -import core.game.container.impl.EquipmentContainer; -import core.game.interaction.OptionHandler; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.global.action.EquipHandler; -import core.game.interaction.IntType; -import core.game.interaction.InteractionListeners; -import core.game.world.GameWorld; - -/** - * Represents the equipment interface. - * @author Emperor - * - */ -@Initializable -public final class EquipmentInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(387, this); - ComponentDefinition.put(667, this); - ComponentDefinition.put(670, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, final int itemId) { - if (component.getId() == 667) { - if (button != 14) { - return false; - } - switch (opcode) { - case 155: - p.getPulseManager().clear(); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - EquipHandler.unequip(p, slot, itemId); - return true; - } - }); - return true; - case 9: - p.sendMessage(p.getEquipment().get(slot).getDefinition().getExamine()); - return true; - case 196: - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - operate(p, slot, itemId); - return true; - } - }); - return true; - } - return false; - } - else if (component.getId() == 670) { - switch (opcode) { - case 155: - p.getPulseManager().clear(); - final Item item = p.getInventory().get(slot); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - if (item == null) return true; - InteractionListeners.run(item.getId(), IntType.ITEM,"equip",p, item); - return true; - } - }); - return true; - case 9: - p.sendMessage(p.getInventory().get(slot).getDefinition().getExamine()); - return true; - } - } - switch (opcode) { - case 206: - if (button != 28) { - return false; - } - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - operate(p, slot, itemId); - return true; - } - }); - return true; - default: - switch (button) { - case 52: - if (p.getInterfaceManager().isOpened() && p.getInterfaceManager().getOpened().getId() == 102) { - return true; - } - 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); - 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); - break; - case 28: - if (opcode == 81) { - p.getPulseManager().clear(); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - EquipHandler.unequip(p, slot, itemId); - return true; - } - }); - return true; - } - break; - case 55: - if (p.getInterfaceManager().isOpened() && p.getInterfaceManager().getOpened().getId() == 667) { - return true; - } - final ContainerListener listener = new ContainerListener() { - @Override - public void update(Container c, ContainerEvent e) { - PacketRepository.send(ContainerPacket.class, new ContainerContext(p, -1, -1, 98, e.getItems(), false, e.getSlots())); - } - - @Override - public void refresh(Container c) { - PacketRepository.send(ContainerPacket.class, new ContainerContext(p, -1, -1, 98, c, false)); - } - }; - p.getInterfaceManager().openComponent(667).setCloseEvent((player, c) -> { - player.removeAttribute("equip_stats_open"); - player.getInterfaceManager().closeSingleTab(); - player.getInventory().getListeners().remove(listener); - return true; - }); - p.setAttribute("equip_stats_open", true); - EquipmentContainer.update(p); - p.getInterfaceManager().openSingleTab(new Component(670)); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[] { "Equip" }, 670, 0, 7, 4, 93); - p.getInventory().getListeners().add(listener); - p.getInventory().refresh(); - ItemDefinition.statsUpdate(p); - p.getPacketDispatch().sendIfaceSettings(1278, 14, 667, 0, 13); - break; - } - } - return true; - } - - /** - * Operates an item. - * @param player The player. - * @param slot The container slot. - * @param itemId The item id. - */ - public void operate(Player player, int slot, int itemId) { - if (slot < 0 || slot > 13) { - return; - } - Item item = player.getEquipment().get(slot); - if (item == null) { - return; - } - if(InteractionListeners.run(item.getId(),IntType.ITEM,"operate",player,item)){ - return; - } - OptionHandler handler = item.getOperateHandler(); - if (handler != null && handler.handle(player, item, "operate")) { - return; - } - player.getPacketDispatch().sendMessage("You can't operate that."); - } - -} diff --git a/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt index 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..69bba13d7 100644 --- a/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt @@ -2,110 +2,128 @@ package content.global.handlers.iface import core.api.* import core.game.event.FairyRingDialEvent -import core.game.component.Component +import core.game.interaction.InterfaceListener import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager import core.game.system.task.Pulse +import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.RegionManager import core.tools.RandomFunction -import core.game.interaction.InterfaceListener -import core.game.world.GameWorld - -val RING_1 = arrayOf('a','d','c','b') -val RING_2 = arrayOf('i','l','k','j') -val RING_3 = arrayOf('p','s','r','q') +import content.data.Quests /** * Handles the fairy ring interface * @author Ceikry */ class FairyRingInterface : InterfaceListener { - - val RINGS = 734 - val TRAVEL_LOG = 735 + companion object { + const val RINGS_IFACE = 734 + const val LOG_IFACE_ID = 735 + const val VARP_F_RING = 816 + const val VB_LOG_SORT_ORDER = 4618 + const val VB_RING_1 = 2341 + const val VB_RING_2 = 2342 + const val VB_RING_3 = 2343 + val RING_1 = arrayOf('a','d','c','b') + val RING_2 = arrayOf('i','l','k','j') + val RING_3 = arrayOf('p','s','r','q') + } override fun defineInterfaceListeners() { - onOpen(RINGS){player, _ -> - player.interfaceManager.openSingleTab(Component(TRAVEL_LOG)) - player.setAttribute("fr:ring1", 0) - player.setAttribute("fr:ring2", 0) - player.setAttribute("fr:ring3", 0) - FairyRing.drawLog(player) + onOpen(RINGS_IFACE){ player, _ -> + openSingleTab(player, LOG_IFACE_ID) + saveVarp(player, VARP_F_RING) return@onOpen true } - onClose(RINGS){player, _ -> - closeTabInterface(player) - player.removeAttribute("fr:ring1") - player.removeAttribute("fr:ring2") - player.removeAttribute("fr:ring3") - 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) { + 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 +179,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int AKS(Location.create(2571, 2956, 0), "Feldip Hills: Jungle Hunter area", 25), ALQ(Location.create(3597, 3495, 0), "Morytania: Haunted Woods east of Canifis", 27) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, ALS(Location.create(2644, 3495, 0), "Kandarin: McGrubor's Wood", 29), @@ -173,7 +191,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int BKQ(Location.create(3041, 4532, 0), "Other realms: Enchanted Valley", 39), BKR(Location.create(3469, 3431, 0), "Morytania: Mort Myre, south of Canifis", 40) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, BLP(Location.create(2437, 5126, 0), "Dungeons: TzHaar area", 42), @@ -181,7 +199,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int BLR(Location.create(2740, 3351, 0), "Kandarin: Legends' Guild", 44), CIP(Location.create(2513, 3884, 0), "Islands: Miscellania", 46) { override fun checkAccess(player: Player): Boolean { - return requireQuest(player, "Fremennik Trials", "to use this ring.") + return requireQuest(player, Quests.THE_FREMENNIK_TRIALS, "to use this ring.") } }, CIQ(Location.create(2528, 3127, 0), "Kandarin: North-west of Yanille", 47), @@ -190,7 +208,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int CKR(Location.create(2801, 3003, 0), "Karamja: South of Tai Bwo Wannai Village", 56), CKS(Location.create(3447, 3470, 0), "Morytania: Canifis", 57) { override fun checkAccess(player: Player) : Boolean { - return requireQuest(player, "Priest in Peril", "to use this ring.") + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.") } }, CLP(Location.create(3082, 3206, 0), "Islands: South of Draynor Village", 58), @@ -209,23 +227,4 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int open fun checkAccess(player: Player) : Boolean { return true } - - companion object { - /** - * Draws the travel log. - * @param player the player. - */ - fun drawLog(player: Player) { - for (i in FairyRing.values().indices) { - if (!player.savedData.globalData.hasTravelLog(i)) { - continue - } - val ring = FairyRing.values()[i] - if (ring.childId == -1) { - continue - } - setInterfaceText(player, "
${ring.tip}", 735, ring.childId) - } - } - } } diff --git a/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt index 1020b6247..a335a5fbb 100644 --- a/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt @@ -6,6 +6,7 @@ import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin import core.game.container.access.InterfaceContainer import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin @@ -162,9 +163,14 @@ class FurClothingInterface : ComponentPlugin(){ return } - removeItem(player, requiredFur, Container.INVENTORY) - removeItem(player, coins, Container.INVENTORY) - addItem(player, clothing.product.id, amount) + if (removeItem(player, requiredFur, Container.INVENTORY) && + removeItem(player, coins, Container.INVENTORY)) { + addItem(player, clothing.product.id, amount) + + if (clothing == FUR_CLOTHING.DASH_CAPE) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 2) + } + } } override fun newInstance(arg: Any?): Plugin { @@ -192,8 +198,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/GameInterface.java b/Server/src/main/content/global/handlers/iface/GameInterface.java deleted file mode 100644 index 0c8754f29..000000000 --- a/Server/src/main/content/global/handlers/iface/GameInterface.java +++ /dev/null @@ -1,185 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.combat.equipment.WeaponInterface.WeaponInterfaces; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Represents the component plugin used for the game interface. - * @author Vexia - * - */ -@Initializable -public final class GameInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(740, this); - return this; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 740: - switch (button){ - case 3: - player.getInterfaceManager().closeChatbox(); - break; - } - return true; - case 746: - switch (button){ - case 12: - player.getPacketDispatch().sendString("When you have finished playing " + GameWorld.getSettings().getName() + ", always use the button below to logout safely. ", 182, 0); - break; - case 49: - player.getPacketDispatch().sendString("Friends List - " + GameWorld.getSettings().getName() + " " + GameWorld.getSettings().getWorldId(), 550, 3); - break; - case 110: - configureWorldMap(player); - break; - } - return true; - case 548: - if (button >= 38 && button<= 44 || button >= 20 && button <= 26) { - player.getInterfaceManager().setCurrentTabIndex(getTabIndex(button)); - } - //Interface buttons that advance the Tutorial Island stages - switch (button) { - case 21://Friends Tab - player.getPacketDispatch().sendString("Friends List -" + GameWorld.getSettings().getName() + " " + GameWorld.getSettings().getWorldId(), 550, 3); - break; - case 22://Ignore Tab - break; - case 24://Settings Tab - break; - case 25://Emotes Tab - break; - case 26://Music Tab - break; - case 38://Attack Tab - if (player.getExtension(WeaponInterface.class) == WeaponInterfaces.STAFF) { - final Component c = new Component(WeaponInterfaces.STAFF.getInterfaceId()); - player.getInterfaceManager().openTab(0, c); - final WeaponInterface inter = player.getExtension(WeaponInterface.class); - inter.updateInterface(); - } - break; - case 39://Skill Tab - break; - case 40://Quest Tab - /*if (GameWorld.isEconomyWorld()) { - player.getQuestRepository().syncronizeTab(player); - } else { - player.getSavedData().getSpawnData().drawStatsTab(player); - }*/ - - player.getQuestRepository().syncronizeTab(player); - break; - case 41://Inventory Tab - player.getInventory().refresh(); - break; - case 42://Worn Equipment Tab - break; - case 43://Prayer Tab - break; - case 44://Magic Tab - break; - case 66://World map - case 110: - configureWorldMap(player); - break; - case 69://Logout - player.getPacketDispatch().sendString("When you have finished playing " + GameWorld.getSettings().getName() + ", always use the button below to logout safely. ", 182, 0); - break; - } - return true; - case 750: - switch (opcode) { - case 155: - switch (button) { - case 1: - player.getSettings().toggleRun(); - break; - } - break; - } - return true; - case 751: - switch (opcode) { - case 155: - switch (button) { - case 27: - openReport(player); - break; - } - break; - } - return true; - } - return true; - } - - /** - * World map - * Thanks, Snickerize! - */ - private void configureWorldMap(Player player) { - if (player.inCombat()) { - player.getPacketDispatch().sendMessage("It wouldn't be very wise opening the world map during combat."); - return; - } - if(player.getLocks().isInteractionLocked() || player.getLocks().isMovementLocked()){ - player.getPacketDispatch().sendMessage("You can't do this right now."); - return; - } - player.getInterfaceManager().openWindowsPane(new Component(755)); - int posHash = (player.getLocation().getZ() << 28) | (player.getLocation().getX() << 14) | player.getLocation().getY(); - player.getPacketDispatch().sendScriptConfigs(622, posHash, "", 0); - player.getPacketDispatch().sendScriptConfigs(674, posHash, "", 0); - } - - /** - * Method used to open the report interface. - * @param player the player. - */ - public static void openReport(final Player player) { - player.getInterfaceManager().open(new Component(553)).setCloseEvent((player1, c) -> { - player1.getPacketDispatch().sendRunScript(80, ""); - player1.getPacketDispatch().sendRunScript(137, ""); - return true; - }); - player.getPacketDispatch().sendRunScript(508, ""); - if (player.getDetails().getRights() != Rights.REGULAR_PLAYER) { - for (int i = 0; i < 18; i++) { - player.getPacketDispatch().sendInterfaceConfig(553, i, false); - } - } - } - - /** - * Gets the tab index. - * @param button The button id. - * @return The tab index. - */ - private static int getTabIndex(int button) { - int tabIndex = button - 38; - if (button < 27) { - tabIndex = (button - 20) + 7; - } - return tabIndex; - } - - /** - * Configures the world map for a player. - * @param player The player. - */ -} diff --git a/Server/src/main/content/global/handlers/iface/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/GliderInterface.java b/Server/src/main/content/global/handlers/iface/GliderInterface.java deleted file mode 100644 index 08459cefe..000000000 --- a/Server/src/main/content/global/handlers/iface/GliderInterface.java +++ /dev/null @@ -1,37 +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 content.global.travel.glider.GliderPulse; -import content.global.travel.glider.Gliders; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the glider interface component. - * @author Emperor - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GliderInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(138, this); - return this; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - final Gliders glider = Gliders.forId(button); - if (glider == null) { - return true; - } - submitWorldPulse(new GliderPulse(1, player, glider)); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/GliderInterface.kt b/Server/src/main/content/global/handlers/iface/GliderInterface.kt new file mode 100644 index 000000000..cc43ede4a --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/GliderInterface.kt @@ -0,0 +1,17 @@ +package content.global.handlers.iface + +import core.api.submitWorldPulse +import content.global.travel.glider.GliderPulse +import content.global.travel.glider.Gliders +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components + +class GliderInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.GLIDERMAP_138) { player, _, _, button, _, _ -> + val glider = Gliders.forId(button) ?: return@on true + submitWorldPulse(GliderPulse(1, player, glider)) + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt index 1d7eca270..d9058006e 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,_ -> @@ -152,11 +152,11 @@ class HairDresserInterface : ComponentPlugin(){ player.setAttribute("beard-setting",false) if(!pl.getAttribute("hairdresser-paid",false)){ val original_hair = player.getAttribute("original-hair",0) - val original_beard = player.getAttribute("original_beard",-1) - val original_color = player.getAttribute("original_color",0) + val original_beard = player.getAttribute("original-beard",-1) + val original_color = player.getAttribute("original-color",0) pl.appearance.hair.changeLook(original_hair) pl.appearance.hair.changeColor(original_color) - if(original_beard != -1) { + if (original_beard != -1) { pl.appearance.beard.changeLook(original_beard) } pl.appearance.sync() @@ -174,7 +174,7 @@ class HairDresserInterface : ComponentPlugin(){ when(button){ 199 -> player.setAttribute("beard-setting",false) 200 -> player.setAttribute("beard-setting",true) - 68,196,274 -> pay(player) + 196,274,68,100 -> pay(player) else -> when(component?.id){ 592 -> { //Female if(femaleColorButtonRange.contains(button)){ diff --git a/Server/src/main/content/global/handlers/iface/LoginInterface.kt b/Server/src/main/content/global/handlers/iface/LoginInterface.kt new file mode 100644 index 000000000..e2464c176 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/LoginInterface.kt @@ -0,0 +1,34 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.api.runTask +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.info.login.LoginConfiguration +import org.rs09.consts.Components + +class LoginInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.WELCOME_SCREEN_378) { player, _, _, buttonID, _, _ -> + val playButton = 140 + val creditButton = 145 + val discordButton = 204 + + when (buttonID) { + playButton -> { + player.locks.lock("login", 2) + closeInterface(player) + runTask(player, 1, 0) { + LoginConfiguration.configureGameWorld(player) + } + } + creditButton -> return@on true + discordButton -> return@on true + } + return@on true + } + + onClose(Components.WELCOME_SCREEN_378) { player, _ -> + return@onClose player.locks.isLocked("login") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java deleted file mode 100644 index f91a3000e..000000000 --- a/Server/src/main/content/global/handlers/iface/LoginInterfacePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.login.LoginConfiguration; -import core.game.system.task.Pulse; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for the login interface. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LoginInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(378, this); - return null; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 140: - if (player.getLocks().isLocked("login")) { - return true; - } - player.getLocks().lock("login", 2); - player.getInterfaceManager().close(); - player.getPulseManager().run(new Pulse(1) { - @Override - public boolean pulse() { - LoginConfiguration.configureGameWorld(player); - return true; - } - }); - break; - case 145://credits - break; - case 204://message centre - break; - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/LogoutInterface.java b/Server/src/main/content/global/handlers/iface/LogoutInterface.java deleted file mode 100644 index ba1784c8f..000000000 --- a/Server/src/main/content/global/handlers/iface/LogoutInterface.java +++ /dev/null @@ -1,40 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the interface used to logout of the game. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LogoutInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(182, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (!player.getZoneMonitor().canLogout()) { - return true; - } - if (player.inCombat()) { - player.getPacketDispatch().sendMessage("You can't log out until 10 seconds after the end of combat."); - return true; - } - if (player.getAttribute("logoutDelay", 0) < GameWorld.getTicks()) { - player.getPacketDispatch().sendLogout(); - player.setAttribute("logoutDelay", GameWorld.getTicks() + 3); - } - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/MagicBookInterface.java b/Server/src/main/content/global/handlers/iface/MagicBookInterface.java deleted file mode 100644 index e29e7f114..000000000 --- a/Server/src/main/content/global/handlers/iface/MagicBookInterface.java +++ /dev/null @@ -1,51 +0,0 @@ -package content.global.handlers.iface; - -import content.global.skill.magic.SpellListener; -import content.global.skill.magic.SpellListeners; -import content.global.skill.magic.SpellUtils; -import core.game.event.SpellCastEvent; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Represents the magic book interface handling of non-combat spells. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MagicBookInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(192, this); - ComponentDefinition.put(193, this); - ComponentDefinition.put(430, this); - return this; - } - - @Override - public boolean handle(final Player player, Component component, int opcode, int button, int slot, int itemId) { - if (GameWorld.getTicks() < player.getAttribute("magic:delay", -1)) { - return true; - } - - SpellBook spellBook = component.getId() == 192 - ? SpellBook.MODERN - : component.getId() == 193 - ? SpellBook.ANCIENT - : SpellBook.LUNAR; - - SpellListeners.run(button, SpellListener.NONE, SpellUtils.getBookFromInterface(component.getId()),player,null); - boolean result = MagicSpell.castSpell(player, spellBook, button, player); - player.dispatch(new SpellCastEvent(spellBook, button)); - - return result; - } -} diff --git a/Server/src/main/content/global/handlers/iface/MainGameInterface.kt b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt index 243337f4e..e7f4edb05 100644 --- a/Server/src/main/content/global/handlers/iface/MainGameInterface.kt +++ b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt @@ -31,32 +31,13 @@ class MainGameInterface : InterfaceListener { } on(TOPLEVEL_FS){player, _, _, buttonID, _, _ -> - when (buttonID) { - 12 -> setInterfaceText(player, - "When you have finished playing " + settings!!.name + ", always use the button below to logout safely. ", - 182, - 0 - ) - 49 -> setInterfaceText(player, - "Friends List - " + settings!!.name + " " + settings!!.worldId, - 550, - 3 - ) - 110 -> configureWorldMap(player) - } + if (buttonID == 110) + configureWorldMap(player) return@on true } - on(TOPLEVEL){player, _, _, buttonID, _, _ -> when (buttonID) { - 21 -> { - player.packetDispatch.sendString( - "Friends List -" + settings!!.name + " " + settings!!.worldId, - 550, - 3 - ) - } 38 -> { if (player.getExtension(WeaponInterface::class.java) === WeaponInterfaces.STAFF) { val c = Component(WeaponInterfaces.STAFF.interfaceId) @@ -67,12 +48,7 @@ class MainGameInterface : InterfaceListener { } 40 -> player.questRepository.syncronizeTab(player) 41 -> player.inventory.refresh() - 66, 110 -> configureWorldMap(player) - 69 -> player.packetDispatch.sendString( - "When you have finished playing " + settings!!.name + ", always use the button below to logout safely. ", - 182, - 0 - ) + 66 -> configureWorldMap(player) } return@on true } @@ -95,11 +71,11 @@ class MainGameInterface : InterfaceListener { private fun configureWorldMap(player: Player) { if (player.inCombat()) { - player.packetDispatch.sendMessage("It wouldn't be very wise opening the world map during combat.") + sendMessage(player, "It wouldn't be very wise opening the world map during combat.") return } if (player.locks.isInteractionLocked || player.locks.isMovementLocked) { - player.packetDispatch.sendMessage("You can't do this right now.") + sendMessage(player, "You can't do this right now.") return } player.interfaceManager.close() diff --git a/Server/src/main/content/global/handlers/iface/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/MusicTabInterface.java b/Server/src/main/content/global/handlers/iface/MusicTabInterface.java deleted file mode 100644 index 579607fe5..000000000 --- a/Server/src/main/content/global/handlers/iface/MusicTabInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.link.music.MusicEntry; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the interface tab buttons. - * @author Emperor - */ -@Initializable -public final class MusicTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(187, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (opcode) { - case 155: - switch (button) { - case 11: - player.getMusicPlayer().toggleLooping(); - return true; - case 1: - MusicEntry entry = player.getMusicPlayer().getUnlocked().get(slot); - if (entry == null) { - if(player.getRights().equals(Rights.ADMINISTRATOR)){ - for(MusicEntry ent : MusicEntry.getSongs().values()){ - if(ent.getIndex() == slot){ - player.getMusicPlayer().unlock(ent.getId()); - break; - } - } - } else { - player.getPacketDispatch().sendMessage("You have not unlocked this piece of music yet!"); - } - return true; - } - player.getMusicPlayer().setPlaying(false); - player.getMusicPlayer().play(entry); - return true; - } - break; - } - return false; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt new file mode 100644 index 000000000..6d9748d09 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantInterface.kt @@ -0,0 +1,57 @@ +package content.global.handlers.iface + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.InterfaceListener +import core.game.node.item.Item +import core.tools.StringUtils +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class MysticStaffEnchantInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(INTERFACE_332) { player, _, _, buttonID, _, _ -> + val staff = buttonMap[buttonID] ?: return@on true + + if (!inInventory(player, staff.basicID)) { + sendMessage(player, "You don't have a${if (StringUtils.isPlusN(getItemName(staff.basicID))) "n" else ""} ${getItemName(staff.basicID)} to enchant.") + return@on true + } + + closeInterface(player) + + if (!inInventory(player, Items.COINS_995, 40000)) { + sendNPCDialogue(player, NPCs.THORMAC_389, "I need ${String.format("%,d", 40000)} coins for materials. Come back when you have the money!", FacialExpression.NEUTRAL) + return@on true + } + + if (removeItem(player, Item(staff.basicID, 1)) && removeItem(player, Item(Items.COINS_995, 40000))) { + sendNPCDialogue(player, NPCs.THORMAC_389, "Just a moment... hang on... hocus pocus abra-cadabra... there you go! Enjoy your enchanted staff!", FacialExpression.NEUTRAL) + addItem(player, staff.enchantedID, 1) + } + return@on true + } + } + + enum class EnchantedStaff(val enchantedID: Int, val basicID: Int, val buttonID: Int) { + AIR(Items.MYSTIC_AIR_STAFF_1405, Items.AIR_BATTLESTAFF_1397, 21), + WATER(Items.MYSTIC_WATER_STAFF_1403, Items.WATER_BATTLESTAFF_1395, 22), + EARTH(Items.MYSTIC_EARTH_STAFF_1407, Items.EARTH_BATTLESTAFF_1399, 23), + FIRE(Items.MYSTIC_FIRE_STAFF_1401, Items.FIRE_BATTLESTAFF_1393, 24), + LAVA(Items.MYSTIC_LAVA_STAFF_3054, Items.LAVA_BATTLESTAFF_3053, 25), + MUD(Items.MYSTIC_MUD_STAFF_6563, Items.MUD_BATTLESTAFF_6562, 26), + STEAM(Items.MYSTIC_STEAM_STAFF_11738, Items.STEAM_BATTLESTAFF_11736, 27), + } + + companion object { + private const val INTERFACE_332 = 332 + + val buttonMap = HashMap() + + init { + for (staff in EnchantedStaff.values()) { + buttonMap[staff.buttonID] = staff + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java deleted file mode 100644 index af798008d..000000000 --- a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java +++ /dev/null @@ -1,95 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import org.rs09.consts.Items; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Plugin; -import core.plugin.Initializable; -import core.tools.StringUtils; - -import java.util.HashMap; - -/** - * Represents the plugin used to handle the agility ticket interface. - * @author afaroutdude - */ -@Initializable -public final class MysticStaffEnchantingPlugin extends ComponentPlugin { - - private final Component COMPONENT = new Component(332); - private final int cost = 40000; - - protected enum EnchantedStaff { - AIR(Items.MYSTIC_AIR_STAFF_1405, Items.AIR_BATTLESTAFF_1397, 21), - WATER(Items.MYSTIC_WATER_STAFF_1403, Items.WATER_BATTLESTAFF_1395, 22), - EARTH(Items.MYSTIC_EARTH_STAFF_1407, Items.EARTH_BATTLESTAFF_1399, 23), - FIRE(Items.MYSTIC_FIRE_STAFF_1401, Items.FIRE_BATTLESTAFF_1393, 24), - LAVA(Items.MYSTIC_LAVA_STAFF_3054, Items.LAVA_BATTLESTAFF_3053, 25), - MUD(Items.MYSTIC_MUD_STAFF_6563, Items.MUD_BATTLESTAFF_6562, 26); - - public final int enchanted; - public final int basic; - public final int child; - - private static final HashMap basicToEnchanted = new HashMap<>(); - private static final HashMap childToBasic = new HashMap<>(); - - static { - for (EnchantedStaff staff : EnchantedStaff.values()) { - basicToEnchanted.put(staff.basic, staff.enchanted); - childToBasic.put(staff.child, staff.basic); - } - } - - EnchantedStaff(int enchantedId, int basicId, int childId) { - this.enchanted = enchantedId; - this.basic = basicId; - this.child = childId; - } - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(332).setPlugin(this); - return this; - } - - @Override - public void open(Player player, Component component) { - super.open(player, component); - // zoom doesn't work, but based on https://youtu.be/qxxhhCdxBsQ?t=75 seems correct anyway - for (EnchantedStaff staff : EnchantedStaff.values()) { - player.getPacketDispatch().sendItemZoomOnInterface(staff.basic, 240, COMPONENT.getId(), staff.child); - } - } - - @Override - public boolean handle(Player player, Component component, int opcode, int buttonId, int slot, int itemId) { - player.getPacketDispatch().sendMessage("op: " + opcode + " button: " + buttonId + " slot: "+ slot + " item: " + itemId); - - if (EnchantedStaff.childToBasic.containsKey(buttonId)) { - Item basicStaff = new Item(EnchantedStaff.childToBasic.get(buttonId)); - Item enchantedStaff = new Item(EnchantedStaff.basicToEnchanted.get(basicStaff.getId())); - - if (!player.getInventory().containsItem(basicStaff)) { - player.getPacketDispatch().sendMessage("You don't have a" + (StringUtils.isPlusN(basicStaff.getName()) ? "n " : " ") + basicStaff.getName() + " to enchant."); - return true; - } - if (!player.getInventory().contains(995, cost)) { - player.getInterfaceManager().close(); - player.getDialogueInterpreter().sendDialogues(389, null, "I need 40,000 coins for materials. Come", "back when you have the money!"); - return true; - } - if (player.getInventory().remove(basicStaff, new Item(995, cost))) { - player.getInterfaceManager().close(); - player.getDialogueInterpreter().sendDialogues(389, null, "Just a moment... hang on... hocus pocus abra-", "cadabra... there you go! Enjoy your enchanted staff!"); - player.getInventory().add(enchantedStaff); - } - } - - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/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 deleted file mode 100644 index b1c9de0db..000000000 --- a/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java +++ /dev/null @@ -1,33 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the prayer interface. - * @author 'Vexia - */ -@Initializable -public final class PrayerTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(271, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - final PrayerType type = PrayerType.get(button); - if (type == null) { - return true; - } - player.getPrayer().toggle(type); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java b/Server/src/main/content/global/handlers/iface/QuestTabInterface.java deleted file mode 100644 index 313e98b30..000000000 --- a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java +++ /dev/null @@ -1,71 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the quest tab reward buttons. - * @author Emperor - * @author Vexia - */ -@Initializable -public class QuestTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(274, this); // Quests - ComponentDefinition.put(259, this); // Achievement diary - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - p.getPulseManager().clear(); - switch (component.getId()) { - case 274: -// if (!GameWorld.isEconomyWorld()) { -// p.getSavedData().getSpawnData().handleButton(p, button); -// } - switch (button) { - case 3: - p.getAchievementDiaryManager().openTab(); - return true; - case 10: - break; - default: -// if (GameWorld.isEconomyWorld()) { - //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; - } -// } - return false; - } - break; - case 259: - switch (button) { - case 8: - p.getInterfaceManager().openTab(2, new Component(274)); - return true; - default: - AchievementDiary diary = p.getAchievementDiaryManager().getDiary(DiaryType.forChild(button)); - if (diary != null) { - diary.open(p); - } - return true; - } - } - return true; - } - -} \ No newline at end of file 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 deleted file mode 100644 index 9c660554b..000000000 --- a/Server/src/main/content/global/handlers/iface/SettingTabInterface.java +++ /dev/null @@ -1,87 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class SettingTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(261, this); - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 10:// brightness - int brightness = button - 7; - p.getSettings().setBrightness(brightness); - break; - case 11: - case 12: - case 13: - case 14: - case 15:// music - int volume = 15 - button; - p.getSettings().setMusicVolume(volume); - break; - case 16://530 settings - /* if (TutorialSession.getExtension(p).getStage() != TutorialSession.MAX_STAGE) { - p.sendMessage("You must finish the tutorial before opening the graphic settings."); - break; - }*/ - p.getInterfaceManager().open(new Component(742)); - break; - case 18://530 settings - p.getInterfaceManager().open(new Component(743)); - break; - case 17: - case 19: - case 20:// sonund - int volume1 = 20 - button; - p.getSettings().setSoundEffectVolume(volume1); - break; - case 29: - case 30: - case 31: - case 32: - case 33:// all sound - int volume11 = 33 - button; - p.getSettings().setAreaSoundVolume(volume11); - break; - case 6:// mouse - p.getSettings().toggleMouseButton(); - break; - case 4:// effects - p.getSettings().toggleChatEffects(); - break; - case 5:// private chat - p.getSettings().toggleSplitPrivateChat(); - break; - case 7:// aid - if (p.getIronmanManager().checkRestriction()) { - return true; - } - p.getSettings().toggleAcceptAid(); - break; - case 3:// run - p.getSettings().toggleRun(); - break; - case 8:// house - p.getInterfaceManager().close(); - p.getConfigManager().set(261, p.getConfigManager().get(261) & 0x1); - p.getInterfaceManager().openSingleTab(new Component(398)); - break; - } - return true; - } -} 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 deleted file mode 100644 index b9d5e5da4..000000000 --- a/Server/src/main/content/global/handlers/iface/SkillInterface.java +++ /dev/null @@ -1,31 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for the skilling interface. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SkillInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(499, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - player.varpManager.get(965) - .setVarbit(0,player.getAttribute("skillMenu",-1)) - .setVarbit(10, button - 10).send(player); - return true; - } -} diff --git a/Server/src/main/content/global/handlers/iface/SkillTabInterface.java b/Server/src/main/content/global/handlers/iface/SkillTabInterface.java deleted file mode 100644 index f2255d97c..000000000 --- a/Server/src/main/content/global/handlers/iface/SkillTabInterface.java +++ /dev/null @@ -1,160 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.LevelUp; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the skilling tab. - * @author Vexia - * @author Splinter - * @version 1.1 - */ -@Initializable -public final class SkillTabInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(320, this); - return this; - } - - @Override - public boolean handle(Player p, Component component, int opcode, int button, int slot, int itemId) { - final SkillConfig config = SkillConfig.forId(button); - if (config == null) { - return true; - } - if (!GameWorld.getSettings().isPvp()) { - if (p.getAttribute("levelup:" + config.getSkillId(), false)) { - p.removeAttribute("levelup:" + config.getSkillId()); - LevelUp.sendFlashingIcons(p, -1); - p.getConfigManager().set(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()); - p.getAttributes().put("skillMenu", config.getConfig()); - } - } else { - if (config.getSkillId() > 6) { - p.getPacketDispatch().sendMessage("You cannot set a target level for this skill."); - return false; - } - if (p.canSpawn()) { - p.sendMessage("You must be inside Edgeville bank to set levels."); - return false; - } - } - return true; - } - - /** - * Holds all the config values of the skill advances. - */ - public static final int[] ADVANCE_CONFIGS = { - 9, 40, 17, 49, - 25, 57, 33, 641, - 659, 664, 121, 649, - 89, 114, 107, 72, - 64, 80, 673, 680, - 99, 698, 689, 705, - }; - - public enum SkillConfig { - ATTACK(125, 1, Skills.ATTACK), - STRENGTH(126, 2, Skills.STRENGTH), - DEFENCE(127, 5, Skills.DEFENCE), - RANGE(128, 3, Skills.RANGE), - PRAYER(129, 7, Skills.PRAYER), - MAGIC(130, 4, Skills.MAGIC), - RUNECRAFTING(131, 12, Skills.RUNECRAFTING), - HITPOINTS(133, 6, Skills.HITPOINTS), - AGILITY(134, 8, Skills.AGILITY), - HERBLORE(135, 9, Skills.HERBLORE), - THIEVING(136, 10, Skills.THIEVING), - CRAFTING(137, 11, Skills.CRAFTING), - FLETCHING(138, 19, Skills.FLETCHING), - SLAYER(139, 20, Skills.SLAYER), - MINING(141, 13, Skills.MINING), - SMITHING(142, 14, Skills.SMITHING), - FISHING(143, 15, Skills.FISHING), - COOKING(144, 16, Skills.COOKING), - FIREMAKING(145, 17, Skills.FIREMAKING), - WOODCUTTING(146, 18, Skills.WOODCUTTING), - FARMING(147, 21, Skills.FARMING), - CONSTRUCTION(132, 22, Skills.CONSTRUCTION), - HUNTER(140, 23, Skills.HUNTER), - SUMMONING(148, 24, Skills.SUMMONING); - - /** - * Constructs a new {@code SkillConfig} {@code Object}. - * @param button the button. - * @param config the config. - */ - SkillConfig(int button, int config, int skillId) { - this.button = button; - this.config = config; - this.skillId = skillId; - } - - /** - * Represents the button. - */ - private int button; - - /** - * Represents the config. - */ - private int config; - - /** - * The skill id. - */ - private int skillId; - - /** - * Gets the skill config. - * @param id the id. - * @return the skill config. - */ - public static SkillConfig forId(int id) { - for (SkillConfig config : SkillConfig.values()) { - if (config.button == id) - return config; - } - return null; - } - - /** - * Gets the button. - * @return the button. - */ - public int getButton() { - return button; - } - - /** - * Gets the config. - * @return the config. - */ - public int getConfig() { - return config; - } - - /** - * Gets the skill id. - * @return The skill id. - */ - public int getSkillId() { - return skillId; - } - } -} diff --git a/Server/src/main/content/global/handlers/iface/TanningInterface.java b/Server/src/main/content/global/handlers/iface/TanningInterface.java deleted file mode 100644 index aa15e4b11..000000000 --- a/Server/src/main/content/global/handlers/iface/TanningInterface.java +++ /dev/null @@ -1,90 +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 content.global.skill.crafting.TanningProduct; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import kotlin.Unit; - -/** - * @author Vexia - * @version 1.2 - */ -@Initializable -public class TanningInterface extends ComponentPlugin { - - /** - * Method used to create a new instance. - * @param arg - * @return - * @throws Throwable - */ - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(324, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - TanningProduct def = null; - switch (button) { - case 1: - def = TanningProduct.SOFT_LEATHER; - break; - case 2: - def = TanningProduct.HARD_LEATHER; - break; - case 3: - def = TanningProduct.SNAKESKIN; - break; - case 4: - def = TanningProduct.SNAKESKIN2; - break; - case 5: - def = TanningProduct.GREEN_DHIDE; - break; - case 6: - def = TanningProduct.BLUEDHIDE; - break; - case 7: - def = TanningProduct.REDDHIDE; - break; - case 8: - def = TanningProduct.BLACKDHIDE; - break; - } - if (def == null) { - return true; - } - int amount = 0; - final TanningProduct deff = def; - switch (opcode){ - case 155: - amount = 1; - break; - case 196: - amount = 5; - break; - case 124: - amount = 10; - case 199: - sendInputDialogue(player, true, "Enter the amount:", (value) -> { - TanningProduct.tan(player, (int) value, deff); - return Unit.INSTANCE; - }); - break; - case 234: - amount = player.getInventory().getAmount(new Item(def.getItem(), 1)); - break; - } - TanningProduct.tan(player, amount, def); - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/iface/TanningInterface.kt b/Server/src/main/content/global/handlers/iface/TanningInterface.kt new file mode 100644 index 000000000..278e991bf --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/TanningInterface.kt @@ -0,0 +1,25 @@ +package content.global.handlers.iface + +import content.global.skill.crafting.TanningProduct +import core.api.amountInInventory +import core.api.sendInputDialogue +import core.game.interaction.InterfaceListener + +class TanningInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(324) { player, _, opcode, buttonID, _, _ -> + val product = TanningProduct.forId(buttonID) + + when (opcode) { + 155 -> TanningProduct.tan(player, 1, product) + 196 -> TanningProduct.tan(player, 5, product) + 124 -> TanningProduct.tan(player, 10, product) + 199 -> sendInputDialogue(player, true, "Enter the amount:") { value -> + TanningProduct.tan(player, value as Int, product) + } + 234 -> TanningProduct.tan(player, amountInInventory(player, product.item), product) + } + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt b/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt index ec6dcf98e..feb6cc5c0 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 @@ -126,7 +127,7 @@ private const val F_FLARES = 72 private const val F_PLAIN_TROUSERS = 70 private const val F_SHORTS = 77 -private val femaleLegIDs = intArrayOf(F_FINE_SKIRT, F_FRILLED_SKIRT, F_LAYERED_SKIRT, F_LONG_NARROW_SKIRT, F_RAGGED_SKIRT, F_TATTERED_SKIRT, F_SHORT_SKIRT, F_SASHED_SKIRT, F_FITTED_SKIRT, F_TORN_TROUSERS, F_LONG_SKIRT, F_TURN_UPS, F_FLARES, F_FLARES, F_PLAIN_TROUSERS, F_SHORTS) +private val femaleLegIDs = intArrayOf(F_FINE_SKIRT, F_FRILLED_SKIRT, F_LAYERED_SKIRT, F_LONG_NARROW_SKIRT, F_RAGGED_SKIRT, F_TATTERED_SKIRT, F_SHORT_SKIRT, F_SASHED_SKIRT, F_FITTED_SKIRT, F_TORN_TROUSERS, F_LONG_SKIRT, F_TURN_UPS, F_FLARES, F_PLAIN_TROUSERS, F_SHORTS) private val COINS = Item(995,1000) @@ -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 deleted file mode 100644 index 756600db3..000000000 --- a/Server/src/main/content/global/handlers/iface/WorldMapInterface.java +++ /dev/null @@ -1,39 +0,0 @@ -package content.global.handlers.iface; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the world map interface. - * @author Emperor - * - */ -@Initializable -public final class WorldMapInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(755).setPlugin(this); - return this; - } - - //Thanks snicker! - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 3: - player.getInterfaceManager().openWindowsPane(new Component(player.getInterfaceManager().isResizable() ? 746 : 548), 2); - player.getPacketDispatch().sendRunScript(1187, "ii", 0, 0); - player.updateSceneGraph(true); - return true; - default: - //SystemLogger.logErr(this.getClass(), "World map: buttonid: " + button + ", opcode: " + opcode + ", slot: " + slot); - return true; - } - } - -} diff --git a/Server/src/main/content/global/handlers/iface/WorldMapInterface.kt b/Server/src/main/content/global/handlers/iface/WorldMapInterface.kt new file mode 100644 index 000000000..cd1fc427b --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/WorldMapInterface.kt @@ -0,0 +1,23 @@ +package content.global.handlers.iface + +import core.game.component.Component +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components + +class WorldMapInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.WORLDMAP_755) { player, _, _, button, _, _ -> + if (button == 3) { + val paneId = if (player.interfaceManager.isResizable) { + Components.TOPLEVEL_FULLSCREEN_746 + } else { + Components.TOPLEVEL_548 + } + player.interfaceManager.openWindowsPane(Component(paneId), 2) + player.packetDispatch.sendRunScript(1187, "ii", 0, 0) + player.updateSceneGraph(true) + } + return@on 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/iface/tabs/ClanTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/ClanTabInterface.kt new file mode 100644 index 000000000..c46293f59 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/ClanTabInterface.kt @@ -0,0 +1,109 @@ +package content.global.handlers.iface.tabs + +import core.api.sendInputDialogue +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.system.communication.ClanRank +import core.game.system.communication.ClanRepository +import core.net.amsc.MSPacketRepository +import core.net.amsc.WorldCommunicator +import core.tools.StringUtils +import org.rs09.consts.Components + +class ClanTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.CLANJOIN_589) { player, _, _, buttonID, _, _ -> + if (buttonID == 9) { + if (player.interfaceManager.opened != null) { + sendMessage(player, "Please close the interface you have open before using 'Clan Setup'") + } else { + ClanRepository.openSettings(player) + } + } + + if (buttonID == 14) { + player.communication.toggleLootshare(player) + } + return@on true + } + + on(Components.CLANSETUP_590) { player, _, opcode, buttonID, _, _ -> + val clan = ClanRepository.get(player.name, true) + + when (buttonID) { + 22 -> { + if (opcode == 155) { + sendInputDialogue(player, false, "Enter clan prefix:", ) { value -> + val clanName = StringUtils.formatDisplayName(value.toString()) + + if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendClanRename(player, clanName) } + + if (clan.name == "Chat disabled") { + sendMessage(player, "Your clan channel has now been enabled!") + sendMessage(player, "Join your channel by clicking 'Join Chat' and typing: ${player.username}") + } + + clan.name = clanName + player.communication.clanName = clanName + clan.updateSettings(player) + clan.update() + } + } + + if (opcode == 196) { + clan.name = "Chat disabled" + player.communication.clanName = "" + if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendClanRename(player, player.communication.clanName) } + clan.updateSettings(player) + clan.delete() + } + } + + 23 -> { + clan.joinRequirement = getRank(opcode) + player.communication.joinRequirement = clan.joinRequirement + MSPacketRepository.setClanSetting(player, 0, clan.joinRequirement) + } + + 24 -> { + clan.messageRequirement = getRank(opcode) + player.communication.messageRequirement = clan.messageRequirement + MSPacketRepository.setClanSetting(player, 1, clan.messageRequirement) + } + + 25 -> { + clan.kickRequirement = getRank(opcode) + player.communication.kickRequirement = clan.kickRequirement + MSPacketRepository.setClanSetting(player, 2, clan.kickRequirement) + } + + 26 -> { + clan.lootRequirement = if (opcode == 155) ClanRank.ADMINISTRATOR else getRank(opcode) + player.communication.lootRequirement = clan.lootRequirement + MSPacketRepository.setClanSetting(player, 3, clan.lootRequirement) + } + + 33 -> sendMessage(player, "CoinShare is not available.") + } + + clan.updateSettings(player) + clan.update() + return@on true + } + } + + fun getRank(opcode: Int): ClanRank { + return when (opcode) { + 155 -> ClanRank.NONE + 196 -> ClanRank.FRIEND + 124 -> ClanRank.RECRUIT + 199 -> ClanRank.CORPORAL + 234 -> ClanRank.SERGEANT + 168 -> ClanRank.LIEUTENANT + 166 -> ClanRank.CAPTAIN + 64 -> ClanRank.GENERAL + 53 -> ClanRank.OWNER + else -> ClanRank.NONE + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/CombatTabInterface.java b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java similarity index 98% rename from Server/src/main/content/global/handlers/iface/CombatTabInterface.java rename to Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java index bd3938d5c..0ca513102 100644 --- a/Server/src/main/content/global/handlers/iface/CombatTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java @@ -1,4 +1,4 @@ -package content.global.handlers.iface; +package content.global.handlers.iface.tabs; import core.game.component.Component; import core.game.component.ComponentDefinition; diff --git a/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt new file mode 100644 index 000000000..db5efdd84 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/EmoteTabInterface.kt @@ -0,0 +1,14 @@ +package content.global.handlers.iface.tabs + +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.emote.Emotes +import org.rs09.consts.Components + +class EmoteTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.EMOTES_464) { player, _, _, buttonID, _, _ -> + Emotes.handle(player, buttonID) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt new file mode 100644 index 000000000..e3dd69b0e --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/EquipmentTabInterface.kt @@ -0,0 +1,136 @@ +package content.global.handlers.iface.tabs + +import content.global.skill.summoning.familiar.BurdenBeast +import core.api.* + +import core.game.container.access.InterfaceContainer +import core.game.container.impl.EquipmentContainer +import core.game.global.action.EquipHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.interaction.InterfaceListener +import core.game.node.entity.combat.DeathTask +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.prayer.PrayerType +import core.tools.Log +import org.rs09.consts.Components +import org.rs09.consts.Items + +class EquipmentTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + onOpen(ITEMS_KEPT_ON_DEATH_102) { player, component -> + /** + * (Highlight white items are auto destroyed on death Enum#616 (Items kept on death interface) TODO: Parse server sided + * CS2 Script 118 - Items kept on death interface + * Credit Woahscam for figuring this all out. + * @arg_0 (Int): Zone type - 0 Default/1 Safe/2 POH/3 Castle Wars/4 Trouble Brewing/5 Barbarian Assault + * @arg_1 (Int): Amount of items kept on death - 0/1/3/4 + * @arg_2 (Object): Item kept on death - slot 0 item id + * @arg_3 (Object): Item kept on death - slot 1 item id + * @arg_4 (Object): Item kept on death - slot 2 item id + * @arg_5 (Object): Item kept on death - slot 3 item id + * @arg_6 (Int): Player is skulled - 0 Not Skulled/1 Skulled + * @arg_7 (Int): Player has BoB summoned with items - 0 BoB not summoned or has no items /1 BoB summoned with items + * @arg_8 (String): String to append based on amount of items kept on death. + */ + val zoneType = player.zoneMonitor.type + val itemsKeptOnDeath = DeathTask.getContainers(player)[0] + val amountKeptOnDeath = if (!player.skullManager.isSkulled && itemsKeptOnDeath.itemCount() < 3) { + if (player.prayer[PrayerType.PROTECT_ITEMS]) 4 else 3 + } else { + itemsKeptOnDeath.itemCount() + } + val slot0 = itemsKeptOnDeath.getId(0) + val slot1 = itemsKeptOnDeath.getId(1) + val slot2 = itemsKeptOnDeath.getId(2) + val slot3 = itemsKeptOnDeath.getId(3) + val hasSkull = if (player.skullManager.isSkulled) 1 else 0 + val beast: BurdenBeast? = if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast) player.familiarManager.familiar as BurdenBeast else null + val hasBob = if (beast != null && !beast.container.isEmpty) 1 else 0 + val message = "You are skulled." + val cs2Args = arrayOf(hasBob, hasSkull, slot3, slot2, slot1, slot0, amountKeptOnDeath, zoneType, message) + + if (amountKeptOnDeath > 4 && zoneType == 0) { + log(this::class.java, Log.ERR, "Items kept on death interface should not contain more than 4 items when not in a safe zone!") + } + + player.packetDispatch.sendRunScript(118, "siiooooii", *cs2Args) + + val settings = IfaceSettingsBuilder().enableAllOptions().build() + player.packetDispatch.sendIfaceSettings(settings, 18, component.id, 0, itemsKeptOnDeath.itemCount()) + player.packetDispatch.sendIfaceSettings(settings, 21, component.id, 0, DeathTask.getContainers(player)[1].itemCount()) + return@onOpen true + } + + on(Components.WORNITEMS_387) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + 28 -> { + if (opcode == 81) EquipHandler.unequip(player, slot, itemID) + if (opcode == 206) operateItem(player, slot) + } + 52 -> openInterface(player, ITEMS_KEPT_ON_DEATH_102) + 55 -> openInterface(player, Components.EQUIP_SCREEN2_667) + } + return@on true + } + + onOpen(Components.EQUIP_SCREEN2_667) { player, component -> + val settings = IfaceSettingsBuilder().enableAllOptions().build() + player.packetDispatch.sendIfaceSettings(settings, 14, component.id, 0, 13) + EquipmentContainer.update(player) + openSingleTab(player, Components.INVENTORY_WEAR2_670) + return@onOpen true + } + + on(Components.EQUIP_SCREEN2_667) { player, _, opcode, buttonID, slot, itemID -> + if (buttonID == 14) { + when (opcode) { + 9 -> sendMessage(player, player.equipment.get(slot).definition.examine) + 155 -> EquipHandler.unequip(player, slot, itemID) + 196 -> operateItem(player, slot) + } + } + return@on true + } + + onClose(Components.EQUIP_SCREEN2_667) { player, _ -> + closeTabInterface(player) + return@onClose true + } + + onOpen(Components.INVENTORY_WEAR2_670) { player, component -> + InterfaceContainer.generateItems(player, player.inventory.toArray(), arrayOf("Equip"), component.id, 0, 7, 4, 93) + return@onOpen true + } + + on(Components.INVENTORY_WEAR2_670) { player, _, opcode, _, slot, _ -> + if (opcode == 9) sendMessage(player, player.inventory.get(slot).definition.examine) + if (opcode == 155) equipItem(player, slot) + return@on true + } + } + + private fun equipItem(player: Player, slot: Int) { + val item = player.inventory.get(slot) ?: return + + if (item.definition.options.any { it in arrayOf("Equip", "Wield", "Wear") } || item.id == Items.BEER_1917) { + InteractionListeners.run(item.id, IntType.ITEM, "equip", player, item) + } else { + sendMessage(player, "You can't wear that.") + } + } + + private fun operateItem(player: Player, slot: Int) { + val item = player.equipment.get(slot) ?: return + + when { + InteractionListeners.run(item.id, IntType.ITEM, "operate", player, item) -> return + item.operateHandler?.handle(player, item, "operate") == true -> return + else -> sendMessage(player, "You can't operate that.") + } + } + + companion object { + private const val ITEMS_KEPT_ON_DEATH_102 = 102 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt new file mode 100644 index 000000000..f40ccd4a6 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/LogoutTabInterface.kt @@ -0,0 +1,34 @@ +package content.global.handlers.iface.tabs + +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.world.repository.Repository +import org.rs09.consts.Components + +class LogoutTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.LOGOUT_182) { player, _, _, buttonID, _, _ -> + if (buttonID == 6) { + return@on when { + !player.zoneMonitor.canLogout() -> true + + player.inCombat() -> { + sendMessage(player, "You can't log out until 10 seconds after the end of combat.") + true + } + + player.isTeleporting -> { + sendMessage(player, "Please finish your teleport before logging out.") + true + } + + else -> { + Repository.disconnectionQueue.add(player) + true + } + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt new file mode 100644 index 000000000..5cdd88867 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/MagicTabInterface.kt @@ -0,0 +1,23 @@ +package content.global.handlers.iface.tabs + +import content.global.skill.magic.SpellListener +import content.global.skill.magic.SpellListeners +import core.api.getAttribute +import core.game.interaction.InterfaceListener +import core.game.node.entity.combat.spell.MagicSpell +import core.game.node.entity.player.link.SpellBookManager.SpellBook +import core.game.world.GameWorld + +class MagicTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + SpellBook.values().forEach { + on(it.interfaceId) { player, _, _, buttonID, _, _ -> + if (GameWorld.ticks < getAttribute(player, "magic:delay", -1)) return@on true + + SpellListeners.run(buttonID, SpellListener.NONE, it.name.lowercase(), player) + + return@on MagicSpell.castSpell(player, it, buttonID, player) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt new file mode 100644 index 000000000..76643299c --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/MusicTabInterface.kt @@ -0,0 +1,38 @@ +package content.global.handlers.iface.tabs + +import core.api.sendMessage +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.music.MusicEntry +import org.rs09.consts.Components + +class MusicTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.MUSIC_V3_187) { player, _, opcode, buttonID, slot, _ -> + if (opcode == 155) { + if (buttonID == 11) { + player.musicPlayer.toggleLooping() + return@on true + } + + if (buttonID == 1) { + if (player.musicPlayer.unlocked[slot] != null) { + player.musicPlayer.play(player.musicPlayer.unlocked[slot]) + return@on true + } + + if (player.isAdmin) { + for (entry in MusicEntry.getSongs().values) { + if (entry.index == slot) { + player.musicPlayer.unlock(entry.id) + } + } + } else { + sendMessage(player, "You have not unlocked this piece of music yet!") + } + return@on true + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt new file mode 100644 index 000000000..684001eaa --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/PrayerTabInterface.kt @@ -0,0 +1,14 @@ +package content.global.handlers.iface.tabs + +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.prayer.PrayerType +import org.rs09.consts.Components + +class PrayerTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.PRAYER_271) { player, _, _, buttonID, _, _ -> + val prayer = PrayerType.get(buttonID) ?: return@on true + return@on player.prayer.toggle(prayer) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt new file mode 100644 index 000000000..2a9703946 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/QuestTabInterface.kt @@ -0,0 +1,36 @@ +package content.global.handlers.iface.tabs + +import content.global.handlers.iface.tabs.QuestTabUtils.showRequirementsInterface +import core.api.openInterface +import core.game.component.Component +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.diary.DiaryType +import org.rs09.consts.Components + +class QuestTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.QUESTJOURNAL_V2_274) { player, _, _, buttonID, _, _ -> + if (buttonID == 3) { + player.achievementDiaryManager.openTab() + } else { + val quest = player.questRepository.forButtonId(buttonID) + if (quest != null) { + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + quest.drawJournal(player, quest.getStage(player)) + } else { + showRequirementsInterface(player, buttonID) + } + } + return@on true + } + + on(Components.AREA_TASK_259) { player, _, _, buttonID, _, _ -> + if (buttonID == 8) { + player.interfaceManager.openTab(2, Component(Components.QUESTJOURNAL_V2_274)) + } else { + player.achievementDiaryManager.getDiary(DiaryType.forChild(buttonID))?.open(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt b/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt new file mode 100644 index 000000000..40e410348 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/QuestTabUtils.kt @@ -0,0 +1,245 @@ +package content.global.handlers.iface.tabs + +import core.game.requirement.* +import core.api.* +import core.tools.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills + +import kotlin.math.* +import java.util.* + +import org.rs09.consts.* +import content.data.Quests + +object QuestTabUtils { + @JvmStatic + fun showRequirementsInterface(player: Player, button: Int) { + val questName = getNameForButton(button) + val questReq = QuestRequirements.values().filter { it.quest.questName == questName }.firstOrNull() ?: return + var (isMet, unmetReqs) = QuestReq(questReq).evaluate(player) + + var messageList = ArrayList() + + val statMap = HashMap() + val questList = HashSet() + var maxQpReq = 0 + var qpPenalty = 0 + closeInterface(player) + for (req in unmetReqs) { + if (req is QuestReq) + questList.add(req.questReq.quest.questName) + else if (req is SkillReq) { + if (statMap[req.skillId] == null || (statMap[req.skillId] != null && statMap[req.skillId]!! < req.level)) + statMap[req.skillId] = req.level + } + else if (req is QPReq && req.amount > maxQpReq) + maxQpReq = req.amount + else if (req is QPCumulative) + qpPenalty += req.amount + } + + messageList.add(colorize("%B[Quests Needed]")) + messageList.addAll(questList.map { "Completion of $it" }) + + messageList.add(" ") + messageList.add(colorize("%B[Skills Needed]")) + + for ((skillId, level) in statMap) { + val name = Skills.SKILL_NAME[skillId] + messageList.add("$level $name") + } + + messageList.add(" ") + messageList.add(colorize("%B[Other Reqs]")) + + val totalQpRequirement = QPReq(min(max(maxQpReq, qpPenalty), player.questRepository.getAvailablePoints())) + val (meetsQp, _) = totalQpRequirement.evaluate(player) + isMet = isMet && meetsQp + + if (isMet) + messageList.add(colorize("%GCongratulations! You've earned this one.")) + + if (!meetsQp) messageList.add("A total of ${totalQpRequirement.amount} Quest Points.") + + messageList.add("") + messageList.add(colorize("%BDISCLAIMER: If you're seeing this screen, this quest is not")) + messageList.add(colorize("%Bimplemented yet. These are the requirements that you need in order")) + messageList.add(colorize("%Bto access implemented content that would normally require this quest")) + messageList.add("") + messageList.add("If you want to see more quests enter the game, consider") + messageList.add("contributing dialogue transcripts!") + + + setInterfaceText(player, questName, Components.QUESTJOURNAL_SCROLL_275, 2) + var lineId = 11 + for(i in 0..299) { + val entry = messageList.elementAtOrNull(i) + if (entry != null) + setInterfaceText(player, entry, Components.QUESTJOURNAL_SCROLL_275, lineId++) + else + setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + } + + private fun getNameForButton(button: Int) : String { + val quest = when (button) { + 10 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + 11 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + 12 -> "Free Quests" + 13 -> Quests.BLACK_KNIGHTS_FORTRESS.questName + 14 -> Quests.COOKS_ASSISTANT.questName + 15 -> Quests.DEMON_SLAYER.questName + 16 -> Quests.DORICS_QUEST.questName + 17 -> Quests.DRAGON_SLAYER.questName + 18 -> Quests.ERNEST_THE_CHICKEN.questName + 19 -> Quests.GOBLIN_DIPLOMACY.questName + 20 -> Quests.IMP_CATCHER.questName + 21 -> Quests.THE_KNIGHTS_SWORD.questName + 22 -> Quests.PIRATES_TREASURE.questName + 23 -> Quests.PRINCE_ALI_RESCUE.questName + 24 -> Quests.THE_RESTLESS_GHOST.questName + 25 -> Quests.ROMEO_JULIET.questName + 26 -> Quests.RUNE_MYSTERIES.questName + 27 -> Quests.SHEEP_SHEARER.questName + 28 -> Quests.SHIELD_OF_ARRAV.questName + 29 -> Quests.VAMPIRE_SLAYER.questName + 30 -> Quests.WITCHS_POTION.questName + 31 -> "Members' Quests" + 32 -> Quests.ANIMAL_MAGNETISM.questName + 33 -> Quests.BETWEEN_A_ROCK.questName + 34 -> Quests.BIG_CHOMPY_BIRD_HUNTING.questName + 35 -> Quests.BIOHAZARD.questName + 36 -> Quests.CABIN_FEVER.questName + 37 -> Quests.CLOCK_TOWER.questName + 38 -> Quests.CONTACT.questName + 39 -> Quests.ZOGRE_FLESH_EATERS.questName + 40 -> Quests.CREATURE_OF_FENKENSTRAIN.questName + 41 -> Quests.DARKNESS_OF_HALLOWVALE.questName + 42 -> Quests.DEATH_TO_THE_DORGESHUUN.questName + 43 -> Quests.DEATH_PLATEAU.questName + 44 -> Quests.DESERT_TREASURE.questName + 45 -> Quests.DEVIOUS_MINDS.questName + 46 -> Quests.THE_DIG_SITE.questName + 47 -> Quests.DRUIDIC_RITUAL.questName + 48 -> Quests.DWARF_CANNON.questName + 49 -> Quests.EADGARS_RUSE.questName + 50 -> Quests.EAGLES_PEAK.questName + 51 -> Quests.ELEMENTAL_WORKSHOP_I.questName + 52 -> Quests.ELEMENTAL_WORKSHOP_II.questName + 53 -> Quests.ENAKHRAS_LAMENT.questName + 54 -> Quests.ENLIGHTENED_JOURNEY.questName + 55 -> Quests.THE_EYES_OF_GLOUPHRIE.questName + 56 -> Quests.FAIRYTALE_I_GROWING_PAINS.questName + 57 -> Quests.FAIRYTALE_II_CURE_A_QUEEN.questName + 58 -> Quests.FAMILY_CREST.questName + 59 -> Quests.THE_FEUD.questName + 60 -> Quests.FIGHT_ARENA.questName + 61 -> Quests.FISHING_CONTEST.questName + 62 -> Quests.FORGETTABLE_TALE.questName + 63 -> Quests.THE_FREMENNIK_TRIALS.questName + 64 -> Quests.WATERFALL_QUEST.questName + 65 -> Quests.GARDEN_OF_TRANQUILITY.questName + 66 -> Quests.GERTRUDES_CAT.questName + 67 -> Quests.GHOSTS_AHOY.questName + 68 -> Quests.THE_GIANT_DWARF.questName + 69 -> Quests.THE_GOLEM.questName + 70 -> Quests.THE_GRAND_TREE.questName + 71 -> Quests.THE_HAND_IN_THE_SAND.questName + 72 -> Quests.HAUNTED_MINE.questName + 73 -> Quests.HAZEEL_CULT.questName + 74 -> Quests.HEROES_QUEST.questName + 75 -> Quests.HOLY_GRAIL.questName + 76 -> Quests.HORROR_FROM_THE_DEEP.questName + 77 -> Quests.ICTHLARINS_LITTLE_HELPER.questName + 78 -> Quests.IN_AID_OF_THE_MYREQUE.questName + 79 -> Quests.IN_SEARCH_OF_THE_MYREQUE.questName + 80 -> Quests.JUNGLE_POTION.questName + 81 -> Quests.LEGENDS_QUEST.questName + 82 -> Quests.LOST_CITY.questName + 83 -> Quests.THE_LOST_TRIBE.questName + 84 -> Quests.LUNAR_DIPLOMACY.questName + 85 -> Quests.MAKING_HISTORY.questName + 86 -> Quests.MERLINS_CRYSTAL.questName + 87 -> Quests.MONKEY_MADNESS.questName + 88 -> Quests.MONKS_FRIEND.questName + 89 -> Quests.MOUNTAIN_DAUGHTER.questName + 90 -> Quests.MOURNINGS_END_PART_I.questName + 91 -> Quests.MOURNINGS_END_PART_II.questName + 92 -> Quests.MURDER_MYSTERY.questName + 93 -> Quests.MY_ARMS_BIG_ADVENTURE.questName + 94 -> Quests.NATURE_SPIRIT.questName + 95 -> Quests.OBSERVATORY_QUEST.questName + 96 -> Quests.ONE_SMALL_FAVOUR.questName + 97 -> Quests.PLAGUE_CITY.questName + 98 -> Quests.PRIEST_IN_PERIL.questName + 99 -> Quests.RAG_AND_BONE_MAN.questName + 100 -> Quests.RATCATCHERS.questName + 101 -> Quests.RECIPE_FOR_DISASTER.questName + 102 -> Quests.RECRUITMENT_DRIVE.questName + 103 -> Quests.REGICIDE.questName + 104 -> Quests.ROVING_ELVES.questName + 105 -> Quests.ROYAL_TROUBLE.questName + 106 -> Quests.RUM_DEAL.questName + 107 -> Quests.SCORPION_CATCHER.questName + 108 -> Quests.SEA_SLUG.questName + 109 -> Quests.THE_SLUG_MENACE.questName + 110 -> Quests.SHADES_OF_MORTTON.questName + 111 -> Quests.SHADOW_OF_THE_STORM.questName + 112 -> Quests.SHEEP_HERDER.questName + 113 -> Quests.SHILO_VILLAGE.questName + 114 -> Quests.A_SOULS_BANE.questName + 115 -> Quests.SPIRITS_OF_THE_ELID.questName + 116 -> Quests.SWAN_SONG.questName + 117 -> Quests.TAI_BWO_WANNAI_TRIO.questName + 118 -> Quests.A_TAIL_OF_TWO_CATS.questName + 119 -> Quests.TEARS_OF_GUTHIX.questName + 120 -> Quests.TEMPLE_OF_IKOV.questName + 121 -> Quests.THRONE_OF_MISCELLANIA.questName + 122 -> Quests.THE_TOURIST_TRAP.questName + 123 -> Quests.WITCHS_HOUSE.questName + 124 -> Quests.TREE_GNOME_VILLAGE.questName + 125 -> Quests.TRIBAL_TOTEM.questName + 126 -> Quests.TROLL_ROMANCE.questName + 127 -> Quests.TROLL_STRONGHOLD.questName + 128 -> Quests.UNDERGROUND_PASS.questName + 129 -> Quests.WANTED.questName + 130 -> Quests.WATCHTOWER.questName + 131 -> Quests.COLD_WAR.questName + 132 -> Quests.THE_FREMENNIK_ISLES.questName + 133 -> Quests.TOWER_OF_LIFE.questName + 134 -> Quests.THE_GREAT_BRAIN_ROBBERY.questName + 135 -> Quests.WHAT_LIES_BELOW.questName + 136 -> Quests.OLAFS_QUEST.questName + 137 -> Quests.ANOTHER_SLICE_OF_HAM.questName + 138 -> Quests.DREAM_MENTOR.questName + 139 -> Quests.GRIM_TALES.questName + 140 -> Quests.KINGS_RANSOM.questName + 141 -> Quests.THE_PATH_OF_GLOUPHRIE.questName + 142 -> Quests.BACK_TO_MY_ROOTS.questName + 143 -> Quests.LAND_OF_THE_GOBLINS.questName + 144 -> Quests.DEALING_WITH_SCABARAS.questName + 145 -> Quests.WOLF_WHISTLE.questName + 146 -> Quests.AS_A_FIRST_RESORT.questName + 147 -> Quests.CATAPULT_CONSTRUCTION.questName + 148 -> Quests.KENNITHS_CONCERNS.questName + 149 -> Quests.LEGACY_OF_SEERGAZE.questName + 150 -> Quests.PERILS_OF_ICE_MOUNTAIN.questName + 151 -> Quests.TOKTZ_KET_DILL.questName + 152 -> Quests.SMOKING_KILLS.questName + 153 -> Quests.ROCKING_OUT.questName + 154 -> Quests.SPIRIT_OF_SUMMER.questName + 155 -> Quests.MEETING_HISTORY.questName + 156 -> Quests.ALL_FIRED_UP.questName + 157 -> Quests.SUMMERS_END.questName + 158 -> Quests.DEFENDER_OF_VARROCK.questName + 159 -> Quests.SWEPT_AWAY.questName + 160 -> Quests.WHILE_GUTHIX_SLEEPS.questName + 161 -> Quests.IN_PYRE_NEED.questName + 162 -> Quests.MYTHS_OF_THE_WHITE_LANDS.questName + else -> "" + } + return quest as String + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt new file mode 100644 index 000000000..29a1669e9 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/SettingsTabInterface.kt @@ -0,0 +1,35 @@ +package content.global.handlers.iface.tabs + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Components + +class SettingsTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.OPTIONS_261) { player, _, _, buttonID, _, _ -> + when (buttonID) { + RUN -> player.settings.toggleRun() + CHAT_EFFECTS -> player.settings.toggleChatEffects() + SPLIT_PM -> player.settings.toggleSplitPrivateChat() + MOUSE -> player.settings.toggleMouseButton() + AID -> restrictForIronman(player, IronmanMode.STANDARD) { player.settings.toggleAcceptAid() } + HOUSE -> openSingleTab(player, Components.POH_HOUSE_OPTIONS_398) + GRAPHICS -> openInterface(player, Components.GRAPHICS_OPTIONS_742) + AUDIO -> openInterface(player, Components.SOUND_OPTIONS_743) + } + return@on true + } + } + + companion object { + const val RUN = 3 + const val CHAT_EFFECTS = 4 + const val SPLIT_PM = 5 + const val MOUSE = 6 + const val AID = 7 + const val HOUSE = 8 + const val GRAPHICS = 16 + const val AUDIO = 18 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt b/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt new file mode 100644 index 000000000..fbe1effc4 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/tabs/StatsTabInterface.kt @@ -0,0 +1,87 @@ +package content.global.handlers.iface.tabs + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.skill.LevelUp +import core.game.node.entity.skill.Skills +import org.rs09.consts.Components + + +class StatsTabInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.STATS_320) { player, _, _, buttonID, _, _ -> + val config = skillMap[buttonID] ?: return@on true + + if (getAttribute(player, "levelup:${config.skillID}", false)) { + removeAttributes(player, "levelup:${config.skillID}") + LevelUp.sendFlashingIcons(player, -1) + setVarp(player, 1230, ADVANCE_CONFIGS[config.skillID]) + openInterface(player, 741) + } else { + openInterface(player, Components.SKILL_GUIDE_V2_499) + setVarp(player, 965, config.configID) + setAttribute(player, "skillMenu", config.configID) + } + return@on true + } + + on(Components.SKILL_GUIDE_V2_499) { player, _, _, buttonID, _, _ -> + setVarbit(player, 3288, getAttribute(player, "skillMenu", -1)) + setVarbit(player, 3289, buttonID - 10) + return@on true + } + + on(LEVEL_UP_INTERFACE_740) { player, component, opcode, buttonID, slot, itemID -> + if (buttonID == 3) { + closeInterface(player) + } + return@on true + } + } + + enum class SkillConfig(val buttonID: Int, val configID: Int, val skillID: Int) { + ATTACK(125, 1, Skills.ATTACK), + STRENGTH(126, 2, Skills.STRENGTH), + DEFENCE(127, 5, Skills.DEFENCE), + RANGE(128, 3, Skills.RANGE), + PRAYER(129, 7, Skills.PRAYER), + MAGIC(130, 4, Skills.MAGIC), + RUNECRAFT(131, 12, Skills.RUNECRAFTING), + HITPOINTS(133, 6, Skills.HITPOINTS), + AGILITY(134, 8, Skills.AGILITY), + HERBLORE(135, 9, Skills.HERBLORE), + THIEVING(136, 10, Skills.THIEVING), + CRAFTING(137, 11, Skills.CRAFTING), + FLETCHING(138, 19, Skills.FLETCHING), + SLAYER(139, 20, Skills.SLAYER), + MINING(141, 13, Skills.MINING), + SMITHING(142, 14, Skills.SMITHING), + FISHING(143, 15, Skills.FISHING), + COOKING(144, 16, Skills.COOKING), + FIREMAKING(145, 17, Skills.FIREMAKING), + WOODCUTTING(146, 18, Skills.WOODCUTTING), + FARMING(147, 21, Skills.FARMING), + CONSTRUCTION(132, 22, Skills.CONSTRUCTION), + HUNTER(140, 23, Skills.HUNTER), + SUMMONING(148, 24, Skills.SUMMONING), + } + + companion object { + private const val LEVEL_UP_INTERFACE_740 = 740 + val skillMap= HashMap() + val ADVANCE_CONFIGS = intArrayOf( + 9, 40, 17, 49, + 25, 57, 33, 641, + 659, 664, 121, 649, + 89, 114, 107, 72, + 64, 80, 673, 680, + 99, 698, 689, 705 + ) + + init { + for (skill in SkillConfig.values()) { + skillMap[skill.buttonID] = skill + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/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..a3ab4d0ca --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EctophialListener.kt @@ -0,0 +1,74 @@ +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..71239cc7c --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt @@ -0,0 +1,117 @@ +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") || node.name.contains("Super ")) { + replaceSlot(player, node.asItem().slot, Item(EmptyItem.getEmpty(Items.POTION_195)!!), node.asItem()) + playAudio(player, EmptyItem.getEmptyAudio(Items.POTION_195)!!) + return@on true + } + 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), + APPLE_MUSH(Items.APPLE_MUSH_5992,Items.BUCKET_1925, "You empty the bucket of apple mush."), + VIAL_OF_WATER(Items.VIAL_OF_WATER_227, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401), + BOWL_OF_WATER(Items.BOWL_OF_WATER_1921, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + BOWL_OF_HOT_WATER(Items.BOWL_OF_HOT_WATER_4456, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + 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_TEA_CUP(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the cup of tea.", Sounds.LIQUID_2401), + NETTLE_TEA_CUP_MILKY(Items.CUP_OF_TEA_4243, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + NETTLE_TEA_PORCELAIN(Items.CUP_OF_TEA_4245, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + NETTLE_TEA_PORCELAIN_MILKY(Items.CUP_OF_TEA_4246, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + 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), + TEA_CUP_WATER(Items.CUP_OF_WATER_4458, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + TEA_CUP_HOT_WATER(Items.CUP_OF_HOT_WATER_4460, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA1(Items.CUP_OF_TEA_7730, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA1_MILKY(Items.CUP_OF_TEA_7731, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA2(Items.CUP_OF_TEA_7733, Items.PORCELAIN_CUP_7732, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA2_MILKY(Items.CUP_OF_TEA_7734, Items.PORCELAIN_CUP_7732, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA3(Items.CUP_OF_TEA_7736, Items.PORCELAIN_CUP_7735, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + BUILDERS_TEA3_MILKY(Items.CUP_OF_TEA_7737, Items.PORCELAIN_CUP_7735, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401), + CLAY_POT_OF_TEA4(Items.POT_OF_TEA_4_7692, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + CLAY_POT_OF_TEA3(Items.POT_OF_TEA_3_7694, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + CLAY_POT_OF_TEA2(Items.POT_OF_TEA_2_7696, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + CLAY_POT_OF_TEA1(Items.POT_OF_TEA_1_7698, Items.TEAPOT_7702, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + PORCELAIN_POT_OF_TEA4(Items.POT_OF_TEA_4_7704, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + PORCELAIN_POT_OF_TEA3(Items.POT_OF_TEA_3_7706, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + PORCELAIN_POT_OF_TEA2(Items.POT_OF_TEA_2_7708, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + PORCELAIN_POT_OF_TEA1(Items.POT_OF_TEA_1_7710, Items.TEAPOT_7714, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + GOLD_PORCELAIN_POT_OF_TEA4(Items.POT_OF_TEA_4_7716, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + GOLD_PORCELAIN_POT_OF_TEA3(Items.POT_OF_TEA_3_7718, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + GOLD_PORCELAIN_POT_OF_TEA2(Items.POT_OF_TEA_2_7720, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + GOLD_PORCELAIN_POT_OF_TEA1(Items.POT_OF_TEA_1_7722, Items.TEAPOT_7726, "You empty the contents of the pot onto the floor", Sounds.LIQUID_2401), + BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + BURNT_GNOMEBOWL(Items.BURNT_GNOMEBOWL_2175, Items.GNOMEBOWL_MOULD_2166, "You empty the contents of the gnomebowl onto the floor."), + BURNT_EGG(Items.BURNT_EGG_7090, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."), + 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") || item.name.contains("Super ")) { + 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..c642372ff --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt @@ -0,0 +1,94 @@ +package content.global.handlers.item + +import core.ServerConstants +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +class EnchantJewelleryTabListener : InteractionListener { + private val LVL_1_ENCHANT = mapOf( + Items.SAPPHIRE_RING_1637 to Items.RING_OF_RECOIL_2550, + Items.SAPPHIRE_NECKLACE_1656 to Items.GAMES_NECKLACE8_3853, + Items.SAPPHIRE_AMULET_1694 to Items.AMULET_OF_MAGIC_1727, + Items.SAPPHIRE_BRACELET_11072 to Items.BRACELET_OF_CLAY_11074 + ) + private val LVL_2_ENCHANT = mapOf( + Items.EMERALD_RING_1639 to Items.RING_OF_DUELLING8_2552, + Items.EMERALD_NECKLACE_1658 to Items.BINDING_NECKLACE_5521, + Items.EMERALD_AMULET_1696 to Items.AMULET_OF_DEFENCE_1729, + Items.EMERALD_BRACELET_11076 to Items.CASTLEWAR_BRACE3_11079 + ) + private val LVL_3_ENCHANT = mapOf( + Items.RUBY_RING_1641 to Items.RING_OF_FORGING_2568, + Items.RUBY_NECKLACE_1660 to Items.DIGSITE_PENDANT_5_11194, + Items.RUBY_AMULET_1698 to Items.AMULET_OF_STRENGTH_1725, + Items.RUBY_BRACELET_11085 to Items.INOCULATION_BRACE_11088 + ) + private val LVL_4_ENCHANT = mapOf( + Items.DIAMOND_RING_1643 to Items.RING_OF_LIFE_2570, + Items.DIAMOND_NECKLACE_1662 to Items.PHOENIX_NECKLACE_11090, + Items.DIAMOND_AMULET_1700 to Items.AMULET_OF_POWER_1731, + Items.DIAMOND_BRACELET_11092 to Items.FORINTHRY_BRACE5_11095 + ) + private val LVL_5_ENCHANT = mapOf( + Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH_2572, + Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE_11113, + Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY_1704, + Items.DRAGON_BRACELET_11115 to Items.COMBAT_BRACELET_11126 + ) + private val LVL_6_ENCHANT = mapOf( + Items.ONYX_RING_6575 to Items.RING_OF_STONE_6583, + Items.ONYX_NECKLACE_6577 to Items.BERSERKER_NECKLACE_11128, + Items.ONYX_AMULET_6581 to Items.AMULET_OF_FURY_6585, + Items.ONYX_BRACELET_11130 to Items.REGEN_BRACELET_11133 + ) + private val TAB_MAPPING = arrayOf( + Pair(Items.ENCHANT_SAPPHIRE_8016, LVL_1_ENCHANT), + Pair(Items.ENCHANT_EMERALD_8017, LVL_2_ENCHANT), + Pair(Items.ENCHANT_RUBY_8018, LVL_3_ENCHANT), + Pair(Items.ENCHANT_DIAMOND_8019, LVL_4_ENCHANT), + Pair(Items.ENCHANT_DRAGONSTN_8020, LVL_5_ENCHANT), + Pair(Items.ENCHANT_ONYX_8021, LVL_6_ENCHANT) + ) + + override fun defineListeners() { + for ((tablet, mapping) in TAB_MAPPING) { + on(tablet, IntType.ITEM, "break") { player, _ -> + sendMessage(player, "Try using the tablet on the item instead.") //TODO authentic message + return@on true + } + for ((unenchanted, enchanted) in mapping) { + onUseWith(IntType.ITEM, tablet, unenchanted) { player, tabItem, node -> + var product = enchanted + if (product == Items.RING_OF_WEALTH_2572 && ServerConstants.RING_OF_WEALTH_TELEPORT) { + product = Items.RING_OF_WEALTH_14638 + } + if (removeItem(player, Item(tabItem.id))) { + closeAllInterfaces(player) + playAudio(player, Sounds.POH_TABLET_BREAK_979) + val anim = Animation(Animations.POH_TABLET_BREAK_4069) + animate(player, anim, true) + delayEntity(player, anim.duration) + queueScript(player, anim.duration, QueueStrength.SOFT) { + val item = node.asItem() + val ret = replaceSlot(player, item.slot, Item(product), item) + if (ret != item) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Unknown slot-replacement problem when enchanting jewellery (adding $product replaced $ret rather than $item)") + } + return@queueScript stopExecuting(player) + } + } + return@onUseWith true + } + } + } + } +} 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..e376d2855 100644 --- a/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt +++ b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt @@ -1,7 +1,7 @@ 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 import core.game.dialogue.DialogueFile import core.game.dialogue.IfTopic @@ -38,12 +38,8 @@ class EnchantedGemDialogue() : DialogueFile() { if(!hasSlayerTask(player!!)) { npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "You need something new to hunt. Come and see me when you can and I'll give you a new task.").also { stage = 1 } } else { - 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.") - } - setVarbit(player!!, 2502, 0, getSlayerTaskFlags(player!!) shr 4) + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill ${SlayerUtils.pluralise(getSlayerTaskName(player!!))}; only ${getSlayerTaskKillsRemaining(player!!)} more to go.") + setVarp(player!!, 2502, getSlayerTaskFlags(player!!) shr 4) stage = 1 } } @@ -63,4 +59,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..549b69051 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) { @@ -45,19 +50,19 @@ class EnchantedJewelleryListener : InteractionListener { jewellery.use(player, item, 0, isEquipped) return } - openDialogue(player,EnchantedJewelleryDialogueFile(jewellery,item,isEquipped)) + openDialogue(player,EnchantedJewelleryDialogueFile(jewellery, item, true, isEquipped)) } } - class EnchantedJewelleryDialogueFile(val jewellery: EnchantedJewellery, val item: Item, val isEquipped: Boolean) : DialogueFile() { + class EnchantedJewelleryDialogueFile(val jewellery: EnchantedJewellery, val item: Item, val replace: Boolean, val isEquipped: Boolean) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { when (stage) { START_DIALOGUE -> { - interpreter!!.sendOptions("Where would you like to go?", *jewellery.options) + interpreter!!.sendOptions("Where would you like to teleport to?", *jewellery.options) stage++ } 1 -> end().also { jewellery.use(player!!, item, buttonID - 1, isEquipped) } } } } -} \ 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..586967573 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, "Choose") { 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..61ac1b029 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt @@ -0,0 +1,31 @@ +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 + +class HouseTeleTabOptionListener : InteractionListener { + override fun defineListeners() { + val homeTabID = 8013 + on(homeTabID, IntType.ITEM, "break") {player, node -> + 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 + } + closeInterface(player) + lock(player, 5) + if (inInventory(player, node.id)) { + player.houseManager.preEnter(player, false) + val location = player.houseManager.getEnterLocation() + if (teleport(player, location, TeleportManager.TeleportType.TELETABS)) { + removeItem(player, Item(node.id, 1)) + player.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..9fbd4060b 100644 --- a/Server/src/main/content/global/handlers/item/ImplingJarListener.kt +++ b/Server/src/main/content/global/handlers/item/ImplingJarListener.kt @@ -7,6 +7,7 @@ import core.api.utils.WeightBasedTable import core.api.utils.WeightedItem import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.global.activity.ttrail.TreasureTrailManager class ImplingJarListener : InteractionListener { @@ -15,8 +16,14 @@ class ImplingJarListener : InteractionListener { override fun defineListeners() { on(JARS, IntType.ITEM, "loot"){ player, node -> val jar = node.asItem() + val table = ImplingLoot.forId(jar.id) ?: return@on false + val effectiveTable = if (TreasureTrailManager.getInstance(player).hasClue()) { + table.excluding(*WeightBasedTable.CLUE_SCROLL_SLOTS) + } else { + table + } - val loot = ImplingLoot.forId(jar.id)?.roll()?.firstOrNull() ?: return@on false + val loot = effectiveTable.roll().firstOrNull() ?: return@on false if(removeItem(player, jar, Container.INVENTORY)) { addItemOrDrop(player, loot.id, loot.amount) @@ -184,7 +191,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 +238,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 +247,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/LightSourceListeners.kt b/Server/src/main/content/global/handlers/item/LightSourceListeners.kt new file mode 100644 index 000000000..989746869 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/LightSourceListeners.kt @@ -0,0 +1,32 @@ +package content.global.handlers.item + +import content.data.LightSource +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import org.rs09.consts.Items +import kotlin.collections.toIntArray + +class LightSourceListeners : InteractionListener { + override fun defineListeners() { + val lightSourceProducts = LightSource.values().map { it.product.id }.toIntArray() + + on(lightSourceProducts, ITEM, "drop") { player, light -> + val active = LightSource.getActiveLightSource(player).product.id + if (player.location.isInRegion(10648) && light.id == active) { //Temple of Ikov dungeon + sendMessage(player, "Dropping the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.") + return@on false + } + val removed = removeItem(player, light.id) + if (removed) { + var droppedId = light.id + if (light.id == Items.MINING_HELMET_5013) { + droppedId = Items.MINING_HELMET_5014 + sendMessage(player, "The helmet goes out as you drop it.") + } + produceGroundItem(player, droppedId) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/MiningHelmetListener.kt b/Server/src/main/content/global/handlers/item/MiningHelmetListener.kt deleted file mode 100644 index 755a1cbde..000000000 --- a/Server/src/main/content/global/handlers/item/MiningHelmetListener.kt +++ /dev/null @@ -1,17 +0,0 @@ -package content.global.handlers.item - -import core.api.* -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import org.rs09.consts.Items - -class MiningHelmetListener : InteractionListener { - override fun defineListeners() { - on(Items.MINING_HELMET_5013, IntType.ITEM, "drop") { player, _ -> - val removed = removeItem(player, Items.MINING_HELMET_5013) - if (removed) produceGroundItem(player, Items.MINING_HELMET_5014) - sendMessage(player, "The helmet goes out as you drop it.") - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/MorphItemListener.kt b/Server/src/main/content/global/handlers/item/MorphItemListener.kt new file mode 100644 index 000000000..b0b2e3857 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/MorphItemListener.kt @@ -0,0 +1,77 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +/** + * Handles morphing with the Ring of Stone and Easter Ring. + */ + +class MorphItemListener : InteractionListener, InterfaceListener { + + // the morphing items + private val morphItems = intArrayOf(Items.RING_OF_STONE_6583, Items.EASTER_RING_7927) + + // the NPCs to morph into + private val easterEggs = intArrayOf( + NPCs.EGG_3689, + NPCs.EGG_3690, + NPCs.EGG_3691, + NPCs.EGG_3692, + NPCs.EGG_3693, + NPCs.EGG_3694 + ) + private val stone = intArrayOf(NPCs.ROCKS_2626) + + // the "unmorph" interface + private val morphIface = 375 + + // equipping the morph items + override fun defineListeners() { + onEquip(morphItems) { player, item -> + morph(player, item.id) + return@onEquip false + } + } + + // the interface that appears when you morph + override fun defineInterfaceListeners() { + + // hitting the 'unmorph' button + on(morphIface){ player, _, _, buttonID, _, _ -> + when(buttonID) { + 3 -> closeAllInterfaces(player) + } + return@on true + } + + // after the interface closes + onClose(morphIface) { player, _ -> + unmorph(player) + return@onClose true + } + } + + // morphs the player + private fun morph(player: Player, item: Int) { + val morphId = when (item) { + Items.RING_OF_STONE_6583 -> stone.random() + else -> easterEggs.random() + } + playAudio(player, Sounds.EASTER06_HUMAN_INTO_EGG_1520) + stopWalk(player) + closeAllInterfaces(player) + player.appearance.transformNPC(morphId) + openInterface(player, morphIface) + } + + // unmorphs the player + private fun unmorph(player: Player) { + player.appearance.transformNPC(-1) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java b/Server/src/main/content/global/handlers/item/MorphItemPlugin.java deleted file mode 100644 index 29c84d805..000000000 --- a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java +++ /dev/null @@ -1,109 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.component.CloseEvent; -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.item.Item; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; -import core.tools.RandomFunction; - -/** - * Handles a morph item. - * @author Vexia - */ -@Initializable -public class MorphItemPlugin implements Plugin { - - /** - * The easter egg ids. - */ - protected static final int[] EASTER_EGG_IDS = new int[] { 3689, 3690, 3691, 3692, 3693, 3694 }; - - /** - * The morph component. - */ - private static final Component COMPONENT = new Component(375).setCloseEvent(new CloseEvent() { - - @Override - public boolean close(Player player, Component c) { - unmorph(player); - return true; - } - - }); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(7927).getHandlers().put("equipment", this); - ItemDefinition.forId(6583).getHandlers().put("equipment", this); - ClassScanner.definePlugin(new MorphInterfacePlugin()); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - final Item item = (Item) args[1]; - switch (identifier) { - case "equip": - morph(player, item); - return false; - } - return true; - } - - /** - * Morphs the player. - * @param player the player. - * @param item the item. - */ - private void morph(Player player, Item item) { - int morphId = item.getId() == 6583 ? 2626 : EASTER_EGG_IDS[RandomFunction.random(EASTER_EGG_IDS.length)]; - player.getInterfaceManager().close(); - player.getAppearance().transformNPC(morphId); - player.getInterfaceManager().removeTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); - player.getLocks().lockMovement(GameWorld.getTicks() + 900000000); - player.getLocks().lockInteractions(GameWorld.getTicks() + 90000000); - player.getLocks().lockTeleport(GameWorld.getTicks() + 900000000); - player.getInterfaceManager().openSingleTab(COMPONENT); - player.getAppearance().sync(); - player.getWalkingQueue().reset(); - } - - /** - * Unmorphs the player. - * @param player the player. - */ - private static void unmorph(Player player) { - player.getAppearance().transformNPC(-1); - player.unlock(); - player.getInterfaceManager().restoreTabs(); - } - - /** - * Handles the morph interface plugin. - * @author Vexia - */ - public class MorphInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(375).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - player.getInterfaceManager().closeSingleTab(); - return true; - } - - } - -} 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/RingForgingOperateListener.kt b/Server/src/main/content/global/handlers/item/RingForgingOperateListener.kt new file mode 100644 index 000000000..f9ed2c366 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/RingForgingOperateListener.kt @@ -0,0 +1,22 @@ +package content.global.handlers.item + +import core.api.getAttribute +import core.api.sendMessage +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class RingForgingOperateListener: InteractionListener { + override fun defineListeners() { + + on(Items.RING_OF_FORGING_2568, IntType.ITEM, "operate") { player, node -> + + //Attribute set at SmeltingPulse.java + val charges = getAttribute(player, "ringOfForgingCharges", 140) + + sendMessage(player, "You can smelt $charges more iron ore before the ring disintegrates.") + + return@on true + } + } +} \ No newline at end of file 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/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..9cc72f6c2 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/TeleTabsListener.kt @@ -0,0 +1,69 @@ +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 + +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), + BLOOD_ALTAR_TELEPORT(13610, Location.create(3559, 9778, 0), 0.0), + 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), + DEATH_ALTAR_TELEPORT(13609, Location.create(1863, 4639, 0), 0.0), + 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), + 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), + VARROCK_TELEPORT(8007, Location.create(3212, 3423, 0), 35.00), + WATCH_TOWER_TELEPORT(8012, Location.create(2548, 3114, 0), 68.00), + 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..a7a276d41 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/GoblinBook.kt @@ -0,0 +1,254 @@ +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.api.* +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display) + storeBookInHouse(player, node) + 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..672890422 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + storeBookInHouse(player, node) + 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..c72efce0f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/StrongholdNotes.kt @@ -0,0 +1,138 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + storeBookInHouse(player, node) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt new file mode 100644 index 000000000..5cdbb874a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipment.kt @@ -0,0 +1,244 @@ +package content.global.handlers.item.equipment + +import core.game.node.item.Item +import org.rs09.consts.Items + +/** + * Barrows equipment information and utilities. + * @author 'Vexia - original code + * @author Damighty - Kotlin conversion and refactor + */ +object BarrowsEquipment { + // Barrows equipment lasts for 15 hours of combat. Each piece has 4 degradation tiers (100, 75, 50, 25). + const val DEGRADATION_TICKS_PER_TIER = (15 * 6000) / 4 // 22500 ticks per tier + private const val MAX_DURABILITY = DEGRADATION_TICKS_PER_TIER * 4 + + /** + * A data class for each Barrows piece. Holds all information related to a specific piece of Barrows gear. + * + * @param brother The Barrows brother this item belongs to. + * @param equipmentType The slot type ("weapon", "body", "legs", "helm"). + * @param itemName The formatted name of the item. + * @param baseRepairCost The full repair cost in GP. + * @param degradationStates A list of item IDs, from fully repaired (index 0) to broken (index 5). + */ + data class BarrowsItemDefinition( + val brother: String, + val equipmentType: String, + val itemName: String, + val baseRepairCost: Int, + val degradationStates: List + ) { + val repairedId: Int = degradationStates.first() + val brokenId: Int = degradationStates.last() + + /** Checks if a given item ID belongs to this specific equipment set. */ + fun contains(itemId: Int): Boolean = itemId in degradationStates + + /** Gets the degradation index for a given item ID (0=repaired, 1=100, ..., 5=broken). */ + fun getDegradationIndex(itemId: Int): Int = degradationStates.indexOf(itemId) + } + + /** All Barrows equipment data. */ + private val barrowsItemDefinitions = listOf( + // Dharok + BarrowsItemDefinition("dharok", "helm", "Dharok's helm", 60_000, + listOf(Items.DHAROKS_HELM_4716, Items.DHAROKS_HELM_100_4880, + Items.DHAROKS_HELM_75_4881, Items.DHAROKS_HELM_50_4882, + Items.DHAROKS_HELM_25_4883, Items.DHAROKS_HELM_0_4884)), + BarrowsItemDefinition("dharok", "weapon", "Dharok's greataxe", 100_000, + listOf(Items.DHAROKS_GREATAXE_4718, Items.DHAROKS_AXE_100_4886, + Items.DHAROKS_AXE_75_4887, Items.DHAROKS_AXE_50_4888, + Items.DHAROKS_AXE_25_4889, Items.DHAROKS_AXE_0_4890)), + BarrowsItemDefinition("dharok", "body", "Dharok's platebody", 90_000, + listOf(Items.DHAROKS_PLATEBODY_4720, Items.DHAROKS_BODY_100_4892, + Items.DHAROKS_BODY_75_4893, Items.DHAROKS_BODY_50_4894, + Items.DHAROKS_BODY_25_4895, Items.DHAROKS_BODY_0_4896)), + BarrowsItemDefinition("dharok", "legs", "Dharok's platelegs", 80_000, + listOf(Items.DHAROKS_PLATELEGS_4722, Items.DHAROKS_LEGS_100_4898, + Items.DHAROKS_LEGS_75_4899, Items.DHAROKS_LEGS_50_4900, + Items.DHAROKS_LEGS_25_4901, Items.DHAROKS_LEGS_0_4902)), + // Guthan + BarrowsItemDefinition("guthan", "helm", "Guthan's helm", 60_000, + listOf(Items.GUTHANS_HELM_4724, Items.GUTHANS_HELM_100_4904, + Items.GUTHANS_HELM_75_4905, Items.GUTHANS_HELM_50_4906, + Items.GUTHANS_HELM_25_4907, Items.GUTHANS_HELM_0_4908)), + BarrowsItemDefinition("guthan", "weapon", "Guthan's warspear", 100_000, + listOf(Items.GUTHANS_WARSPEAR_4726, Items.GUTHANS_SPEAR_100_4910, + Items.GUTHANS_SPEAR_75_4911, Items.GUTHANS_SPEAR_50_4912, + Items.GUTHANS_SPEAR_25_4913, Items.GUTHANS_SPEAR_0_4914)), + BarrowsItemDefinition("guthan", "body", "Guthan's platebody", 90_000, + listOf(Items.GUTHANS_PLATEBODY_4728, Items.GUTHANS_BODY_100_4916, + Items.GUTHANS_BODY_75_4917, Items.GUTHANS_BODY_50_4918, + Items.GUTHANS_BODY_25_4919, Items.GUTHANS_BODY_0_4920)), + BarrowsItemDefinition("guthan", "legs", "Guthan's chainskirt", 80_000, + listOf(Items.GUTHANS_CHAINSKIRT_4730, Items.GUTHANS_SKIRT_100_4922, + Items.GUTHANS_SKIRT_75_4923, Items.GUTHANS_SKIRT_50_4924, + Items.GUTHANS_SKIRT_25_4925, Items.GUTHANS_SKIRT_0_4926)), + // Torag + BarrowsItemDefinition("torag", "helm", "Torag's helm", 60_000, + listOf(Items.TORAGS_HELM_4745, Items.TORAGS_HELM_100_4952, + Items.TORAGS_HELM_75_4953, Items.TORAGS_HELM_50_4954, + Items.TORAGS_HELM_25_4955, Items.TORAGS_HELM_0_4956)), + BarrowsItemDefinition("torag", "weapon", "Torag's hammers", 100_000, + listOf(Items.TORAGS_HAMMERS_4747, Items.TORAGS_HAMMER_100_4958, + Items.TORAGS_HAMMER_75_4959, Items.TORAGS_HAMMER_50_4960, + Items.TORAGS_HAMMER_25_4961, Items.TORAGS_HAMMER_0_4962)), + BarrowsItemDefinition("torag", "body", "Torag's platebody", 90_000, + listOf(Items.TORAGS_PLATEBODY_4749, Items.TORAGS_BODY_100_4964, + Items.TORAGS_BODY_75_4965, Items.TORAGS_BODY_50_4966, + Items.TORAGS_BODY_25_4967, Items.TORAGS_BODY_0_4968)), + BarrowsItemDefinition("torag", "legs", "Torag's platelegs", 80_000, + listOf(Items.TORAGS_PLATELEGS_4751, Items.TORAGS_LEGS_100_4970, + Items.TORAGS_LEGS_75_4971, Items.TORAGS_LEGS_50_4972, + Items.TORAGS_LEGS_25_4973, Items.TORAGS_LEGS_0_4974)), + // Verac + BarrowsItemDefinition("verac", "helm", "Verac's helm", 60_000, + listOf(Items.VERACS_HELM_4753, Items.VERACS_HELM_100_4976, + Items.VERACS_HELM_75_4977, Items.VERACS_HELM_50_4978, + Items.VERACS_HELM_25_4979, Items.VERACS_HELM_0_4980)), + BarrowsItemDefinition("verac", "weapon", "Verac's flail", 100_000, + listOf(Items.VERACS_FLAIL_4755, Items.VERACS_FLAIL_100_4982, + Items.VERACS_FLAIL_75_4983, Items.VERACS_FLAIL_50_4984, + Items.VERACS_FLAIL_25_4985, Items.VERACS_FLAIL_0_4986)), + BarrowsItemDefinition("verac", "body", "Verac's brassard", 90_000, + listOf(Items.VERACS_BRASSARD_4757, Items.VERACS_TOP_100_4988, + Items.VERACS_TOP_75_4989, Items.VERACS_TOP_50_4990, + Items.VERACS_TOP_25_4991, Items.VERACS_TOP_0_4992)), + BarrowsItemDefinition("verac", "legs", "Verac's plateskirt", 80_000, + listOf(Items.VERACS_PLATESKIRT_4759, Items.VERACS_SKIRT_100_4994, + Items.VERACS_SKIRT_75_4995, Items.VERACS_SKIRT_50_4996, + Items.VERACS_SKIRT_25_4997, Items.VERACS_SKIRT_0_4998)), + // Ahrim + BarrowsItemDefinition("ahrim", "helm", "Ahrim's hood", 60_000, + listOf(Items.AHRIMS_HOOD_4708, Items.AHRIMS_HOOD_100_4856, + Items.AHRIMS_HOOD_75_4857, Items.AHRIMS_HOOD_50_4858, + Items.AHRIMS_HOOD_25_4859, Items.AHRIMS_HOOD_0_4860)), + BarrowsItemDefinition("ahrim", "weapon", "Ahrim's staff", 100_000, + listOf(Items.AHRIMS_STAFF_4710, Items.AHRIMS_STAFF_100_4862, + Items.AHRIMS_STAFF_75_4863, Items.AHRIMS_STAFF_50_4864, + Items.AHRIMS_STAFF_25_4865, Items.AHRIMS_STAFF_0_4866)), + BarrowsItemDefinition("ahrim", "body", "Ahrim's robetop", 90_000, + listOf(Items.AHRIMS_ROBETOP_4712, Items.AHRIMS_TOP_100_4868, + Items.AHRIMS_TOP_75_4869, Items.AHRIMS_TOP_50_4870, + Items.AHRIMS_TOP_25_4871, Items.AHRIMS_TOP_0_4872)), + BarrowsItemDefinition("ahrim", "legs", "Ahrim's robeskirt", 80_000, + listOf(Items.AHRIMS_ROBESKIRT_4714, Items.AHRIMS_SKIRT_100_4874, + Items.AHRIMS_SKIRT_75_4875, Items.AHRIMS_SKIRT_50_4876, + Items.AHRIMS_SKIRT_25_4877, Items.AHRIMS_SKIRT_0_4878)), + // Karil + BarrowsItemDefinition("karil", "helm", "Karil's coif", 60_000, + listOf(Items.KARILS_COIF_4732, Items.KARILS_COIF_100_4928, + Items.KARILS_COIF_75_4929, Items.KARILS_COIF_50_4930, + Items.KARILS_COIF_25_4931, Items.KARILS_COIF_0_4932)), + BarrowsItemDefinition("karil", "weapon", "Karil's crossbow", 100_000, + listOf(Items.KARILS_CROSSBOW_4734, Items.KARILS_X_BOW_100_4934, + Items.KARILS_X_BOW_75_4935, Items.KARILS_X_BOW_50_4936, + Items.KARILS_X_BOW_25_4937, Items.KARILS_X_BOW_0_4938)), + BarrowsItemDefinition("karil", "body", "Karil's leathertop", 90_000, + listOf(Items.KARILS_LEATHERTOP_4736, Items.KARILS_TOP_100_4940, + Items.KARILS_TOP_75_4941, Items.KARILS_TOP_50_4942, + Items.KARILS_TOP_25_4943, Items.KARILS_TOP_0_4944)), + BarrowsItemDefinition("karil", "legs", "Karil's leatherskirt", 80_000, + listOf(Items.KARILS_LEATHERSKIRT_4738, Items.KARILS_SKIRT_100_4946, + Items.KARILS_SKIRT_75_4947, Items.KARILS_SKIRT_50_4948, + Items.KARILS_SKIRT_25_4949, Items.KARILS_SKIRT_0_4950)) + ) + + /** Cached access to an item's full definition from its ID */ + private val itemIdToDefinitionMap: Map by lazy { + barrowsItemDefinitions.flatMap { def -> + def.degradationStates.map { id -> id to def } + }.toMap() + } + + /** Gets all degradation state arrays for degradation registration */ + fun getAllEquipmentSets(): Collection = barrowsItemDefinitions.map { it.degradationStates.toIntArray() } + + /** Gets all repairable Barrows item IDs (anything not fully repaired) */ + fun getAllRepairableBarrowsIds(): List = itemIdToDefinitionMap.filter { !isFullyRepaired(it.key) }.keys.toList() + + /** Gets the full definition for a Barrows item */ + @JvmStatic + fun getDefinition(itemId: Int): BarrowsItemDefinition? = itemIdToDefinitionMap[itemId] + + /** Checks if an item ID is any Barrows item */ + @JvmStatic + fun isBarrowsItem(itemId: Int): Boolean = itemId in itemIdToDefinitionMap + + /** Checks if a Barrows item is fully repaired */ + @JvmStatic + fun isFullyRepaired(itemId: Int): Boolean = getDefinition(itemId)?.repairedId == itemId + + /** Checks if a Barrows item is broken */ + @JvmStatic + fun isBroken(itemId: Int): Boolean = getDefinition(itemId)?.brokenId == itemId + + /** + * Calculates the repair cost for a degraded Barrows item + * @param item The degraded Barrows item + * @return The repair cost in GP, or -1 if the item cannot be repaired + */ + fun getRepairCost(item: Item): Int { + val def = getDefinition(item.id) ?: return -1 + if (isFullyRepaired(item.id)) return -1 + + val totalRemainingDurability = calculateTotalRemainingDurability(item, def) + val durabilityLost = MAX_DURABILITY - totalRemainingDurability + val cost = ((durabilityLost.toDouble() / MAX_DURABILITY) * def.baseRepairCost).toInt() + + return cost + } + + /** + * Reduces the durability of a Barrows item by 20% of its total remaining durability + * @param item The Barrows item + * @return The item with reduced durability, or null if the item is not a valid Barrows piece + */ + @JvmStatic + fun graveDeathDurabilityReduction(item: Item): Item? { + val def = getDefinition(item.id) ?: return null + if (isBroken(item.id)) return item + + val totalRemainingDurability = calculateTotalRemainingDurability(item, def) + val durabilityReduction = (totalRemainingDurability * 0.2).toInt() + val newRemainingDurability = totalRemainingDurability - durabilityReduction + + return createItemFromDurability(def, newRemainingDurability, item.amount) + } + + /** + * Calculates the total remaining durability ticks (charge) for a Barrows item + */ + private fun calculateTotalRemainingDurability(item: Item, def: BarrowsItemDefinition): Int { + return when (val index = def.getDegradationIndex(item.id)) { + -1 -> 0 // Invalid + 0 -> MAX_DURABILITY // Fully repaired + 5 -> 0 // Broken + else -> { + // For degraded items (100, 75, 50, 25), durability is the number of fully remaining tiers below it, plus the charge of the current tier. + // Index 1 (100) has 3 tiers below it. Index 4 (25) has 0 tiers below it. + val remainingTiers = 4 - index + (remainingTiers * DEGRADATION_TICKS_PER_TIER) + item.charge + } + } + } + + /** + * Creates a new Item instance based on a total durability value + */ + private fun createItemFromDurability(def: BarrowsItemDefinition, durability: Int, amount: Int): Item { + if (durability <= 0) { + return Item(def.brokenId, amount) + } + // Tier is 1-indexed (1=25, 2=50, 3=75, 4=100) + val tier = ((durability - 1) / DEGRADATION_TICKS_PER_TIER) + 1 + // Degradation index is 4-indexed (4=25, 3=50, 2=75, 1=100) + val degradationIndex = 5 - tier + + val newId = def.degradationStates[degradationIndex] + val newCharge = (durability - 1) % DEGRADATION_TICKS_PER_TIER + 1 + + return Item(newId, amount, newCharge) + } +} diff --git a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt index d7e57ab9e..1a0960711 100644 --- a/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt +++ b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt @@ -4,73 +4,16 @@ import core.plugin.Initializable import core.plugin.Plugin @Initializable -class BarrowsEquipmentRegister : Plugin{ - public companion object { - // Barrows equipment lasts for 15 hours of combat, and each piece has 4 stages of degredation - @JvmField - public val TICKS = (15 * 6000) / 4 - } - - val DHAROK_HELM = arrayOf(4716,4880,4881,4882,4883,4884) - val DHAROK_AXE = arrayOf(4718,4886,4887,4888,4889,4890) - val DHAROK_BODY = arrayOf(4720,4892,4893,4894,4895,4896) - val DHAROK_LEGS = arrayOf(4722,4898,4899,4900,4901,4902) - - val GUTHAN_HELM = arrayOf(4724,4904,4905,4906,4907,4908) - val GUTHAN_SPEAR = arrayOf(4726,4910,4911,4912,4913,4914) - val GUTHAN_BODY = arrayOf(4728,4916,4917,4918,4919,4920) - val GUTHAN_SKIRT = arrayOf(4730,4922,4923,4924,4925,4926) - - val TORAG_HELM = arrayOf(4745,4952,4953,4954,4955,4956) - val TORAG_HAMMER = arrayOf(4747,4958,4959,4960,4961,4962) - val TORAG_BODY = arrayOf(4749,4964,4965,4966,4967,4968) - val TORAG_LEGS = arrayOf(4751,4970,4971,4972,4973,4974) - - val VERAC_HELM = arrayOf(4753,4976,4977,4978,4979,4980) - val VERAC_FLAIL = arrayOf(4755,4982,4983,4984,4985,4986) - val VERAC_BRASS = arrayOf(4757,4988,4989,4990,4991,4992) - val VERAC_SKIRT = arrayOf(4759,4994,4995,4996,4997,4998) - - val AHRIM_HOOD = arrayOf(4708,4856,4857,4858,4859,4860) - val AHRIM_STAFF = arrayOf(4710,4862,4863,4864,4865,4866) - val AHRIM_TOP = arrayOf(4712,4868,4869,4870,4871,4872) - val AHRIM_SKIRT = arrayOf(4714,4874,4875,4876,4877,4878) - - val KARIL_COIF = arrayOf(4732,4928,4929,4930,4931,4932) - val KARIL_CBOW = arrayOf(4734,4934,4935,4936,4937,4938) - val KARIL_TOP = arrayOf(4736,4940,4941,4942,4943,4944) - val KARIL_SKIRT = arrayOf(4738,4946,4947,4948,4949,4950) - +class BarrowsEquipmentRegister : Plugin { override fun newInstance(arg: Any?): Plugin { - EquipmentDegrader.registerSet(TICKS, AHRIM_HOOD) - EquipmentDegrader.registerSet(TICKS, AHRIM_STAFF) - EquipmentDegrader.registerSet(TICKS, AHRIM_TOP) - EquipmentDegrader.registerSet(TICKS, AHRIM_SKIRT) - EquipmentDegrader.registerSet(TICKS, KARIL_COIF) - EquipmentDegrader.registerSet(TICKS, KARIL_CBOW) - EquipmentDegrader.registerSet(TICKS, KARIL_TOP) - EquipmentDegrader.registerSet(TICKS, KARIL_SKIRT) - EquipmentDegrader.registerSet(TICKS, DHAROK_HELM) - EquipmentDegrader.registerSet(TICKS, DHAROK_AXE) - EquipmentDegrader.registerSet(TICKS, DHAROK_BODY) - EquipmentDegrader.registerSet(TICKS, DHAROK_LEGS) - EquipmentDegrader.registerSet(TICKS, GUTHAN_HELM) - EquipmentDegrader.registerSet(TICKS, GUTHAN_SPEAR) - EquipmentDegrader.registerSet(TICKS, GUTHAN_BODY) - EquipmentDegrader.registerSet(TICKS, GUTHAN_SKIRT) - EquipmentDegrader.registerSet(TICKS, TORAG_HELM) - EquipmentDegrader.registerSet(TICKS, TORAG_HAMMER) - EquipmentDegrader.registerSet(TICKS, TORAG_BODY) - EquipmentDegrader.registerSet(TICKS, TORAG_LEGS) - EquipmentDegrader.registerSet(TICKS, VERAC_HELM) - EquipmentDegrader.registerSet(TICKS, VERAC_FLAIL) - EquipmentDegrader.registerSet(TICKS, VERAC_BRASS) - EquipmentDegrader.registerSet(TICKS, VERAC_SKIRT) + BarrowsEquipment.getAllEquipmentSets().forEach { + degradationSet -> + EquipmentDegrader.registerSet(BarrowsEquipment.DEGRADATION_TICKS_PER_TIER, degradationSet.toTypedArray()) + } return this } override fun fireEvent(identifier: String?, vararg args: Any?): Any { return Unit } - } diff --git a/Server/src/main/content/global/handlers/item/equipment/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..a0801835e 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,8 +12,8 @@ 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.ArrayList; import java.util.List; /** @@ -34,13 +32,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(); @@ -64,18 +55,20 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { entity.getProperties().getCombatPulse().stop(); return -1; } - @SuppressWarnings("rawtypes") - List list = victim instanceof NPC ? RegionManager.getSurroundingNPCs(victim, 14, entity) : RegionManager.getSurroundingPlayers(victim, 14, entity); - BattleState[] targets = new BattleState[list.size()]; + + // ! Initial capacity of 14, but has dynamic size? i.e. we are not restricting to a max of 14 targets hit here now + List targetCandidates = new ArrayList<>(14); + targetCandidates.addAll(RegionManager.getSurroundingNPCs(victim, 14, entity)); + targetCandidates.addAll(RegionManager.getSurroundingPlayers(victim, 14, entity)); + BattleState[] targets = new BattleState[targetCandidates.size()]; int count = 0; - for (Object o : list) { - Entity e = (Entity) o; - if (canSwing(entity, e) != InteractionType.NO_INTERACT) { + for (Entity e : targetCandidates) { + if (canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { BattleState s = targets[count++] = new BattleState(entity, e); s.setStyle(CombatStyle.RANGE); int hit = 0; 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); } @@ -103,7 +96,7 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { } } entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true); - if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_DEFENSIVE) { + if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_LONG_RANGE) { entity.getSkills().addExperience(Skills.RANGE, hit * 2, true); entity.getSkills().addExperience(Skills.DEFENCE, hit * 2, true); } else { diff --git a/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java index 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..ba3ff8ed0 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. @@ -46,7 +49,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements @Override public void impact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s); @@ -78,13 +81,13 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements int count = 0; for (Object o : list) { Entity e = (Entity) o; - if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) { + if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { BattleState s = targets[count++] = new BattleState(entity, e); int hit = 0; if (isAccurateImpact(entity, e)) { - hit = RandomFunction.random(calculateHit(entity, e, 1.0)); + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); } - s.setStyle(CombatStyle.MELEE); + s.setStyle(CombatStyle.MELEE); s.setEstimatedHit(hit); } } @@ -94,12 +97,13 @@ 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); } @Override public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().animate(victim.getProperties().getDefenceAnimation()); 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..2595b0479 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. @@ -46,7 +49,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements @Override public void impact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s); @@ -79,11 +82,11 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements int count = 0; for (Object o : list) { Entity e = (Entity) o; - if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) { + if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { BattleState s = targets[count++] = new BattleState(entity, e); int hit = 0; - hit = RandomFunction.random(calculateHit(entity, e, 1.0)); - s.setStyle(CombatStyle.MELEE); + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); + s.setStyle(CombatStyle.MELEE); s.setEstimatedHit(hit); } } @@ -93,16 +96,18 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529); entity.visualize(ANIMATION, GRAPHIC); } @Override public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().animate(victim.getProperties().getDefenceAnimation()); } + } 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..6c4b9cedc 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. @@ -61,16 +64,16 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug BattleState[] targets = getTargets(entity, victim, state); state.setTargets(targets); for (BattleState s : targets) { - s.setStyle(CombatStyle.MELEE); + s.setStyle(CombatStyle.MELEE); int hit = 0; - if (isAccurateImpact(entity, s.getVictim(), CombatStyle.MELEE, 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); } @@ -87,7 +90,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug */ private BattleState[] getTargets(Entity entity, Entity victim, BattleState state) { if (!entity.getProperties().isMultiZone() || !victim.getProperties().isMultiZone()) { - return new BattleState[] { state }; + return new BattleState[]{state}; } Location vl = victim.getLocation(); int x = vl.getX(); @@ -99,6 +102,9 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug if (n instanceof Familiar) { continue; } + if (!n.isAttackable(entity, CombatStyle.MELEE, false)) { + continue; + } if (n.getLocation().equals(vl.transform(dir.getStepY(), dir.getStepX(), 0)) || n.getLocation().equals(vl.transform(-dir.getStepY(), -dir.getStepX(), 0))) { l.add(new BattleState(entity, n)); if (l.size() >= 3) { @@ -124,7 +130,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug @Override public void impact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s); @@ -143,7 +149,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug @Override public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (state.getTargets() != null) { + if (state != null && state.getTargets() != null) { for (BattleState s : state.getTargets()) { if (s != null) { s.getVictim().animate(victim.getProperties().getDefenceAnimation()); @@ -156,6 +162,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..57f51641e 100644 --- a/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java +++ b/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java @@ -1,5 +1,6 @@ package content.global.handlers.item.toys; +import content.global.skill.construction.decoration.pohstorage.StorableFamily; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -27,7 +28,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 +44,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); + //filter out items the player already has in their bank, POH, inventory, or equipped //only send items if there are some to send if(reclaimables.length > 0) { @@ -68,18 +65,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) && !player.getPOHStorageState().getContainer(StorableFamily.TOY_BOX).contains(item.getId()) + && (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/CalquatKeg.kt b/Server/src/main/content/global/handlers/item/withitem/CalquatKeg.kt new file mode 100644 index 000000000..56b5cb77e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/CalquatKeg.kt @@ -0,0 +1,19 @@ +package content.global.handlers.item.withitem + +import core.api.addItem +import core.api.animate +import core.api.removeItem +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class CalquatKeg : InteractionListener{ + override fun defineListeners() { + onUseWith(ITEM, Items.CALQUAT_FRUIT_5980, Items.KNIFE_946) { player, _, _ -> + if (removeItem(player, Items.CALQUAT_FRUIT_5980)){ + animate(player,2290) + addItem(player, Items.CALQUAT_KEG_5769) + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt index 207102b5f..15d12ca62 100644 --- a/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt +++ b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt @@ -24,9 +24,11 @@ class CrystalKeyCreateListener : InteractionListener { return@onUseWith false } - addItem(player, Items.CRYSTAL_KEY_989) - sendMessage(player, "You join the loop half of a key and the tooth half of a key to make a crystal key.") + if (!addItem(player, Items.CRYSTAL_KEY_989)) { + return@onUseWith false + } + sendMessage(player, "You join the loop half of a key and the tooth half of a key to make a crystal key.") return@onUseWith true } } diff --git a/Server/src/main/content/global/handlers/item/withitem/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/GrindToothListener.kt b/Server/src/main/content/global/handlers/item/withitem/GrindToothListener.kt new file mode 100644 index 000000000..93a6989da --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/GrindToothListener.kt @@ -0,0 +1,24 @@ +package content.global.handlers.item.withitem + +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor +import core.game.node.item.Item + +class GrindToothListener : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.PESTLE_AND_MORTAR_233, Items.SUQAH_TOOTH_9079) { player, _, with -> + val item = with as Item + val res = replaceSlot(player, item.slot, Item(Items.GROUND_TOOTH_9082)) + if (res?.id == Items.SUQAH_TOOTH_9079) { + sendMessage(player, "You grind the suqah tooth to dust.") //https://www.youtube.com/watch?v=RdIcNH50v7I + } else { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Player ground item ${res?.name} instead of a suqah tooth - potential slot-based manipulation attempt") + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withitem/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/OilFishingRodListener.kt b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt index 1dcd15cd4..318f8a0a8 100644 --- a/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt +++ b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt @@ -15,10 +15,11 @@ class OilFishingRodListener : InteractionListener { override fun pulse(): Boolean { when (counter++) { 1 -> { - removeItem(player, used.asItem()) && removeItem(player, with.asItem()) - addItem(player, Items.VIAL_229) - addItem(player, Items.OILY_FISHING_ROD_1585) - sendMessage(player, "You rub the oil into the fishing rod.") + if (removeItem(player, used.asItem()) && removeItem(player, with.asItem()) && + addItem(player, Items.VIAL_229) && + addItem(player, Items.OILY_FISHING_ROD_1585)) { + sendMessage(player, "You rub the oil into the fishing rod.") + } } } return false @@ -35,9 +36,10 @@ class OilFishingRodListener : InteractionListener { when (counter++) { 0 -> player.animator.animate(Animation(364)) 3 -> { - removeItem(player, Items.THIN_SNAIL_3363) - removeItem(player, Items.SAMPLE_BOTTLE_3377) - addItem(player, Items.BLAMISH_SNAIL_SLIME_1581) + if (removeItem(player, Items.THIN_SNAIL_3363) && + removeItem(player, Items.SAMPLE_BOTTLE_3377)) { + addItem(player, Items.BLAMISH_SNAIL_SLIME_1581) + } } } return false diff --git a/Server/src/main/content/global/handlers/item/withitem/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/withitem/WatermelonOnSack.kt b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt index da829ed82..f332b0590 100644 --- a/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt +++ b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt @@ -14,11 +14,10 @@ class WatermelonOnSack : InteractionListener { override fun defineListeners() { onUseWith(IntType.ITEM, SACK, WATERMELON){ player, used, _ -> if(getStatLevel(player, Skills.FARMING) >= 23){ - removeItem(player,SACK, Container.INVENTORY) - removeItem(player,WATERMELON,Container.INVENTORY) - addItem(player, Items.SCARECROW_6059) - rewardXP(player, Skills.FARMING, 25.0) - sendMessage(player, "You stab the watermelon on top of the spear, finishing your scarecrow") + if (removeItem(player,SACK, Container.INVENTORY) && removeItem(player,WATERMELON,Container.INVENTORY) && addItem(player, Items.SCARECROW_6059)) { + rewardXP(player, Skills.FARMING, 25.0) + sendMessage(player, "You stab the watermelon on top of the spear, finishing your scarecrow") + } }else{ sendMessage(player, "Your Farming level is not high enough to do this") } diff --git a/Server/src/main/content/global/handlers/item/withnpc/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/BonesOnStrayDog.kt b/Server/src/main/content/global/handlers/item/withnpc/BonesOnStrayDog.kt deleted file mode 100644 index e6e7fd03a..000000000 --- a/Server/src/main/content/global/handlers/item/withnpc/BonesOnStrayDog.kt +++ /dev/null @@ -1,84 +0,0 @@ -package content.global.handlers.item.withnpc - -import core.api.removeItem -import core.api.sendChat -import core.api.sendMessage -import content.global.skill.prayer.Bones -import core.game.node.entity.npc.NPC -import core.game.node.item.Item -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -class BonesOnStrayDog : InteractionListener { - override fun defineListeners() { - val bones = Bones.array - - val dogs = intArrayOf( - NPCs.STRAY_DOG_4766, - NPCs.STRAY_DOG_4767, - NPCs.STRAY_DOG_5917, - NPCs.STRAY_DOG_5918 - ) - - onUseWith(IntType.NPC, bones, *dogs) { player, used, with -> - used as Item; with as NPC - - var woof = "Woof" - - if (removeItem(player, used)) { - sendMessage( - player, - "You feed the ${with.definition.name.lowercase()} your ${used.definition.name.lowercase()}." - ) - - when (used.id) { - Items.BURNT_BONES_528 -> { - sendMessage( - player, - "The dog looks at you, disappointed, but takes the bones anyway." - ) - - woof = "Woof..." - } - - Items.FAYRG_BONES_4830, - Items.RAURG_BONES_4832, - Items.OURG_BONES_4834 -> { - sendMessage( - player, - "The dog looks at you, confused, but takes the bones anyway." - ) - - woof = "Woof..?" - } - - Items.BABYDRAGON_BONES_534, - Items.BIG_BONES_532 -> { - sendMessage( - player, - "The dog seems to be very happy." - ) - - woof = "Woof!" - } - - Items.WYVERN_BONES_6812, - Items.DRAGON_BONES_536 -> { - sendMessage( - player, - "The dog seems to be extremely overjoyed." - ) - - woof = "WOOF!" - } - } - - sendChat(with, woof) - } - - return@onUseWith true - } - } -} \ 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/CiderOnForester.kt b/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt index 31cb87218..ab0018b8d 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt +++ b/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt @@ -43,7 +43,7 @@ class CiderOnForesterDialogue : DialogueFile() { end() } else if(player!!.getAttribute("seersCiderPub",-1) !in 0..3 && !player!!.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,0,6)){ - player!!.setAttribute("seersCiderPub",0) + player!!.setAttribute("seersCiderPub", 1) end() }else if(player!!.getAttribute("seersCiderPub",-1) in 0..3){ player!!.incrementAttribute("seersCiderPub",1) diff --git a/Server/src/main/content/global/handlers/item/withnpc/FeedOnStrayDog.kt b/Server/src/main/content/global/handlers/item/withnpc/FeedOnStrayDog.kt new file mode 100644 index 000000000..1c1013ac4 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/FeedOnStrayDog.kt @@ -0,0 +1,118 @@ +package content.global.handlers.item.withnpc + +import core.api.removeItem +import core.api.sendChat +import core.api.sendMessage +import content.global.skill.prayer.Bones +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import content.data.Meat +import content.data.MeatState +import core.api.animate +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Animations + +/** + * Represents the option Listener use-with on a stray dog. + * Use bones, raw & cooked meats + */ +class FeedOnStrayDog : InteractionListener { + override fun defineListeners() { + + val bones = Bones.array + + val consumableMeats = Meat.values() + .filter { it.state == MeatState.INEDIBLE_RAW || it.state == MeatState.EDIBLE_COOKED } + .map { it.id } + .toIntArray() + + val allAllowedItems = bones + consumableMeats + + val dogs = intArrayOf( + NPCs.STRAY_DOG_4766, + NPCs.STRAY_DOG_4767, + NPCs.STRAY_DOG_5917, + NPCs.STRAY_DOG_5918 + ) + + onUseWith(IntType.NPC, allAllowedItems, *dogs) { player, used, with -> + used as Item; with as NPC + + var woof = "Woof" + + if (removeItem(player, used)) { + sendMessage( + player, + "You feed the ${with.definition.name.lowercase()} your ${used.definition.name.lowercase()}." + ) + + animate(player, Animations.HUMAN_BURYING_BONES_827) + + when { + bones.contains(used.id) -> { + when (used.id) { + Items.BURNT_BONES_528 -> { + sendMessage( + player, + "The dog looks at you, disappointed, but takes the bones anyway." + ) + + woof = "Woof..." + } + + Items.FAYRG_BONES_4830, + Items.RAURG_BONES_4832, + Items.OURG_BONES_4834 -> { + sendMessage( + player, + "The dog looks at you, confused, but takes the bones anyway." + ) + + woof = "Woof..?" + } + + Items.BABYDRAGON_BONES_534, + Items.BIG_BONES_532 -> { + sendMessage( + player, + "The dog seems to be very happy." + ) + + woof = "Woof!" + } + + Items.WYVERN_BONES_6812, + Items.DRAGON_BONES_536 -> { + sendMessage( + player, + "The dog seems to be extremely overjoyed." + ) + + woof = "WOOF!" + } + } + } + + consumableMeats.contains(used.id) -> { + sendMessage( + player, + "The dog wagged its tail happily." + ) + + animate(with, Animation(4777)) + + woof = "Woof woof!" + } + } + + sendChat(with, woof) + } + + 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/SandSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt index 799084610..bbde6bc21 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt @@ -35,8 +35,9 @@ class SandSourceListener : InteractionListener { animate(player, ANIMATION) } - sendMessage(player, "You fill the bucket with sand.") - addItem(player, Items.BUCKET_OF_SAND_1783) + if (addItem(player, Items.BUCKET_OF_SAND_1783)) { + sendMessage(player, "You fill the bucket with sand.") + } } animationTrigger++ diff --git a/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt index 3607fda98..13ee4000a 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt @@ -61,8 +61,9 @@ class SapCollectListener : InteractionListener { override fun pulse(): Boolean { if (removeItem(player, Items.BUCKET_1925)) { animate(player, ANIMATION) - sendMessage(player, "You cut the tree and allow its sap to drip down into your bucket.") - addItem(player, Items.BUCKET_OF_SAP_4687) + if (addItem(player, Items.BUCKET_OF_SAP_4687)) { + sendMessage(player, "You cut the tree and allow its sap to drip down into your bucket.") + } return true } return false diff --git a/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java b/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java index 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..084e7f117 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 ) } @@ -81,9 +82,11 @@ class SpiritShieldBlessListener : InteractionListener { return@onUseWith false } - addItem(player, Items.BLESSED_SPIRIT_SHIELD_13736) - sendMessage(player, "You successfully bless the shield using the holy elixir.") + if (!addItem(player, Items.BLESSED_SPIRIT_SHIELD_13736)) { + return@onUseWith false + } + sendMessage(player, "You successfully bless the shield using the holy elixir.") return@onUseWith true } diff --git a/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt index caf12172e..75652b75d 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 @@ -104,6 +129,14 @@ class WaterSourceListener : InteractionListener { FISHBOWL( inputs = intArrayOf(Items.FISHBOWL_6667), output = Items.FISHBOWL_6668 + ), + KETTLE( + inputs = intArrayOf(Items.KETTLE_7688), + output = Items.FULL_KETTLE_7690 + ), + CUP( + inputs = intArrayOf(Items.EMPTY_CUP_1980), + output = Items.CUP_OF_WATER_4458 ); companion object diff --git a/Server/src/main/content/global/handlers/npc/BankerNPC.kt b/Server/src/main/content/global/handlers/npc/BankerNPC.kt index e59ac6b69..7dff2f0d7 100644 --- a/Server/src/main/content/global/handlers/npc/BankerNPC.kt +++ b/Server/src/main/content/global/handlers/npc/BankerNPC.kt @@ -1,62 +1,54 @@ package content.global.handlers.npc +import content.global.handlers.scenery.BankBoothListener +import core.ServerConstants import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode import core.game.world.map.Direction import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import content.global.handlers.scenery.BankBoothListener /** * Provides dialogue tree for all generic banker NPCs as well as * handles all the common interactions like 'bank' and 'collect'. * * @author vddCore + * @author Player Name */ @Initializable class BankerNPC : AbstractNPC, InteractionListener { + val NPC_IDS = intArrayOf( + NPCs.BANKER_44, NPCs.BANKER_45, NPCs.BANKER_494, NPCs.BANKER_495, NPCs.BANKER_496, NPCs.BANKER_497, + NPCs.BANKER_498, NPCs.BANKER_499, NPCs.BANKER_1036, NPCs.BANKER_1360, NPCs.BANKER_2163, NPCs.BANKER_2164, + NPCs.BANKER_2354, NPCs.BANKER_2355, NPCs.BANKER_2568, NPCs.BANKER_2569, NPCs.BANKER_2570, NPCs.BANKER_3198, + NPCs.BANKER_3199, NPCs.BANKER_5258, NPCs.BANKER_5259, NPCs.BANKER_5260, NPCs.BANKER_5261, NPCs.BANKER_5776, + NPCs.BANKER_5777, NPCs.BANKER_5912, NPCs.BANKER_5913, NPCs.BANKER_6200, NPCs.BANKER_6532, NPCs.BANKER_6533, + NPCs.BANKER_6534, NPCs.BANKER_6535, NPCs.BANKER_7445, NPCs.BANKER_7446, NPCs.BANKER_7605, + NPCs.GUNDAI_902, + NPCs.GHOST_BANKER_1702, NPCs.GNOME_BANKER_166, NPCs.NARDAH_BANKER_3046, NPCs.MAGNUS_GRAM_5488, NPCs.TZHAAR_KET_ZUH_2619, + NPCs.SIRSAL_BANKER_4519, NPCs.FADLI_958, NPCs.BANK_TUTOR_4907, NPCs.JADE_4296, + NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050, + NPCs.BANKER_6538 + ) + companion object { private const val LUNAR_ISLE_BANK_REGION = 8253 - val SPECIAL_NPC_IDS = intArrayOf( - NPCs.SIRSAL_BANKER_4519, NPCs.FADLI_958, NPCs.BANK_TUTOR_4907, NPCs.JADE_4296, - NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050, NPCs.ARNOLD_LYDSPOR_3824, - - /* Maximillian Sackville - Near Wilderness bounty-hunter area. */ - NPCs.BANKER_6538 - ) - - val NPC_IDS = intArrayOf( - NPCs.BANKER_44, NPCs.BANKER_45, NPCs.BANKER_494, NPCs.BANKER_495, NPCs.BANKER_496, NPCs.BANKER_497, - NPCs.BANKER_498, NPCs.BANKER_499, NPCs.BANKER_1036, NPCs.BANKER_1360, NPCs.BANKER_2163, NPCs.BANKER_2164, - NPCs.BANKER_2354, NPCs.BANKER_2355, NPCs.BANKER_2568, NPCs.BANKER_2569, NPCs.BANKER_2570, NPCs.BANKER_3198, - NPCs.BANKER_3199, NPCs.BANKER_5258, NPCs.BANKER_5259, NPCs.BANKER_5260, NPCs.BANKER_5261, NPCs.BANKER_5776, - NPCs.BANKER_5777, NPCs.BANKER_5912, NPCs.BANKER_5913, NPCs.BANKER_6200, NPCs.BANKER_6532, NPCs.BANKER_6533, - NPCs.BANKER_6534, NPCs.BANKER_6535, NPCs.BANKER_7445, NPCs.BANKER_7446, NPCs.BANKER_7605, - NPCs.GUNDAI_902, - - NPCs.GHOST_BANKER_1702, NPCs.GNOME_BANKER_166, NPCs.NARDAH_BANKER_3046, NPCs.MAGNUS_GRAM_5488, NPCs.TZHAAR_KET_ZUH_2619, - ) - - private val ALL_BANKER_NPC_IDS = intArrayOf( - *SPECIAL_NPC_IDS, - *NPC_IDS - ) - /** * This is poorly named, but performs a few checks to see if the player * is trying to access the bank on the Lunar Isle and returns a boolean * controlling whether or not to pass the quick bank or collection use. - * - * TODO - * The location of this method is shit too. Find a better place for it? */ fun checkLunarIsleRestriction(player: Player, node: Node): Boolean { if (node.location.regionId != LUNAR_ISLE_BANK_REGION) @@ -121,37 +113,35 @@ class BankerNPC : AbstractNPC, InteractionListener { private fun provideDestinationOverride(entity: Entity, node: Node): Location { val npc = node as NPC - return when(npc.id) { /* Ogress bankers are 2x2 with their spawn being offset to south-western tile. */ - NPCs.OGRESS_BANKER_7049, - NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0) - - NPCs.BANKER_6532, NPCs.BANKER_6533, - NPCs.BANKER_6534, NPCs.BANKER_6535 -> npc.location.transform(npc.direction, 1) - + NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0) /* Magnus has no bank booth nearby so we need to handle that edge case here. */ NPCs.MAGNUS_GRAM_5488 -> npc.location.transform(Direction.NORTH, 2) - + /* Special-cased NPCs, idk why */ + NPCs.BANKER_6532, NPCs.BANKER_6533, NPCs.BANKER_6534, NPCs.BANKER_6535 -> npc.location.transform(npc.direction, 1) else -> { if (npc is BankerNPC) { npc.findAdjacentBankBoothLocation()?.let { return it.second } } - return npc.location } } } override fun defineListeners() { - on(ALL_BANKER_NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank) - on(ALL_BANKER_NPC_IDS, IntType.NPC, "collect", handler = Companion::attemptCollect) + on(NPC_IDS, IntType.NPC, "bank", handler = Companion::attemptBank) + on(NPC_IDS, IntType.NPC, "collect", handler = Companion::attemptCollect) + on(NPC_IDS, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, BankerDialogueLabellerFile(), node as NPC) + return@on true + } } override fun defineDestinationOverrides() { - setDest(IntType.NPC, ALL_BANKER_NPC_IDS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride) + setDest(IntType.NPC, NPC_IDS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride) } override fun init() { @@ -167,5 +157,167 @@ class BankerNPC : AbstractNPC, InteractionListener { } } - override fun getIds(): IntArray = ALL_BANKER_NPC_IDS + class BankerDialogueLabellerFile : DialogueLabeller() { + val BANKERS_WITH_EXTRA_OPTION = intArrayOf(NPCs.BANK_TUTOR_4907, NPCs.JADE_4296, NPCs.BANKER_6538) + + override fun addConversation() { + exec { player, npc -> + if (npc.id == NPCs.SIRSAL_BANKER_4519 && !hasSealOfPassage(player)) { + loadLabel(player, "no seal of passage") + } + if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + if (npc.id == NPCs.JADE_4296) loadLabel(player, "uim for jade") + loadLabel(player, "uim") + } + if (npc.id == NPCs.JADE_4296) loadLabel(player, "hello for jade") + loadLabel(player, "hello") + } + label("no seal of passage") + npc(ChatAnim.ANNOYED, "What are you doing here, Fremennik?!") + player(ChatAnim.WORRIED, "I have a Seal of Pass...") + npc(ChatAnim.ANGRY, "No you don't! Begone!") + // todo: kick them out, but only one of the two banker types should do so (see historical wiki) + goto("nowhere") + label("uim for jade") + npc(ChatAnim.ANNOYED, "Greetings, warrior. I wish I could help you, but our services are not available for Ultimate Iron@g[men,women].") + goto("nowhere") + label("uim") + npc(ChatAnim.ANNOYED, "My apologies, dear @g[sir,madam], our services are not available for Ultimate Iron@g[men,women]") + goto("nowhere") + label("hello for jade") + npc("Greetings warrior, how may I help you?") + exec { player, _ -> loadLabel(player, if (hasAwaitingGrandExchangeCollections(player)) "ge collect" else "main options") } + label("hello") + npc("Good day, would you like to access your bank account?") + exec { player, _ -> loadLabel(player, if (hasAwaitingGrandExchangeCollections(player)) "ge collect" else "main options") } + label("ge collect") + npc("Before we go any further, I should inform you that you have items ready for collection from the Grand Exchange.") + goto("main options") + + label("main options") + options( + DialogueOption("how to use", "How do I use the bank?") { _, npc -> return@DialogueOption npc.id == NPCs.BANK_TUTOR_4907 }, + DialogueOption("who is the bounty hunter banker", "Who are you?") { _, npc -> return@DialogueOption npc.id == NPCs.BANKER_6538 }, + DialogueOption("access", "I'd like to access my bank account please.", expression = ChatAnim.ASKING), + DialogueOption("buy second bank", "I'd like to open a secondary bank account.") { player, _ -> return@DialogueOption ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) }, + DialogueOption("switch second bank", "I'd like to switch to my primary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && isUsingSecondaryBankAccount(player) }, + DialogueOption("switch second bank", "I'd like to switch to my secondary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && !isUsingSecondaryBankAccount(player) }, + DialogueOption("pin", "I'd like to check my PIN settings."), + DialogueOption("collect", "I'd like to collect items."), + DialogueOption("what is this place", "What is this place?") { _, npc -> return@DialogueOption npc.id !in BANKERS_WITH_EXTRA_OPTION }, + DialogueOption("jade's employment history", "How long have you worked here?") { _, npc -> return@DialogueOption npc.id == NPCs.JADE_4296 }, + DialogueOption("is the bounty hunter banker afraid", "Aren't you afraid of working in the Wilderness?") { _, npc -> return@DialogueOption npc.id == NPCs.BANKER_6538 && !ServerConstants.SECOND_BANK } + ) + + label("how to use") + options( + DialogueOption("using bank", "Using the bank itself.", "Using the bank itself. I'm not sure how....?"), + DialogueOption("using deposit", "Using bank deposit boxes.", "Using Bank deposit boxes.... what are they?"), + DialogueOption("using PIN", "What's this PIN thing that people keep talking about?", "What's this PIN thing that people keep talking about?"), + DialogueOption("nowhere", "Goodbye.") + ) + + label("using bank") + npc("Speak to any banker and ask to see your bank", "account. If you have set a PIN you will be asked for", "it, then all the belongings you have placed in the bank", "will appear in the window. To withdraw one item, left-") + npc("click on it once.") + npc("To withdraw many, right-click on the item and select", "from the menu. The same for depositing, left-click on", "the item in your inventory to deposit it in the bank.", "Right-click on it to deposit many of the same items.") + npc("To move things around in your bank: firstly select", "Swap or Insert as your default moving mode, you can", "find these buttons on the bank window itself. Then click", "and drag an item to where you want it to appear.") + npc("You may withdraw 'notes' or 'certificates' when the", "items you are trying to withdraw do not stack in your", "inventory. This will only work for items which are", "tradeable.") + npc("For instance, if you wanted to sell 100 logs to another", "player, they would not fit in one inventory and you", "would need to do multiple trades. Instead, click the", "Note button to withdraw the logs as 'certs' or 'notes'") + npc("then withdraw the items you need.") + goto("how to use") + + label("using deposit") + npc("They look like grey pillars, there's one just over there,", "near the desk. Bank deposit boxes save so much time.", "If you're simply wanting to deposit a single item, 'Use'", "it on the deposit box.") + npc("Otherwise, simply click once on the box and it will give", "you a choice of what to deposit in an interface very", "similar to the bank itself. Very quick for when you're", "simply fishing or mining etc.") + goto("how to use") + + label("using PIN") + npc("The PIN - Personal Identification Number - can be", "set on your bank account to protect the items there in", "case someone finds out your account password. It", "consists of four numbers that you remember and tell") + npc("no one.") + npc("So if someone did manage to get your password they", "couldn't steal your items if they were in the bank.") + goto("how to use") + + label("who is the bounty hunter banker") + npc(ChatAnim.NEUTRAL, "How inconsiderate of me, dear @g[sir,madam]. My name is Maximillian Sackville and I conduct operations here on behalf of The Bank of Gielinor.") + goto("main options") + + label("access") + exec { player, _ -> openBankAccount(player) } + goto("nowhere") + + label("buy second bank") + npc("Certainly. We offer secondary accounts to all our customers.") + npc("The secondary account comes with a standard fee of 5,000,000 coins. The fee is non-refundable and account activation is permanent.") + npc("If your inventory does not contain enough money to cover the costs, we will complement the amount with the money inside your primary bank account.") + npc("Knowing all this, would you like to proceed with opening your secondary bank account?") + options( + DialogueOption("buy second bank yes", "Yes, I am still interested.", expression = ChatAnim.HAPPY), + DialogueOption("buy second bank no", "Actually, I've changed my mind.", expression = ChatAnim.ANNOYED) + ) + + label("buy second bank yes") + exec { player, _ -> loadLabel(player, when (activateSecondaryBankAccount(player)) { + SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> "already" + SecondaryBankAccountActivationResult.INTERNAL_FAILURE -> "failure" + SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> "not enough cash" + SecondaryBankAccountActivationResult.SUCCESS -> "success" + }) } + label("already") + npc(ChatAnim.FRIENDLY, "Your bank account was already activated, there is no need to pay twice.") + goto("main options") + label("failure") + npc(ChatAnim.ANNOYED, "I must apologize, the transaction was not successful. Please check your primary bank account and your inventory - if there's money missing, please screenshot your chatbox and contact the game developers.") + goto("main options") + label("not enough cash") + npc(ChatAnim.ANNOYED, "It appears that you do not have the money necessary to cover the costs associated with opening a secondary bank account. I will be waiting here until you do.") + goto("main options") + label("success") + npc(ChatAnim.FRIENDLY, "Your secondary bank account has been opened and can be accessed through any of the Bank of Gielinor's employees. Thank you for choosing our services.") + goto("main options") + + label("buy second bank no") + npc("Very well. Should you decide a secondary bank account is needed, do not hesitate to contact any of the Bank of Gielinor's stationary employees. We will be happy to help.") + goto("main options") + + label("switch second bank") + exec { player, _ -> + toggleBankAccount(player) + loadLabel(player, if (isUsingSecondaryBankAccount(player)) "now secondary" else "now primary") + } + label("now primary") + npc("Your active bank account has been switched. You can now access your primary account.") + goto("main options") + label("now secondary") + npc("Your active bank account has been switched. You can now access your secondary account.") + goto("main options") + + label("pin") + exec { player, _ -> openBankPinSettings(player) } + goto("nowhere") + + label("collect") + exec { player, _ -> openGrandExchangeCollectionBox(player) } + goto("nowhere") + + label("what is this place") + npc("This is a branch of the Bank of Gielinor. We have branches in many towns.") + player("And what do you do?") + npc("We will look after your items and money for you. Leave your valuables with us if you want to keep them safe.") + goto("main options") + + label("jade's employment history") + npc(ChatAnim.FRIENDLY, "Oh, ever since the Guild opened. I like it here.") + player(ChatAnim.ASKING, "Why's that?") + npc(ChatAnim.FRIENDLY, "Well... what with all these warriors around, there's not much chance of my bank being robbed, is there?") + goto("nowhere") + + label("is the bounty hunter banker afraid") + npc(ChatAnim.NEUTRAL, "While the Wilderness is quite a dangerous place, The Bank of Gielinor offers us - roving bankers - extraordinary benefits for our hard work in hazardous environments.") + npc(ChatAnim.NEUTRAL, "This allows us to provide our services to customers regardless of their current whereabouts. Our desire to serve is stronger than our fear of the Wilderness.") + goto("nowhere") + } + } + + override fun getIds(): IntArray = NPC_IDS } diff --git a/Server/src/main/content/global/handlers/npc/ChinchompaDeathListener.kt b/Server/src/main/content/global/handlers/npc/ChinchompaDeathListener.kt new file mode 100644 index 000000000..3a146eaf4 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/ChinchompaDeathListener.kt @@ -0,0 +1,52 @@ +package content.global.handlers.npc + +import core.api.* +import core.api.playGlobalAudio +import core.api.sendGraphics +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.tools.RandomFunction +import core.game.world.update.flag.context.Graphics +import core.game.node.entity.combat.ImpactHandler.HitsplatType; + +private val chinchompaIDs = intArrayOf( + NPCs.CHINCHOMPA_5079, + NPCs.CARNIVOROUS_CHINCHOMPA_5080 +) + +/** + * Minimal evasive approach to the Chinchompas exploding on death cause by combat and not by trapping. + */ +class ChinchompaDeathListener: NPCBehavior(*chinchompaIDs) { + override fun onDeathFinished(self: NPC, killer: Entity) { + + // If it 'died' from getting trapped an attribute is set of 'hunter'. + if (!self.attributes.containsKey("hunter") && killer is Player) { + val explosionLoc = self.location + val validExplosionDmgTiles = explosionLoc.surroundingTiles + + val chinExplosionGFX = Graphics(157, 96) + sendGraphics(chinExplosionGFX, explosionLoc) + playGlobalAudio(explosionLoc, 158) + + if (validExplosionDmgTiles.contains(killer.location)) { + //As weird this looks, it does allow to have a hit from 0 to 2 + val hitAmount = RandomFunction.random(0, 3) + impact(killer, hitAmount, HitsplatType.NORMAL) + } + } + } + + /** + * At content.global.skill.hunter.TrapSetting + * sets an attribute of 'hunter' during it's getCatchPulse for reward rolling, but it never cleans it up after. + * These NPCs is a member of NPC class although exists a HunterNPC class, but it's mainly used for imps. + * Logically you'd do this after death finishes, but it would interfere with the reward rolls and I fear it may affect other hunter NPCs. + */ + override fun onRespawn(self: NPC) { + self.removeAttribute("hunter") + } +} \ No newline at end of file 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..368835e32 100644 --- a/Server/src/main/content/global/handlers/npc/DecantListener.kt +++ b/Server/src/main/content/global/handlers/npc/DecantListener.kt @@ -1,46 +1,155 @@ package content.global.handlers.npc import content.data.consumables.Consumables +import content.global.skill.cooking.fermenting.CalquatDecant +import core.api.* import core.game.consumable.Potion -import core.game.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() + val decantables = potions + CalquatDecant.allKegIds + } + override fun defineListeners() { on(IntType.NPC,"decant"){ player, node -> - decant(player) + // Decant potions + val (potionRemove, potionAdd) = decantContainer(player.inventory) + for (item in potionRemove) + if (!removeItem(player, item)) { return@on false } + for (item in potionAdd) + addItem(player, item.id, item.amount) + + // Decant kegs + val (kegRemove, kegAdd) = CalquatDecant.decantCalquat(player.inventory) + for (item in kegRemove) + if (!removeItem(player, item)) { return@on false } + for (item in kegAdd) + addItem(player, item.id, item.amount) + player.dialogueInterpreter.open(DecantingDialogue(),node.asNpc()) return@on true } - } + onUseWith(IntType.ITEM, decantables, *decantables) { 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))) + // Check if these are kegs - handle with CalquatDecant + if (CalquatDecant.isKeg(used.id) || CalquatDecant.isKeg(with.id)) { + return@onUseWith CalquatDecant.combineKegs(player, used, with) } + + // Otherwise handle as potions + // Verify these are both the same potion types + val potionUsed = Consumables.getConsumableById(used.id)?.consumable as? Potion ?: return@onUseWith false + val potionWith = Consumables.getConsumableById(with.id)?.consumable as? Potion ?: return@onUseWith false + + if (potionUsed != potionWith) { + return@onUseWith false + } + + // 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 } - 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])) + + // Decant keg into beer glass + onUseWith(IntType.ITEM, Items.BEER_GLASS_1919, *CalquatDecant.allKegIds) { player, used, with -> + if (used !is Item || with !is Item) return@onUseWith false + + // Determine which is the glass and which is the keg + val (glass, keg) = if (used.id == Items.BEER_GLASS_1919) { + used to with + } else { + with to used + } + + return@onUseWith CalquatDecant.pourIntoGlass(player, glass, keg) + } + + // Barbarian decanting: use a 4-dose potion and an empty vial together to make two 2-dose potions + onUseWith(IntType.ITEM, potions, Items.VIAL_229) { player, used, with -> + // Check if player has started barbarian herblore training + if (getAttribute(player, "/save:barbtraining:herblore", 0) == 0) { + return@onUseWith false + } + + if (used !is Item) return@onUseWith false + if (with !is Item) return@onUseWith false + + // Determine which item is the potion and which is the vial + val (potionItem) = if (used.id == Items.VIAL_229) { + Pair(with, used) + } else { + Pair(used, with) + } + + // Check if the potion is 4 doses + val potion = Consumables.getConsumableById(potionItem.id)?.consumable as? Potion ?: return@onUseWith false + val dose = potion.getDose(potionItem) + + if (dose != 4) { + return@onUseWith false + } + + // Get the 2-dose version of this potion by index + val twoDoseId = potion.ids[2] + + // Replace ingredients with products + replaceSlot(player, with.slot, Item(twoDoseId)) + replaceSlot(player, used.slot, Item(twoDoseId)) + + playAudio(player, 2401) + + return@onUseWith true } } @@ -52,5 +161,4 @@ class DecantListener : InteractionListener { } } } - } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/npc/GuardNPC.java b/Server/src/main/content/global/handlers/npc/GuardNPC.java index ccdf7e10d..9cde38cc7 100644 --- a/Server/src/main/content/global/handlers/npc/GuardNPC.java +++ b/Server/src/main/content/global/handlers/npc/GuardNPC.java @@ -15,7 +15,7 @@ public final class GuardNPC extends AbstractNPC { /** * The NPC ids of NPCs using this plugin. */ - private static final int[] ID = { 9, 32, 163, 164, 196, 197, 206, 253, 254, 255, 256, 274, 275, 296, 298, 299, 447, 448, 449, 489, 609, 678, 799, 837, 842, 862, 870, 877, 917, 1200, 1203, 1204, 1317, 1710, 1711, 1712, 2073, 2074, 2134, 2135, 2136, 2236, 2571, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 3715, 4257, 4258, 4259, 4260, 4307, 4308, 4309, 4310, 4311, 4336, 4375, 4603, 4604, 4605, 4606, 5800, 5801, 5919, 5920 }; + private static final int[] ID = { 9, 32, 163, 164, 196, 197, 206, 253, 254, 255, 256, 274, 275, 296, 298, 299, 447, 448, 449, 609, 678, 799, 837, 842, 862, 870, 877, 917, 1200, 1203, 1204, 1317, 1710, 1711, 1712, 2073, 2074, 2134, 2135, 2136, 2236, 2571, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 3715, 4257, 4258, 4259, 4260, 4307, 4308, 4309, 4310, 4311, 4336, 4375, 4603, 4604, 4605, 4606, 5800, 5801, 5919, 5920 }; /** * Constructs a new {@code GuardNPC} {@code Object}. diff --git a/Server/src/main/content/global/handlers/npc/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..4f5764b0d 100644 --- a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt +++ b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt @@ -5,8 +5,14 @@ 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 +import org.rs09.consts.NPCs /** * Handles the NPC talk-to option. @@ -31,13 +37,32 @@ 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) { + // Why the fuck is this here of all places? Now look at what you've made me do: + if (npc.id == NPCs.SANDWICH_LADY_3117) { + // https://www.youtube.com/watch?v=ek8r3ZS929E + player.dialogueInterpreter.sendDialogue("The sandwich lady doesn't seem interested in selling you any", "refreshments.") + } else { + sendMessage(player, "They aren't interested in talking to you.") + } + } else { + AntiMacro.getEventNpc(player)?.talkTo(node.asNpc()) + } + return@on true + //TODO bring sanity here + } + if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) { + if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) { player.sendMessage("They aren't interested in talking to you.") } else { - content.global.ame.RandomEventManager.getInstance(player)!!.event!!.talkTo(node.asNpc()) + 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) } @@ -62,4 +87,4 @@ class NPCTalkListener : InteractionListener { return@on player.dialogueInterpreter.open(npc.id, npc) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/npc/RatNPC.java b/Server/src/main/content/global/handlers/npc/RatNPC.java index 11f4fce94..f8a380db4 100644 --- a/Server/src/main/content/global/handlers/npc/RatNPC.java +++ b/Server/src/main/content/global/handlers/npc/RatNPC.java @@ -7,6 +7,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.world.map.Location; +import content.data.Quests; /** * Represents a rat npc. @@ -51,7 +52,7 @@ public class RatNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { final Player p = ((Player) killer); - if (p.getQuestRepository().getQuest("Witch's Potion").isStarted(p)) { + if (p.getQuestRepository().getQuest(Quests.WITCHS_POTION).isStarted(p)) { GroundItemManager.create(RAT_TAIL, getLocation(), p); } } diff --git a/Server/src/main/content/global/handlers/npc/SheepBehavior.kt b/Server/src/main/content/global/handlers/npc/SheepBehavior.kt new file mode 100644 index 000000000..c6ed46374 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/SheepBehavior.kt @@ -0,0 +1,141 @@ +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) + if (!addItem(player, Items.WOOL_1737)) { // 5160 + return@on false + } + sendMessage(player, "You get some wool.") + 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.x + (sheepOppositeDirection.getStepX() * 3) // Gets x location, if set, 3 steps away from player + val yWalkLocation = sheepLocation.y + (sheepOppositeDirection.getStepY() * 3) // Gets y location, if set, 3 steps away from player + val sheepWalkToLocation = Location(xWalkLocation, yWalkLocation, sheepLocation.z); // 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/ShooAwayStrayDogListener.kt b/Server/src/main/content/global/handlers/npc/ShooAwayStrayDogListener.kt new file mode 100644 index 000000000..e159f89ab --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/ShooAwayStrayDogListener.kt @@ -0,0 +1,80 @@ +package content.global.handlers.npc + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.NPCs +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.update.flag.context.Animation +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import org.rs09.consts.Animations +import org.rs09.consts.Sounds + +private val strayDogsIds = intArrayOf( + NPCs.STRAY_DOG_4766, + NPCs.STRAY_DOG_4767, + NPCs.STRAY_DOG_5917, + NPCs.STRAY_DOG_5918 +) + +/** + * Represents the option Listener used to 'shoo-away' a stray dog. + * Convert from legacy plugin src/main/content/region/misthalin/varrock/handlers/ShooAwayStrayDogPlugin.java + * @author Vexia + beckrickert + * + */ +class ShooAwayStrayDogListener : NPCBehavior(*strayDogsIds), InteractionListener { + override fun defineListeners() { + + on(strayDogsIds,IntType.NPC, "shoo-away") { player, node -> + + val theDog = node as NPC + + //Pause dog location + lock(theDog, 2) + stopWalk(theDog) + theDog.faceTemporary(player, 1) + + sendChat(player,"Thbbbbt!") + animate(player, Animations.HUMAN_BLOW_RASPBERRY_2110) + + //Dog whine sound + playAudio(player, Sounds.WOLF_DEATH_911) + sendChat(theDog,"Whine!") + + //Dog whine kneeling down animation + animate(theDog, Animation(4774)) + + //run away from player + dogWhineRunAway(player, theDog) + + return@on true + } + } +} + +fun dogWhineRunAway(player: Player, theDog: NPC) { + val playerCurLoc = player.getLocation() + val dogCurLoc = theDog.getLocation() + + //Get dog direction is facing from player's location + val dogDirection = Direction.getDirection(dogCurLoc, playerCurLoc) + val dogDirectionOpp = dogDirection.opposite + + val xWalkLoc = dogCurLoc.x + (dogDirectionOpp.stepX * 12) + val yWalkLoc = dogCurLoc.y + (dogDirectionOpp.stepY * 12) + val dogWalkToNewLoc = Location(xWalkLoc, yWalkLoc, dogCurLoc.z) + + //unlock entity movement so it can run away + unlock(theDog) + + //allow to "moon-walk" to the new location + //Does weird stuff or ignores this when in 'following' mode + //theDog.face(player) + + forceWalk(theDog, dogWalkToNewLoc, "dumb") + +} \ No newline at end of file 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..f88132cc1 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) { @@ -84,17 +85,19 @@ class MillingListener : InteractionListener { if (removeItem(player, EMPTY_POT)) { if (getAttribute(player, "milling:sweetcorn", 0) > 0) { setAttribute(player, "/save:milling:sweetcorn", (getAttribute(player, "milling:sweetcorn", 0) - 1)) - addItem(player, POT_OF_CORNFLOUR) - sendMessage(player, if (player.getAttribute("milling:sweetcorn", 0) > 0) "You fill a pot with cornflour from the bin." else "You fill a pot with the last of the cornflour in the bin.") + if (addItem(player, POT_OF_CORNFLOUR)) { + sendMessage(player, if (player.getAttribute("milling:sweetcorn", 0) > 0) "You fill a pot with cornflour from the bin." else "You fill a pot with the last of the cornflour in the bin.") + } } else if (getAttribute(player, "milling:grain", 0) > 0) { setAttribute(player, "/save:milling:grain", (getAttribute(player, "milling:grain", 0) - 1)) - addItem(player, POT_OF_FLOUR) - sendMessage(player, if (player.getAttribute("milling:grain", 0) > 0) "You fill a pot with flour from the bin." else "You fill a pot with the last of the flour in the bin.") + if (addItem(player, POT_OF_FLOUR)) { + sendMessage(player, if (player.getAttribute("milling:grain", 0) > 0) "You fill a pot with flour from the bin." else "You fill a pot with the last of the flour in the bin.") + } } if (getAttribute(player, "milling:sweetcorn", 0) + getAttribute(player, "milling:grain", 0) <= 0) { - 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/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/TableListener.kt b/Server/src/main/content/global/handlers/scenery/TableListener.kt new file mode 100644 index 000000000..c35ebd27b --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/TableListener.kt @@ -0,0 +1,426 @@ +package content.global.handlers.scenery + +import content.global.skill.summoning.pet.Pets +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.graves.GraveController +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor +import core.game.node.entity.player.info.login.PlayerParser +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.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +/** To allow players to place items on tables. Copies DropListener for restrictions. */ +class TableListener : InteractionListener { + companion object { + private val DROP_COINS_SOUND = Sounds.EYEGLO_COIN_10 + private val DROP_ITEM_SOUND = Sounds.PUT_DOWN_2739 + } + override fun defineListeners() { + onUseAnyWith( + IntType.SCENERY, + Scenery.TABLE_10258, + Scenery.TABLE_10414, + Scenery.TABLE_10490, + Scenery.TABLE_10509, + Scenery.TABLE_10808, + Scenery.TABLE_11202, + Scenery.TABLE_11203, + Scenery.TABLE_11500, + Scenery.TABLE_11501, + Scenery.TABLE_11502, + Scenery.TABLE_11503, + Scenery.TABLE_11504, + Scenery.TABLE_11551, + Scenery.TABLE_11619, + Scenery.TABLE_11660, + Scenery.TABLE_11682, + Scenery.TABLE_11743, + Scenery.TABLE_11748, + Scenery.TABLE_11749, + Scenery.TABLE_11849, + Scenery.TABLE_11892, + Scenery.TABLE_12106, + Scenery.TABLE_12108, + Scenery.TABLE_12386, + Scenery.TABLE_12387, + Scenery.TABLE_12388, + Scenery.TABLE_12450, + Scenery.TABLE_12541, + Scenery.TABLE_12544, + Scenery.TABLE_12734, + Scenery.TABLE_12855, + Scenery.TABLE_12884, + Scenery.TABLE_12962, + Scenery.TABLE_12971, + Scenery.TABLE_12972, + Scenery.TABLE_14044, + Scenery.TABLE_14049, + Scenery.TABLE_14163, + Scenery.TABLE_14363, + Scenery.TABLE_14762, + Scenery.TABLE_14763, + Scenery.TABLE_14920, + Scenery.TABLE_15044, + Scenery.TABLE_15092, + Scenery.TABLE_15137, + Scenery.TABLE_15680, + Scenery.TABLE_15684, + Scenery.TABLE_15719, + Scenery.TABLE_15982, + Scenery.TABLE_16169, + Scenery.TABLE_16170, + Scenery.TABLE_16562, + Scenery.TABLE_16737, + Scenery.TABLE_16738, + Scenery.TABLE_16739, + Scenery.TABLE_16740, + Scenery.TABLE_16741, + Scenery.TABLE_16742, + Scenery.TABLE_16815, + Scenery.TABLE_16827, + Scenery.TABLE_16828, + Scenery.TABLE_16874, + Scenery.TABLE_16974, + Scenery.TABLE_17099, + Scenery.TABLE_17145, + Scenery.TABLE_17235, + Scenery.TABLE_17299, + Scenery.TABLE_17348, + Scenery.TABLE_17545, + Scenery.TABLE_17546, + Scenery.TABLE_17661, + Scenery.TABLE_17662, + Scenery.TABLE_17666, + Scenery.TABLE_17762, + Scenery.TABLE_18051, + Scenery.TABLE_18059, + Scenery.TABLE_18062, + Scenery.TABLE_18314, + Scenery.TABLE_18486, + Scenery.TABLE_18902, + Scenery.TABLE_1901, + Scenery.TABLE_19158, + Scenery.TABLE_19928, + Scenery.TABLE_20058, + Scenery.TABLE_20060, + Scenery.TABLE_20351, + Scenery.TABLE_20388, + Scenery.TABLE_20996, + Scenery.TABLE_21359, + Scenery.TABLE_21360, + Scenery.TABLE_21388, + Scenery.TABLE_21389, + Scenery.TABLE_21608, + Scenery.TABLE_21609, + Scenery.TABLE_21610, + Scenery.TABLE_21793, + Scenery.TABLE_22159, + Scenery.TABLE_22160, + Scenery.TABLE_22431, + Scenery.TABLE_22672, + Scenery.TABLE_22677, + Scenery.TABLE_22678, + Scenery.TABLE_22690, + Scenery.TABLE_22693, + Scenery.TABLE_22694, + Scenery.TABLE_22705, + Scenery.TABLE_22830, + Scenery.TABLE_22831, + Scenery.TABLE_22832, + Scenery.TABLE_22844, + Scenery.TABLE_22845, + Scenery.TABLE_22846, + Scenery.TABLE_22847, + Scenery.TABLE_22848, + Scenery.TABLE_22849, + Scenery.TABLE_23177, + Scenery.TABLE_23942, + Scenery.TABLE_24011, + Scenery.TABLE_24012, + Scenery.TABLE_24013, + Scenery.TABLE_24014, + Scenery.TABLE_24028, + Scenery.TABLE_24076, + Scenery.TABLE_24084, + Scenery.TABLE_24088, + Scenery.TABLE_24104, + Scenery.TABLE_24301, + Scenery.TABLE_24302, + Scenery.TABLE_24303, + Scenery.TABLE_24307, + Scenery.TABLE_24308, + Scenery.TABLE_24309, + Scenery.TABLE_24320, + Scenery.TABLE_24335, + Scenery.TABLE_24336, + Scenery.TABLE_24667, + Scenery.TABLE_24684, + Scenery.TABLE_24900, + Scenery.TABLE_24922, + Scenery.TABLE_24927, + Scenery.TABLE_25047, + Scenery.TABLE_25603, + Scenery.TABLE_25615, + Scenery.TABLE_25733, + Scenery.TABLE_25734, + Scenery.TABLE_25778, + Scenery.TABLE_25928, + Scenery.TABLE_25930, + Scenery.TABLE_25931, + Scenery.TABLE_26020, + Scenery.TABLE_26074, + Scenery.TABLE_26142, + Scenery.TABLE_2650, + Scenery.TABLE_26783, + Scenery.TABLE_26807, + Scenery.TABLE_27014, + Scenery.TABLE_27052, + Scenery.TABLE_27056, + Scenery.TABLE_27083, + Scenery.TABLE_27245, + Scenery.TABLE_27850, + Scenery.TABLE_28199, + Scenery.TABLE_28262, + Scenery.TABLE_28508, + Scenery.TABLE_28621, + Scenery.TABLE_28628, + Scenery.TABLE_28629, + Scenery.TABLE_28660, + Scenery.TABLE_2906, + Scenery.TABLE_29131, + Scenery.TABLE_2940, + Scenery.TABLE_29467, + Scenery.TABLE_29468, + Scenery.TABLE_30074, + Scenery.TABLE_30075, + Scenery.TABLE_30076, + Scenery.TABLE_30077, + Scenery.TABLE_30078, + Scenery.TABLE_30079, + Scenery.TABLE_30080, + Scenery.TABLE_30081, + Scenery.TABLE_30222, + Scenery.TABLE_30734, + Scenery.TABLE_30827, + Scenery.TABLE_30828, + Scenery.TABLE_30830, + Scenery.TABLE_30939, + Scenery.TABLE_32121, + Scenery.TABLE_33052, + Scenery.TABLE_33054, + Scenery.TABLE_33129, + Scenery.TABLE_33159, + Scenery.TABLE_33201, + Scenery.TABLE_33202, + Scenery.TABLE_33233, + Scenery.TABLE_34204, + Scenery.TABLE_34429, + Scenery.TABLE_34431, + Scenery.TABLE_34469, + Scenery.TABLE_34470, + Scenery.TABLE_34473, + Scenery.TABLE_34474, + Scenery.TABLE_34518, + Scenery.TABLE_34519, + Scenery.TABLE_34520, + Scenery.TABLE_34521, + Scenery.TABLE_34799, + Scenery.TABLE_34927, + Scenery.TABLE_34968, + Scenery.TABLE_36255, + Scenery.TABLE_36350, + Scenery.TABLE_36366, + Scenery.TABLE_36573, + Scenery.TABLE_36574, + Scenery.TABLE_36575, + Scenery.TABLE_36576, + Scenery.TABLE_36577, + Scenery.TABLE_36578, + Scenery.TABLE_36579, + Scenery.TABLE_36580, + Scenery.TABLE_36581, + Scenery.TABLE_36582, + Scenery.TABLE_36583, + Scenery.TABLE_36584, + Scenery.TABLE_36585, + Scenery.TABLE_36586, + Scenery.TABLE_36727, + Scenery.TABLE_36728, + Scenery.TABLE_36730, + Scenery.TABLE_36731, + Scenery.TABLE_36820, + Scenery.TABLE_36821, + Scenery.TABLE_36910, + Scenery.TABLE_37160, + Scenery.TABLE_37161, + Scenery.TABLE_37162, + Scenery.TABLE_37163, + Scenery.TABLE_37164, + Scenery.TABLE_37165, + Scenery.TABLE_37166, + Scenery.TABLE_37167, + Scenery.TABLE_37350, + Scenery.TABLE_37419, + Scenery.TABLE_37815, + Scenery.TABLE_38020, + Scenery.TABLE_38021, + Scenery.TABLE_38102, + Scenery.TABLE_38316, + Scenery.TABLE_38317, + Scenery.TABLE_38318, + Scenery.TABLE_38319, + Scenery.TABLE_38320, + Scenery.TABLE_39167, + Scenery.TABLE_39168, + Scenery.TABLE_40030, + Scenery.TABLE_40031, + Scenery.TABLE_40818, + Scenery.TABLE_40819, + Scenery.TABLE_40820, + Scenery.TABLE_41105, + Scenery.TABLE_41106, + Scenery.TABLE_41107, + Scenery.TABLE_41196, + Scenery.TABLE_41289, + Scenery.TABLE_41462, + Scenery.TABLE_41469, + Scenery.TABLE_4272, + Scenery.TABLE_4372, + Scenery.TABLE_4458, + Scenery.TABLE_4459, + Scenery.TABLE_4460, + Scenery.TABLE_4461, + Scenery.TABLE_4462, + Scenery.TABLE_4463, + Scenery.TABLE_4464, + Scenery.TABLE_4651, + Scenery.TABLE_4652, + Scenery.TABLE_4653, + Scenery.TABLE_5349, + Scenery.TABLE_5879, + Scenery.TABLE_593, + Scenery.TABLE_594, + Scenery.TABLE_595, + Scenery.TABLE_596, + Scenery.TABLE_597, + Scenery.TABLE_598, + Scenery.TABLE_599, + Scenery.TABLE_600, + Scenery.TABLE_601, + Scenery.TABLE_602, + Scenery.TABLE_603, + Scenery.TABLE_604, + Scenery.TABLE_605, + Scenery.TABLE_606, + Scenery.TABLE_607, + Scenery.TABLE_6075, + Scenery.TABLE_613, + Scenery.TABLE_614, + Scenery.TABLE_616, + Scenery.TABLE_6196, + Scenery.TABLE_6197, + Scenery.TABLE_6198, + Scenery.TABLE_6199, + Scenery.TABLE_620, + Scenery.TABLE_6200, + Scenery.TABLE_6201, + Scenery.TABLE_621, + Scenery.TABLE_6211, + Scenery.TABLE_622, + Scenery.TABLE_623, + Scenery.TABLE_6231, + Scenery.TABLE_624, + Scenery.TABLE_6246, + Scenery.TABLE_7331, + Scenery.TABLE_8700, + Scenery.TABLE_8701, + Scenery.TABLE_8751, + Scenery.TABLE_8768, + Scenery.TABLE_8769, + Scenery.TABLE_9082, + Scenery.TABLE_9430, + Scenery.TABLE_9431, + Scenery.TABLE_9432, + Scenery.TABLE_9433, + Scenery.TABLE_9434, + Scenery.TABLE_9614, + Scenery.TABLE_9685, + Scenery.TABLE_9702 + ) { player, used, with -> + + used as Item + with as core.game.node.scenery.Scenery + + // Similar to DropListener.handleDropAction to keep it consistent + if (Pets.forId(used.id) != null) { + player.familiarManager.summon(used, true, true) + return@onUseAnyWith true + } + if (player.locks.equipmentLock != null) { + return@onUseAnyWith false + } + + // determine if item can be dropped + var canDrop = false; + for (option in used.getDefinition().getOptions()) { + if (option != null && (option.contains("drop"))) { + canDrop = true + } + } + if (!canDrop) { + sendMessage(player, "You cannot place this item on the table!") + return@onUseAnyWith false + } + + // Calculate nearest + val minX = with.location.x + val maxX = with.location.x + with.getSizeX() - 1 + val minY = with.location.y + val maxY = with.location.y + with.getSizeY() - 1 + val nearestX = when { + player.location.x < minX -> minX + player.location.x > maxX -> maxX + else -> player.location.x + } + val nearestY = when { + player.location.y < minY -> minY + player.location.y > maxY -> maxY + else -> player.location.y + } + + // Similar to DropListener.handleDropAction to keep it consistent + closeAllInterfaces(player) + player.animate(Animation(537)) + 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(used.slot) + if (current == null || current !== used) { + return@queueScript stopExecuting(player) + } + if (player.inventory.replace(null, used.slot) !== used) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential exploit attempt when player ${player.name} tried to place on table ${used.amount}x ${used.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 = used.dropItem + if (droppedItem.id == Items.COINS_995) playAudio(player, DROP_COINS_SOUND) else playAudio(player, DROP_ITEM_SOUND) + GroundItemManager.create(droppedItem, Location(nearestX, nearestY), player) + + setAttribute(player, "droppedItem:${droppedItem.id}", getWorldTicks() + 2) + PlayerParser.save(player) + return@queueScript stopExecuting(player) + } + + return@onUseAnyWith true + } + } +} \ No newline at end of file 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..c12b7d4fc 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); @@ -139,6 +139,13 @@ public final class AgilityHandler { * @return The force movement instance, if force movement is used. */ public static ForceMovement forceWalk(final Player player, final int courseIndex, Location start, Location end, Animation animation, int speed, final double experience, final String message) { + return forceWalk(player, courseIndex, start, end, animation, speed, experience, message, null); + } + + /** + * Walks across an obstacle while using an explicit force-movement facing direction. + */ + public static ForceMovement forceWalk(final Player player, final int courseIndex, Location start, Location end, Animation animation, int speed, final double experience, final String message, Direction direction) { player.logoutListeners.put("forcewalk", p -> { p.setLocation(player.getLocation().transform(0,0,0)); return Unit.INSTANCE; @@ -158,6 +165,9 @@ public final class AgilityHandler { player.logoutListeners.remove("forcewalk"); } }; + if (direction != null) { + movement.setDirection(direction); + } movement.start(); GameWorld.getPulser().submit(movement); return movement; 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..d48d00a1a 100644 --- a/Server/src/main/content/global/skill/agility/WildernessCourse.kt +++ b/Server/src/main/content/global/skill/agility/WildernessCourse.kt @@ -2,6 +2,7 @@ package content.global.skill.agility import core.api.* import core.cache.def.impl.SceneryDefinition +import core.game.interaction.QueueStrength import core.game.node.Node import core.game.node.scenery.Scenery import core.game.node.entity.player.Player @@ -116,25 +117,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 +148,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 +156,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) } @@ -174,25 +169,27 @@ class WildernessCourse private fun handleSteppingStones(player: Player, `object`: Scenery) { lock(player, 50) val fail = AgilityHandler.hasFailed(player, 1, 0.3) - val origLoc = player.location - registerLogoutListener(player, "steppingstone"){p -> - teleport(p, origLoc) - } - submitWorldPulse(object : Pulse(2, player){ - var counter = 0 - override fun pulse(): Boolean { - if (counter == 3 && fail) { - AgilityHandler.fail(player, -1, Location.create(3001, 3963, 0), Animation.create(771), (player.skills.lifepoints * 0.26).toInt(), "...You lose your footing and fall into the lava.") - return true - } - AgilityHandler.forceWalk(player, if (counter == 5) 2 else -1, player.location, player.location.transform(-1, 0, 0), Animation.create(741), 10, if (counter == 5) 20.0 else 0.0, if (counter != 0) null else "You carefully start crossing the stepping stones...") - if(++counter == 6){ - unlock(player) - clearLogoutListener(player, "steppingstone") - } - return counter == 6 + queueScript(player, 0, QueueStrength.SOFT) { stage -> + val courseIndex = if (stage == 5) 2 else -1 + val start = player.location + val end = player.location.transform(-1, 0, 0) + val anim = Animation(741) + val xp = if (stage == 5) 20.0 else 0.0 + val message = if (stage == 0) "You carefully start crossing the stepping stones..." else null + AgilityHandler.forceWalk(player, courseIndex, start, end, anim, 10, xp, message) + if (stage == 2 && fail) { + val dest = Location(3001, 3963, 0) + val failanim = Animation(771) + val hit = (player.skills.lifepoints * 0.26).toInt() + AgilityHandler.fail(player, -1, dest, failanim, hit, "... You lose your footing and fall into the lava.") + return@queueScript stopExecuting(player) } - }) + if (stage == 5) { + unlock(player) + return@queueScript stopExecuting(player) + } + return@queueScript delayScript(player, 2) + } } /** @@ -254,7 +251,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/BladeTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java index 773cd9907..5303bde7d 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java @@ -5,6 +5,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,10 @@ public final class BladeTrap implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); + e.lock(5); if(e.isPlayer()) { 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/brimhaven/BrimhavenCourse.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java index 4fb8bdb52..adb8f14fd 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java @@ -119,6 +119,8 @@ public final class BrimhavenCourse extends OptionHandler { final Direction dir = direction; final Direction faceDirection = Direction.get(object.getDirection().toInteger() - 1 & 3); final Location start = player.getLocation(); + player.setDirection(faceDirection); + player.faceLocation(start.transform(faceDirection)); player.getAppearance().setAnimations(Animation.create(1118 + m)); player.getAppearance().sync(); AgilityHandler.climb(player, -1, new Animation(1117 + m), start.transform(dir), 0.0, null); @@ -143,12 +145,12 @@ public final class BrimhavenCourse extends OptionHandler { } else if (count == 6) { player.getAppearance().setAnimations(); player.getAppearance().sync(); - AgilityHandler.forceWalk(player, -1, last, last.transform(dir), Animation.create(1120 + m), 5, getExp(player, 22.0), null).setDirection(faceDirection); + AgilityHandler.forceWalk(player, -1, last, last.transform(dir), Animation.create(1120 + m), 5, getExp(player, 22.0), null, faceDirection); player.logoutListeners.remove("brimcourse"); return true; } player.logoutListeners.remove("brimcourse"); - AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null).setDirection(faceDirection); + AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null, faceDirection); return false; } }); @@ -272,7 +274,7 @@ public final class BrimhavenCourse extends OptionHandler { public boolean pulse() { if (!finish) { setDelay(2); - AgilityHandler.fail(player, 1, player.getLocation().transform(0, 0, -3), Animation.create(189), getHitAmount(player), "You stepped on a broken piece of plank!"); + AgilityHandler.fail(player, 1, player.getLocation().transform(0, 0, -3), Animation.create(768), getHitAmount(player), "You stepped on a broken piece of plank!"); finish = true; return false; } @@ -282,7 +284,7 @@ public final class BrimhavenCourse extends OptionHandler { }); return; } - AgilityHandler.walk(player, -1, start, end, ForceMovement.WALK_ANIMATION, getExp(player, 6.0), null); + AgilityHandler.walk(player, -1, start, end, Animation.create(1426), getExp(player, 6.0), null); } /** @@ -389,6 +391,7 @@ public final class BrimhavenCourse extends OptionHandler { } break; case 3551: + case 3583: return n.getLocation(); } return null; diff --git a/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java index 086a59522..3ac7da4a3 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java @@ -7,6 +7,8 @@ import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -29,6 +31,9 @@ public final class DartTrap implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(6); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java index 69111c6bf..2885a7f6a 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,9 @@ public final class FloorSpikes implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(5); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java index a2bc07cb2..d27a76bcf 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,10 @@ public final class PressurePad implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = dest.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); + e.lock(5); if(e.isPlayer()) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java index a13815767..3d2a9c1e2 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java @@ -6,6 +6,9 @@ import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; +import core.net.packet.PacketRepository; +import core.net.packet.context.PlayerContext; +import core.net.packet.out.ClearMinimapFlag; import kotlin.Unit; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -24,6 +27,9 @@ public final class SpinningBlades implements MovementHook { final Direction dir = e.getDirection(); final Player player = (Player) e; final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); e.lock(5); if(e.isPlayer()) { @@ -70,4 +76,4 @@ public final class SpinningBlades implements MovementHook { } player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(l), Animation.create(1107)); } -} \ No newline at end of file +} 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..2ee636337 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. @@ -86,8 +90,6 @@ public final class BuildingUtils { for (int menuIndex = 0; menuIndex < 7; menuIndex++) { int itemsStringOffset = 97 + (menuIndex * 5); - - //97 + if (menuIndex >= hotspot.getDecorations().length || (hotspot.getDecorations()[menuIndex] != null && hotspot.getDecorations()[menuIndex].isInvisibleNode())) { for (int j = 0; j < 5; j++) { player.getPacketDispatch().sendString("", 396, itemsStringOffset + j); @@ -100,43 +102,24 @@ public final class BuildingUtils { Decoration decoration = hotspot.getDecorations()[menuIndex]; items[BUILD_INDEXES[menuIndex]] = new Item(decoration.getInterfaceItem()); player.getPacketDispatch().sendString(ItemDefinition.forId(decoration.getInterfaceItem()).getName(), 396, itemsStringOffset); - boolean hasRequirements = player.getSkills().getLevel(Skills.CONSTRUCTION) >= decoration.getLevel(); + boolean hasRequirements = player.getSkills().getLevel(Skills.CONSTRUCTION) >= decoration.getLevel(); //nails are checked in buildDecoration + Item[] reqItems = decoration.getItems(); + String[] reqsText = decoration.getReqsText(); for (int j = 0; j < 4; j++) { - if (j >= decoration.getItems().length) { - if (j == decoration.getItems().length && decoration.getNailAmount() > 0) { - player.getPacketDispatch().sendString("Nails: " + decoration.getNailAmount(), 396, (itemsStringOffset + 1) + j); - } else { - player.getPacketDispatch().sendString("", 396, (itemsStringOffset + 1) + j); - } - } else { - Item item = decoration.getItems()[j]; - if (!player.getInventory().containsItem(item)) { - hasRequirements = false; - } - String s = item.getName() + ": " + item.getAmount(); - /*if (j > 1 && (decoration == Decoration.RUNE_CASE1 || decoration == Decoration.RUNE_CASE2)) { - if (j == 3) { - offset--; - item = decoration.getItems()[++j]; - s = item.getName() + ": " + item.getAmount(); - } - item = decoration.getItems()[j + 1]; - s += ", " + item.getName() + ": " + item.getAmount(); - player.getPacketDispatch().sendString(s, 396, 15 + offset + j); - continue; - }*/ - player.getPacketDispatch().sendString(s, 396, (itemsStringOffset + 1) + j); + if (j < reqItems.length && !player.getInventory().containsItem(reqItems[j])) { + hasRequirements = false; } + player.getPacketDispatch().sendString(reqsText[j], 396, (itemsStringOffset + 1) + j); } 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 +184,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 +270,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 +328,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 +348,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 +394,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 +536,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 +634,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..750654efd --- /dev/null +++ b/Server/src/main/content/global/skill/construction/ConstructionGuideBook.kt @@ -0,0 +1,70 @@ +package content.global.skill.construction + +import core.api.sendMessage +import core.api.storeBookInHouse +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, node -> + if (settings!!.isDevMode && settings!!.isBeta) { + for (item in RESOURCES) { + if (!player.inventory.contains(item.id, item.amount)) { + player.inventory.add(item, player) + } + } + } + storeBookInHouse(player, node) + 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..4191cb1a9 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.STEEL_FRAMED_BENCH_8377), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), + WORKBENCH_WITH_LATHE (13708, 8379, 77, 1000, new Item[] { new Item(Items.OAK_WORKBENCH_8376), new Item(Items.OAK_PLANK_8778, 2), new Item(Items.STEEL_BAR_2353) }), + /** * Workshop repair benches/stands */ - REPAIR_BENCH(13713, 8389, 15, 120.0, new Item(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) }, new String[] { "Teak plank: 3", "Amulet of Glory", "(uncharged): 1" }), + 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,108 @@ public enum Decoration { * The object ids depending on styling. */ private final int[] objectIds; - + /** - * If this node should be invisible to user build options + * If this node should be invisible to user build options. */ private boolean invisibleNode; - + /** - * Constructs a new {@code Portal} {@code 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 items The items required. + * The requirements text to show for this node. */ - private Decoration(int objectId, int interfaceItem, int level, double experience, Item... items) { - this(objectId, interfaceItem, level, experience, new int[] { 2347, 8794 }, items); + private String[] reqsText = {"", "", "", ""}; + + private String[] generateDefaultReqsText(Item[] items, int nailAmount) { + String[] ret = {"", "", "", ""}; + for (int i = 0; i < 4; i++) { + if (i >= items.length) { + if (i == items.length && nailAmount > 0) { + ret[i] = "Nails: " + nailAmount; + } + } else { + ret[i] = items[i].getName() + ": " + items[i].getAmount(); + } + } + return ret; } - + /** - * Constructs a new {@code Portal} {@code Object}. + * Constructs a new object, default items, default tools, default refund items, default requirements text. + * @param objectId The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + */ + 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[] {}, new String[] {}); + } + + /** + * Constructs a new object, default tools, default refund items, default requirements text. * @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[] {}, new String[] {}); + this.reqsText = generateDefaultReqsText(items, getNailAmount()); + } + + /** + * Constructs a new object, default refund items, default requirements text. + * @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[] {}, new String[] {}); + this.reqsText = generateDefaultReqsText(items, getNailAmount()); + } + + /** + * 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, new String[] {}); + this.reqsText = generateDefaultReqsText(items, getNailAmount()); + } + + /** + * Constructs a new object, default 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, String[] reqsText) { + this(objectId, interfaceItem, level, experience, new int[] { Items.HAMMER_2347, Items.SAW_8794 }, items, refundItems, reqsText); + } + + /** + * 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. + * @param reqsText The requirements text to be shown in the interface. + */ + Decoration(int objectId, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems, String[] reqsText) { this.objectId = objectId; this.objectIds = null; this.interfaceItem = interfaceItem; @@ -1136,39 +834,45 @@ public enum Decoration { this.experience = experience; this.tools = tools; this.items = items; + this.refundItems = refundItems; + if (reqsText.length > 0) { + System.arraycopy(reqsText, 0, this.reqsText, 0, reqsText.length); + } } - + /** * 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. * @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. */ - 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, Item[] refundItems) { this.objectId = objectIds[0]; this.objectIds = objectIds; this.interfaceItem = interfaceItem; @@ -1176,6 +880,7 @@ public enum Decoration { this.experience = experience; this.tools = tools; this.items = items; + this.refundItems = refundItems; } /** @@ -1202,7 +907,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 +921,7 @@ public enum Decoration { } return null; } - + public static Decoration forName(String name) { for (Decoration d : Decoration.values()) { if (d.name().equals(name)) { @@ -1238,7 +943,7 @@ public enum Decoration { } return 0; } - + /** * Gets the objectId. * @param style The current housing style. @@ -1250,7 +955,7 @@ public enum Decoration { } return objectId; } - + /** * Gets the objectId. * @return The objectId. @@ -1271,7 +976,7 @@ public enum Decoration { * Gets the experience. * @return The experience. */ - public double getExperience() { + public int getExperience() { return experience; } @@ -1283,6 +988,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 +1020,19 @@ 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 + /** + * Gets the requirements text. + * @return The requirements text. + */ + public String[] getReqsText() { + return reqsText; + } +} 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..4db8e8191 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,36 +98,6 @@ public final class HouseManager { } - public void save(ByteBuffer buffer) { - buffer.put((byte) location.ordinal()); - buffer.put((byte) style.ordinal()); - if (hasServant()) { - servant.save(buffer); - } else { - buffer.put((byte) -1); - } - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room room = rooms[z][x][y]; - if (room != null) { - buffer.put((byte) z).put((byte) x).put((byte) y); - buffer.put((byte) room.getProperties().ordinal()); - buffer.put((byte) room.getRotation().toInteger()); - for (int i = 0; i < room.getHotspots().length; i++) { - if (room.getHotspots()[i].getDecorationIndex() > -1) { - buffer.put((byte) i); - buffer.put((byte) room.getHotspots()[i].getDecorationIndex()); - } - } - buffer.put((byte) -1); - } - } - } - } - buffer.put((byte) -1);//Eof - } - public void parse(JSONObject data){ location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())]; style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())]; @@ -159,43 +124,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 +165,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 +187,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 +195,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 +204,7 @@ public final class HouseManager { } /** - * Leaves this house. + * Leaves this house through the portal. * @param player The player leaving. */ public static void leave(Player player) { @@ -252,7 +214,7 @@ public final class HouseManager { } if (house.isInHouse(player)) { player.animate(Animation.RESET); - player.setLocation(house.location.getExitLocation()); + player.getProperties().setTeleportLocation(house.location.getExitLocation()); } } @@ -270,8 +232,7 @@ public final class HouseManager { if (enable) { expelGuests(player); } - buildingMode = enable; - reload(player, enable); + enter(player, enable); player.getPacketDispatch().sendMessage("Building mode is now " + (buildingMode ? "on." : "off.")); } } @@ -280,6 +241,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 +285,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++) { @@ -385,6 +347,7 @@ public final class HouseManager { Room room = rooms[0][4][3] = new Room(RoomProperties.GARDEN); room.configure(style); room.getHotspots()[0].setDecorationIndex(0); + redecorate(HousingStyle.BASIC_WOOD); this.location = location; } @@ -396,10 +359,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 +376,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 +394,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 +414,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 +509,7 @@ public final class HouseManager { break; } case SOUTH: { + chunkX = 7 - chunkX; chunkY = 7 - chunkY; break; } @@ -743,8 +704,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 +853,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..31a57f6f8 100644 --- a/Server/src/main/content/global/skill/construction/HouseZone.java +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -1,10 +1,18 @@ package content.global.skill.construction; +import core.api.Container; +import core.game.world.map.Location; +import org.rs09.consts.Items; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.zone.MapZone; -import core.game.world.map.zone.ZoneRestriction; +import core.game.world.map.RegionManager; +import core.game.world.map.Region; +import core.game.system.task.Pulse; +import core.game.world.map.zone.ZoneType; + +import static core.api.ContentAPIKt.*; /** * Handles the player owned house zone. @@ -32,26 +40,43 @@ public final class HouseZone extends MapZone { * Constructs the house zone object. */ public HouseZone(HouseManager house) { - super("poh-zone" + house, true, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.FOLLOWERS); + super("poh-zone" + house, true); this.house = house; } @Override public void configure() { + setZoneType(ZoneType.P_O_H.getId()); + unregisterOldRegions(); + registerRegion(house.getHouseRegion().getId()); + if (house.getDungeonRegion() != null) { + 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 +85,73 @@ 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; + + // if the player is moving between the dungeon and main house, don't trigger the leave sequence + if (house.getDungeonRegion() != null && !logout) { + // current loc + Location dest = p.getProperties().getTeleportLocation(); + int currentRegionId = p.getLocation().getRegionId(); + int houseId = house.getHouseRegion().getId(); + int dungeonId = house.getDungeonRegion().getId(); + + // check if still in house + boolean currentlyInHouse = (currentRegionId == houseId || currentRegionId == dungeonId); + + // check if moving between house regions + int destRegionId = (dest != null) ? dest.getRegionId() : -1; + boolean movingToHouse = (destRegionId == houseId || destRegionId == dungeonId); + + // if yes, return true and stop the leave sequence + if (currentlyInHouse || movingToHouse) { + return true; + } } + + remove_items(p); + // The below tears down the house if the owner was the one who left + if (house == p.getHouseManager()) { + house.expelGuests(p); + int toRemove = previousRegion; + int dungRemove = previousDungeon; + submitWorldPulse(new Pulse(1) { + public boolean pulse() { + Region r = RegionManager.forId(toRemove); + Region dr = dungRemove != -1 ? RegionManager.forId(dungRemove) : null; + RegionManager.removeRegion(toRemove); + unregisterRegion(toRemove); + r.flagInactive(); + if (dungRemove != -1) { + RegionManager.removeRegion(dungRemove); + unregisterRegion(dungRemove); + dr.flagInactive(); + } + 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 + + private void remove_items(Player p) { + for (int item = Items.KETTLE_7688; item <= Items.CHEFS_DELIGHT_7755; item++) {//Removes all PoH versions of tea and beer barrel-related items + removeAll(p, item, Container.INVENTORY); + removeAll(p, item, Container.BoB); + } + } +} diff --git a/Server/src/main/content/global/skill/construction/HousingStyle.java b/Server/src/main/content/global/skill/construction/HousingStyle.java index e4d59b90d..eace0e7bb 100644 --- a/Server/src/main/content/global/skill/construction/HousingStyle.java +++ b/Server/src/main/content/global/skill/construction/HousingStyle.java @@ -3,12 +3,16 @@ package content.global.skill.construction; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; +import java.util.Arrays; + /** * The styles of houses. * @author Emperor * */ public enum HousingStyle { + + // open door ids are doorId + 1 BASIC_WOOD (1, 5000, 7503, 0, 13100, 13101, 13098, Decoration.BASIC_WOOD_WINDOW), BASIC_STONE (10, 5000, 7503, 1, 13094, 13096, 1902, Decoration.BASIC_STONE_WINDOW), @@ -16,6 +20,28 @@ public enum HousingStyle { FREMENNIK_STYLE_WOOD(30, 10000, 7503, 3, 13109, 13107, 13111, Decoration.FREMENNIK_WINDOW), TROPICAL_WOOD (40, 15000, 7759, 0, 13016, 13015, 13011, Decoration.TROPICAL_WOOD_WINDOW), FANCY_STONE (50, 25000, 7759, 1, 13119, 13118, 13116, Decoration.FANCY_STONE_WINDOW); + + /** + * Array of all Dungeon Wall IDs. + * From Region 7503, Location.create(1898, 5084, 0) + */ + private static final int[] DUNGEON_WALL_IDS = { + 13019, 13020, 13021, 13022, 13023, 13024, 13025, 13026, 13027, 13028, + 13029, 13030, 13031, 13032, 13033, 13034, 13035, 13036, 13037, 13046, + 13048, 13049, 13050, 13051, 13055, 13056, 13058, 13059, 13060, 13061, + 13062, 13063, 13065, 13066, 13067, 13068, 13069, 13070, 13072, 13073, + 13074, 13075, 13076, 13077, 13079, 13080, 13081, 13082, 13083, 13084, + 13086, 13087, 13088, 13089 + }; + + /** + * Checks if the provided ID is a dungeon wall. + * @param id The object ID. + * @return {@code true} if it's a dungeon wall. + */ + public static boolean isDungeonWall(int id) { + return Arrays.binarySearch(DUNGEON_WALL_IDS, id) >= 0; + } /** * The level required. diff --git a/Server/src/main/content/global/skill/construction/RemovalDialogue.java b/Server/src/main/content/global/skill/construction/RemovalDialogue.java index 5b2a9f869..3edbc360f 100644 --- a/Server/src/main/content/global/skill/construction/RemovalDialogue.java +++ b/Server/src/main/content/global/skill/construction/RemovalDialogue.java @@ -53,10 +53,23 @@ public final class RemovalDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { pos = (int[]) args[1]; - plane = player.getLocation().getZ(); - if (HouseManager.isInDungeon(player)) { - plane = 3; - } + + if (args.length > 2 && args[2] instanceof Integer) { + plane = (Integer) args[2]; + } else { + plane = player.getLocation().getZ(); + if (HouseManager.isInDungeon(player)) { + plane = 3; + } + } + + // check if room exists + room = player.getHouseManager().getRooms()[plane][pos[0]][pos[1]]; + if (room == null || room.getProperties().isRoof()) { + interpreter.sendPlainMessage(false, "There is no room there to remove."); + return false; + } + room = player.getHouseManager().getRooms()[plane][pos[0]][pos[1]]; player.getDialogueInterpreter().sendOptions("Remove the " + (room != null ? room.getProperties().getName() : "room") + "?", "Yes", "No"); stage = 0; diff --git a/Server/src/main/content/global/skill/construction/Room.java b/Server/src/main/content/global/skill/construction/Room.java index c8a02bfda..ac7b56cec 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,18 @@ 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) { + int id = object.getId(); + boolean isBuilt = object instanceof Constructed; + boolean isWall = HousingStyle.isDungeonWall(id) || id == house.getStyle().getWallId(); + boolean isDoor = id == house.getStyle().getDoorId() || id == house.getStyle().getSecondDoorId(); + if (!isBuilt && !isWall && !isDoor) { + SceneryBuilder.remove(object); chunk.remove(object); } } @@ -184,65 +191,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 +424,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 deleted file mode 100644 index d1f8f7bc9..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/BeerBarrelPlugin.java +++ /dev/null @@ -1,75 +0,0 @@ -package content.global.skill.construction.decoration; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the Construction beer barrels. - * @author Splinter - */ -@Initializable -public class BeerBarrelPlugin extends UseWithHandler { - - /** - * The object ids - */ - private static final int[] OBJECTS = new int[] { - 13568, 13569, 13570, 13571, 13572, 13573 - }; - - /** - * Constructs a new {@Code BeerBarrelPlugin} {@Code Object} - */ - public BeerBarrelPlugin() { - super(1919); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : OBJECTS) { - addHandler(id, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Player player = event.getPlayer(); - final Scenery object = (Scenery) event.getUsedWith(); - - if (player.getInventory().remove(new Item(1919))) { - player.animate(Animation.create(833)); - player.sendMessage("You fill up your glass."); - player.getInventory().add(new Item(getReward(object.getId()), 1)); - } - return true; - } - - /** - * Get the beer reward based on the interaced barrel. - * @return the item to give. - */ - public int getReward(int barrelId) { - switch (barrelId) { - case 13568: - return 1917; - case 13569: - return 5763; - case 13570: - return 1905; - case 13571: - return 1909; - case 13572: - return 1911; - case 13573: - return 5755; - } - return 1917; - } -} 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/StaircasePlugin.java b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java index a1fe9ed3e..8c22d84ab 100644 --- a/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java @@ -29,15 +29,19 @@ public final class StaircasePlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { ClassScanner.definePlugin(new BuildDialogue()); ClassScanner.definePlugin(new ClimbPohLadder()); - for (int i = 13497; i < 13507; i++) { + // 13497 - 13502 are normal stairs + // 13503 - 13506 are spiral stairs + for (int i = 13497; i <= 13506; i++) { SceneryDefinition.forId(i).getHandlers().put("option:climb", this); SceneryDefinition.forId(i).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(i).getHandlers().put("option:climb-down", this); SceneryDefinition.forId(i).getHandlers().put("option:remove-room", this); } + // 13409 is dungeon entrance SceneryDefinition.forId(13409).getHandlers().put("option:enter", this); SceneryDefinition.forId(13409).getHandlers().put("option:remove-room", this); - for (int id = 13328; id < 13331; id++) { + // 13328 - 13330 are dungeon ladders + for (int id = 13328; id <= 13330; id++) { SceneryDefinition.forId(id).getHandlers().put("option:climb", this); SceneryDefinition.forId(id).getHandlers().put("option:remove-room", this); } @@ -69,13 +73,47 @@ public final class StaircasePlugin extends OptionHandler { SceneryBuilder.replace(object, object.transform(object.getId() - 3)); return true; case "remove-room": - if (player.getLocation().getZ() != 0) { - player.getPacketDispatch().sendMessage("The room below is supporting this room!"); - return true; - } - return false; + + int currentZ = player.getLocation().getZ(); + int chunkX = player.getLocation().getChunkX(); + int chunkY = player.getLocation().getChunkY(); + + // must be in building mode + if(!player.getHouseManager().isBuildingMode()) { + player.getPacketDispatch().sendMessage("You can only do this in building mode."); + return true; + } + + // if you're in the dungeon, try and remove the room at Z = 0 from the house region. + if (HouseManager.isInDungeon(player)) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 0); + + // From ground floor (z=0), stairs could lead to 1st floor (z=1) or the dungeon (z=3) + } else { + // garden dungeon entrance and oubliette ladders don't need a height selection + if(node.getId() == 13409 || node.getId() == 13675 || node.getId() == 13676 || node.getId() == 13677 || node.getId() == 13678 || node.getId() == 13679 || node.getId() == 13680) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); + } else { + player.getDialogueInterpreter().sendOptions("Remove which room?", "Room Above", "Room Below", "Cancel"); + player.getDialogueInterpreter().addAction((interId, btn) -> { + if (btn == 2) { // Above + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ + 1); + } else if (btn == 3) { // Below + if (currentZ - 1 == -1) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); + } else { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ - 1); + } + + } + }); + } + + } + return true; + case "climb": - if (house.getDungeonRegion() == player.getViewport().getRegion()) { + if (HouseManager.isInDungeon(player)) { climb(player, 1, house, object); return true; } @@ -379,10 +417,11 @@ public final class StaircasePlugin extends OptionHandler { for (Direction d : dirs) { if (d == room.getRotation()) { r.setRotation(d); - Hotspot stairs = room.getStairs(); - int index = stairs != null ? stairs.getDecorationIndex() : -1; + // todo this just puts a default set of stairs in the dungeon. The below code works for skill/quest hall, but not a dungeon entrance. + //Hotspot stairs = room.getStairs(); + //int index = stairs != null ? stairs.getDecorationIndex() : -1; BuildingUtils.buildRoom(player, r, plane, roomX, roomY, r.getExits(), true); - r.getStairs().setDecorationIndex(index); + r.getStairs().setDecorationIndex(/*index*/1); end(); return true; } 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/ToyBoxPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/costume/ToyBoxPlugin.kt deleted file mode 100644 index 86b5007d1..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/costume/ToyBoxPlugin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package content.global.skill.construction.decoration.costume - -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import content.global.handlers.item.toys.DiangoReclaimInterface -import core.game.node.Node -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.plugin.Plugin - -/** - * Handles the Toy Box POH - * ToyBoxPlugin.java - * @author Lee - * @date 10/2/2017 - */ -@Initializable -class ToyBoxPlugin : OptionHandler() { - @Throws(Throwable::class) - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.forId(18802).handlers["option:open"] = this - return this - } - - override fun handle(player: Player, node: Node, option: String): Boolean { - DiangoReclaimInterface.open(player) - return true - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/AbstractContainer.kt b/Server/src/main/content/global/skill/construction/decoration/kitchen/AbstractContainer.kt new file mode 100644 index 000000000..6086aff25 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/AbstractContainer.kt @@ -0,0 +1,78 @@ +package content.global.skill.construction.decoration.kitchen + +import core.api.addItem +import core.api.freeSlots +import core.api.sendDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.tools.END_DIALOGUE + +abstract class AbstractContainer(containerId: Int, containers: Map>>, private val containerName: String) : DialogueFile() { + + private val container = containers[containerId]!! + private val containerItemsNames = container.map { it.first } + private val containerItemsIds = container.map { it.second } + private val size = container.size + private val itemsPerPage = 4 + + private fun checkItem(player: Player, idx : Int){ + val item = containerItemsIds[idx] + if (addItem(player, item)){ + end() + } + else{ + sendDialogue(player, "You need at least one free inventory space to take from the $containerName.").also { stage = END_DIALOGUE } + } + } + + private fun createTopics(page: Int): MutableList> { + val startIndex = (page - 1) * itemsPerPage + val endIndex = (startIndex + itemsPerPage).coerceAtMost(size) + + val topics = containerItemsNames.subList(startIndex, endIndex) + .mapIndexed { index, item -> Topic(item, startIndex + index + 1, skipPlayer = true) } + .toMutableList() + + // Add "More" button if there's another page or to loop back + if (endIndex < size || page > 1) { + topics.add(Topic("More", endIndex + 1, skipPlayer = true)) + } + + return topics + } + + + + override fun handle(componentID: Int, buttonID: Int) { + if (freeSlots(player!!) == 0) { + sendDialogue(player!!, "You need at least one free inventory space to take from the $containerName.").also { stage = END_DIALOGUE } + return + } + val itemsPerPage = 4 + + when (stage) { + 0, size + 1 -> { // Initial stage or final more button clicked + val topics = createTopics(1) + showTopics(*topics.toTypedArray()).also { stage = 1 } + } + in 1..size -> { + val currentPage = (stage-1) / itemsPerPage + val startIndex = currentPage * itemsPerPage + + if (buttonID == itemsPerPage + 1) { // "More" button clicked (but not the last one) + val topics = createTopics(currentPage + 1) + showTopics(*topics.toTypedArray()).also { stage = currentPage*itemsPerPage + 1 } + } else { // Handle item selection + val itemIndex = startIndex + (buttonID - 1) + if (itemIndex < size) { + checkItem(player!!, itemIndex) + } + } + } + else -> end() + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelInteraction.kt b/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelInteraction.kt new file mode 100644 index 000000000..4c6ee0949 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelInteraction.kt @@ -0,0 +1,29 @@ +package content.global.skill.construction.decoration.kitchen + +import core.api.* +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class BeerBarrelInteraction : InteractionListener { + + companion object{ + private val barrelMap = mapOf( + org.rs09.consts.Scenery.BEER_BARREL_13568 to Items.BEER_7740, + org.rs09.consts.Scenery.CIDER_BARREL_13569 to Items.CIDER_7752, + org.rs09.consts.Scenery.ASGARNIAN_ALE_13570 to Items.ASGARNIAN_ALE_7744, + org.rs09.consts.Scenery.GREENMAN_S_ALE_13571 to Items.GREENMANS_ALE_7746, + org.rs09.consts.Scenery.DRAGON_BITTER_13572 to Items.DRAGON_BITTER_7748, + org.rs09.consts.Scenery.CHEF_S_DELIGHT_13573 to Items.CHEFS_DELIGHT_7754, + ) + } + override fun defineListeners() { + onUseWith(SCENERY, Items.BEER_GLASS_7742, *barrelMap.keys.toIntArray()){ player, used, with -> + val drinkName = with.name.lowercase().replace("barrel", "").trim()+"." + if (removeItem(player, used)){ + sendMessage(player, "You fill up your glass with $drinkName") + addItem(player, barrelMap[with.id]!!) + } + return@onUseWith true + } + } +} \ 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 deleted file mode 100644 index 6a8c19044..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelPlugin.java +++ /dev/null @@ -1,76 +0,0 @@ -package content.global.skill.construction.decoration.kitchen; - - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the Construction beer barrels. - * @author Splinter - */ -@Initializable -public class BeerBarrelPlugin extends UseWithHandler { - - /** - * The object ids - */ - private static final int[] OBJECTS = new int[] { - 13568, 13569, 13570, 13571, 13572, 13573 - }; - - /** - * Constructs a new {@Code BeerBarrelPlugin} {@Code Object} - */ - public BeerBarrelPlugin() { - super(1919); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : OBJECTS) { - addHandler(id, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Player player = event.getPlayer(); - final Scenery object = (Scenery) event.getUsedWith(); - - if (player.getInventory().remove(new Item(1919))) { - player.animate(Animation.create(3661 + (object.getId() - 13569))); - player.sendMessage("You fill up your glass with " + object.getName().toLowerCase().replace("barrel", "").trim() + "."); - player.getInventory().add(new Item(getReward(object.getId()), 1)); - } - return true; - } - - /** - * Get the beer reward based on the interacted barrel. - * @return the item to give. - */ - public int getReward(int barrelId) { - switch (barrelId) { - case 13568: - return 1917; - case 13569: - return 5763; - case 13570: - return 1905; - case 13571: - return 1909; - case 13572: - return 1911; - case 13573: - return 5755; - } - return 1917; - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderListener.kt b/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderListener.kt new file mode 100644 index 000000000..1bff6bc47 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderListener.kt @@ -0,0 +1,42 @@ +package content.global.skill.construction.decoration.kitchen + +import core.api.openDialogue +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class LarderListener : InteractionListener { + + companion object { + private val wooden_larder_items = listOf( + "Tea Leaves" to Items.TEA_LEAVES_7738, + "Bucket of Milk" to Items.BUCKET_OF_MILK_1927 + ) + private val oak_larder_items = wooden_larder_items + listOf( + "Eggs" to Items.EGG_1944, + "Pot of Flour" to Items.POT_OF_FLOUR_1933 + ) + private val teak_larder_items = oak_larder_items + listOf( + "Potatoes" to Items.POTATO_1942, + "Garlic" to Items.GARLIC_1550, + "Onions" to Items.ONION_1957, + "Cheese" to Items.CHEESE_1985 + ) + + val larders = mapOf( + Scenery.LARDER_13565 to wooden_larder_items, + Scenery.LARDER_13566 to oak_larder_items, + Scenery.LARDER_13567 to teak_larder_items + ) + } + + override fun defineListeners() { + on(larders.keys.toIntArray(), IntType.SCENERY, "Search") { player, node -> + openDialogue(player, LarderDialogue(node.id)) + return@on true + } + } + + class LarderDialogue(shelfId: Int) : AbstractContainer(shelfId, larders, "larder") +} \ No newline at end of file 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 deleted file mode 100644 index f30f0cebc..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderPlugin.java +++ /dev/null @@ -1,148 +0,0 @@ -package content.global.skill.construction.decoration.kitchen; - - -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Handles the interactions for the three Larders. - * @author Splinter - */ -@Initializable -public final class LarderPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ClassScanner.definePlugin(new LarderDialogue()); - SceneryDefinition.forId(13565).getHandlers().put("option:search", this); - SceneryDefinition.forId(13566).getHandlers().put("option:search", this); - SceneryDefinition.forId(13567).getHandlers().put("option:search", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(42048, node.getId()); - return true; - } - - /** - * Dialogue options for the Larders. - * @author Splinter - * @version 1.0 - */ - public final class LarderDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LarderDialogue} {@code Object}. - */ - public LarderDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LarderDialogue} {@code Object}. - * - * @param player - * the player. - */ - public LarderDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LarderDialogue(player); - } - - @Override - public boolean open(Object... args) { - int id = (int) args[0]; - switch (id) { - case 13565: - interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk"); - stage = 1; - break; - case 13566: - interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk", "Eggs", "Pot of Flour"); - stage = 1; - break; - case 13567: - interpreter.sendOptions("Select an Option", "Tea Leaves", "Bucket of Milk", "Eggs", "Pot of Flour", "More Options"); - stage = 1; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (player.getInventory().freeSlots() < 1) { - player.sendMessage("You need at least one free inventory space to take from the larder."); - end(); - return true; - } - switch (stage) { - case 1: - switch (buttonId) { - case 1: - player.getInventory().add(new Item(7738, 1)); - end(); - break; - case 2: - player.getInventory().add(new Item(1927, 1)); - end(); - break; - case 3: - player.getInventory().add(new Item(1944, 1)); - end(); - break; - case 4: - player.getInventory().add(new Item(1933, 1)); - end(); - break; - case 5: - player.getDialogueInterpreter().sendOptions( - "Select an Option", "Potatoes", "Garlic", "Onions", "Cheese"); - stage = 2; - break; - } - break; - case 2: - switch (buttonId) { - case 1: - player.getInventory().add(new Item(1942, 1)); - end(); - break; - case 2: - player.getInventory().add(new Item(1550, 1)); - end(); - break; - case 3: - player.getInventory().add(new Item(1957, 1)); - end(); - break; - case 4: - player.getInventory().add(new Item(1985, 1)); - end(); - break; - } - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 42048 }; - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfListener.kt b/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfListener.kt new file mode 100644 index 000000000..bf39a34dc --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfListener.kt @@ -0,0 +1,97 @@ +package content.global.skill.construction.decoration.kitchen + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import org.rs09.consts.Scenery + + +class ShelfListener : InteractionListener { + companion object { + // Source for text https://www.youtube.com/watch?v=SNuqelEft3Y + + private val woodshelf_1_items = listOf( + "Kettle" to Items.KETTLE_7688, + "Teapot" to Items.TEAPOT_7702, + "Cup" to Items.EMPTY_CUP_7728 + ) + + private val woodshelf_2_items = woodshelf_1_items + listOf( + "Beer glass" to Items.BEER_GLASS_7742 + ) + + private val woodshelf_3_items = woodshelf_2_items.map { (name, itemId) -> + when (name) { + "Cup" -> { + "Porcelain cup" to Items.PORCELAIN_CUP_7732 + } + + "Teapot" -> { + "Teapot" to Items.TEAPOT_7714 + } + + else -> { + name to itemId + } + } + } + listOf("Cake tin" to Items.CAKE_TIN_1887) + + private val oakshelf_1_items = woodshelf_2_items + listOf("Cake tin" to Items.CAKE_TIN_1887) + listOf("Bowl" to Items.BOWL_1923) + + private val oakshelf_2_items = oakshelf_1_items.map { (name, itemId) -> + when (name) { + "Cup" -> { + "Porcelain cup" to Items.PORCELAIN_CUP_7732 + } + + "Teapot" -> { + "Teapot" to Items.TEAPOT_7714 + } + + else -> { + name to itemId + } + } + } + listOf("Pie dish" to Items.PIE_DISH_2313) + + private val teakshelf_1_items = oakshelf_2_items + listOf( + "Pot" to Items.EMPTY_POT_1931 + ) + + private val teakshelf_2_items = teakshelf_1_items.map { (name, itemId) -> + when (name) { + "Porcelain cup" -> { + "Porcelain cup" to Items.PORCELAIN_CUP_7735 + } + + "Teapot" -> { + "Teapot" to Items.TEAPOT_7726 + } + + else -> { + name to itemId + } + } + } + listOf("Chef's Hat" to Items.CHEFS_HAT_1949) + + val shelves = mapOf( + Scenery.SHELVES_13545 to woodshelf_1_items, + Scenery.SHELVES_13546 to woodshelf_2_items, + Scenery.SHELVES_13547 to woodshelf_3_items, + Scenery.SHELVES_13548 to oakshelf_1_items, + Scenery.SHELVES_13549 to oakshelf_2_items, + Scenery.SHELVES_13550 to teakshelf_1_items, + Scenery.SHELVES_13551 to teakshelf_2_items, + ) + } + + override fun defineListeners() { + on(shelves.keys.toIntArray(), IntType.SCENERY, "Search") { player, node -> + openDialogue(player, ShelfDialogue(node.id)) + return@on true + } + } + + class ShelfDialogue(shelfId: Int) : AbstractContainer(shelfId, shelves, "shelf") +} \ No newline at end of file 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 deleted file mode 100644 index 660749037..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfPlugin.java +++ /dev/null @@ -1,238 +0,0 @@ -package content.global.skill.construction.decoration.kitchen; - - -import core.cache.def.impl.SceneryDefinition; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Handles the shelves in the kitchen room. - * @author Splinter - */ -@Initializable -public final class ShelfPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ClassScanner.definePlugin(new ShelfDialogue()); - for (int i = 13545; i < 13552; i++) { - SceneryDefinition.forId(i).getHandlers().put("option:search", this); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(778341, node.getId()); - return true; - } - - /** - * Dialogue options for the shelves, what a mess! - * @author Splinter - * @version 1.0 - */ - public final class ShelfDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ShelfDialogue} {@code Object}. - */ - public ShelfDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ShelfDialogue} {@code Object}. - * @param player the player. - */ - public ShelfDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShelfDialogue(player); - } - - @Override - public boolean open(Object... args) { - int id = (int) args[0]; - switch (id) { - case 13545:// wood 1 - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup"); - stage = 1; - break; - case 13546:// wood 2 - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass"); - stage = 1; - break; - case 13547:// wood 3 - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass", "Cake tin"); - stage = 1; - break; - case 13548:// oak 1 - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Clay cup", "Empty beer glass", "Bowl"); - stage = 2; - break; - case 13549:// oak 2 - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Porcelain cup", "Empty beer glass", "More Options"); - stage = 3; - break; - case 13550:// teak 1 - case 13551: - interpreter.sendOptions("Select an Option", "Kettle", "Teapot", "Porcelain cup", "Empty beer glass", "More Options"); - stage = 5; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (player.getInventory().freeSlots() < 1) { - player.sendMessage("You need at least one free inventory space to take from the shelves."); - end(); - return true; - } - switch (stage) { - case 1:// all wood shelves - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(7688, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(7702, 1)); - break; - case 3: - end(); - player.getInventory().add(new Item(7728, 1)); - break; - case 4: - end(); - player.getInventory().add(new Item(1919, 1)); - break; - case 5: - end(); - player.getInventory().add(new Item(1887, 1)); - break; - } - break; - case 2:// Oak shelf #1 - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(7688, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(7702, 1)); - break; - case 3: - end(); - player.getInventory().add(new Item(7728, 1)); - break; - case 4: - end(); - player.getInventory().add(new Item(1919, 1)); - break; - case 5: - end(); - player.getInventory().add(new Item(1923, 1)); - break; - } - break; - case 3:// Oak shelves #2 only - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(7688, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(7702, 1)); - break; - case 3: - end(); - player.getInventory().add(new Item(4244, 1)); - break; - case 4: - end(); - player.getInventory().add(new Item(1919, 1)); - break; - case 5: - interpreter.sendOptions("Select an Option", "Bowl", "Cake tin"); - stage = 4; - break; - } - break; - case 4:// Oak shelves #2 only - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(1923, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(1887, 1)); - break; - } - case 5:// teak shelves - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(7688, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(7702, 1)); - break; - case 3: - end(); - player.getInventory().add(new Item(7735, 1)); - break; - case 4: - end(); - player.getInventory().add(new Item(1919, 1)); - break; - case 5: - interpreter.sendOptions("Select an Option", "Bowl", "Pie dish", "Empty pot"); - stage = 6; - break; - } - break; - case 6:// teak shelves - switch (buttonId) { - case 1: - end(); - player.getInventory().add(new Item(1923, 1)); - break; - case 2: - end(); - player.getInventory().add(new Item(2313, 1)); - break; - case 3: - end(); - player.getInventory().add(new Item(1931, 1)); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 778341 }; - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/StoveInteraction.kt b/Server/src/main/content/global/skill/construction/decoration/kitchen/StoveInteraction.kt new file mode 100644 index 000000000..74b0c35f4 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/StoveInteraction.kt @@ -0,0 +1,88 @@ +package content.global.skill.construction.decoration.kitchen + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import org.rs09.consts.Scenery as SceneryConst +import org.rs09.consts.Items +import core.game.node.scenery.Scenery +import core.tools.secondsToTicks + + +class StoveInteraction : InteractionListener { + + // Imagine if these indexes incremented logically... + companion object{ + val valid_stoves = intArrayOf( + SceneryConst.FIREPIT_WITH_HOOK_13529, + SceneryConst.FIREPIT_WITH_POT_13531, + SceneryConst.SMALL_OVEN_13533, + SceneryConst.LARGE_OVEN_13536, + SceneryConst.STEEL_RANGE_13539, + SceneryConst.FANCY_RANGE_13542 + ) + val in_progress_stoves = intArrayOf( + SceneryConst.FIREPIT_WITH_HOOK_13530, + SceneryConst.FIREPIT_WITH_POT_13532, + SceneryConst.SMALL_OVEN_13534, + SceneryConst.LARGE_OVEN_13537, + SceneryConst.STEEL_RANGE_13541, + SceneryConst.FANCY_RANGE_13544 + + ) + val stoves_with_kettle = intArrayOf( + SceneryConst.FIREPIT_WITH_HOOK_13530, + SceneryConst.FIREPIT_WITH_POT_13532, + SceneryConst.SMALL_OVEN_13535, + SceneryConst.LARGE_OVEN_13538, + SceneryConst.STEEL_RANGE_13540, + SceneryConst.FANCY_RANGE_13543 + + ) + const val kettleBoiled = "kettle_boiled" + } + override fun defineListeners() { + onUseWith(SCENERY, Items.FULL_KETTLE_7690, *valid_stoves){ player, used, with -> + val idx = valid_stoves.indexOf(with.id) + if (removeItem(player, used)){ + replaceScenery(with as Scenery, in_progress_stoves[idx], -1) + sendMessage(player, "You place the kettle over the fire.") + setAttribute(player, kettleBoiled, 1) + queueScript(player, secondsToTicks(10), QueueStrength.SOFT){ _ -> + sendMessage(player, "The kettle boils.") + // Of course they changed how these items work so we need 2 different cases + setAttribute(player, kettleBoiled, 2) + if (with.id in intArrayOf(SceneryConst.FIREPIT_WITH_HOOK_13529, SceneryConst.FIREPIT_WITH_POT_13531)) + return@queueScript stopExecuting(player) + else{ + getScenery(with.location)?.let { replaceScenery(it, stoves_with_kettle[idx], -1) } + return@queueScript stopExecuting(player) + } + } + } + return@onUseWith true + } + + + on(stoves_with_kettle, SCENERY, "take-kettle"){ player, node -> + if (getAttribute(player, kettleBoiled, 0) == 0){ + sendMessage(player, "This is not your kettle.") + return@on false + } + else if (getAttribute(player, kettleBoiled, 0) == 1){ + sendMessage(player, "The kettle has not boiled yet.") + return@on false + } + if(addItem(player, Items.HOT_KETTLE_7691)){ + replaceScenery(node as Scenery, valid_stoves[stoves_with_kettle.indexOf(node.id)], -1) + setAttribute(player, kettleBoiled, 0) + return@on true + } + else { + sendMessage(player, "You do not have space to do that.") + return@on false + } + } + } + +} diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/POHStorageProxyContainer.java b/Server/src/main/content/global/skill/construction/decoration/pohstorage/POHStorageProxyContainer.java new file mode 100644 index 000000000..07c934d4a --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/POHStorageProxyContainer.java @@ -0,0 +1,28 @@ +package content.global.skill.construction.decoration.pohstorage; + +import core.game.container.Container; +import core.game.container.ContainerType; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; + +/** + * A proxy Container used to indicate an item was found in POH storage. + * This is not a real container, just a dummy so that hasAnItem() can be easily extended to check POH storage + */ +public class POHStorageProxyContainer extends Container { + + public POHStorageProxyContainer(Player player) { + super(1, ContainerType.DEFAULT); + } + + // Prevents external code from trying to fetch items from this container because it doesn't actually store anything + @Override + public Item get(int slot) { + return null; + } + + @Override + public String toString() { + return "POHStorageProxyContainer"; + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/Storable.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/Storable.kt new file mode 100644 index 000000000..d4b0b71c5 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/Storable.kt @@ -0,0 +1,444 @@ +package content.global.skill.construction.decoration.pohstorage + +import content.data.Quests +import core.api.getItemName +import core.api.isQuestComplete +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import org.rs09.consts.Items + +/** + * Represents a database of all the storable items in the POH Costume Room and Bookcase across all box types. + */ + +/* + * The below function should be copied into each book's listener to store the book in the POH. It is part of ContentAPI. + * import core.api.* + * storeBookInHouse(player, node) + * + * Example: src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt + * + * todo: Periodically, this storables list should be audited as new books are added to the game. Make sure the new books are going in the bookcase. + */ + +// todo: Right now there is no logic to check: +// 1. for complete or incomplete sets +// 2. for single instances of an item +// This allows things like storing multiple god capes, a Rune (g) set with both platelegs and plateskirt, +// an elegant shirt/pants and blouse/skirt at once, and all 3 sets of castle wars armour at once. I don't +// think any of this is game-breaking, so have chosen to omit this logic for now. + +enum class Storable( + val displayId: Int, // display ID is the item that is shown in the UI + val takeIds: IntArray, // take IDs is the item(s) that are taken from the player's inventory + val type: StorableFamily, // the family (mapped to container) that the items get stored in + val setName: String = getItemName(displayId), // the set/book name for each item. defaults to display item + + // special properties just for the books + val bitIndex: Int = -1, // This index is used to save the big list of books the player has read, instead of having an individual attribute per book. + // todo make the quest requirement check part of a separate object against this list of books. only a subset of books are applicable. + val questReq: Quests? = null // Once read, books should only show up in the POH after completing their attached quest. https://runescape.wiki/w/Bookcase?oldid=1650176 +) { + // Books - https://runescape.wiki/w/List_of_texts_and_tomes?oldid=848552 + AbyssalBook(Items.ABYSSAL_BOOK_5520, intArrayOf(Items.ABYSSAL_BOOK_5520), type = StorableFamily.BOOKCASE, "Abyssal Research Notes", bitIndex = 0), // abyss miniquest - IMPLEMENTED + AncientBook(Items.ANCIENT_BOOK_7633, intArrayOf(Items.ANCIENT_BOOK_7633), type = StorableFamily.BOOKCASE, "The Sleeping Seven", bitIndex = 1, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest + AncientDiary(Items.DIARY_3846, intArrayOf(Items.DIARY_3846), type = StorableFamily.BOOKCASE, "Ancient Diary", bitIndex = 2, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest + ArenaBook(Items.ARENA_BOOK_6891, intArrayOf(Items.ARENA_BOOK_6891), type = StorableFamily.BOOKCASE, "Magic Training Arena Lore Book", bitIndex = 3), // mage training arena minigame + AstronomyBook(Items.ASTRONOMY_BOOK_600, intArrayOf(Items.ASTRONOMY_BOOK_600), type = StorableFamily.BOOKCASE, "Astronomy Book", bitIndex = 4, Quests.OBSERVATORY_QUEST), // observatory quest - IMPLEMENTED + BatteredBook(Items.BATTERED_BOOK_2886, intArrayOf(Items.BATTERED_BOOK_2886), type = StorableFamily.BOOKCASE, "Book of the Elemental Shield", bitIndex = 5, Quests.ELEMENTAL_WORKSHOP_I), // elemental workshop I quest - IMPLEMENTED + BatteredTome(Items.BATTERED_TOME_7634, intArrayOf(Items.BATTERED_TOME_7634), type = StorableFamily.BOOKCASE, "Histories of Hallowland", bitIndex = 6, Quests.IN_AID_OF_THE_MYREQUE), // in aid of myreque quest + BeatenBook(Items.BEATEN_BOOK_9717, intArrayOf(Items.BEATEN_BOOK_9717), type = StorableFamily.BOOKCASE, "Book of the Elemental Helm", bitIndex = 7, Quests.ELEMENTAL_WORKSHOP_II), // elemental workshop II quest + BigBookOfBangs(Items.BIG_BOOK_OF_BANGS_3230, intArrayOf(Items.BIG_BOOK_OF_BANGS_3230), type = StorableFamily.BOOKCASE, "Big Book of Bangs", bitIndex = 8, Quests.REGICIDE), // regicide quest + BindingBook(Items.BINDING_BOOK_730, intArrayOf(Items.BINDING_BOOK_730), type = StorableFamily.BOOKCASE, "Book of Binding", bitIndex = 9, Quests.LEGENDS_QUEST), // legends quest - IMPLEMENTED + BirdBook(Items.BIRD_BOOK_10173, intArrayOf(Items.BIRD_BOOK_10173), type = StorableFamily.BOOKCASE, "William Oddity's Guide to the Avian", bitIndex = 10, Quests.EAGLES_PEAK), // eagles peak quest + BookOnBaxtorian(Items.BOOK_ON_BAXTORIAN_292, intArrayOf(Items.BOOK_ON_BAXTORIAN_292), type = StorableFamily.BOOKCASE, "Book on Baxtorian", bitIndex = 11, Quests.WATERFALL_QUEST), // waterfall quest - IMPLEMENTED + BookOnChemicals(Items.BOOK_ON_CHEMICALS_711, intArrayOf(Items.BOOK_ON_CHEMICALS_711), type = StorableFamily.BOOKCASE, "Volatile chemicals - Experimental Test Notes", bitIndex = 12, Quests.THE_DIG_SITE), // dig site quest - IMPLEMENTED + BookOPiracy(Items.BOOK_O_PIRACY_7144, intArrayOf(Items.BOOK_O_PIRACY_7144), type = StorableFamily.BOOKCASE, "Book o' Piracy", bitIndex = 13, Quests.CABIN_FEVER), // cabin fever quest + BrewinGuide(Items.BREWIN_GUIDE_8989, intArrayOf(Items.BREWIN_GUIDE_8989), type = StorableFamily.BOOKCASE, "Brewin' Guide", bitIndex = 14), // trouble brewing quest/minigame + BurntDiary(Items.BURNT_DIARY_7965, intArrayOf(Items.BURNT_DIARY_7965), type = StorableFamily.BOOKCASE, "Burnt Diary", bitIndex = 15, Quests.ROYAL_TROUBLE), // royal trouble quest + CadarnLineage(Items.CADARN_LINEAGE_4209, intArrayOf(Items.CADARN_LINEAGE_4209), type = StorableFamily.BOOKCASE, "Cadarn Lineage", bitIndex = 16, Quests.ROVING_ELVES), // roving elves quest + ClockworkBook(Items.CLOCKWORK_BOOK_10594, intArrayOf(Items.CLOCKWORK_BOOK_10594), type = StorableFamily.BOOKCASE, "Clockwork Toys - A Clockwork Mechanism, Chapter 1.0", bitIndex = 17, Quests.COLD_WAR), // cold war quest + CocktailGuide(Items.COCKTAIL_GUIDE_2023, intArrayOf(Items.COCKTAIL_GUIDE_2023), type = StorableFamily.BOOKCASE, "The Blurberry Cocktail Guide", bitIndex = 18), // gnome restaurant minigame + ConstructionGuide(Items.CONSTRUCTION_GUIDE_8463, intArrayOf(Items.CONSTRUCTION_GUIDE_8463), type = StorableFamily.BOOKCASE, "Guide to Construction", bitIndex = 19), // after you buy a house from estate agent - IMPLEMENTED ish + CrumblingTome(Items.CRUMBLING_TOME_4707, intArrayOf(Items.CRUMBLING_TOME_4707), type = StorableFamily.BOOKCASE, "Legend of the Brothers", bitIndex = 20), // barrows minigame + CrystalSinging(Items.CRYSTAL_OF_SEREN_4313, intArrayOf(Items.CRYSTAL_OF_SEREN_4313), type = StorableFamily.BOOKCASE, "Crystal singing for beginners", bitIndex = 21, Quests.ROVING_ELVES), // roving elves quest + DagonhaiHistory(Items.DAGONHAI_HISTORY_11001, intArrayOf(Items.DAGONHAI_HISTORY_11001), type = StorableFamily.BOOKCASE, "History of the Dagon'hai", bitIndex = 22), // found in varrock castle library + DemonicTome(Items.DEMONIC_TOME_6749, intArrayOf(Items.DEMONIC_TOME_6749), type = StorableFamily.BOOKCASE, "The Confessions of Ellemar", bitIndex = 23, Quests.SHADOW_OF_THE_STORM), // shadow of the storm quest + DiaryHerbi(Items.DIARY_3395, intArrayOf(Items.DIARY_3395), type = StorableFamily.BOOKCASE, "Diary of Herbi Flax", bitIndex = 24, Quests.SHADES_OF_MORTTON), // shades of mort'ton quest + DrozalsJournal(Items.JOURNAL_6755, intArrayOf(Items.JOURNAL_6755), type = StorableFamily.BOOKCASE, "Drozal's Journal", bitIndex = 25, Quests.MAKING_HISTORY), // making history quest + DwarvenLore(Items.DWARVEN_LORE_4568, intArrayOf(Items.DWARVEN_LORE_4568), type = StorableFamily.BOOKCASE, "The Arzinian Being of Bordanzan", bitIndex = 26, Quests.BETWEEN_A_ROCK), // between a rock quest + EasternDiscovery(Items.EASTERN_DISCOVERY_6075, intArrayOf(Items.EASTERN_DISCOVERY_6075), type = StorableFamily.BOOKCASE, "The Exploration of the Eastern Realm", bitIndex = 27, Quests.MOURNINGS_END_PART_I), // mourning's end I quest + EasternSettlement(Items.EASTERN_SETTLEMENT_6077, intArrayOf(Items.EASTERN_SETTLEMENT_6077), type = StorableFamily.BOOKCASE, "The Settlement of the East", bitIndex = 28, Quests.MOURNINGS_END_PART_I), // mourning's end I quest + EdernsJournal(Items.EDERNS_JOURNAL_6649, intArrayOf(Items.EDERNS_JOURNAL_6649), type = StorableFamily.BOOKCASE, "Journal of Nissyen Edern", bitIndex = 29, Quests.MOURNINGS_END_PART_II), // mourning's end II quest + ExplorersNotes(Items.EXPLORERS_NOTES_11677, intArrayOf(Items.EXPLORERS_NOTES_11677), type = StorableFamily.BOOKCASE, "Beyond Trollheim", bitIndex = 30), // found in keldagrim library + EmbalmingManual(Items.EMBALMING_MANUAL_4686, intArrayOf(Items.EMBALMING_MANUAL_4686), type = StorableFamily.BOOKCASE, "The Little Book of Embalming", bitIndex = 31, Quests.ICTHLARINS_LITTLE_HELPER), // icthlarin's little helper quest + FarmingManual(Items.FARMING_MANUAL_9903, intArrayOf(Items.FARMING_MANUAL_9903), type = StorableFamily.BOOKCASE, "Farmer Gricoller's Farming Manual", bitIndex = 32, Quests.MY_ARMS_BIG_ADVENTURE), // my arm's big adventure quest + FeatheredJournal(Items.FEATHERED_JOURNAL_10179, intArrayOf(Items.FEATHERED_JOURNAL_10179), type = StorableFamily.BOOKCASE, "Feathered Journal of Arthur Artimus", bitIndex = 33, Quests.EAGLES_PEAK), // eagle's peak + GameBook(Items.GAME_BOOK_7681, intArrayOf(Items.GAME_BOOK_7681), type = StorableFamily.BOOKCASE, "Party Pete's Bumper Book of Games", bitIndex = 34), // poh game room. I think the bookshelf is the only source for this + GhrimsBook(Items.GHRIMS_BOOK_3901, intArrayOf(Items.GHRIMS_BOOK_3901), type = StorableFamily.BOOKCASE, "Managing Thine Kingdom for Noobes", bitIndex = 35, Quests.THRONE_OF_MISCELLANIA), // kingdom of misc quest + GiannesCookBook(Items.GIANNES_COOK_BOOK_2167, intArrayOf(Items.GIANNES_COOK_BOOK_2167), type = StorableFamily.BOOKCASE, "Gianne's Cook Book", bitIndex = 36), // gnome retaurant minigame - IMPLEMENTED + GlassblowingBook(Items.GLASSBLOWING_BOOK_11656, intArrayOf(Items.GLASSBLOWING_BOOK_11656), type = StorableFamily.BOOKCASE, "Glassblowing Book", bitIndex = 37), // found at the observatory + GoblinBook(Items.GOBLIN_BOOK_10999, intArrayOf(Items.GOBLIN_BOOK_10999), type = StorableFamily.BOOKCASE, "The Book of the Big High War God", bitIndex = 38), // dropped by goblins - IMPLEMENTED + GoblinSymbolBook(Items.GOBLIN_SYMBOL_BOOK_5009, intArrayOf(Items.GOBLIN_SYMBOL_BOOK_5009), type = StorableFamily.BOOKCASE, "A History of the Goblin Race", bitIndex = 39, Quests.THE_LOST_TRIBE), // lost tribe quest - IMPLEMENTED + ElvenBook(Items.THE_GREAT_DIVIDE_6079, intArrayOf(Items.THE_GREAT_DIVIDE_6079), type = StorableFamily.BOOKCASE, "The Great Divide", bitIndex = 40, Quests.MOURNINGS_END_PART_I), // mourning's end I quest + GuideBook(Items.GUIDE_BOOK_1856, intArrayOf(Items.GUIDE_BOOK_1856), type = StorableFamily.BOOKCASE, "Tourist Guide to Ardougne", bitIndex = 41), // found by estate agent in ardy - IMPLEMENTED + AHandwrittenBook(Items.A_HANDWRITTEN_BOOK_9627, intArrayOf(Items.A_HANDWRITTEN_BOOK_9627), type = StorableFamily.BOOKCASE, "Crystal Singing for Beginners (trans. Oaknock) (Transcribed from the original Elvish by Oaknock the Engineer)", bitIndex = 42, Quests.THE_EYES_OF_GLOUPHRIE), // eyes of glouphrie quest + HermansBook(Items.HERMANS_BOOK_7951, intArrayOf(Items.HERMANS_BOOK_7951), type = StorableFamily.BOOKCASE, "Dionysius: A Legend in His Own Lifetime", bitIndex = 43, Quests.SWAN_SONG), // swan song quest + HistoryOfIban(Items.HISTORY_OF_IBAN_1494, intArrayOf(Items.HISTORY_OF_IBAN_1494), type = StorableFamily.BOOKCASE, "The Tale of Iban", bitIndex = 44, Quests.UNDERGROUND_PASS), // underground pass quest + InstructionManual(Items.INSTRUCTION_MANUAL_5, intArrayOf(Items.INSTRUCTION_MANUAL_5), type = StorableFamily.BOOKCASE, "Dwarf Multicannon Manual", bitIndex = 45, Quests.DWARF_CANNON), // dwarf cannon quest - IMPLEMENTED + JossiksJournal(Items.JOURNAL_3845, intArrayOf(Items.JOURNAL_3845), type = StorableFamily.BOOKCASE, "Jossik's Diary", bitIndex = 46, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest + LeatherBook(Items.LEATHER_BOOK_7635, intArrayOf(Items.LEATHER_BOOK_7635), type = StorableFamily.BOOKCASE, "Modern Day Morytania", bitIndex = 47, Quests.IN_AID_OF_THE_MYREQUE), // in aid of the myreque quest + Manual(Items.MANUAL_3847, intArrayOf(Items.MANUAL_3847), type = StorableFamily.BOOKCASE, "Lighthouse Manual", bitIndex = 48, Quests.HORROR_FROM_THE_DEEP), // horror from the deep quest + MoonclanManual(Items.MOONCLAN_MANUAL_9078, intArrayOf(Items.MOONCLAN_MANUAL_9078), type = StorableFamily.BOOKCASE, "Basics of Magic", bitIndex = 49, Quests.LUNAR_DIPLOMACY), // lunar diplomacy quest + MyNotes(Items.MY_NOTES_11339, intArrayOf(Items.MY_NOTES_11339), type = StorableFamily.BOOKCASE, "'My notes'", bitIndex = 50), // during barbarian training + OldJournal(Items.OLD_JOURNAL_1493, intArrayOf(Items.OLD_JOURNAL_1493), type = StorableFamily.BOOKCASE, "The Journal of Randas", bitIndex = 51, Quests.UNDERGROUND_PASS), // underground pass quest + OldTome(Items.OLD_TOME_13593, intArrayOf(Items.OLD_TOME_13593), type = StorableFamily.BOOKCASE, "Old Tome", bitIndex = 52, Quests.MEETING_HISTORY), // meeting history quest + PrayerBook(Items.PRAYER_BOOK_10890, intArrayOf(Items.PRAYER_BOOK_10890), type = StorableFamily.BOOKCASE, "Prayer of Deliverance from Poisons", bitIndex = 53, Quests.THE_GREAT_BRAIN_ROBBERY), // the great brain robbery quest + PieRecipeBook(Items.PIE_RECIPE_BOOK_7162, intArrayOf(Items.PIE_RECIPE_BOOK_7162), type = StorableFamily.BOOKCASE, "Pie Recipe Book", bitIndex = 54), // from romily weaklax + PrifddinasHistory(Items.PRIFDDINAS_HISTORY_6073, intArrayOf(Items.PRIFDDINAS_HISTORY_6073), type = StorableFamily.BOOKCASE, "The Creation of Prifddinas", bitIndex = 55), // found in lletya bookshelves + QueenHelpBook(Items.QUEEN_HELP_BOOK_10562, intArrayOf(Items.QUEEN_HELP_BOOK_10562), type = StorableFamily.BOOKCASE, "Queen Help Book", bitIndex = 56), // barbarian assault minigame + RatPitsManual(Items.BOOK_6767, intArrayOf(Items.BOOK_6767), type = StorableFamily.BOOKCASE, "The RatPits Manual", bitIndex = 57, Quests.RATCATCHERS), // rat catchers quest + ScabariteNotes(Items.SCABARITE_NOTES_11975, intArrayOf(Items.SCABARITE_NOTES_11975), type = StorableFamily.BOOKCASE, "My Notes, Archaeological exploration and the cult of Scabaras", bitIndex = 58, Quests.DEALING_WITH_SCABARAS), // dealing with scabaras quest + SecurityBook(Items.SECURITY_BOOK_9003, intArrayOf(Items.SECURITY_BOOK_9003), type = StorableFamily.BOOKCASE, "" + GameWorld.settings!!.name + " Account Security", bitIndex = 59), // from stronghold of security - IMPLEMENTED + ShamansTome(Items.SHAMANS_TOME_729, intArrayOf(Items.SHAMANS_TOME_729), type = StorableFamily.BOOKCASE, "Shaman's Tome", bitIndex = 60, Quests.LEGENDS_QUEST), // legends quest - IMPLEMENTED + TheShieldOfArravBook(Items.BOOK_757, intArrayOf(Items.BOOK_757), type = StorableFamily.BOOKCASE, "The Shield of Arrav", bitIndex = 61, Quests.SHIELD_OF_ARRAV), // shield of arrav quest - IMPLEMENTED + SinkethsDiary(Items.SINKETHS_DIARY_11002, intArrayOf(Items.SINKETHS_DIARY_11002), type = StorableFamily.BOOKCASE, "Sin'keth's Diary", bitIndex = 62, Quests.WHAT_LIES_BELOW), // what lies below quest - IMPLEMENTED + StrongholdNotes(Items.STRONGHOLD_NOTES_9004, intArrayOf(Items.STRONGHOLD_NOTES_9004), type = StorableFamily.BOOKCASE, "Stronghold of Security - Notes", bitIndex = 63), // from stronghold of security - IMPLEMENTED + TarnsDiary(Items.TARNS_DIARY_10587, intArrayOf(Items.TARNS_DIARY_10587), type = StorableFamily.BOOKCASE, "The Diary of Tarn Razorlor", bitIndex = 64), // lair of tarn miniquest + Translation(Items.TRANSLATION_4655, intArrayOf(Items.TRANSLATION_4655), type = StorableFamily.BOOKCASE, "Translation Primer (Four Diamonds Translation)", bitIndex = 65, Quests.DESERT_TREASURE), // desert treasure quest - IMPLEMENTED + TranslationBook(Items.TRANSLATION_BOOK_784, intArrayOf(Items.TRANSLATION_BOOK_784), type = StorableFamily.BOOKCASE, "Gnome-English Translation Dictionary", bitIndex = 66, Quests.THE_EYES_OF_GLOUPHRIE), // eyes of glouphrie quest - IMPLEMENTED + TzhaarTouristGuide(Items.TZHAAR_TOURIST_GUIDE_13244, intArrayOf(Items.TZHAAR_TOURIST_GUIDE_13244), type = StorableFamily.BOOKCASE, "Tzhaar Tourist Guide", bitIndex = 67, Quests.TOKTZ_KET_DILL), // toktz-ket-dill quest + VarmensNotes(Items.VARMENS_NOTES_4616, intArrayOf(Items.VARMENS_NOTES_4616), type = StorableFamily.BOOKCASE, "Notes from Varmen's Expedition to Uzer", bitIndex = 68, Quests.THE_GOLEM), // the golem quest - IMPLEMENTED + WitchesDiary(Items.DIARY_2408, intArrayOf(Items.DIARY_2408), type = StorableFamily.BOOKCASE, "Witch's Diary", bitIndex = 69, Quests.WITCHS_HOUSE), // witch's house quest - IMPLEMENTED + YewnocksNotes(Items.YEWNOCKS_NOTES_11750, intArrayOf(Items.YEWNOCKS_NOTES_11750), type = StorableFamily.BOOKCASE, "Yewnock's Notes on Crystals", bitIndex = 70, Quests.THE_PATH_OF_GLOUPHRIE), // path of glouphrie quest + + // Capes - https://runescape.wiki/w/Cape_rack?oldid=848849 + LegendsCape(Items.CAPE_OF_LEGENDS_1052, intArrayOf(Items.CAPE_OF_LEGENDS_1052), type = StorableFamily.CAPE_RACK, "Legends cape"), + ObsidianCape(Items.OBSIDIAN_CAPE_6568, intArrayOf(Items.OBSIDIAN_CAPE_6568), type = StorableFamily.CAPE_RACK), + FireCape(Items.FIRE_CAPE_6570, intArrayOf(Items.FIRE_CAPE_6570), type = StorableFamily.CAPE_RACK), + TeamCape(Items.TEAM_1_CAPE_10638, (4315..4413 step 2).toList().toIntArray(), type = StorableFamily.CAPE_RACK, "Wilderness cape"), + GodCape(Items.GUTHIX_CAPE_2413, intArrayOf(Items.GUTHIX_CAPE_2413, Items.SARADOMIN_CAPE_2412, Items.ZAMORAK_CAPE_2414), type = StorableFamily.CAPE_RACK, "God cape"), + AttackCape(Items.ATTACK_CAPE_10639, intArrayOf(Items.ATTACK_CAPE_9747, Items.ATTACK_HOOD_9749, Items.ATTACK_CAPET_9748), type = StorableFamily.CAPE_RACK_SKILL), + DefenceCape(Items.DEFENCE_CAPE_10641, intArrayOf(Items.DEFENCE_CAPE_9753, Items.DEFENCE_CAPET_9754, Items.DEFENCE_HOOD_9755), type = StorableFamily.CAPE_RACK_SKILL), + StrengthCape(Items.STRENGTH_CAPE_10640, intArrayOf(Items.STRENGTH_CAPE_9750, Items.STRENGTH_CAPET_9751, Items.STRENGTH_HOOD_9752), type = StorableFamily.CAPE_RACK_SKILL), + HitpointsCape(Items.HITPOINTS_CAPE_10647, intArrayOf(Items.HITPOINTS_CAPE_9768, Items.HITPOINTS_CAPET_9769, Items.HITPOINTS_HOOD_9770), type = StorableFamily.CAPE_RACK_SKILL), + AgilityCape(Items.AGILITY_CAPE_10648, intArrayOf(Items.AGILITY_CAPE_9771, Items.AGILITY_CAPET_9772, Items.AGILITY_HOOD_9773), type = StorableFamily.CAPE_RACK_SKILL), + CookingCape(Items.COOKING_CAPE_10658, intArrayOf(Items.COOKING_CAPE_9801, Items.COOKING_CAPET_9802, Items.COOKING_HOOD_9803), type = StorableFamily.CAPE_RACK_SKILL), + ConstructionCape(Items.CONSTRUCT_CAPE_10654, intArrayOf(Items.CONSTRUCT_CAPE_9789, Items.CONSTRUCT_CAPET_9790, Items.CONSTRUCT_HOOD_9791), type = StorableFamily.CAPE_RACK_SKILL, "Construction cape"), + CraftingCape(Items.CRAFTING_CAPE_10651, intArrayOf(Items.CRAFTING_CAPE_9780, Items.CRAFTING_CAPET_9781, Items.CRAFTING_HOOD_9782), type = StorableFamily.CAPE_RACK_SKILL), + FarmingCape(Items.FARMING_CAPE_10661, intArrayOf(Items.FARMING_CAPE_9810, Items.FARMING_CAPET_9811, Items.FARMING_HOOD_9812), type = StorableFamily.CAPE_RACK_SKILL), + FiremakingCape(Items.FIREMAKING_CAPE_10659, intArrayOf(Items.FIREMAKING_CAPE_9804, Items.FIREMAKING_CAPET_9805, Items.FIREMAKING_HOOD_9806), type = StorableFamily.CAPE_RACK_SKILL), + FishingCape(Items.FISHING_CAPE_10657, intArrayOf(Items.FISHING_CAPE_9798, Items.FISHING_CAPET_9799, Items.FISHING_HOOD_9800), type = StorableFamily.CAPE_RACK_SKILL), + FletchingCape(Items.FLETCHING_CAPE_10652, intArrayOf(Items.FLETCHING_CAPE_9783, Items.FLETCHING_CAPET_9784, Items.FLETCHING_HOOD_9785), type = StorableFamily.CAPE_RACK_SKILL), + HerbloreCape(Items.HERBLORE_CAPE_10649, intArrayOf(Items.HERBLORE_CAPE_9774, Items.HERBLORE_CAPET_9775, Items.HERBLORE_HOOD_9776), type = StorableFamily.CAPE_RACK_SKILL), + MagicCape(Items.MAGIC_CAPE_10644, intArrayOf(Items.MAGIC_CAPE_9762, Items.MAGIC_CAPET_9763, Items.MAGIC_HOOD_9764), type = StorableFamily.CAPE_RACK_SKILL), + MiningCape(Items.MINING_CAPE_10655, intArrayOf(Items.MINING_CAPE_9792, Items.MINING_CAPET_9793, Items.MINING_HOOD_9794), type = StorableFamily.CAPE_RACK_SKILL), + PrayerCape(Items.PRAYER_CAPE_10643, intArrayOf(Items.PRAYER_CAPE_9759, Items.PRAYER_CAPET_9760, Items.PRAYER_HOOD_9761), type = StorableFamily.CAPE_RACK_SKILL), + RangingCape(Items.RANGING_CAPE_10642, intArrayOf(Items.RANGING_CAPE_9756, Items.RANGING_CAPET_9757, Items.RANGING_HOOD_9758), type = StorableFamily.CAPE_RACK_SKILL), + RunecraftCape(Items.RUNECRAFT_CAPE_10645, intArrayOf(Items.RUNECRAFT_CAPE_9765, Items.RUNECRAFT_CAPET_9766, Items.RUNECRAFTING_HOOD_9767), type = StorableFamily.CAPE_RACK_SKILL, "Runecrafting cape"), + SlayerCape(Items.SLAYER_CAPE_10653, intArrayOf(Items.SLAYER_CAPE_9786, Items.SLAYER_CAPET_9787, Items.SLAYER_HOOD_9788), type = StorableFamily.CAPE_RACK_SKILL), + SmithingCape(Items.SMITHING_CAPE_10656, intArrayOf(Items.SMITHING_CAPE_9795, Items.SMITHING_CAPET_9796, Items.SMITHING_HOOD_9797), type = StorableFamily.CAPE_RACK_SKILL), + ThievingCape(Items.THIEVING_CAPE_10650, intArrayOf(Items.THIEVING_CAPE_9777, Items.THIEVING_CAPET_9778, Items.THIEVING_HOOD_9779), type = StorableFamily.CAPE_RACK_SKILL), + WoodcuttingCape(Items.WOODCUTTING_CAPE_10660, intArrayOf(Items.WOODCUTTING_CAPE_9807, Items.WOODCUT_CAPET_9808, Items.WOODCUTTING_HOOD_9809), type = StorableFamily.CAPE_RACK_SKILL), + HunterCape(Items.HUNTER_CAPE_10646, intArrayOf(Items.HUNTER_CAPE_9948, Items.HUNTER_CAPET_9949, Items.HUNTER_HOOD_9950), type = StorableFamily.CAPE_RACK_SKILL), + QuestCape(Items.QUEST_POINT_CAPE_10662, intArrayOf(Items.QUEST_POINT_CAPE_9813, Items.QUEST_POINT_HOOD_9814), type = StorableFamily.CAPE_RACK_SKILL), + SummoningCape(Items.SUMMONING_CAPE_12524, intArrayOf(Items.SUMMONING_CAPE_12169, Items.SUMMONING_CAPET_12170, Items.SUMMONING_HOOD_12171), type = StorableFamily.CAPE_RACK_SKILL), + SpottedCape(Items.SPOTTED_CAPE_10663, intArrayOf(Items.SPOTTED_CAPE_10663), type = StorableFamily.CAPE_RACK, "Spotted hunting cape"), + SpottierCape(Items.SPOTTIER_CAPE_10664, intArrayOf(Items.SPOTTIER_CAPE_10664), type = StorableFamily.CAPE_RACK, "Spottier hunting cape"), + + // Fancy Dress - https://runescape.wiki/w/Fancy_dress_box?oldid=848873 + MimeCostume(Items.MIME_MASK_10629, intArrayOf(Items.MIME_MASK_3057, Items.MIME_TOP_3058, Items.MIME_LEGS_3059, Items.MIME_GLOVES_3060, Items.MIME_BOOTS_3061), type = StorableFamily.FANCY_DRESS, "Mime Costume"), + RoyalFrogCostume(Items.PRINCESS_BLOUSE_10630, intArrayOf(Items.PRINCE_TUNIC_6184, Items.PRINCE_LEGGINGS_6185, Items.PRINCESS_BLOUSE_6186, Items.PRINCESS_SKIRT_6187), type = StorableFamily.FANCY_DRESS, "Royal frog costume"), + FrogMask(Items.FROG_MASK_10721, intArrayOf(Items.FROG_MASK_6188), type = StorableFamily.FANCY_DRESS), + ZombieOutfit(Items.ZOMBIE_SHIRT_10631, intArrayOf(Items.ZOMBIE_MASK_7594, Items.ZOMBIE_SHIRT_7592, Items.ZOMBIE_TROUSERS_7593, Items.ZOMBIE_GLOVES_7595, Items.ZOMBIE_BOOTS_7596), type = StorableFamily.FANCY_DRESS, "Zombie Outfit"), + CamoOutfit(Items.CAMO_TOP_10632, intArrayOf(Items.CAMO_HELMET_6656, Items.CAMO_TOP_6654, Items.CAMO_BOTTOMS_6655), type = StorableFamily.FANCY_DRESS, "Camo outfit"), + LederhosenOutfit(Items.LEDERHOSEN_TOP_10633, intArrayOf(Items.LEDERHOSEN_TOP_6180, Items.LEDERHOSEN_SHORTS_6181, Items.LEDERHOSEN_HAT_6182), type = StorableFamily.FANCY_DRESS, "Lederhosen outfit"), + ShadeRobes(Items.SHADE_ROBE_10634, intArrayOf(Items.SHADE_ROBE_546, Items.SHADE_ROBE_548), type = StorableFamily.FANCY_DRESS, "Shade robes"), + + // Toys - https://runescape.wiki/w/Toy_box?oldid=848855 + BunnyEars(Items.BUNNY_EARS_10734, intArrayOf(Items.BUNNY_EARS_1037), type = StorableFamily.TOY_BOX), + Scythe(Items.SCYTHE_10735, intArrayOf(Items.SCYTHE_1419), type = StorableFamily.TOY_BOX), + YoYo(Items.YO_YO_10733, intArrayOf(Items.YO_YO_4079), type = StorableFamily.TOY_BOX), + RubberChicken(Items.RUBBER_CHICKEN_10732, intArrayOf(Items.RUBBER_CHICKEN_4566), type = StorableFamily.TOY_BOX), + ZombieHead(Items.ZOMBIE_HEAD_10731, intArrayOf(Items.ZOMBIE_HEAD_6722), type = StorableFamily.TOY_BOX), + EasterRing(Items.EASTER_RING_10729, intArrayOf(Items.EASTER_RING_7927), type = StorableFamily.TOY_BOX), + BobbleHat(Items.BOBBLE_HAT_9815, intArrayOf(Items.BOBBLE_HAT_6856), type = StorableFamily.TOY_BOX), + BobbleScarf(Items.BOBBLE_SCARF_9816, intArrayOf(Items.BOBBLE_SCARF_6857), type = StorableFamily.TOY_BOX), + JesterHat(Items.JESTER_HAT_6858, intArrayOf(Items.JESTER_HAT_6858), type = StorableFamily.TOY_BOX), + JesterScarf(Items.JESTER_SCARF_6859, intArrayOf(Items.JESTER_SCARF_6859), type = StorableFamily.TOY_BOX), + TriJesterHat(Items.TRI_JESTER_HAT_6860, intArrayOf(Items.TRI_JESTER_HAT_6860), type = StorableFamily.TOY_BOX), + TriJesterScarf(Items.TRI_JESTER_SCARF_6861, intArrayOf(Items.TRI_JESTER_SCARF_6861), type = StorableFamily.TOY_BOX), + WoollyHat(Items.WOOLLY_HAT_6862, intArrayOf(Items.WOOLLY_HAT_6862), type = StorableFamily.TOY_BOX), + WoollyScarf(Items.WOOLLY_SCARF_6863, intArrayOf(Items.WOOLLY_SCARF_6863), type = StorableFamily.TOY_BOX), + Marionette(Items.BLUE_MARIONETTE_10730, intArrayOf(Items.BLUE_MARIONETTE_6865,Items.GREEN_MARIONETTE_6866,Items.RED_MARIONETTE_6867), type = StorableFamily.TOY_BOX), // this authentically displays as 'blue marionette' in the source video + JackLanternMask(Items.JACK_LANTERN_MASK_10723, intArrayOf(Items.JACK_LANTERN_MASK_9920), type = StorableFamily.TOY_BOX), + SkeletonBoots(Items.SKELETON_BOOTS_10724, intArrayOf(Items.SKELETON_BOOTS_9921), type = StorableFamily.TOY_BOX), + SkeletonGloves(Items.SKELETON_GLOVES_10725, intArrayOf(Items.SKELETON_GLOVES_9922), type = StorableFamily.TOY_BOX), + SkeletonLeggings(Items.SKELETON_LEGGINGS_10726, intArrayOf(Items.SKELETON_LEGGINGS_9923), type = StorableFamily.TOY_BOX), + SkeletonShirt(Items.SKELETON_SHIRT_10727, intArrayOf(Items.SKELETON_SHIRT_9924), type = StorableFamily.TOY_BOX), + SkeletonMask(Items.SKELETON_MASK_10728, intArrayOf(Items.SKELETON_MASK_9925), type = StorableFamily.TOY_BOX), + ReindeerHat(Items.REINDEER_HAT_10722, intArrayOf(Items.REINDEER_HAT_10507), type = StorableFamily.TOY_BOX), + WintumberTree(Items.WINTUMBER_TREE_10508, intArrayOf(Items.WINTUMBER_TREE_10508), type = StorableFamily.TOY_BOX), + ChickenFeet(Items.CHICKEN_FEET_11019, intArrayOf(Items.CHICKEN_FEET_11019), type = StorableFamily.TOY_BOX), + ChickenLegs(Items.CHICKEN_LEGS_11022, intArrayOf(Items.CHICKEN_LEGS_11022), type = StorableFamily.TOY_BOX), + ChickenWings(Items.CHICKEN_WINGS_11020, intArrayOf(Items.CHICKEN_WINGS_11020), type = StorableFamily.TOY_BOX), + ChickenHead(Items.CHICKEN_HEAD_11021, intArrayOf(Items.CHICKEN_HEAD_11021), type = StorableFamily.TOY_BOX), + GrimReaperHood(Items.GRIM_REAPER_HOOD_11789, intArrayOf(Items.GRIM_REAPER_HOOD_11789), type = StorableFamily.TOY_BOX), + SnowGlobe(Items.SNOW_GLOBE_11949, intArrayOf(Items.SNOW_GLOBE_11949), type = StorableFamily.TOY_BOX), + ChocaticeCape(Items.CHOCATRICE_CAPE_12634, intArrayOf(Items.CHOCATRICE_CAPE_12645), type = StorableFamily.TOY_BOX), + WarlockTop(Items.WARLOCK_TOP_14076, intArrayOf(Items.WARLOCK_TOP_14076, Items.WITCH_TOP_14078), type = StorableFamily.TOY_BOX), + WarlockLegs(Items.WARLOCK_LEGS_14077, intArrayOf(Items.WARLOCK_LEGS_14077, Items.WITCH_SKIRT_14079), type = StorableFamily.TOY_BOX), + WarlockCloak(Items.WARLOCK_CLOAK_14081, intArrayOf(Items.WARLOCK_CLOAK_14081, Items.WITCH_CLOAK_14088), type = StorableFamily.TOY_BOX), + SantaCostumeTop(Items.SANTA_COSTUME_TOP_14595, intArrayOf(Items.SANTA_COSTUME_TOP_14595, Items.SANTA_COSTUME_TOP_14600), type = StorableFamily.TOY_BOX), + SantaCostumeLegs(Items.SANTA_COSTUME_LEGS_14603, intArrayOf(Items.SANTA_COSTUME_LEGS_14603, Items.SANTA_COSTUME_LEGS_14604), type = StorableFamily.TOY_BOX), + SantaCostumeGloves(Items.SANTA_COSTUME_GLOVES_14602, intArrayOf(Items.SANTA_COSTUME_GLOVES_14602), type = StorableFamily.TOY_BOX), + SantaCostumeBoots(Items.SANTA_COSTUME_BOOTS_14605, intArrayOf(Items.SANTA_COSTUME_BOOTS_14605), type = StorableFamily.TOY_BOX), + IceAmulet(Items.ICE_AMULET_14596, intArrayOf(Items.ICE_AMULET_14596), type = StorableFamily.TOY_BOX), + Cornucopia(Items.CORNUCOPIA_14570, intArrayOf(Items.CORNUCOPIA_14537), type = StorableFamily.TOY_BOX), + + // Treasure Trails - low - https://runescape.wiki/w/Treasure_chest?oldid=848831 + TrimmedBlackArmour(Items.BLACK_PLATEBODY_T_10690, intArrayOf(Items.BLACK_FULL_HELMT_2587, Items.BLACK_PLATEBODY_T_2583, Items.BLACK_PLATELEGS_T_2585, Items.BLACK_KITESHIELD_T_2589, Items.BLACK_PLATESKIRT_T_3472), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed black armour"), + GoldTrimmedBlackArmour(Items.BLACK_PLATEBODY_G_10691, intArrayOf(Items.BLACK_FULL_HELMG_2595, Items.BLACK_PLATEBODY_G_2591, Items.BLACK_PLATELEGS_G_2593, Items.BLACK_KITESHIELD_G_2597, Items.BLACK_PLATESKIRT_G_3473), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed black armour"), + BlackHeraldicH1(Items.BLACK_HELM_H1_10699, intArrayOf(Items.BLACK_HELM_H1_10306), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"), + BlackHeraldicH2(Items.BLACK_HELM_H2_10700, intArrayOf(Items.BLACK_HELM_H2_10308), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"), + BlackHeraldicH3(Items.BLACK_HELM_H3_10701, intArrayOf(Items.BLACK_HELM_H3_10310), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"), + BlackHeraldicH4(Items.BLACK_HELM_H4_10702, intArrayOf(Items.BLACK_HELM_H4_10312), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"), + BlackHeraldicH5(Items.BLACK_HELM_H5_10703, intArrayOf(Items.BLACK_HELM_H5_10314), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic helm"), + BlackHeraldicKiteshieldH1(Items.BLACK_SHIELDH1_10665, intArrayOf(Items.BLACK_SHIELDH1_7332), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"), + BlackHeraldicKiteshieldH2(Items.BLACK_SHIELDH2_10668, intArrayOf(Items.BLACK_SHIELDH2_7338), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"), + BlackHeraldicKiteshieldH3(Items.BLACK_SHIELDH3_10671, intArrayOf(Items.BLACK_SHIELDH3_7344), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"), + BlackHeraldicKiteshieldH4(Items.BLACK_SHIELDH4_10674, intArrayOf(Items.BLACK_SHIELDH4_7350), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"), + BlackHeraldicKiteshieldH5(Items.BLACK_SHIELDH5_10677, intArrayOf(Items.BLACK_SHIELDH5_7356), type = StorableFamily.TREASURE_CHEST_LOW, "Black heraldic kiteshield"), + TrimmedStuddedArmour(Items.STUDDED_BODY_T_10681, intArrayOf(Items.STUDDED_BODY_T_7364, Items.STUDDED_CHAPS_T_7368), type = StorableFamily.TREASURE_CHEST_LOW, "Fur-trimmed studded leather"), + GoldTrimmedStuddedArmour(Items.STUDDED_BODY_G_10680, intArrayOf(Items.STUDDED_BODY_G_7362, Items.STUDDED_CHAPS_G_7366), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed studded leather"), + TrimmedWizardRobes(Items.WIZARD_ROBE_T_10687, intArrayOf(Items.WIZARD_HAT_T_7396, Items.WIZARD_ROBE_T_7392, Items.BLUE_SKIRT_T_7388), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed wizard's robes"), + GoldTrimmedWizardRobes(Items.WIZARD_ROBE_G_10686, intArrayOf(Items.WIZARD_HAT_G_7394, Items.WIZARD_ROBE_G_7390, Items.BLUE_SKIRT_G_7386), type = StorableFamily.TREASURE_CHEST_LOW, "Gold-trimmed wizard's robes"), + WizardBoots(Items.WIZARD_BOOTS_10689, intArrayOf(Items.WIZARD_BOOTS_2579), type = StorableFamily.TREASURE_CHEST_LOW), + TrimmedAmuletOfMagic(Items.AMULET_OF_MAGICT_10738, intArrayOf(Items.AMULET_OF_MAGICT_10366), type = StorableFamily.TREASURE_CHEST_LOW, "Trimmed amulet of magic"), + HighwaymanMask(Items.HIGHWAYMAN_MASK_10692, intArrayOf(Items.HIGHWAYMAN_MASK_2631), type = StorableFamily.TREASURE_CHEST_LOW), + Pantaloons(Items.PANTALOONS_10744, intArrayOf(Items.PANTALOONS_10396), type = StorableFamily.TREASURE_CHEST_LOW), + PowderedWig(Items.A_POWDERED_WIG_10740, intArrayOf(Items.A_POWDERED_WIG_10392), type = StorableFamily.TREASURE_CHEST_LOW), + FlaredTrousers(Items.FLARED_TROUSERS_10742, intArrayOf(Items.FLARED_TROUSERS_10394), type = StorableFamily.TREASURE_CHEST_LOW), + SleepingCap(Items.SLEEPING_CAP_10746, intArrayOf(Items.SLEEPING_CAP_10398), type = StorableFamily.TREASURE_CHEST_LOW), + BlackBeret(Items.BLACK_BERET_10694, intArrayOf(Items.BLACK_BERET_2635), type = StorableFamily.TREASURE_CHEST_LOW), + WhiteBeret(Items.WHITE_BERET_10695, intArrayOf(Items.WHITE_BERET_2637), type = StorableFamily.TREASURE_CHEST_LOW), + BlueBeret(Items.BLUE_BERET_10693, intArrayOf(Items.BLUE_BERET_2633), type = StorableFamily.TREASURE_CHEST_LOW), + BobTheCatShirt(Items.BOB_SHIRT_10714, intArrayOf(Items.BOB_SHIRT_10316), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (red)"), + BobTheCatShirt1(Items.BOB_SHIRT_10715, intArrayOf(Items.BOB_SHIRT_10318), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (blue)"), + BobTheCatShirt2(Items.BOB_SHIRT_10716, intArrayOf(Items.BOB_SHIRT_10320), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (green)"), + BobTheCatShirt3(Items.BOB_SHIRT_10717, intArrayOf(Items.BOB_SHIRT_10322), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (black)"), + BobTheCatShirt4(Items.BOB_SHIRT_10718, intArrayOf(Items.BOB_SHIRT_10324), type = StorableFamily.TREASURE_CHEST_LOW, "Bob the Cat shirt (purple)"), + RedElegant(Items.ELEGANT_SHIRT_10750, intArrayOf(Items.RED_ELE_SHIRT_10404, Items.RED_ELE_LEGS_10406, Items.RED_ELE_BLOUSE_10424, Items.RED_ELE_SKIRT_10426), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (red)"), + BlueElegant(Items.ELEGANT_SHIRT_10752, intArrayOf(Items.BLUE_ELE_SHIRT_10408, Items.BLUE_ELE_LEGS_10410, Items.BLUE_ELE_BLOUSE_10428, Items.BLUE_ELE_SKIRT_10430), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (blue)"), + GreenElegant(Items.ELEGANT_SHIRT_10754, intArrayOf(Items.GREEN_ELE_SHIRT_10412,Items.GREEN_ELE_LEGS_10414, Items.GREEN_ELE_BLOUSE_10432, Items.GREEN_ELE_SKIRT_10434), type = StorableFamily.TREASURE_CHEST_LOW, "Elegant clothes (green)"), + BeretAndMask(Items.BERET_MASK_11278, intArrayOf(Items.BERET_AND_MASK_11282), type = StorableFamily.TREASURE_CHEST_LOW), + BlackCane(Items.BLACK_CANE_13163, intArrayOf(Items.BLACK_CANE_13095), type = StorableFamily.TREASURE_CHEST_LOW), + SpikedHelmet(Items.SPIKED_HELMET_13168, intArrayOf(Items.SPIKED_HELMET_13105), type = StorableFamily.TREASURE_CHEST_LOW), + + // Treasure Trails - medium - https://runescape.wiki/w/Treasure_chest?oldid=848831 + TrimmedAdamantiteArmour(Items.ADAM_PLATEBODY_T_10697, intArrayOf(Items.ADAM_FULL_HELMT_2605, Items.ADAM_PLATEBODY_T_2599, Items.ADAM_PLATELEGS_T_2601, Items.ADAM_KITESHIELD_T_2603, Items.ADAM_PLATESKIRT_T_3474), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed adamantite armour"), + GoldTrimmedAdamantiteArmour(Items.ADAM_PLATEBODY_G_10698, intArrayOf(Items.ADAM_FULL_HELMG_2613, Items.ADAM_PLATEBODY_G_2607, Items.ADAM_PLATELEGS_G_2609, Items.ADAM_KITESHIELD_G_2611, Items.ADAM_PLATESKIRT_G_3475), type = StorableFamily.TREASURE_CHEST_MED, "Gold-trimmed adamantite armour"), + AdamantHeraldicH1(Items.ADAMANT_HELM_H1_10709, intArrayOf(Items.ADAMANT_HELM_H1_10296), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"), + AdamantHeraldicH2(Items.ADAMANT_HELM_H2_10710, intArrayOf(Items.ADAMANT_HELM_H2_10298), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"), + AdamantHeraldicH3(Items.ADAMANT_HELM_H3_10711, intArrayOf(Items.ADAMANT_HELM_H3_10300), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"), + AdamantHeraldicH4(Items.ADAMANT_HELM_H4_10712, intArrayOf(Items.ADAMANT_HELM_H4_10302), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"), + AdamantHeraldicH5(Items.ADAMANT_HELM_H5_10713, intArrayOf(Items.ADAMANT_HELM_H5_10304), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic helm"), + AdamantKiteshieldH1(Items.ADAMANT_SHIELDH1_10666, intArrayOf(Items.ADAMANT_SHIELDH1_7334), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"), + AdamantKiteshieldH2(Items.ADAMANT_SHIELDH2_10669, intArrayOf(Items.ADAMANT_SHIELDH2_7340), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"), + AdamantKiteshieldH3(Items.ADAMANT_SHIELDH3_10672, intArrayOf(Items.ADAMANT_SHIELDH3_7346), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"), + AdamantKiteshieldH4(Items.ADAMANT_SHIELDH4_10675, intArrayOf(Items.ADAMANT_SHIELDH4_7352), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"), + AdamantKiteshieldH5(Items.ADAMANT_SHIELDH5_10678, intArrayOf(Items.ADAMANT_SHIELDH5_7358), type = StorableFamily.TREASURE_CHEST_MED, "Adamant heraldic kiteshield"), + TrimmedGreenDhide(Items.DHIDE_BODY_T_10683, intArrayOf(Items.DHIDE_BODY_T_7372, Items.DHIDE_CHAPS_T_7380), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed green dragonhide armour"), + GoldTrimmedGreenDhide(Items.DHIDE_BODYG_10682, intArrayOf(Items.DHIDE_BODYG_7370, Items.DHIDE_CHAPS_G_7378), type = StorableFamily.TREASURE_CHEST_MED, "Gold-trimmed green dragonhide armour"), + RangerBoots(Items.RANGER_BOOTS_10696, intArrayOf(Items.RANGER_BOOTS_2577), type = StorableFamily.TREASURE_CHEST_MED), + TrimmedAmuletOfStrength(Items.STRENGTH_AMULETT_10736, intArrayOf(Items.STRENGTH_AMULETT_10364), type = StorableFamily.TREASURE_CHEST_MED, "Trimmed amulet of strength"), + RedHeadband(Items.RED_HEADBAND_10768, intArrayOf(Items.RED_HEADBAND_2645), type = StorableFamily.TREASURE_CHEST_MED), + BlackHeadband(Items.BLACK_HEADBAND_10770, intArrayOf(Items.BLACK_HEADBAND_2647), type = StorableFamily.TREASURE_CHEST_MED), + BrownHeadband(Items.BROWN_HEADBAND_10772, intArrayOf(Items.BROWN_HEADBAND_2649), type = StorableFamily.TREASURE_CHEST_MED), + RedBoater(Items.RED_BOATER_10758, intArrayOf(Items.RED_BOATER_7319), type = StorableFamily.TREASURE_CHEST_MED, "Red straw boater"), + OrangeBoater(Items.ORANGE_BOATER_10760, intArrayOf(Items.ORANGE_BOATER_7321), type = StorableFamily.TREASURE_CHEST_MED, "Orange straw boater"), + GreenBoater(Items.GREEN_BOATER_10762, intArrayOf(Items.GREEN_BOATER_7323), type = StorableFamily.TREASURE_CHEST_MED, "Green straw boater"), + BlueBoater(Items.BLUE_BOATER_10764, intArrayOf(Items.BLUE_BOATER_7325), type = StorableFamily.TREASURE_CHEST_MED, "Blue straw boater"), + BlackBoater(Items.BLACK_BOATER_10766, intArrayOf(Items.BLACK_BOATER_7327), type = StorableFamily.TREASURE_CHEST_MED, "Black straw boater"), + BlackElegant(Items.ELEGANT_SHIRT_10748, intArrayOf(Items.BLACK_ELE_SHIRT_10400, Items.BLACK_ELE_LEGS_10402, Items.WHITE_ELE_BLOUSE_10420, Items.WHITE_ELE_SKIRT_10422), type = StorableFamily.TREASURE_CHEST_MED, "Elegant clothes (black)"), + PurpleElegant(Items.ELEGANT_SHIRT_10756, intArrayOf(Items.PURPLE_ELE_BLOUSE_10436, Items.PURPLE_ELE_SKIRT_10438), type = StorableFamily.TREASURE_CHEST_MED, "Elegant clothes (purple)"), + AdamantCane(Items.ADAMANT_CANE_13164, intArrayOf(Items.ADAMANT_CANE_13097), type = StorableFamily.TREASURE_CHEST_MED), + SheepMask(Items.SHEEP_MASK_13169, intArrayOf(Items.SHEEP_MASK_13107), type = StorableFamily.TREASURE_CHEST_MED), + BatMask(Items.BAT_MASK_13171, intArrayOf(Items.BAT_MASK_13111), type = StorableFamily.TREASURE_CHEST_MED), + WolfMask(Items.WOLF_MASK_13173, intArrayOf(Items.WOLF_MASK_13115), type = StorableFamily.TREASURE_CHEST_MED), + PenguinMask(Items.PENGUIN_MASK_13170, intArrayOf(Items.PENGUIN_MASK_13109), type = StorableFamily.TREASURE_CHEST_MED), + CatMask(Items.CAT_MASK_13172, intArrayOf(Items.CAT_MASK_13113), type = StorableFamily.TREASURE_CHEST_MED), + PithHelmet(Items.PITH_HELMET_13167, intArrayOf(Items.PITH_HELMET_13103), type = StorableFamily.TREASURE_CHEST_MED), + + // Treasure Trails - hard - https://runescape.wiki/w/Treasure_chest?oldid=848831 + TrimmedRuneArmour(Items.RUNE_PLATEBODY_T_10800, intArrayOf(Items.RUNE_FULL_HELM_T_2627, Items.RUNE_PLATEBODY_T_2623, Items.RUNE_PLATELEGS_T_2625, Items.RUNE_KITESHIELD_T_2629, Items.RUNE_PLATESKIRT_T_3477), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed rune armour"), + GoldTrimmedRuneArmour(Items.RUNE_PLATEBODY_G_10798, intArrayOf(Items.RUNE_FULL_HELMG_2619, Items.RUNE_PLATEBODY_G_2615, Items.RUNE_PLATELEGS_G_2617, Items.RUNE_KITESHIELD_G_2621, Items.RUNE_PLATESKIRT_G_3476), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-trimmed rune armour"), + GildedArmour(Items.GILDED_PLATEBODY_10782, intArrayOf(Items.GILDED_FULL_HELM_3486, Items.GILDED_PLATEBODY_3481, Items.GILDED_PLATELEGS_3483, Items.GILDED_KITESHIELD_3488, Items.GILDED_PLATESKIRT_3485), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-plated rune armour"), + RuneHelmH1(Items.RUNE_HELM_H1_10704, intArrayOf(Items.RUNE_HELM_H1_10286), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"), + RuneHelmH2(Items.RUNE_HELM_H2_10705, intArrayOf(Items.RUNE_HELM_H2_10288), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"), + RuneHelmH3(Items.RUNE_HELM_H3_10706, intArrayOf(Items.RUNE_HELM_H3_10290), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"), + RuneHelmH4(Items.RUNE_HELM_H4_10707, intArrayOf(Items.RUNE_HELM_H4_10292), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"), + RuneHelmH5(Items.RUNE_HELM_H5_10708, intArrayOf(Items.RUNE_HELM_H5_10294), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic helm"), + RuneShieldH1(Items.RUNE_SHIELDH1_10667, intArrayOf(Items.RUNE_SHIELDH1_7336), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"), + RuneShieldH2(Items.RUNE_SHIELDH2_10670, intArrayOf(Items.RUNE_SHIELDH2_7342), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"), + RuneShieldH3(Items.RUNE_SHIELDH3_10673, intArrayOf(Items.RUNE_SHIELDH3_7348), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"), + RuneShieldH4(Items.RUNE_SHIELDH4_10676, intArrayOf(Items.RUNE_SHIELDH4_7354), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"), + RuneShieldH5(Items.RUNE_SHIELDH5_10679, intArrayOf(Items.RUNE_SHIELDH5_7360), type = StorableFamily.TREASURE_CHEST_HIGH, "Rune heraldic kiteshield"), + ZamorakRuneArmour(Items.ZAMORAK_PLATEBODY_10776, intArrayOf(Items.ZAMORAK_FULL_HELM_2657, Items.ZAMORAK_PLATEBODY_2653, Items.ZAMORAK_PLATELEGS_2655, Items.ZAMORAK_KITESHIELD_2659, Items.ZAMORAK_PLATESKIRT_3478), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak rune armour"), + SaradominRuneArmour(Items.SARADOMIN_PLATE_10778, intArrayOf(Items.SARADOMIN_FULL_HELM_2665, Items.SARADOMIN_PLATEBODY_2661, Items.SARADOMIN_PLATELEGS_2663, Items.SARADOMIN_KITESHIELD_2667, Items.SARADOMIN_PLATESKIRT_3479), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin rune armour"), + GuthixRuneArmour(Items.GUTHIX_PLATEBODY_10780, intArrayOf(Items.GUTHIX_FULL_HELM_2673, Items.GUTHIX_PLATEBODY_2669, Items.GUTHIX_PLATELEGS_2671, Items.GUTHIX_KITESHIELD_2675, Items.GUTHIX_PLATESKIRT_3480), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix rune armour"), + TrimmedBlueDhide(Items.DHIDE_BODY_T_10685, intArrayOf(Items.DHIDE_BODY_T_7376, Items.DHIDE_CHAPS_T_7384), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed blue dragonhide armour"), + GoldTrimmedBlueDhide(Items.DHIDE_BODY_G_10684, intArrayOf(Items.DHIDE_BODY_G_7374, Items.DHIDE_CHAPS_G_7382), type = StorableFamily.TREASURE_CHEST_HIGH, "Gold-trimmed blue dragonhide armour"), + SaradominBlessedDhide(Items.SARADOMIN_DHIDE_10792, intArrayOf(Items.SARADOMIN_DHIDE_10386, Items.SARADOMIN_CHAPS_10388, Items.SARADOMIN_BRACERS_10384, Items.SARADOMIN_COIF_10390), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin blessed dragonhide"), + GuthixBlessedDhide(Items.GUTHIX_DRAGONHIDE_10794, intArrayOf(Items.GUTHIX_DRAGONHIDE_10378, Items.GUTHIX_CHAPS_10380, Items.GUTHIX_BRACERS_10376, Items.GUTHIX_COIF_10382), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix blessed dragonhide"), + ZamorakBlessedDhide(Items.ZAMORAK_DHIDE_10790, intArrayOf(Items.ZAMORAK_DHIDE_10370, Items.ZAMORAK_CHAPS_10372, Items.ZAMORAK_BRACERS_10368, Items.ZAMORAK_COIF_10374), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak blessed dragonhide"), + RobinHoodHat(Items.ROBIN_HOOD_HAT_10796, intArrayOf(Items.ROBIN_HOOD_HAT_2581), type = StorableFamily.TREASURE_CHEST_HIGH), + EnchantedRobes(Items.ENCHANTED_TOP_10688, intArrayOf(Items.ENCHANTED_HAT_7400, Items.ENCHANTED_TOP_7399, Items.ENCHANTED_ROBE_7398), type = StorableFamily.TREASURE_CHEST_HIGH, "Enchanted robes"), + SaradominVestments(Items.SARADOMIN_ROBE_TOP_10784, intArrayOf(Items.SARADOMIN_MITRE_10452, Items.SARADOMIN_ROBE_TOP_10458, Items.SARADOMIN_ROBE_LEGS_10464, Items.SARADOMIN_STOLE_10470, Items.SARADOMIN_CLOAK_10446, Items.SARADOMIN_CROZIER_10440), type = StorableFamily.TREASURE_CHEST_HIGH, "Saradomin vestments"), + ZamorakVestments(Items.ZAMORAK_ROBE_TOP_10786, intArrayOf(Items.ZAMORAK_MITRE_10456, Items.ZAMORAK_ROBE_TOP_10460, Items.ZAMORAK_ROBE_LEGS_10468, Items.ZAMORAK_STOLE_10474, Items.ZAMORAK_CLOAK_10450, Items.ZAMORAK_CROZIER_10444), type = StorableFamily.TREASURE_CHEST_HIGH, "Zamorak vestments"), + GuthixVestments(Items.GUTHIX_ROBE_TOP_10788, intArrayOf(Items.GUTHIX_MITRE_10454, Items.GUTHIX_ROBE_TOP_10462, Items.GUTHIX_ROBE_LEGS_10466, Items.GUTHIX_STOLE_10472, Items.GUTHIX_CLOAK_10448, Items.GUTHIX_CROZIER_10442), type = StorableFamily.TREASURE_CHEST_HIGH, "Guthix vestments"), + TrimmedAmuletOfGlory(Items.AMULET_OF_GLORYT_10719, intArrayOf(Items.AMULET_OF_GLORYT_10362), type = StorableFamily.TREASURE_CHEST_HIGH, "Trimmed amulet of glory"), + PiratesHat(Items.PIRATES_HAT_10774, intArrayOf(Items.PIRATES_HAT_2651), type = StorableFamily.TREASURE_CHEST_HIGH), + CavalierMask(Items.CAVALIER_MASK_11277, intArrayOf(Items.CAVALIER_AND_MASK_11280), type = StorableFamily.TREASURE_CHEST_HIGH), + DarkCavalier(Items.DARK_CAVALIER_10804, intArrayOf(Items.DARK_CAVALIER_2641), type = StorableFamily.TREASURE_CHEST_HIGH, "Dark brown cavalier"), + TanCavalier(Items.TAN_CAVALIER_10802, intArrayOf(Items.TAN_CAVALIER_2639), type = StorableFamily.TREASURE_CHEST_HIGH), + BlackCavalier(Items.BLACK_CAVALIER_10806, intArrayOf(Items.BLACK_CAVALIER_2643), type = StorableFamily.TREASURE_CHEST_HIGH), + RuneCane(Items.RUNE_CANE_13165, intArrayOf(Items.RUNE_CANE_13099), type = StorableFamily.TREASURE_CHEST_HIGH), + TopHat(Items.TOP_HAT_13166, intArrayOf(Items.TOP_HAT_13101), type = StorableFamily.TREASURE_CHEST_HIGH), + + // Magic Wardrobe - https://runescape.wiki/w/Magic_wardrobe?oldid=848846 + MysticBlueSet(Items.MYSTIC_HAT_10601, intArrayOf(Items.MYSTIC_HAT_4089, Items.MYSTIC_ROBE_TOP_4091, Items.MYSTIC_ROBE_BOTTOM_4093, Items.MYSTIC_GLOVES_4095, Items.MYSTIC_BOOTS_4097), type = StorableFamily.MAGIC_WARDROBE, "Mystic robes"), + MysticDarkSet(Items.MYSTIC_HAT_10602, intArrayOf(Items.MYSTIC_HAT_4099, Items.MYSTIC_ROBE_TOP_4101, Items.MYSTIC_ROBE_BOTTOM_4103, Items.MYSTIC_GLOVES_4105, Items.MYSTIC_BOOTS_4107), type = StorableFamily.MAGIC_WARDROBE, "Dark mystic robes"), + MysticLightSet(Items.MYSTIC_HAT_10603, intArrayOf(Items.MYSTIC_HAT_4109, Items.MYSTIC_ROBE_TOP_4111, Items.MYSTIC_ROBE_BOTTOM_4113, Items.MYSTIC_GLOVES_4115, Items.MYSTIC_BOOTS_4117), type = StorableFamily.MAGIC_WARDROBE, "Light mystic robes"), + SkeletalSet(Items.SKELETAL_HELM_10604, intArrayOf(Items.SKELETAL_HELM_6137, Items.SKELETAL_TOP_6139, Items.SKELETAL_BOTTOMS_6141, Items.SKELETAL_GLOVES_6153, Items.SKELETAL_BOOTS_6147), type = StorableFamily.MAGIC_WARDROBE, "Skeletal"), + InfinitySet(Items.INFINITY_TOP_10605, intArrayOf(Items.INFINITY_HAT_6918, Items.INFINITY_TOP_6916, Items.INFINITY_BOTTOMS_6924, Items.INFINITY_GLOVES_6922, Items.INFINITY_BOOTS_6920), type = StorableFamily.MAGIC_WARDROBE, "Infinity robes"), + SplitbarkSet(Items.SPLITBARK_HELM_10606, intArrayOf(Items.SPLITBARK_HELM_3385, Items.SPLITBARK_BODY_3387, Items.SPLITBARK_LEGS_3389, Items.SPLITBARK_GAUNTLETS_3391, Items.SPLITBARK_BOOTS_3393), type = StorableFamily.MAGIC_WARDROBE, "Splitbark armour"), + GhostlySet(Items.GHOSTLY_BOOTS_10607, intArrayOf(Items.GHOSTLY_ROBE_6107, Items.GHOSTLY_ROBE_6108, Items.GHOSTLY_HOOD_6109, Items.GHOSTLY_GLOVES_6110, Items.GHOSTLY_BOOTS_6106, Items.GHOSTLY_CLOAK_6111), type = StorableFamily.MAGIC_WARDROBE, "Ghostly robes"), + MoonclanSet(Items.MOONCLAN_HAT_10608, intArrayOf(Items.MOONCLAN_HELM_9068, Items.MOONCLAN_ARMOUR_9070, Items.MOONCLAN_SKIRT_9071, Items.MOONCLAN_GLOVES_9072, Items.MOONCLAN_BOOTS_9073), type = StorableFamily.MAGIC_WARDROBE, "Moonclan robes"), + LunarSet(Items.LUNAR_HELM_10609, intArrayOf(Items.LUNAR_HELM_9096, Items.LUNAR_TORSO_9097, Items.LUNAR_LEGS_9098, Items.LUNAR_GLOVES_9099, Items.LUNAR_BOOTS_9100, Items.LUNAR_CAPE_9101, Items.LUNAR_AMULET_9102, Items.LUNAR_RING_9104), type = StorableFamily.MAGIC_WARDROBE, "Lunar robes"), + RunecrafterYellow(Items.RUNECRAFTER_HAT_13656, intArrayOf(Items.RUNECRAFTER_HAT_13615, Items.RUNECRAFTER_ROBE_13614, Items.RUNECRAFTER_SKIRT_13617), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (yellow)"), + RunecrafterGreen(Items.RUNECRAFTER_HAT_13657, intArrayOf(Items.RUNECRAFTER_HAT_13620, Items.RUNECRAFTER_ROBE_13619, Items.RUNECRAFTER_SKIRT_13622), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (green)"), + RunecrafterBlue(Items.RUNECRAFTER_HAT_13658, intArrayOf(Items.RUNECRAFTER_HAT_13625, Items.RUNECRAFTER_ROBE_13624, Items.RUNECRAFTER_SKIRT_13627), type = StorableFamily.MAGIC_WARDROBE, "Runecrafter robes (blue)"), + DagonHaiSet(Items.DAGONHAI_ROBE_TOP_14497, intArrayOf(Items.DAGONHAI_HAT_14499, Items.DAGONHAI_ROBE_TOP_14497, Items.DAGONHAI_ROBE_BOTTOM_14501), type = StorableFamily.MAGIC_WARDROBE, "Dagon'Hai Robes"), + + // Armour Case - https://runescape.wiki/w/Armour_case?oldid=848835 + DecorativeArmour(Items.DECORATIVE_ARMOUR_10610, intArrayOf(Items.DECORATIVE_ARMOUR_4069, Items.DECORATIVE_SWORD_4068, Items.DECORATIVE_ARMOUR_4070, Items.DECORATIVE_HELM_4071, Items.DECORATIVE_SHIELD_4072, Items.DECORATIVE_ARMOUR_4504, Items.DECORATIVE_SWORD_4503, Items.DECORATIVE_ARMOUR_4505, Items.DECORATIVE_HELM_4506, Items.DECORATIVE_SHIELD_4507, Items.DECORATIVE_ARMOUR_4509, Items.DECORATIVE_SWORD_4508, Items.DECORATIVE_ARMOUR_4510, Items.DECORATIVE_HELM_4511, Items.DECORATIVE_SHIELD_4512), type = StorableFamily.ARMOUR_CASE, "Castlewars armour"), // should be any one colour set per wiki + VoidKnightArmour(Items.VOID_KNIGHT_TOP_10611, intArrayOf(Items.VOID_KNIGHT_TOP_8839, Items.VOID_KNIGHT_ROBE_8840, Items.VOID_KNIGHT_GLOVES_8842), type = StorableFamily.ARMOUR_CASE, "Void Knight armour"), + RogueArmour(Items.ROGUE_MASK_10612, intArrayOf(Items.ROGUE_MASK_5554, Items.ROGUE_TOP_5553, Items.ROGUE_TROUSERS_5555), type = StorableFamily.ARMOUR_CASE, "Rogue armour"), + SpinedArmour(Items.SPINED_HELM_10614, intArrayOf(Items.SPINED_HELM_6131, Items.SPINED_BODY_6133, Items.SPINED_CHAPS_6135, Items.SPINED_BOOTS_6143, Items.SPINED_GLOVES_6149), type = StorableFamily.ARMOUR_CASE, "Spined armour"), + RockshellArmour(Items.ROCK_SHELL_HELM_10613, intArrayOf(Items.ROCK_SHELL_HELM_6128, Items.ROCK_SHELL_PLATE_6129, Items.ROCK_SHELL_LEGS_6130, Items.ROCK_SHELL_BOOTS_6145, Items.ROCK_SHELL_GLOVES_6151), type = StorableFamily.ARMOUR_CASE, "Rockshell armour"), + TribalMaskP(Items.TRIBAL_MASK_10615, intArrayOf(Items.TRIBAL_MASK_6335), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (poison)"), + TribalMaskD(Items.TRIBAL_MASK_10616, intArrayOf(Items.TRIBAL_MASK_6337), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (disease)"), + TribalMaskC(Items.TRIBAL_MASK_10617, intArrayOf(Items.TRIBAL_MASK_6339), type = StorableFamily.ARMOUR_CASE, "Broodoo mask (combat)"), + WhiteKnightArmour(Items.WHITE_PLATEBODY_10618, intArrayOf(Items.WHITE_PLATEBODY_6617, Items.WHITE_FULL_HELM_6623, Items.WHITE_PLATELEGS_6625, Items.WHITE_PLATESKIRT_6627), type = StorableFamily.ARMOUR_CASE, "White knight armour"), + TempleKnightInitiateArmour(Items.INITIATE_HAUBERK_10619, intArrayOf(Items.INITIATE_SALLET_5574, Items.INITIATE_HAUBERK_5575, Items.INITIATE_CUISSE_5576), type = StorableFamily.ARMOUR_CASE, "Temple Knight initiate armour"), + TempleKnightProselyteArmour(Items.PROSELYTE_HAUBERK_10620, intArrayOf(Items.PROSELYTE_SALLET_9672, Items.PROSELYTE_HAUBERK_9674, Items.PROSELYTE_CUISSE_9676, Items.PROSELYTE_TASSET_9678), type = StorableFamily.ARMOUR_CASE, "Temple Knight proselyte armour"), + MournerGear(Items.MOURNER_TOP_10621, intArrayOf(Items.MOURNER_TOP_6065, Items.MOURNER_TROUSERS_6066, Items.MOURNER_BOOTS_6069, Items.MOURNER_CLOAK_6070), type = StorableFamily.ARMOUR_CASE, "Mourner gear"), + GraahkHunterGear(Items.GRAAHK_TOP_10624, intArrayOf(Items.GRAAHK_TOP_10049, Items.GRAAHK_LEGS_10047, Items.GRAAHK_HEADDRESS_10051), type = StorableFamily.ARMOUR_CASE, "Graahk hunter gear"), + LarupiaHunterGear(Items.LARUPIA_TOP_10623, intArrayOf(Items.LARUPIA_TOP_10043, Items.LARUPIA_LEGS_10041, Items.LARUPIA_HAT_10045), type = StorableFamily.ARMOUR_CASE, "Larupia hunter gear"), + KyattHunterGear(Items.KYATT_TOP_10622, intArrayOf(Items.KYATT_TOP_10037, Items.KYATT_LEGS_10035, Items.KYATT_HAT_10039), type = StorableFamily.ARMOUR_CASE, "Kyatt hunter gear"), + PolarCamouflageGear(Items.POLAR_CAMO_TOP_10628, intArrayOf(Items.POLAR_CAMO_TOP_10628, Items.POLAR_CAMO_LEGS_10067), type = StorableFamily.ARMOUR_CASE, "Polar camouflage gear"), + JungleCamouflageGear(Items.JUNGLE_CAMO_TOP_10626, intArrayOf(Items.JUNGLE_CAMO_TOP_10057, Items.JUNGLE_CAMO_LEGS_10059), type = StorableFamily.ARMOUR_CASE, "Jungle camouflage gear"), + WoodCamouflageGear(Items.WOOD_CAMO_TOP_10625, intArrayOf(Items.WOOD_CAMO_TOP_10053, Items.WOOD_CAMO_LEGS_10055), type = StorableFamily.ARMOUR_CASE, "Wood camouflage gear"), + DesertCamouflageGear(Items.DESERT_CAMO_TOP_10627, intArrayOf(Items.DESERT_CAMO_TOP_10061, Items.DESERT_CAMO_LEGS_10063), type = StorableFamily.ARMOUR_CASE, "Desert camouflage gear"), + BuildersCostume(Items.BUILDERS_SHIRT_10863, intArrayOf(Items.BUILDERS_SHIRT_10863, Items.BUILDERS_TROUSERS_10864, Items.BUILDERS_BOOTS_10865), type = StorableFamily.ARMOUR_CASE, "Builder's costume"), + LumberjackCostume(Items.LUMBERJACK_TOP_10945, intArrayOf(Items.LUMBERJACK_TOP_10939, Items.LUMBERJACK_LEGS_10940, Items.LUMBERJACK_HAT_10941), type = StorableFamily.ARMOUR_CASE, "Lumberjack costume"), + BomberJacketCostume(Items.BOMBER_JACKET_11135, intArrayOf(Items.BOMBER_JACKET_9944, Items.BOMBER_CAP_9945), type = StorableFamily.ARMOUR_CASE, "Bomber jacket costume"), + HAMRobes(Items.HAM_SHIRT_11274, intArrayOf(Items.HAM_SHIRT_4298, Items.HAM_ROBE_4300, Items.HAM_HOOD_4302, Items.HAM_CLOAK_4304, Items.HAM_LOGO_4306, Items.BOOTS_4310, Items.GLOVES_4308), type = StorableFamily.ARMOUR_CASE, "H.A.M. robes"), + VoidMelee(Items.VOID_MELEE_HELM_11676, intArrayOf(Items.VOID_MELEE_HELM_11665), type = StorableFamily.ARMOUR_CASE, "Void melee helm"), + VoidRanger(Items.VOID_RANGER_HELM_11675, intArrayOf(Items.VOID_RANGER_HELM_11664), type = StorableFamily.ARMOUR_CASE, "Void ranger helm"), + VoidMage(Items.VOID_MAGE_HELM_11674, intArrayOf(Items.VOID_MAGE_HELM_11663), type = StorableFamily.ARMOUR_CASE, "Void mage helm"), + BlackNaval(Items.BLACK_NAVAL_SHIRT_13185, intArrayOf(Items.BLACK_NAVAL_SHIRT_8956, Items.BLACK_NAVY_SLACKS_8995, Items.BLACK_TRICORN_HAT_8963),type = StorableFamily.ARMOUR_CASE, "Black naval costume"), + RedNaval(Items.RED_NAVAL_SHIRT_13183, intArrayOf(Items.RED_TRICORN_HAT_8961, Items.RED_NAVAL_SHIRT_8954, Items.RED_NAVY_SLACKS_8993), type = StorableFamily.ARMOUR_CASE, "Red naval costume"), + BlueNaval(Items.BLUE_NAVAL_SHIRT_13181, intArrayOf(Items.BLUE_TRICORN_HAT_8959, Items.BLUE_NAVAL_SHIRT_8952, Items.BLUE_NAVY_SLACKS_8991), type = StorableFamily.ARMOUR_CASE, "Blue naval costume"), + BrownNaval(Items.BROWN_NAVAL_SHIRT_13184, intArrayOf(Items.BROWN_TRICORN_HAT_8962, Items.BROWN_NAVAL_SHIRT_8955, Items.BROWN_NAVY_SLACKS_8994), type = StorableFamily.ARMOUR_CASE, "Brown naval costume"), + GreenNaval(Items.GREEN_NAVAL_SHIRT_13182, intArrayOf(Items.GREEN_TRICORN_HAT_8960, Items.GREEN_NAVAL_SHIRT_8953, Items.GREEN_NAVY_SLACKS_8992), type = StorableFamily.ARMOUR_CASE, "Green naval costume"), + GreyNaval(Items.GREY_NAVAL_SHIRT_13187, intArrayOf(Items.GREY_TRICORN_HAT_8965, Items.GREY_NAVAL_SHIRT_8958, Items.GREY_NAVY_SLACKS_8997), type = StorableFamily.ARMOUR_CASE, "Grey naval costume"), + PurpleNaval(Items.PURPLE_NAVAL_SHIRT_13186, intArrayOf(Items.PURPLE_TRICORN_HAT_8964, Items.PURPLE_NAVAL_SHIRT_8957, Items.PURPLE_NAVY_SLACKS_8996), type = StorableFamily.ARMOUR_CASE, "Purple naval costume"), + EliteBlackArmour(Items.ELITE_BLACK_PLATEBODY_14492, intArrayOf(Items.ELITE_BLACK_FULL_HELM_14494, Items.ELITE_BLACK_PLATEBODY_14492, Items.ELITE_BLACK_PLATELEGS_14490), type = StorableFamily.ARMOUR_CASE, "Elite Black Armour") + ; + + companion object { + /** + * Map of all item ids to their [Storable]. + */ + private val idToStorable: Map by lazy { + values().flatMap { storable -> + storable.takeIds.map { id -> id to storable } + }.toMap() + } + + /** + * Set of all item ids across all [Storable]. + */ + private val allItemIds: Set by lazy { idToStorable.keys } + + /** + * Precomputed set of item ids for each Storable for fast lookup. + */ + private val storableIdSets: Map> by lazy { + values().associateWith { it.takeIds.toSet() } + } + + /** + * Returns all item ids from all storables. + */ + fun allIds(): Set = allItemIds + + /** + * Finds the storable for the given item id. + */ + fun fromId(id: Int): Storable? = idToStorable[id] + + /** + * Checks if the player owns at least one item from + * the storable in equipment or bank. + */ + fun hasStorable(player: Player, storable: Storable): Boolean { + val ownedIds = (player.equipment.toArray() + player.bank.toArray()) + .mapNotNull { it?.id } + .toSet() + return storableIdSets[storable]?.any { it in ownedIds } ?: false + } + + /** + * Checks if the player owns any item + * from any storable in equipment or bank. + */ + fun hasAnyStorable(player: Player): Boolean { + val ownedIds = (player.equipment.toArray() + player.bank.toArray()) + .mapNotNull { it?.id } + .toSet() + return allItemIds.any { it in ownedIds } + } + + /** + * Checks if the player has at least one + * item from the given storable equipped. + */ + fun hasStorableEquipped(player: Player, storable: Storable): Boolean { + val equippedIds = player.equipment.toArray().mapNotNull { it?.id }.toSet() + return storableIdSets[storable]?.any { it in equippedIds } ?: false + } + + /** + * Checks if the player has at least one + * item from the given storable in inventory. + */ + fun hasStorableInInventory(player: Player, storable: Storable): Boolean { + val inventoryIds = player.inventory.toArray().mapNotNull { it?.id }.toSet() + return storableIdSets[storable]?.any { it in inventoryIds } ?: false + } + + /** + * Gets all [Storable]s that the player owns in their bank. + */ + fun getStorablesInBank(player: Player): List { + val bankIds = player.bank.toArray().mapNotNull { it?.id }.toSet() + return values().filter { storable -> + storableIdSets[storable]?.any { it in bankIds } ?: false + } + } + } +} + diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorableFamily.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorableFamily.kt new file mode 100644 index 000000000..ab8832baf --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorableFamily.kt @@ -0,0 +1,18 @@ +package content.global.skill.construction.decoration.pohstorage + +/** + * Types (families) of storables in Construction's POH Costume Room or Bookcase. + */ + +enum class StorableFamily { + FANCY_DRESS, + ARMOUR_CASE, + MAGIC_WARDROBE, + TREASURE_CHEST_LOW, + TREASURE_CHEST_MED, + TREASURE_CHEST_HIGH, + BOOKCASE, + CAPE_RACK, + CAPE_RACK_SKILL, + TOY_BOX +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageContainer.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageContainer.kt new file mode 100644 index 000000000..a10cc532f --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageContainer.kt @@ -0,0 +1,74 @@ +package content.global.skill.construction.decoration.pohstorage + +import org.json.simple.JSONArray +import org.json.simple.JSONObject + +/** + * This represents the container where all the POH storable items are. + */ + +class StorageContainer { + + // item IDs stored in this container + private val stored: MutableSet = mutableSetOf() + + // page index for the active interface + private var currentPage: Int = 0 + + // functions to add item, withdraw, check for, etc. + fun addItem(id: Int) { + stored.add(id) + } + + fun withdraw(id: Int): Boolean { + return stored.remove(id) + } + + fun contains(id: Int): Boolean = id in stored + + fun getItems(): List = stored.toList() + + // paging + fun getPageIndex(): Int = currentPage + + fun nextPage(totalItems: Int, pageSize: Int) { + val next = currentPage + 1 + if (next * pageSize < totalItems) { + currentPage = next + } + } + + fun prevPage() { + if (currentPage > 0) { + currentPage-- + } + } + + fun resetPage() { + currentPage = 0 + } + + // saving to json and reading back + fun toJson(): JSONObject { + val json = JSONObject() + val items = JSONArray() + stored.forEach { items.add(it) } + json["items"] = items + return json + } + + fun readJson(json: JSONObject) { + stored.clear() + val items = json["items"] as? JSONArray ?: return + for (id in items) { + stored.add((id as Number).toInt()) + } + currentPage = 0 + } + + // clear storage + fun clear() { + stored.clear() + currentPage = 0 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageInterface.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageInterface.kt new file mode 100644 index 000000000..f6d9ccf81 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageInterface.kt @@ -0,0 +1,385 @@ +package content.global.skill.construction.decoration.pohstorage + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ContainerContext +import core.net.packet.out.ContainerPacket +import org.rs09.consts.Items + +/** + * Handles the interface and interactions for + * the Construction Costume Room storage boxes and Bookshelves + * https://youtu.be/tAIDQRh4U2g?si=oDaKEJT-ZPs4MoZa&t=261 + * https://www.youtube.com/watch?v=D2aVfpPCAgY&t=41s - great view of all the treasure chests + * https://youtu.be/AK6wVwumvHY?si=XAJsQeSdRlBZSM1f - mid-level costume room + * https://www.youtube.com/watch?v=sNEOq_Barlg&t=126s - 2011, but a good video + */ +class StorageInterface : InterfaceListener { + companion object { + private const val INTERFACE = 467 + private const val COMPONENT = 164 + private const val SIZE = 30 + private const val PAGE_SIZE = SIZE - 1 + private const val BUTTON_MORE = Items.MORE_10165 + private const val BUTTON_BACK = Items.BACK_10166 + + /** + * Singleton instance of the storage interface. + */ + lateinit var instance: StorageInterface + private set + + /** + * Public method to get the storage container for a specific family. + */ + fun getStorageContainer(player: Player, type: StorableFamily) = + player.getPOHStorageState().getContainer(type) + + /** + * Opens the storage box. + * + * @param player The player. + * @param type The type of storage to open. + * @param box the specific tier of storage we are opening. + */ + fun openStorage(player: Player, type: StorableFamily, box: StorageOptionPlugin.StorageBox) { + instance.openStorageForType(player, type, box) + } + + /** + * Finds the Storable enum entry associated with a given display item ID. + */ + fun findStorableByDisplayId(id: Int): Storable? = + Storable.values().find { it.displayId == id } + } + + /** + * The attribute of whether the player has read a book or not is stored in a set of two bitfields. + * There are 70 books so that's why two longs are needed. + */ + object BookcaseBitfields { + // 0-63 + const val BOOKCASE_LOW_ATTR = "/save:bookcase_bitfield_low" + // 64-127 + const val BOOKCASE_HIGH_ATTR = "/save:bookcase_bitfield_high" + + data class BookcaseBits(var low: Long, var high: Long) + + // Retrieves both Long bitfields. + fun get(player: Player): BookcaseBits { + val low = getAttribute(player, BOOKCASE_LOW_ATTR, 0L) as? Long ?: 0L + val high = getAttribute(player, BOOKCASE_HIGH_ATTR, 0L) as? Long ?: 0L + return BookcaseBits(low, high) + } + + // Sets both Long bitfields. + fun set(player: Player, bits: BookcaseBits) { + setAttribute(player, BOOKCASE_LOW_ATTR, bits.low) + setAttribute(player, BOOKCASE_HIGH_ATTR, bits.high) + } + + // Checks if a specific bit is set within the two Longs. + fun isBitSet(bits: BookcaseBits, bitIndex: Int): Boolean { + return if (bitIndex < 64) { + (bits.low and (1L shl bitIndex)) != 0L + } else { + val highIndex = bitIndex - 64 + (bits.high and (1L shl highIndex)) != 0L + } + } + + // Sets a specific bit within the two Longs. + fun setBit(bits: BookcaseBits, bitIndex: Int): BookcaseBits { + if (bitIndex < 64) { + bits.low = bits.low or (1L shl bitIndex) + } else { + val highIndex = bitIndex - 64 + bits.high = bits.high or (1L shl highIndex) + } + return bits + } + } + + init { + instance = this + } + + override fun defineInterfaceListeners() { + on(INTERFACE) { player, _, _, buttonId, _, _ -> + val typeName = getAttribute(player, "con:storage:type", null) as? String ?: return@on true + val type = StorableFamily.valueOf(typeName.uppercase()) + handleStorageInteraction(player, buttonId, type) + return@on true + } + } + + /** + * Gets the StorableFamilies that should be displayed in the interface for a given primary type. + * This handles the special case for the Cape Rack because capes are classified as two different families/types + * (normal and skillcape) but displayed in the same storage. + */ + private fun getFamiliesToDisplay(primaryType: StorableFamily): List { + return when (primaryType) { + // if the cape rack can hold skill capes, also show normal capes + StorableFamily.CAPE_RACK_SKILL -> listOf(StorableFamily.CAPE_RACK, StorableFamily.CAPE_RACK_SKILL) + // else only display the items for the primary family + else -> listOf(primaryType) + } + } + + /** + * This handles filtering the list for books. + * Only items are shown that have already been read. + * If the book is part of a quest, the quest must be complete before it shows up in the POH. + * If the index is -1 it is not a book, and not filtered. + */ + private fun getFilteredItems(player: Player, families: List, type: StorableFamily): List { + // filter for normal items + var allItems = Storable.values().filter { it.type in families } + + // filter for bookcase + if (type == StorableFamily.BOOKCASE) { + val bookcaseBits = BookcaseBitfields.get(player) + allItems = allItems.filter { storable -> + // check if read + val isRead = if (storable.bitIndex == -1) true else BookcaseBitfields.isBitSet(bookcaseBits, storable.bitIndex) + // check if quest is done + val questMet = storable.questReq == null || isQuestComplete(player, storable.questReq) + isRead && questMet + } + } + + return allItems + } + + /** + * Handles interaction with storage interface. + */ + private fun handleStorageInteraction(player: Player, buttonId: Int, type: StorableFamily) { + val container = getStorageContainer(player, type) + val families = getFamiliesToDisplay(type) + val allItems = getFilteredItems(player, families, type) + val pageIndex = container.getPageIndex() + + val pageItems = allItems.drop(pageIndex * PAGE_SIZE).take(PAGE_SIZE) + val slots = MutableList(SIZE) { null } + + var idx = 0 + pageItems.forEach { + if (idx >= SIZE) return@forEach + slots[idx++] = it + } + + val hasPrev = pageIndex > 0 + val hasNext = allItems.size > (pageIndex + 1) * PAGE_SIZE + + if (hasNext && idx < SIZE) slots[idx++] = "MORE" + if (hasPrev && idx < SIZE) slots[idx] = "BACK" + + val slotIndex = when { + buttonId in 56..(56 + (SIZE - 1) * 2) step 2 -> (buttonId - 56) / 2 + buttonId in 165..223 step 2 -> (buttonId - 165) / 2 + else -> return + } + + when (val clicked = slots.getOrNull(slotIndex)) { + "MORE" -> { + container.nextPage(allItems.size, PAGE_SIZE) + openInterface(player, INTERFACE) + updateStorageInterface(player, type) + } + "BACK" -> { + container.prevPage() + openInterface(player, INTERFACE) + updateStorageInterface(player, type) + } + is Storable -> processItemTransaction(player, clicked, type) + } + } + + /** + * Handles taking or depositing an item from/to the storage. + */ + private fun processItemTransaction(player: Player, item: Storable, type: StorableFamily) { + // container for the interface/capacity check based on the object the player clicked + val interfaceContainer = getStorageContainer(player, type) + + // container for the actual storage/retrieval based on the item's family + val targetContainer = getStorageContainer(player, item.type) + + // get stored items from the target container + val storedItems = targetContainer.getItems().toSet() + + val box = getAttribute(player, "con:storage:box", null) as? StorageOptionPlugin.StorageBox ?: return + val capacity = box.capacity + + // gets the currently stored IDs + val storedTakeIds = item.takeIds.filter { it in storedItems } + + if (storedTakeIds.isNotEmpty()) { + // withdraw items + if (freeSlots(player) < storedTakeIds.size) { + sendMessage(player, "You don't have enough inventory space.") + return + } + sendMessage(player, "You take the item from the box.") + + storedTakeIds.forEach { id -> + addItem(player, id, 1) + targetContainer.withdraw(id) + } + } else { + // deposit items + + // capacity check + if (capacity < Int.MAX_VALUE) { + + val familyToCheck = when (type) { + // for cape racks, only skillcapes are counted against the capacity + StorableFamily.CAPE_RACK_SKILL -> StorableFamily.CAPE_RACK_SKILL + // for all other storage, check against the primary family type for capacity + else -> type + } + + // check if skillcape + if (item.type == familyToCheck) { + + val capacityCheckItems = interfaceContainer.getItems().toSet() + + // calculate stored skillcapes + val storedSetsCount = Storable.values() + .filter { it.type == familyToCheck } + .count { storable -> + storable.takeIds.any { it in capacityCheckItems } + } + + // enforce capacity limits + if (storedSetsCount >= capacity) { + val message = when (familyToCheck) { + StorableFamily.CAPE_RACK_SKILL -> "You cannot store any more skillcapes in this container." + else -> "You cannot store any more items in this container." + } + sendMessage(player, message) + return + } + } + } + + val presentTakeIds = item.takeIds.filter { player.inventory.contains(it, 1) } + + if (presentTakeIds.isEmpty()) { + sendMessage(player, "You don't have that item in your inventory.") + return + } + sendMessage(player, "You put the item into the box.") + + presentTakeIds.forEach { id -> + removeItem(player, Item(id)) + targetContainer.addItem(id) + } + } + updateStorageInterface(player, type) + } + + private fun updateStorageInterface(player: Player, type: StorableFamily) { + val container = getStorageContainer(player, type) + val families = getFamiliesToDisplay(type) + val allItems = getFilteredItems(player, families, type) + val allStoredItems = mutableSetOf() + + families.forEach { family -> + val familyContainer = getStorageContainer(player, family) + allStoredItems.addAll(familyContainer.getItems()) + } + + val stored = allStoredItems.toSet() + val pageIndex = container.getPageIndex() + + val pageItems = allItems.drop(pageIndex * PAGE_SIZE).take(PAGE_SIZE) + val slots = MutableList(SIZE) { null } + + var idx = 0 + pageItems.forEach { + if (idx >= SIZE) return@forEach + slots[idx++] = it + } + + val hasPrev = pageIndex > 0 + val hasNext = allItems.size > (pageIndex + 1) * PAGE_SIZE + + if (hasNext && idx < SIZE) slots[idx++] = "MORE" + if (hasPrev && idx < SIZE) slots[idx] = "BACK" + + // names each box + val title = when { + type.name.contains("FANCY_DRESS") -> "Fancy dress box" + type.name.contains("TOY_BOX") -> "Toy box" + type.name.contains("MAGIC_WARDROBE") -> "Magic wardrobe" + type.name.contains("ARMOUR_CASE") -> "Armour case" + type.name.contains("BOOKCASE") -> "Bookcase" + type.name.contains("CAPE_RACK") -> "Cape rack" + type.name.contains("TREASURE_CHEST_LOW") -> "Low-level Treasure Trail rewards" + type.name.contains("TREASURE_CHEST_MED") -> "Medium-level Treasure Trail rewards" + type.name.contains("TREASURE_CHEST_HIGH") -> "High-level Treasure Trail rewards" + + // to catch any other name + else -> type.name.lowercase().replaceFirstChar(Char::titlecase) + " box" + } + // child 225 is the title block + setInterfaceText(player, title, INTERFACE, 225) + + val itemsArray = slots.mapNotNull { + when (it) { + is Storable -> Item(it.displayId) + "MORE" -> Item(BUTTON_MORE) + "BACK" -> Item(BUTTON_BACK) + else -> null + } + }.toTypedArray() + + PacketRepository.send( + ContainerPacket::class.java, + ContainerContext(player, INTERFACE, COMPONENT, SIZE, itemsArray, false) + ) + + repeat(SIZE) { i -> + val nameComponent = 55 + i * 2 + val iconComponent = 165 + i * 2 + val hiddenIconComponent = 166 + i * 2 + + val obj = slots[i] + val (name, isStored) = when (obj) { + is Storable -> { + val isStored = obj.takeIds.any { it in stored } + obj.setName to isStored + } + "MORE" -> "More..." to true + "BACK" -> "Back..." to true + else -> "" to false + } + + setInterfaceText(player, name, INTERFACE, nameComponent) + setComponentVisibility(player, INTERFACE, nameComponent, false) + + if (obj is Storable) { + // this displays if the items are stored or not stored + setComponentVisibility(player, INTERFACE, iconComponent, !isStored) + setComponentVisibility(player, INTERFACE, hiddenIconComponent, isStored) + } else { + // this is for navigation buttons or empty slots + setComponentVisibility(player, INTERFACE, iconComponent, true) + setComponentVisibility(player, INTERFACE, hiddenIconComponent, true) + } + } + } + + private fun openStorageForType(player: Player, type: StorableFamily, box: StorageOptionPlugin.StorageBox) { + setAttribute(player, "con:storage:type", type.name) + setAttribute(player, "con:storage:box", box) + openInterface(player, INTERFACE) + updateStorageInterface(player, type) + } +} diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageOptionPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageOptionPlugin.kt new file mode 100644 index 000000000..549b543d0 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageOptionPlugin.kt @@ -0,0 +1,182 @@ +package content.global.skill.construction.decoration.pohstorage + +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.scenery.Scenery +import core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.Sounds +import org.rs09.consts.Scenery as Obj + +/** + * Handles interactions with storage objects. + */ +@Initializable +class StorageOptionPlugin : OptionHandler() { + + /** + * Represents all storage boxes & configuration. + */ + enum class StorageBox(val objectIds: IntArray, val storableFamily: StorableFamily? = null, val openable: Boolean = false, val closable: Boolean = false, val capacity: Int = Int.MAX_VALUE) { + + /* Bookcase + * Wooden: all + * Oak: all + * Mahogany: all */ + BOOKCASE(intArrayOf(Obj.BOOKCASE_13597, Obj.BOOKCASE_13598, Obj.BOOKCASE_13599), StorableFamily.BOOKCASE), + + /* Cape Rack + * Oak: normal capes + * Teak: 1 skillcape + * Mahogany: 5 skillcapes + * Gilded: 10 skillcapes + * Marble: all + * Magic: all */ + CAPE_RACK(intArrayOf(Obj.OAK_CAPE_RACK_18766), StorableFamily.CAPE_RACK), + CAPE_RACK_ONE(intArrayOf(Obj.TEAK_CAPE_RACK_18767), StorableFamily.CAPE_RACK_SKILL, capacity = 1), + CAPE_RACK_FIVE(intArrayOf(Obj.MAHOGANY_CAPE_RACK_18768), StorableFamily.CAPE_RACK_SKILL, capacity = 5), + CAPE_RACK_TEN(intArrayOf(Obj.GILDED_CAPE_RACK_18769), StorableFamily.CAPE_RACK_SKILL, capacity = 10), + CAPE_RACK_ALL(intArrayOf(Obj.MARBLE_CAPE_RACK_18770, Obj.MAGIC_CAPE_RACK_18771), StorableFamily.CAPE_RACK_SKILL), + + /* Fancy Dress Box + * Oak: 2 + * Teak: 4 + * Mahogany: all */ + FANCY_BOX_2(intArrayOf(Obj.FANCY_DRESS_BOX_18773), StorableFamily.FANCY_DRESS, false, true, 2), + FANCY_BOX_4(intArrayOf(Obj.FANCY_DRESS_BOX_18775), StorableFamily.FANCY_DRESS, false, true, 4), + FANCY_BOX_ALL(intArrayOf(Obj.FANCY_DRESS_BOX_18777), StorableFamily.FANCY_DRESS, false, true), + FANCY_BOX_OPEN(intArrayOf(Obj.FANCY_DRESS_BOX_18772, Obj.FANCY_DRESS_BOX_18774, Obj.FANCY_DRESS_BOX_18776), openable = true), + + /* Toy Box + * Oak: all + * Teak: all + * Mahogany: all */ + TOY_BOX_OPEN(intArrayOf(Obj.TOY_BOX_18798, Obj.TOY_BOX_18800, Obj.TOY_BOX_18802), openable = true), + TOY_BOX_CLOSE(intArrayOf(Obj.TOY_BOX_18799, Obj.TOY_BOX_18801, Obj.TOY_BOX_18803), closable = true, storableFamily = StorableFamily.TOY_BOX), + + /* Treasure Chest + * Oak: Low + * Teak: Low, Medium + * Mahogany: Low, Medium, High */ + TREASURE_LOW(intArrayOf(Obj.TREASURE_CHEST_18805), StorableFamily.TREASURE_CHEST_LOW), + TREASURE_MED(intArrayOf(Obj.TREASURE_CHEST_18807), StorableFamily.TREASURE_CHEST_MED), + TREASURE_HIGH(intArrayOf(Obj.TREASURE_CHEST_18809), StorableFamily.TREASURE_CHEST_HIGH), + TREASURE_OPEN(intArrayOf(Obj.TREASURE_CHEST_18804, Obj.TREASURE_CHEST_18806, Obj.TREASURE_CHEST_18808), openable = true), + + /* Magic Wardobe + * Oak: 1 set + * Carved oak: 2 sets + * Teak: 3 sets + * Carved Teak: 4 sets + * Mahogany: 5 sets + * Gilded: 6 sets + * Marble: all sets */ + WARDROBE_1(intArrayOf(Obj.MAGIC_WARDROBE_18785), StorableFamily.MAGIC_WARDROBE, false, true, 1), + WARDROBE_2(intArrayOf(Obj.MAGIC_WARDROBE_18787), StorableFamily.MAGIC_WARDROBE, false, true, 2), + WARDROBE_3(intArrayOf(Obj.MAGIC_WARDROBE_18789), StorableFamily.MAGIC_WARDROBE, false, true, 3), + WARDROBE_4(intArrayOf(Obj.MAGIC_WARDROBE_18791), StorableFamily.MAGIC_WARDROBE, false, true, 4), + WARDROBE_5(intArrayOf(Obj.MAGIC_WARDROBE_18793), StorableFamily.MAGIC_WARDROBE, false, true, 5), + WARDROBE_6(intArrayOf(Obj.MAGIC_WARDROBE_18795), StorableFamily.MAGIC_WARDROBE, false, true, 6), + WARDROBE_ALL(intArrayOf(Obj.MAGIC_WARDROBE_18797), StorableFamily.MAGIC_WARDROBE), + WARDROBE_OPEN(intArrayOf(Obj.MAGIC_WARDROBE_18784, Obj.MAGIC_WARDROBE_18786, Obj.MAGIC_WARDROBE_18788, Obj.MAGIC_WARDROBE_18790, Obj.MAGIC_WARDROBE_18792, Obj.MAGIC_WARDROBE_18794, Obj.MAGIC_WARDROBE_18796), openable = true), + + /* Armour Case + * Oak: 2 + * Teak: 4 + * Mahogany: all */ + ARMOUR_2(intArrayOf(Obj.ARMOUR_CASE_18779), StorableFamily.ARMOUR_CASE, false, true, 2), + ARMOUR_4(intArrayOf(Obj.ARMOUR_CASE_18781), StorableFamily.ARMOUR_CASE, false, true, 4), + ARMOUR_ALL(intArrayOf(Obj.ARMOUR_CASE_18783), StorableFamily.ARMOUR_CASE), + ARMOUR_OPEN(intArrayOf(Obj.ARMOUR_CASE_18778, Obj.ARMOUR_CASE_18780, Obj.ARMOUR_CASE_18782), openable = true) + } + + private val allBoxes = StorageBox.values() + + override fun newInstance(arg: Any?): Plugin { + allBoxes.forEach { box -> + box.objectIds.forEach { id -> + SceneryDefinition.forId(id)?.let { def -> + if (box.storableFamily != null) def.handlers["option:search"] = this + if (box.openable) def.handlers["option:open"] = this + if (box.closable) def.handlers["option:close"] = this + } + } + } + return this + } + + override fun handle(player: Player, node: Node, option: String): Boolean { + val obj = node as Scenery + val box = allBoxes.firstOrNull { obj.id in it.objectIds } ?: return true + + when (option) { + "search" -> { + when (obj.id) { + // special handling for med and high level treasure chests since they have a dialogue option first + Obj.TREASURE_CHEST_18807 -> { + sendDialogueOptions(player, "What sort of Treasure Trail items do you want to see?", "Low-level rewards", "Medium-level rewards") + addDialogueAction(player) { p, button -> + val family = when (button) { + 2 -> StorableFamily.TREASURE_CHEST_LOW + 3 -> StorableFamily.TREASURE_CHEST_MED + else -> null + } + family?.let { StorageInterface.openStorage(p, it, box) } + return@addDialogueAction + } + } + Obj.TREASURE_CHEST_18809 -> { + sendDialogueOptions(player, "What sort of Treasure Trail items do you want to see?", "Low-level rewards", "Medium-level rewards", "High-level rewards") + addDialogueAction(player) { p, button -> + val family = when (button) { + 2 -> StorableFamily.TREASURE_CHEST_LOW + 3 -> StorableFamily.TREASURE_CHEST_MED + 4 -> StorableFamily.TREASURE_CHEST_HIGH + else -> null + } + family?.let { StorageInterface.openStorage(p, it, box) } + return@addDialogueAction + } + } + // bookcases need to re-store the games book in them on open. I think the bookshelf is the only source. + Obj.BOOKCASE_13597 -> { + storeBookInHouse(player, Items.GAME_BOOK_7681) + box.storableFamily?.let { StorageInterface.openStorage(player, it, box) } + } + Obj.BOOKCASE_13598 -> { + storeBookInHouse(player, Items.GAME_BOOK_7681) + box.storableFamily?.let { StorageInterface.openStorage(player, it, box) } + } + Obj.BOOKCASE_13599 -> { + storeBookInHouse(player, Items.GAME_BOOK_7681) + box.storableFamily?.let { StorageInterface.openStorage(player, it, box) } + } + else -> { + // default open for rest + box.storableFamily?.let { StorageInterface.openStorage(player, it, box) } + } + } + } + "open" -> if (box.openable) openBox(player, obj) + "close" -> if (box.closable) closeBox(player, obj) + } + return true + } + + private fun openBox(player: Player, obj: Scenery) { + playAudio(player, Sounds.CHEST_OPEN_52) + animate(player, Animations.HUMAN_OPEN_CHEST_536) + replaceScenery(obj, obj.id + 1, -1) + } + + private fun closeBox(player: Player, obj: Scenery) { + playAudio(player, Sounds.CHEST_CLOSE_51) + animate(player, 538) + replaceScenery(obj, obj.id - 1, -1) + } +} diff --git a/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageState.kt b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageState.kt new file mode 100644 index 000000000..9108a6013 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/pohstorage/StorageState.kt @@ -0,0 +1,37 @@ +package content.global.skill.construction.decoration.pohstorage + +import org.json.simple.JSONObject +import core.game.node.entity.player.Player + +class StorageState(val player: Player) { + + private val containers = StorableFamily.values().associateWith { StorageContainer() }.toMutableMap() + + fun getContainer(type: StorableFamily): StorageContainer = containers[type]!! + + fun toJson(): JSONObject { + val root = JSONObject() + val containerData = JSONObject() + + containers.forEach { (group, container) -> + containerData[group.name.lowercase()] = container.toJson() + } + + root["containers"] = containerData + return root + } + + fun readJson(data: JSONObject) { + val containerData = data["containers"] as? JSONObject ?: return + + for (group in StorableFamily.values()) { + val json = containerData[group.name.lowercase()] as? JSONObject ?: continue + containers[group]?.readJson(json) + } + } + + // clear storage + fun clear() { + containers.values.forEach { it.clear() } + } +} 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..c154bef68 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 @@ -1,6 +1,7 @@ package content.global.skill.construction.decoration.portalchamber; +import content.data.Quests; import content.global.skill.construction.Hotspot; import content.global.skill.runecrafting.Rune; import core.cache.def.impl.SceneryDefinition; @@ -17,6 +18,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; +import static content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE; +import static core.api.ContentAPIKt.*; + /** * PortalChamberPlugin * @author Clayton Williams @@ -87,9 +91,14 @@ public class PortalChamberPlugin extends OptionHandler { } for (Locations l : Locations.values()) { if (l.name().contains(identifier)) { + if ((l == Locations.ARDOUGNE && !getAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, false)) || + (l == Locations.KHARYRLL && !isQuestComplete(player, Quests.DESERT_TREASURE))) { + sendMessage(player, "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"); + sendMessage(player, "You do not have the required runes to build this portal."); return; } player.getInventory().remove(runes); @@ -215,7 +224,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..b5196af4d 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,65 +1,72 @@ package content.global.skill.construction.decoration.questhall +import content.data.EnchantedJewellery +import content.global.handlers.item.EnchantedJewelleryListener.EnchantedJewelleryDialogueFile +import core.api.delayScript +import core.api.lock +import core.api.openDialogue +import core.api.playGlobalAudio +import core.api.queueScript +import core.api.resetAnimator +import core.api.stopExecuting import core.api.teleport +import core.api.visualize +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.audio.Audio; -import core.game.system.task.Pulse -import core.game.world.map.Location +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.game.interaction.InteractionListener -import core.game.interaction.IntType -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit +import org.rs09.consts.Items +import org.rs09.consts.Sounds class MountedGlory : InteractionListener { - val MOUNTED_GLORY = 13523 - - val TELEPORTS = arrayOf( - Location.create(3087, 3495, 0), - Location.create(2919, 3175, 0), - Location.create(3104, 3249, 0), - Location.create(3304, 3124, 0) - ) + val TELEPORTS = EnchantedJewellery.AMULET_OF_GLORY.locations 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 + } + on(MOUNTED_GLORY, IntType.SCENERY, "Rub") { player, _ -> + openDialogue(player,EnchantedJewelleryDialogueFile(EnchantedJewellery.AMULET_OF_GLORY, Item(Items.AMULET_OF_GLORY4_1712), false, false)) return@on true } } - private fun mountedGloryTeleport(player : Player, int : Int) { - Executors.newSingleThreadScheduledExecutor().schedule({ - player.pulseManager.run(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 1 -> { - player.lock(5) - player.visualize(Animation(714), Graphics(308, 100, 50)) - player.getAudioManager().send(Audio(200), true); - } - 4 -> player.animator.reset().also { teleport(player, TELEPORTS[int]) } - } - return false + 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 + } + queueScript(player, 0, QueueStrength.SOFT) { stage -> + if (stage == 0) { + lock(player, 4) + visualize(player, Animation(714), Graphics(308, 100, 50)) + playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) } - }) - }, 0, TimeUnit.SECONDS) + resetAnimator(player) + teleport(player, TELEPORTS[int]) + return@queueScript stopExecuting(player) + } } -} \ 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 deleted file mode 100644 index 4323ce498..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGloryPlugin.java +++ /dev/null @@ -1,96 +0,0 @@ -package content.global.skill.construction.decoration.questhall; - - -import core.cache.def.impl.SceneryDefinition; -import core.game.dialogue.DialogueAction; -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; -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 POH Mounted Glory. - * @author Splinter - */ -@Initializable -public class MountedGloryPlugin extends OptionHandler { - - /** - * Represents the teleport animation. - */ - private static final Animation ANIMATION = new Animation(714); - - /** - * Represents the graphics to use. - */ - private static final Graphics GRAPHICS = new Graphics(308, 100, 50); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(13523).getHandlers().put("option:rub", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - DialogueInterpreter interpreter = player.getDialogueInterpreter(); - interpreter.sendOptions("Select a location.", "Edgeville", "Karamja", "Draynor Village", "Al-Kharid", "Nowhere."); - interpreter.addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - switch (buttonId) { - case 2: - teleport(player, Location.create(3087, 3495, 0)); - break; - case 3: - teleport(player, Location.create(2919, 3175, 0)); - break; - case 4: - teleport(player, Location.create(3081, 3250, 0)); - break; - case 5: - teleport(player, Location.create(3304, 3124, 0)); - break; - } - } - - }); - return true; - } - - /** - * Method used to teleport to a location. - * @param player the player. - * @param location the location. - */ - private boolean teleport(final Player player, final Location location) { - if (player.isTeleBlocked()) { - player.sendMessage("A magical force has stopped you from teleporting."); - return false; - } - player.lock(); - player.visualize(ANIMATION, GRAPHICS); - player.getAudioManager().send(new Audio(200), true); - player.getImpactHandler().setDisabledTicks(4); - GameWorld.getPulser().submit(new Pulse(4, player) { - @Override - public boolean pulse() { - player.unlock(); - player.getProperties().setTeleportLocation(location); - player.getAnimator().reset(); - return true; - } - }); - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/skillhall/MountedCockatriceHead.kt b/Server/src/main/content/global/skill/construction/decoration/skillhall/MountedCockatriceHead.kt new file mode 100644 index 000000000..54472597e --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/skillhall/MountedCockatriceHead.kt @@ -0,0 +1,68 @@ +package content.global.skill.construction.decoration.skillhall + +import content.global.skill.construction.HouseManager +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.world.repository.Repository +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery as Sceneries + +class MountedCockatriceHead : InteractionListener { + override fun defineListeners() { + on(Sceneries.COCKATRICE_HEAD_13482, IntType.SCENERY, "talk-to") { player, _ -> + val house = player.getAttribute("poh_entry", player.houseManager) + if (house == player.houseManager) { + openDialogue(player, MountedCockatriceHeadOwnerDialogue()) + } else { + val ownerName = Repository.players.first { it.houseManager == house }.username.capitalize() + openDialogue(player, MountedCockatriceHeadGuestDialogue(ownerName)) + } + return@on true + } + } +} + +class MountedCockatriceHeadOwnerDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_NORMAL, "You deaded me!").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "Well, yes.").also { stage++ } + 2 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_NORMAL, "What did you do that for?").also { stage++ } + 3 -> options("A Slayer Master told me to.", "So I could mount your head on my wall.", "I just wanted to.").also { stage++ } + 4 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "A Slayer Master told me to.").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "So I could mount your head on my wall.").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "I just wanted to.").also { stage = 30 } + } + 10 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_SAD, "Why do the Slayer Masters all pick on poor cockatrice?").also { stage++ } + 11 -> playerl(FacialExpression.NEUTRAL, "They pick on lots of other creatures too.").also { stage++ } + 12 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_SAD, "Then mount one of them on your wall and let poor cockatrice rest in peace!").also { stage = END_DIALOGUE } + 20 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_SAD, "Another cockatrice falls victim to the dreaded mirror shield!").also { stage++ } + 21 -> playerl(FacialExpression.NEUTRAL, "Don't take it personally. You look good on my wall.").also { stage++ } + 22 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_SAD, "I don't care! I think I looked better with a body!").also { stage = END_DIALOGUE } + 30 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "You dirty rotten swine, you!").also { stage++ } + 31 -> playerl(FacialExpression.AMAZED, "Steady on.").also { stage++ } + 32 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "I will kill you with my paralyzing-type magic eyes look!").also { stage++ } + 33 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "Dots appear in air between eyes and victim. Dot! Dot! Dotty!").also { stage++ } + 34 -> playerl(FacialExpression.NEUTRAL, "Er, nothing's happening...").also { stage++ } + 35 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "Concentrates mental power. Eyes narrow beak clenches veins on head stand out.").also { stage++ } + 36 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "Strain!").also { stage++ } + 37 -> playerl(FacialExpression.ASKING, "You're dead, cockatrice. Your eyes are glass beads. It won't work.").also { stage++ } + 38 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_ANGRY, "STRA-A-AIN!").also { stage++ } + 39 -> playerl(FacialExpression.ASKING, "I think I'll leave you to it.").also { stage = END_DIALOGUE } + } + } +} + +class MountedCockatriceHeadGuestDialogue(private val ownerName: String) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> playerl(FacialExpression.HAPPY, "Hey, a cockatrice!").also { stage++ } + 1 -> npcl(NPCs.COCKATRICE_4227, FacialExpression.CHILD_SAD, "$ownerName deaded me! That wasn't very nice!").also { stage = END_DIALOGUE } + } + } +} 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..44a050754 100644 --- a/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt @@ -4,17 +4,19 @@ import core.game.dialogue.DialoguePlugin import content.data.RepairItem import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler -import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment -import content.region.misthalin.lumbridge.dialogue.BobDialogue.BarrowsEquipment.BarrowsFullEquipment +import content.global.handlers.item.equipment.BarrowsEquipment import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin import kotlin.math.ceil +import org.rs09.consts.Items + +private val ALL_REPAIRABLE_ITEM_IDS = (RepairItem.repairableItemIds + BarrowsEquipment.getAllRepairableBarrowsIds()).toIntArray() @Initializable -class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474, 504, 476, 506, 478, 6741, 4856, 4857, 4858, 4859, 4860, 4862, 4863, 4864, 4865, 4866, 4868, 4869, 4870, 4871, 4872, 4874, 4875, 4876, 4877, 4878, 4880, 4881, 4882, 4883, 4884, 4886, 4887, 4888, 4889, 4890, 4892, 4893, 4894, 4895, 4896, 4898, 4899, 4900, 4901, 4902, 4904, 4905, 4906, 4907, 4908, 4910, 4911, 4912, 4913, 4914, 4916, 4917, 4918, 4919, 4920, 4922, 4923, 4924, 4925, 4926, 4928, 4929, 4930, 4931, 4932, 4934, 4935, 4936, 4937, 4938, 4940, 4941, 4942, 4943, 4944, 4946, 4947, 4948, 4949, 4950, 4952, 4953, 4954, 4955, 4956, 4958, 4959, 4960, 4961, 4962, 4964, 4965, 4966, 4967, 4968, 4970, 4971, 4972, 4973, 4974, 4976, 4977, 4978, 4979, 4980, 4982, 4983, 4984, 4985, 4986, 4988, 4989, 4990, 4991, 4992, 4994, 4995, 4996, 4997, 4998){ +class ArmourStand : UseWithHandler(*ALL_REPAIRABLE_ITEM_IDS) { override fun newInstance(arg: Any?): Plugin { addHandler(13715, OBJECT_TYPE, this) return this @@ -23,76 +25,90 @@ class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474, override fun handle(event: NodeUsageEvent?): Boolean { event ?: return false val player = event.player - val repairItem = RepairItem.forId(event.used.id) + val usedItem = event.used.asItem() - var baseCost = 0.0 + var baseCost = 0 var product: Item? = null - if(repairItem != null){ - baseCost = repairItem.cost * 1.0 - product = repairItem.product - } else if(BarrowsEquipment.isBarrowsItem(event.used.id)){ - //Begin terrible code thanks to Vexia - val type = BarrowsEquipment.formatedName(event.used.id) - val single = BarrowsEquipment.getSingleName(type) - val equipment = BarrowsEquipment.getEquipmentType(type) - val newString = type.toLowerCase().replace(single, "").trim { it <= ' ' }.replace("'s", "") - val newewString = StringBuilder() - newewString.append(newString).append(" $equipment") - val fullequip = BarrowsFullEquipment.forName(newewString.toString()) - baseCost = BarrowsEquipment.getFormatedCost(equipment,event.used.asItem()) * 1.0 - product = fullequip.full - //End terrible code thanks to Vexia - } + val repairItem = RepairItem.forId(usedItem.id) + val barrowsDef = BarrowsEquipment.getDefinition(usedItem.id) - if((repairItem == null && baseCost == 0.0)){ + if (repairItem != null) { + baseCost = repairItem.cost + product = repairItem.product + } else if (barrowsDef != null) { + if (BarrowsEquipment.isFullyRepaired(event.used.id)) { + player.sendMessage("That item can't be repaired.") + return true + } + baseCost = BarrowsEquipment.getRepairCost(usedItem) + product = Item(barrowsDef.repairedId) + } else { player.sendMessage("That item can't be repaired.") return true } - val cost: Int = ceil(((100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0) ) / 100.0) * baseCost).toInt() - - player.dialogueInterpreter.open(58824213,event.used,cost,product) + val discountMultiplier = (100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0)) / 100.0 + val cost = ceil(discountMultiplier * baseCost).toInt() + player.dialogueInterpreter.open(58824213,usedItem, cost, product) return true } @Initializable - class RepairDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return RepairDialogue(player) - } - var item: Item? = null - var cost: Int = 0 - var product: Item? = null + class RepairDialogue(player: Player? = null) : DialoguePlugin(player) { + + private var item: Item? = null + private var cost: Int = 0 + private var product: Item? = null + + override fun newInstance(player: Player?): DialoguePlugin = RepairDialogue(player) override fun open(vararg args: Any?): Boolean { item = args[0] as Item cost = args[1] as Int product = args[2] as Item - player.dialogueInterpreter.sendDialogue("Would you like to repair your ${(item as Item).name.toLowerCase()}","for $cost gp?") + + val itemName = item?.name?.lowercase() ?: "item" + player.dialogueInterpreter.sendDialogue( + "Would you like to repair your $itemName", + "for $cost gp?" + ) stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - item ?: return false - product ?: return false - when(stage){ - 0 -> options("Yes, please","No, thanks").also{stage++} - 1 -> when(buttonId){ - 1 -> exchangeItems(item as Item,cost,product as Item).also { end() } + val currentItem = item ?: return false + val currentProduct = product ?: return false + + when (stage) { + 0 -> { + options("Yes, please", "No, thanks") + stage++ + } + 1 -> when (buttonId) { + 1 -> { + exchangeItems(currentItem, cost, currentProduct) + end() + } 2 -> end() } } return true } - fun exchangeItems(item: Item, cost: Int, product: Item) { - val coins = Item(995, cost) + private fun exchangeItems(item: Item, cost: Int, product: Item) { + val coins = Item(Items.COINS_995, cost) + if (player.inventory.containsItem(coins) && player.inventory.containsItem(item)) { - player.inventory.remove(item, coins) - player.inventory.add(product) - player.sendMessage("You repair your ${product.name.toLowerCase()} for $cost.") + if (player.inventory.remove(item, coins)) { + if (player.inventory.add(product)) { + val costText = if (cost > 0) "${cost}gp" else "free" + player.sendMessage("You repair your ${product.name.lowercase()} for $costText.") + return + } + } + player.sendMessage("Report this to an administrator!") } else { player.sendMessage("You can't afford that.") } diff --git a/Server/src/main/content/global/skill/construction/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/ToolsPlugin.java b/Server/src/main/content/global/skill/construction/decoration/workshop/ToolsPlugin.java deleted file mode 100644 index 2341c3a2a..000000000 --- a/Server/src/main/content/global/skill/construction/decoration/workshop/ToolsPlugin.java +++ /dev/null @@ -1,171 +0,0 @@ -package content.global.skill.construction.decoration.workshop; - - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles the 5 types of tool stores - * ToolsPlugin.java - * @author Clayton Williams - * @date Oct 29, 2015 - * - */ -@Initializable -public class ToolsPlugin extends OptionHandler { - - /** - * The different tool stores - * @author Clayton Williams - * @date Oct 29, 2015 - */ - private enum ToolStore { - - TOOLSTORE_1(13699, 8794, 1755, 2347, 1735), - TOOLSTORE_2(13700, 1925, 952, 590), - TOOLSTORE_3(13701, 1757, 1785, 1733), - TOOLSTORE_4(13702, 1595, 1597, 1592, 1599, 5523), - TOOLSTORE_5(13703, 5341, 952, 676, 5343, 5331); - - /** - * The object id of the tool store - */ - private int objectId; - - /** - * The tools included - */ - private int[] tools; - - /** - * ToolStore - * @param objectId - * @param tools - */ - ToolStore(int objectId, int... tools) { - this.objectId = objectId; - this.tools = tools; - } - - /** - * Gets the toolstore from an object id - * @param objectId - the object id - * @return - */ - private static ToolStore forId(int objectId) { - for (ToolStore t : ToolStore.values()) { - if (t.objectId == objectId) { - return t; - } - } - return null; - } - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (ToolStore t : ToolStore.values()) { - SceneryDefinition.forId(t.objectId).getHandlers().put("option:search", this); - } - ClassScanner.definePlugin(new ToolDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Scenery object = node.asScenery(); - ToolStore ts = ToolStore.forId(object.getId()); - if (ts != null) { - player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey("con:tools"), ts); - } - return true; - } - - /** - * Handles the tool dialogue - * @author Clayton Williams - * @date Oct 29, 2015 - */ - private final class ToolDialogue extends DialoguePlugin { - - /** - * The tool store being used - */ - private ToolStore toolStore; - - /** - * ToolDialogue - */ - private ToolDialogue() { - /** - * Empty - */ - } - - /** - * ToolDialogue - * @param player - */ - public ToolDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ToolDialogue(player); - } - - @Override - public boolean open(Object... args) { - toolStore = (ToolStore) args[0]; - List itemNames = new ArrayList(); - for(int itemId : toolStore.tools) { - ItemDefinition n = ItemDefinition.forId(itemId); - itemNames.add(n.getName()); - } - interpreter.sendOptions("Select a Tool", itemNames.toArray(new String[itemNames.size()])); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - Item item = new Item(toolStore.tools[buttonId - 1], 1); - if (player.getInventory().freeSlots() <= 0) { - interpreter.sendDialogue("You have no space in your inventory."); - stage = 2; - return true; - } - player.getInventory().add(item); - end(); - return true; - case 2: - end(); - return true; - } - return false; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("con:tools") }; - } - - } - -} diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/ToolsPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/workshop/ToolsPlugin.kt new file mode 100644 index 000000000..22e3a28cb --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ToolsPlugin.kt @@ -0,0 +1,233 @@ +package content.global.skill.construction.decoration.workshop + + + +import core.api.* +import core.cache.def.impl.SceneryDefinition +import core.game.dialogue.DialogueInterpreter +import core.game.dialogue.DialoguePlugin +import core.game.interaction.OptionHandler +import core.game.node.Node +import core.game.node.entity.player.Player +import core.plugin.ClassScanner.definePlugin +import core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.Items + +/** + * Handles the 5 types of tool stores + * ToolsPlugin.java + * @author Clayton Williams + * @date Oct 29, 2015 + */ + + +@Initializable +class ToolsPlugin : OptionHandler() { + val id =0 + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin { + definePlugin(ToolDialogue()) + SceneryDefinition.forId(13699).getHandlers().put("option:search", this) + SceneryDefinition.forId(13700).getHandlers().put("option:search", this) + SceneryDefinition.forId(13701).getHandlers().put("option:search", this) + SceneryDefinition.forId(13702).getHandlers().put("option:search", this) + SceneryDefinition.forId(13703).getHandlers().put("option:search", this) + return this + } + override fun handle(player: Player, node: Node, option: String?): Boolean { + openDialogue(player,DialogueInterpreter.getDialogueKey("con:tools"), node.getId()) + return true + } + + /** + * Handles the tool dialogue + * @author Clayton Williams + * @date Oct 29, 2015 + */ + private inner class ToolDialogue : DialoguePlugin { + private var id: Int? = null + /** + * Constructs a new `ToolDialogue` `Object`. + */ + constructor() + + /** + * Constructs a new `ToolDialogue` `Object`. + * + * @param player + * the player. + */ + constructor(player: Player?) : super(player) + + fun addItemMsg(player: Player, id: Int, amount: Int = 1) { + + if (!addItem(player, id,amount)){ + sendDialogue(player, "You have no space in your inventory.") + stage = 3 //Stage 3, player just needs to confirm they received the no inventory space dialogue + } else {end()} //Player received their Item, close dialogue + } + + public override fun newInstance(player: Player?): DialoguePlugin { + return ToolDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + id = args[0] as Int + when (id) { + 13699 -> { + sendDialogueOptions(player,"Select a Tool", "Saw", "Chisel", "Hammer", "Shears") + } + + 13700 -> { + sendDialogueOptions(player,"Select a Tool", "Bucket", "Knife", "Spade", "Tinderbox") + } + + 13701 -> { + sendDialogueOptions(player,"Select a Tool", "Brown apron", "Glassblowing pipe", "Needle") + } + + 13702 -> { + sendDialogueOptions(player,"Select a Tool","Amulet mould","Necklace mould","Ring mould","Holy mould","More Options" + ) + stage = 1 + } + + 13703 -> { + sendDialogueOptions(player,"Select a Tool", "Rake", "Spade", "Trowel", "Seed dibber", "Watering can") + } + } + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (stage == 3) {//If reply was on full inventory confirmation, close dialogue + end() + return true + } + when (id) { + 13699 -> { + when (buttonId) { + 1 -> { + addItemMsg(player, Items.SAW_8794) + } + + 2 -> { + addItemMsg(player, Items.CHISEL_1755) + } + + 3 -> { + addItemMsg(player, Items.HAMMER_2347) + } + + 4 -> { + addItemMsg(player, Items.SHEARS_1735) + } + } + } + + 13700 -> { + when (buttonId) { + 1 -> { + addItemMsg(player, Items.BUCKET_1925) + } + + 2 -> { + addItemMsg(player, Items.KNIFE_946) + } + + 3 -> { + addItemMsg(player, Items.SPADE_952) + } + + 4 -> { + addItemMsg(player, Items.TINDERBOX_590) + } + } + } + + 13701 -> { + when (buttonId) { + 1 -> { + addItemMsg(player, Items.BROWN_APRON_1757) + } + + 2 -> { + addItemMsg(player, Items.GLASSBLOWING_PIPE_1785) + } + + 3 -> { + addItemMsg(player, Items.NEEDLE_1733) + } + } + } + + 13702 -> { + when (stage) { + 1 -> when (buttonId) { + 1 -> { + addItemMsg(player, Items.AMULET_MOULD_1595) + } + + 2 -> { + addItemMsg(player, Items.NECKLACE_MOULD_1597) + } + + 3 -> { + addItemMsg(player, Items.RING_MOULD_1592) + } + + 4 -> { + addItemMsg(player, Items.HOLY_MOULD_1599) + } + + 5 -> { + sendDialogueOptions(player, + "Select a Tool", "Bracelet mould", "Tiara mould" + ) + stage = 2 + } + } + 2 -> { + when (buttonId) { + 1 -> { + addItemMsg(player, Items.BRACELET_MOULD_11065) + } + + 2 -> { + addItemMsg(player, Items.TIARA_MOULD_5523) + } + } + } + } + } + 13703 -> { + when (buttonId) { + 1 -> { + addItemMsg(player, Items.RAKE_5341) + } + + 2 -> { + addItemMsg(player, Items.SPADE_952) + } + + 3 -> { + addItemMsg(player, Items.TROWEL_676) + } + + 4 -> { + addItemMsg(player, Items.SEED_DIBBER_5343) + } + + 5 -> { + addItemMsg(player, Items.WATERING_CAN_5331) + } + } + } + } + return true + } + override fun getIds(): IntArray { + return intArrayOf(DialogueInterpreter.getDialogueKey("con:tools")) + } + } +} 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/CakeListener.kt b/Server/src/main/content/global/skill/cooking/CakeListener.kt new file mode 100644 index 000000000..dc8eb948f --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/CakeListener.kt @@ -0,0 +1,39 @@ +package content.global.skill.cooking + +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 CakeListener : InteractionListener { + private val cakeArr = intArrayOf( + Items.EGG_1944, + Items.BUCKET_OF_MILK_1927, + Items.POT_OF_FLOUR_1933 + ) + + override fun defineListeners() { + onUseWith(IntType.ITEM, cakeArr, Items.CAKE_TIN_1887) { player, _, _ -> + if (getDynLevel(player, Skills.COOKING) < 40) { + sendMessage(player, "You need a Cooking level of at least 40 in order to do this.") + return@onUseWith true + } + + if(inInventory(player, Items.EGG_1944) && inInventory(player, Items.BUCKET_OF_MILK_1927) && inInventory(player, Items.POT_OF_FLOUR_1933) && inInventory(player, Items.CAKE_TIN_1887)) { + if(removeItem(player, Items.EGG_1944) && removeItem(player, Items.BUCKET_OF_MILK_1927) && removeItem(player, Items.POT_OF_FLOUR_1933) && removeItem(player, Items.CAKE_TIN_1887)){ + addItem(player, Items.EMPTY_POT_1931) + addItem(player, Items.UNCOOKED_CAKE_1889) + addItem(player, Items.BUCKET_1925) + sendMessage(player, "You mix the milk, flour and egg together to make a raw cake mix.") + + } + } + else{ + // Not sure about the authentic message + sendMessage(player, "You don't have enough ingredients to make that.") + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/CakeMakingPlugin.java b/Server/src/main/content/global/skill/cooking/CakeMakingPlugin.java deleted file mode 100644 index 084abf4c4..000000000 --- a/Server/src/main/content/global/skill/cooking/CakeMakingPlugin.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to make a cake. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CakeMakingPlugin extends UseWithHandler { - - /** - * Represents the bucket of milk item. - */ - private static final Item BUCKET_OF_MILK = new Item(1927); - - /** - * Represents the egg item. - */ - private static final Item EGG = new Item(1944); - - /** - * Represents the cake tin item. - */ - private static final Item CAKE_TIN = new Item(1887); - - /** - * Represents the pot of flour item. - */ - private static final Item POT_OF_FLOUR = new Item(1933); - - /** - * Represents the uncooked cake item. - */ - private static final Item UNCOOKED_CAKE = new Item(1889); - - /** - * Constructs a new {@code CakeMakingPlugin} {@code Object}. - */ - public CakeMakingPlugin() { - super(1933); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(1887, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - if (event.getUsedItem().getId() == 1887 && ((Item) event.getUsedWith()).getId() == 1933 || event.getUsedWith().getName().equalsIgnoreCase("cake tin") && ((Item) event.getUsedItem()).getName().equalsIgnoreCase("pot of flour")) { - if (event.getPlayer().getInventory().contains(1933, 1) && event.getPlayer().getInventory().contains(1927, 1) && event.getPlayer().getInventory().contains(1944, 1)) { - if (event.getPlayer().getInventory().remove(BUCKET_OF_MILK, EGG, CAKE_TIN, POT_OF_FLOUR)) { - event.getPlayer().getInventory().add(UNCOOKED_CAKE); - event.getPlayer().getPacketDispatch().sendMessage("You mix the milk, flour and egg together to make a raw cake mix."); - return true; - } - } - } - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/CoconutListener.kt b/Server/src/main/content/global/skill/cooking/CoconutListener.kt new file mode 100644 index 000000000..5f7da66d4 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/CoconutListener.kt @@ -0,0 +1,31 @@ +package content.global.skill.cooking + +import core.api.addItem +import core.api.removeItem +import core.api.sendMessage +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class CoconutListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.COCONUT_5974, Items.HAMMER_2347) { player, used, _ -> + if(removeItem(player, used.id)) { + addItem(player, Items.COCONUT_5976) + sendMessage(player, "You crush the coconut with a hammer.") + return@onUseWith true + } + return@onUseWith false + } + onUseWith(IntType.ITEM, Items.COCONUT_5976, Items.VIAL_229) { player, used, with -> + if(removeItem(player, used.id) && removeItem(player, with.id)) { + addItem(player, Items.COCONUT_SHELL_5978) + addItem(player, Items.COCONUT_MILK_5935) + sendMessage(player, "You overturn the coconut into a vial.") + return@onUseWith true + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/CoconutMakePlugin.java b/Server/src/main/content/global/skill/cooking/CoconutMakePlugin.java deleted file mode 100644 index 7520ff115..000000000 --- a/Server/src/main/content/global/skill/cooking/CoconutMakePlugin.java +++ /dev/null @@ -1,56 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to make a coconut. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CoconutMakePlugin extends UseWithHandler { - - /** - * Represents the items related to coconut making plugin. - */ - private static final Item[] ITEMS = new Item[] { new Item(5974, 1), new Item(5976, 1), new Item(229), new Item(5935, 1), new Item(5978) }; - - /** - * Constructs a new {@code CoconutMakePlugin} {@code Object}. - */ - public CoconutMakePlugin() { - super(5974, 5976); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2347, ITEM_TYPE, this); - addHandler(229, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item usedWith = (Item) event.getUsedWith(); - if (usedWith.getId() == 5974 && event.getUsedItem().getId() == 2347 || usedWith.getId() == 2347 && event.getUsedItem().getId() == 5974) { - player.getInventory().remove(ITEMS[0]); - player.getInventory().add(ITEMS[1]); - player.getPacketDispatch().sendMessage("You crush the coconut with a hammer."); - } - if (usedWith.getId() == 5976 && event.getUsedItem().getId() == 229 || usedWith.getId() == 229 && event.getUsedItem().getId() == 5976) { - player.getInventory().remove(ITEMS[1]); - player.getInventory().remove(ITEMS[2]); - player.getInventory().add(ITEMS[3]); - player.getInventory().add(ITEMS[4]); - player.getPacketDispatch().sendMessage("You overturn the coconut into a vial."); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/CookableItems.java b/Server/src/main/content/global/skill/cooking/CookableItems.java index 4ae79ffb7..4917db765 100644 --- a/Server/src/main/content/global/skill/cooking/CookableItems.java +++ b/Server/src/main/content/global/skill/cooking/CookableItems.java @@ -6,148 +6,168 @@ 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, 53, 78, 412, 78, 412), + BOWL_EGG(7078, 7076, 7090, 13, 50, 0, 0, 90, 438), + BOWL_ONION(7084, 1871, 7092, 43, 60, 36, 322, 36, 322), + BOWL_MUSHROOM(7082, 7080, 7094, 46, 60, 16, 282, 16, 282), + + // 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), + BARLEY(Items.BARLEY_6006, 0, Items.BARLEY_MALT_6008, 0, 0, 0, 0, 0, 0), + + /** + * Sinew gets overridden by BEEF in this enum, due to values being looked up by the items RAW id. + * 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 deleted file mode 100644 index 9ef36bcaf..000000000 --- a/Server/src/main/content/global/skill/cooking/CookingRecipePlugin.java +++ /dev/null @@ -1,132 +0,0 @@ -package content.global.skill.cooking; - -import content.global.skill.cooking.recipe.Recipe; -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.system.task.Pulse; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.dialogue.SkillDialogueHandler; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a cooking recipe plugin. This is used to handle the multiple - * recipes used in cooking these recipes can range from making a pizza or making - * a pie. - * @author 'Vexia - * @version 1.9 - */ -@Initializable -public final class CookingRecipePlugin extends UseWithHandler { - - /** - * Constructs a new {@code CookingRecipePlugin} {@code Object}. - */ - public CookingRecipePlugin() { - super(getAllowedNodes()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Recipe recipe : Recipe.RECIPES) { - for (Item ingredient : recipe.getIngredients()) { - addHandler(ingredient.getId(), ITEM_TYPE, this); - } - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Recipe recipe = null; - // TODO: Transitioning to a Listener would save an O(n) pass through the recipes list on every use-with - 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; - } - } - } - } 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()) { - if (k == i) {// represents that this ingredient can - // mix with the other. - recipe = temp; - break; - } - } - } - } - } - } - if (recipe != null) { - final Player player = event.getPlayer(); - final Recipe recipe_ = recipe; - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, recipe.getProduct()) { - @Override - public void create(final int amount, int index) { - player.getPulseManager().run(new Pulse(2) { - int count = 0; - @Override - public boolean pulse() { - recipe_.mix(player, event); - return ++count >= amount; - } - }); - } - - @Override - public int getAll(int index) { - return player.getInventory().getAmount(recipe_.getBase()); - } - }; - if (player.getInventory().getAmount(recipe.getBase()) == 1) { - recipe_.mix(player, event); - } else { - handler.open(); - } - return true; - } - return false; - } - - /** - * Method used to get the allowed nodes for this plugin. - * @return the allowed nodes. - */ - private final static int[] getAllowedNodes() { - List bases = new ArrayList<>(10); - for (Recipe recipe : Recipe.RECIPES) { - for (Item base : recipe.getParts()) { - if (bases.contains(base.getId())) { - continue; - } - bases.add(base.getId()); - } - if (bases.contains(recipe.getBase().getId())) { - continue; - } - bases.add(recipe.getBase().getId()); - } - int[] baseArray = new int[bases.size()]; - for (int i = 0; i < bases.size(); i++) { - baseArray[i] = bases.get(i); - } - return baseArray; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt index 5da124cb8..36679ef92 100644 --- a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt +++ b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt @@ -1,20 +1,27 @@ package content.global.skill.cooking +import content.region.misc.tutisland.handlers.TutorialStage +import core.api.* import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.node.scenery.Scenery +import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.Items.BREAD_DOUGH_2307 import org.rs09.consts.Items.RAW_BEAR_MEAT_2136 import org.rs09.consts.Items.RAW_BEEF_2132 import org.rs09.consts.Items.SEAWEED_401 import org.rs09.consts.Items.UNCOOKED_CAKE_1889 +import org.rs09.consts.Sounds /** * @author Ceikry * @author bushtail - added bear meat for sinew making + * @author Woah - added single food cooking */ class CookingRewrite : InteractionListener { @@ -25,13 +32,43 @@ class CookingRewrite : InteractionListener { list.add(Items.COOKED_MEAT_2142) list.add(RAW_BEEF_2132) list.add(RAW_BEAR_MEAT_2136) + list.add(Items.BARLEY_6006) list.add(SEAWEED_401) + list.add(Items.BOWL_OF_WATER_1921) RAW_FOODS = list.toIntArray() } override fun defineListeners() { - onUseWith(IntType.SCENERY,RAW_FOODS, *COOKING_OBJs){ player, used, with -> + if (!getAttribute(player, "/save:tutorial:complete", false)) { + // On tutorial island, we don't want to show the cook-x menu and we always want to burn our first shrimp + // This requirement of a simplified and predictable cooking system means that, for the second time, I have to reinvent the wheel of cooking + // (We only need to care about shrimp here, the tutorial island range is special and has its own separate listener) + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 0) { + val FIRE_ANIMATION = Animation(897, Animator.Priority.HIGH) + lock(player, FIRE_ANIMATION.duration) + lockInteractions(player, FIRE_ANIMATION.duration) + animate(player, FIRE_ANIMATION) + playAudio(player, Sounds.FRY_2577) + return@queueScript delayScript(player, FIRE_ANIMATION.duration) + } + val tutStage = getAttribute(player, "/save:tutorial:stage", 0) + if (tutStage < 15) { + replaceSlot(player, used.asItem().slot, Item(Items.BURNT_SHRIMP_7954), used.asItem()) + setAttribute(player, "tutorial:stage", 15) + TutorialStage.load(player, 15) + } else { + replaceSlot(player, used.asItem().slot, Item(Items.SHRIMPS_315), used.asItem()) + if (tutStage == 15) { + setAttribute(player, "tutorial:stage", 16) + TutorialStage.load(player, 16) + } + } + return@queueScript stopExecuting(player) + } + return@onUseWith true + } val item = used.asItem() val obj = with.asScenery() val range = obj.name.toLowerCase().contains("range") @@ -40,21 +77,35 @@ class CookingRewrite : InteractionListener { player.dialogueInterpreter.open(CookingDialogue(item.id,9436,true,obj,item.id)) return@onUseWith true } - BREAD_DOUGH_2307, UNCOOKED_CAKE_1889 -> if (!range) { + BREAD_DOUGH_2307, UNCOOKED_CAKE_1889, Items.BARLEY_6006 -> if (!range) { player.packetDispatch.sendMessage("You need to cook this on a range.") return@onUseWith false } + Items.BOWL_OF_WATER_1921 ->{ + cook(player, obj, Items.BOWL_OF_WATER_1921, Items.BOWL_OF_HOT_WATER_4456, 1) + return@onUseWith true + } } //cook a standard item - 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, 3769, 3775, 4265, 4266, 5249, 5499, 5631, 5632, 5981, 9682, 10433, 11404, 11405, 11406, 12102, 12796, 13337, 13881, 14169, 14919, 15156, 20000, 20001, 21620, 21792, 22713, 22714, 23046, 24283, 24284, 25155, 25156, 25465, 25730, 27297, 29139, 30017, 32099, 33500, 34495, 34546, 36973, 37597, 37629, 37726, 114, 4172, 5275, 8750, 16893, 22154, 34410, 34565, 114, 9085, 9086, 9087, 12269, 15398, 25440, 25441, 2724, 2725, 2726, 4618, 4650, 5165, 6093, 6094, 6095, 6096, 8712, 9374, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 18170, 21795, 24285, 24329, 27251, 33498, 35449, 36815, 36816, 37426, 40110, 10377) @JvmStatic fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) { diff --git a/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt index a60f89ff7..267d9ccfe 100644 --- a/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt +++ b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt @@ -1,5 +1,6 @@ package content.global.skill.cooking +import content.region.misc.tutisland.handlers.TutorialStage import core.api.* import core.game.event.ResourceProducedEvent import core.game.node.entity.skill.Skills @@ -24,7 +25,16 @@ class DoughMakingListener : InteractionListener { FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS.keys.toIntArray(), Items.POT_OF_FLOUR_1933 ) { player, waterContainer, flourContainer -> - openDialogue(player, DoughMakeDialogue(waterContainer.asItem(), flourContainer.asItem())) + if (getAttribute(player, "/save:tutorial:complete", false)) { + openDialogue(player, DoughMakeDialogue(waterContainer.asItem(), flourContainer.asItem())) + return@onUseWith true + } + // Continue the tutorial + replaceSlot(player, waterContainer.asItem().slot, Item(Items.BUCKET_1925), waterContainer.asItem()) + replaceSlot(player, flourContainer.asItem().slot, Item(Items.EMPTY_POT_1931), flourContainer.asItem()) + addItemOrDrop(player, Items.BREAD_DOUGH_2307) + setAttribute(player, "tutorial:stage", 20) + TutorialStage.load(player, 20) return@onUseWith true } } @@ -73,7 +83,7 @@ class DoughMakingListener : InteractionListener { sendMessage( player!!, - "You mix the flower and the water to make some ${selectedDoughProduct.itemName.toLowerCase()}." + "You mix the flour and the water to make some ${selectedDoughProduct.itemName.toLowerCase()}." ) } } else { diff --git a/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java b/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java index ffb0c23da..3e974f313 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 @@ -40,14 +43,14 @@ public class IntentionalBurnPulse extends StandardCookingPulse { @Override public boolean cook(Player player, Scenery object, boolean burned, int initial, int product) { - super.animate(); Item initialItem = new Item(initial); Item productItem = new Item(product); 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); + super.animate(); return true; } return false; diff --git a/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt b/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt new file mode 100644 index 000000000..ac1765428 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/NettleTeaListener.kt @@ -0,0 +1,18 @@ +package content.global.skill.cooking + +import org.rs09.consts.Items +import core.api.replaceSlot +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item + +class NettleTeaListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.EMPTY_CUP_1980, Items.NETTLE_TEA_4239) { player, used, with -> + replaceSlot(player, with.asItem().slot, Item(Items.BOWL_1923), with.asItem()) + replaceSlot(player, used.asItem().slot, Item(Items.CUP_OF_TEA_4242), used.asItem()) + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java b/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java deleted file mode 100644 index 161c99991..000000000 --- a/Server/src/main/content/global/skill/cooking/NettleTeaPlugin.java +++ /dev/null @@ -1,61 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to create nettle tea in a cup. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class NettleTeaPlugin extends UseWithHandler { - - /** - * Represents the empty cup item. - */ - private static final Item EMPTY_CUP = new Item(1980, 1); - - /** - * Represents the nettle tea item. - */ - private static final Item NETTLE_TEA = new Item(4239, 1); - - /** - * Represents the bowl item. - */ - private static final Item BOWL = new Item(1923); - - /** - * Represents the cup of tea item. - */ - private static final Item CUP_OF_TEA = new Item(4242, 1); - - /** - * Constructs a new {@code NettleTeaPlugin} {@code Object}. - */ - public NettleTeaPlugin() { - super(1980); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(4239, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().remove(EMPTY_CUP) && player.getInventory().remove(NETTLE_TEA)) { - player.getInventory().add(BOWL); - player.getInventory().add(CUP_OF_TEA); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java b/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java deleted file mode 100644 index e8dbeb385..000000000 --- a/Server/src/main/content/global/skill/cooking/NettleWaterPlugin.java +++ /dev/null @@ -1,35 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * @author Adam - */ -@Initializable -public class NettleWaterPlugin extends UseWithHandler { - - public NettleWaterPlugin() { - super(1921); - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - player.getInventory().remove(new Item(1921, 1)); - player.getInventory().remove(new Item(4241, 1)); - player.getInventory().add(new Item(4237, 1)); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(4241, ITEM_TYPE, this); - return this; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/PieShellPlugin.java b/Server/src/main/content/global/skill/cooking/PieShellPlugin.java deleted file mode 100644 index 4278836e8..000000000 --- a/Server/src/main/content/global/skill/cooking/PieShellPlugin.java +++ /dev/null @@ -1,56 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the pie shell making plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class PieShellPlugin extends UseWithHandler { - - /** - * Represents the pie sell item. - */ - private static final Item PIE_SHELL = new Item(2315, 1); - - /** - * Represents the pie dish item. - */ - private static final Item PIE_DISH = new Item(2313, 1); - - /** - * Represents the pastry dough item. - */ - private static final Item PASTRY_DOUGH = new Item(1953, 1); - - /** - * Constructs a new {@code PieMakingPlugin} {@code Object}. - */ - public PieShellPlugin() { - super(1953); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2313, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().remove(PASTRY_DOUGH, PIE_DISH)) { - player.getInventory().add(PIE_SHELL); - player.getPacketDispatch().sendMessage("You put the pastry dough into the pie dish to make a pie shell."); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/SkeweredFoodListener.kt b/Server/src/main/content/global/skill/cooking/SkeweredFoodListener.kt new file mode 100644 index 000000000..8465d2b88 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/SkeweredFoodListener.kt @@ -0,0 +1,38 @@ +package content.global.skill.cooking + +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 SkeweredFoodListener : InteractionListener { + val skewerMap = hashMapOf( + Items.RAW_CHOMPY_2876 to Items.SKEWERED_CHOMPY_7230, + Items.RAW_RABBIT_3226 to Items.SKEWERED_RABBIT_7224, + Items.RAW_BIRD_MEAT_9978 to Items.SKEWERED_BIRD_MEAT_9984, + Items.RAW_BEAST_MEAT_9986 to Items.SKEWERED_BEAST_9992 + ) + + override fun defineListeners() { + onUseWith(IntType.ITEM, skewerMap.keys.toIntArray(), Items.IRON_SPIT_7225) { player, used, with -> + val level = when (used.id) { + Items.RAW_BIRD_MEAT_9978 -> 11 + Items.RAW_RABBIT_3226 -> 16 + Items.RAW_BEAST_MEAT_9986 -> 21 + Items.RAW_CHOMPY_2876 -> 30 + else -> 0 + } + + if (getDynLevel(player, Skills.COOKING) < level) { + sendMessage(player, "You need a Cooking level of at least $level in order to do this.") + return@onUseWith true + } + if (removeItem(player, used.id) && removeItem(player, with.id)) { + skewerMap[used.id]?.let { addItem(player, it) } + return@onUseWith true + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java b/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java deleted file mode 100644 index d7be08283..000000000 --- a/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java +++ /dev/null @@ -1,111 +0,0 @@ -package content.global.skill.cooking; - -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 plugin used to make skwered items. - * @author 'Vexia - * @date 22/12/2013 - */ -@Initializable -public class SkeweredFoodPlugin extends UseWithHandler { - - /** - * Represents the level required. - */ - private final int LEVEL = 20; - - /** - * Constructs a new {@code SkeweredFoodPlugin} {@code Object}. - */ - public SkeweredFoodPlugin() { - super(7225); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (SkeweredSet set : SkeweredSet.values()) { - addHandler(set.getRaw().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getSkills().getLevel(Skills.FIREMAKING) < LEVEL) { - player.getPacketDispatch().sendMessage("You meed a Firemaking level of at least " + LEVEL + " in order to do this."); - return true; - } - final SkeweredSet set = SkeweredSet.forItem(event.getBaseItem().getId() == 7225 ? event.getUsedItem() : event.getBaseItem()); - if (player.getInventory().remove(event.getBaseItem()) && player.getInventory().remove(event.getUsedItem())) { - player.getInventory().add(set.getProduct()); - } - return true; - } - - /** - * Represents a set of skwered items. - * @author 'Vexia - * @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)); - - /** - * Represents the raw item. - */ - private final Item raw; - - /** - * Represents the product item. - */ - private final Item product; - - /** - * Constructs a new {@code SkeweredFoodPlugin} {@code Object}. - * @param raw the raw item. - * @param product the product. - */ - private SkeweredSet(Item raw, Item product) { - this.raw = raw; - this.product = product; - } - - /** - * Gets the raw. - * @return The raw. - */ - public Item getRaw() { - return raw; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the skwered set. - * @param item the item. - * @return the set. - */ - public static SkeweredSet forItem(final Item item) { - for (SkeweredSet set : values()) { - if (set.getRaw().getId() == item.getId()) { - return set; - } - } - return null; - } - } -} diff --git a/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java index 3ef44db7c..34dc67319 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,40 +121,41 @@ 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; } public boolean cook(final Player player, final Scenery object, final boolean burned, final int initial, final int product) { Item initialItem = new Item(initial); Item productItem = new Item(product); - animate(); //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 +176,18 @@ 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); + animate(); return true; } return false; @@ -180,14 +200,25 @@ public class StandardCookingPulse extends Pulse { if (product.getId() == Items.SODA_ASH_1781) { return "You burn the seaweed into soda ash."; } + if (product.getId() == Items.BARLEY_MALT_6008) { + return null; // Both live branches of Jagexscape lack a message for this and I can't find period evidence of a message + } if (CookableItems.intentionalBurn(food.getId())) { return "You deliberately burn the perfectly good piece of meat."; } - 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/TeaInteraction.kt b/Server/src/main/content/global/skill/cooking/TeaInteraction.kt new file mode 100644 index 000000000..5890ad372 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/TeaInteraction.kt @@ -0,0 +1,166 @@ +package content.global.skill.cooking + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items + +/** + * This deals with all tea mixtures + * cup of tea + * nettle tea + * nettle water + * nettle tea (cup) + * poh tea + * clay + * porcelain + * gold trim + * Flask + */ +class TeaInteraction : InteractionListener { + private val teaMilkMap = mapOf( + Items.NETTLE_TEA_4239 to Items.NETTLE_TEA_4240, + Items.CUP_OF_TEA_4242 to Items.CUP_OF_TEA_4243, + Items.CUP_OF_TEA_4245 to Items.CUP_OF_TEA_4246, + Items.CUP_OF_TEA_7730 to Items.CUP_OF_TEA_7731, + Items.CUP_OF_TEA_7733 to Items.CUP_OF_TEA_7734, + Items.CUP_OF_TEA_7736 to Items.CUP_OF_TEA_7737, + ) + + private val teaCupMap = mapOf( + Items.NETTLE_TEA_4239 to Items.CUP_OF_TEA_4242, + Items.NETTLE_TEA_4240 to Items.CUP_OF_TEA_4243, + Items.BOWL_OF_HOT_WATER_4456 to Items.CUP_OF_HOT_WATER_4460 + ) + + // This is specifically for the porcelain cup without a handle for Ghost Ahoy + private val teaCupPorcelainMap = mapOf( + Items.NETTLE_TEA_4239 to Items.CUP_OF_TEA_4245, + Items.NETTLE_TEA_4240 to Items.CUP_OF_TEA_4246 + ) + + private val emptyTeaPot = intArrayOf( + Items.TEAPOT_7702, + Items.TEAPOT_7714, + Items.TEAPOT_7726 + ) + + private val leafTeaPot = intArrayOf( + Items.TEAPOT_WITH_LEAVES_7700, + Items.TEAPOT_WITH_LEAVES_7712, + Items.TEAPOT_WITH_LEAVES_7724 + ) + + private val filledTeaPot = intArrayOf( + Items.POT_OF_TEA_4_7692, + Items.POT_OF_TEA_3_7694, + Items.POT_OF_TEA_2_7696, + Items.POT_OF_TEA_1_7698, + Items.POT_OF_TEA_4_7704, + Items.POT_OF_TEA_3_7706, + Items.POT_OF_TEA_2_7708, + Items.POT_OF_TEA_1_7710, + Items.POT_OF_TEA_4_7716, + Items.POT_OF_TEA_3_7718, + Items.POT_OF_TEA_2_7720, + Items.POT_OF_TEA_1_7722, + ) + + private val emptyBuilderCup = intArrayOf( + Items.EMPTY_CUP_7728, + Items.PORCELAIN_CUP_7732, + Items.PORCELAIN_CUP_7735 + ) + + override fun defineListeners() { + onUseWith(ITEM, teaMilkMap.keys.toIntArray(), Items.BUCKET_OF_MILK_1927){ player, used, with -> + if (removeItem(player, used) && removeItem(player, with)){ + addItem(player, teaMilkMap[used.id]!!) + addItem(player, Items.BUCKET_1925) + } + return@onUseWith true + } + + onUseWith(ITEM, teaCupMap.keys.toIntArray(), Items.EMPTY_CUP_1980){ player, used, with -> + if (used.id != Items.BOWL_OF_HOT_WATER_4456){ + if (getDynLevel(player, Skills.COOKING) < 20){ + sendDialogue(player, "You need a cooking level of 20 to make tea.") + return@onUseWith false + } + } + if (removeItem(player, used) && removeItem(player, with)) { + addItem(player, teaCupMap[used.id]!!) + addItem(player, Items.BOWL_1923) + } + return@onUseWith true + } + + onUseWith(ITEM, Items.BOWL_OF_WATER_1921, Items.NETTLES_4241) { player, used, with -> + if (removeItem(player, used) && removeItem(player, with)){ + addItem(player, Items.NETTLE_WATER_4237) + } + return@onUseWith true + } + + onUseWith(ITEM, teaCupPorcelainMap.keys.toIntArray(), Items.PORCELAIN_CUP_4244){player, used, with -> + if (getDynLevel(player, Skills.COOKING) < 20){ + sendDialogue(player, "You need a cooking level of 20 to make tea.") + return@onUseWith false + } + + if(removeItem(player, used) && removeItem(player, with)){ + addItem(player, teaCupPorcelainMap[used.id]!!) + addItem(player, Items.BOWL_1923) + } + return@onUseWith true + } + + onUseWith(ITEM, emptyTeaPot, Items.TEA_LEAVES_7738) { player, used, with -> + if (removeItem(player, used) && removeItem(player, with)){ + addItem(player, used.id - 2) + } + return@onUseWith true + } + + onUseWith(ITEM, leafTeaPot, Items.HOT_KETTLE_7691) { player, used, with -> + if (getDynLevel(player, Skills.COOKING) < 20){ + sendDialogue(player,"You need to be level 20 to make tea.") + return@onUseWith false + } + if (removeItem(player, used) && removeItem(player, with)){ + addItem(player, Items.KETTLE_7688) + addItem(player, used.id - 8) + rewardXP(player, Skills.COOKING, 53.0) + + } + return@onUseWith true + } + + onUseWith(ITEM, filledTeaPot, *emptyBuilderCup) { player, used, with -> + return@onUseWith fillBuildersTea(player, used, with) + } + } + + private fun fillBuildersTea(player: Player, used: Node, with: Node) : Boolean { + if (removeItem(player, used) && removeItem(player, with)){ + // Thanks tea pot with tea leaves + if (used.id + 4 in emptyTeaPot){ + addItem(player, used.id + 4) + } + else{ + addItem(player, used.id + 2) + } + // Why do these cups have a noted form! + if (with.id == Items.EMPTY_CUP_7728){ + addItem(player, with.id + 2) + } + else{ + addItem(player, with.id + 1) + } + return true + } + return false + } +} diff --git a/Server/src/main/content/global/skill/cooking/WatermelonSliceListener.kt b/Server/src/main/content/global/skill/cooking/WatermelonSliceListener.kt new file mode 100644 index 000000000..12f1ee22d --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/WatermelonSliceListener.kt @@ -0,0 +1,20 @@ +package content.global.skill.cooking + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class WatermelonSliceListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.WATERMELON_5982, Items.KNIFE_946) { player, used, _ -> + if(removeItem(player, used.asItem())) { + addItemOrDrop(player, Items.WATERMELON_SLICE_5984, 3) + sendMessage(player, "You slice the watermelon into three slices.") + return@onUseWith true + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/WatermelonSlicePlugin.java b/Server/src/main/content/global/skill/cooking/WatermelonSlicePlugin.java deleted file mode 100644 index d8273968f..000000000 --- a/Server/src/main/content/global/skill/cooking/WatermelonSlicePlugin.java +++ /dev/null @@ -1,59 +0,0 @@ -package content.global.skill.cooking; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to slice a watermelon. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WatermelonSlicePlugin extends UseWithHandler { - - /** - * Represents the knife item. - */ - private static final Item KNIFE = new Item(946); - - /** - * Represents the watermelon item. - */ - private static final Item WATERMELON = new Item(5982); - - /** - * Represents the watermelon slice item. - */ - private static final Item WATERMELON_SLICE = new Item(5984); - - /** - * Constructs a new {@code WatermelonSlicePlugin.java} {@code Object}. - */ - public WatermelonSlicePlugin() { - super(KNIFE.getId()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(5982, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - if (event.getPlayer().getInventory().remove(WATERMELON)) { - for (int i = 0; i < 3; i++) { - if (!event.getPlayer().getInventory().add(WATERMELON_SLICE)) { - GroundItemManager.create(WATERMELON_SLICE, event.getPlayer()); - } - } - event.getPlayer().getPacketDispatch().sendMessage("You slice the watermelon into three slices."); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/WineFermentListener.kt b/Server/src/main/content/global/skill/cooking/WineFermentListener.kt new file mode 100644 index 000000000..1c4c67b9d --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/WineFermentListener.kt @@ -0,0 +1,30 @@ +package content.global.skill.cooking + +import content.global.skill.cooking.fermenting.WineFermentingPulse +import core.api.addItem +import core.api.getDynLevel +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.world.GameWorld.Pulser +import org.rs09.consts.Items + +class WineFermentListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.GRAPES_1987, Items.JUG_OF_WATER_1937) { player, used, with -> + if (getDynLevel(player, Skills.COOKING) < 35) { + sendMessage(player, "You need a cooking level of 35 to do this.") + return@onUseWith true + } + if (removeItem(player, used.id) && removeItem(player, with.id)) { + addItem(player, Items.UNFERMENTED_WINE_1995) + Pulser.submit(WineFermentingPulse(1, player)) + return@onUseWith true + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/WineFermentPlugin.java b/Server/src/main/content/global/skill/cooking/WineFermentPlugin.java deleted file mode 100644 index b2e96678d..000000000 --- a/Server/src/main/content/global/skill/cooking/WineFermentPlugin.java +++ /dev/null @@ -1,63 +0,0 @@ -package content.global.skill.cooking; - -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.fermenting.WineFermentingPulse; -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.GameWorld; -import core.plugin.Plugin; - -/** - * Represents the plugin used to ferment wine. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WineFermentPlugin extends UseWithHandler { - - /** - * Represents the grapes item. - */ - private static final Item GRAPES = new Item(1987, 1); - - /** - * Represents the jug of water item. - */ - private static final Item JUG_OF_WATER = new Item(1937, 1); - - /** - * Represents the unfermented wine item. - */ - private static final Item UNFERMENTED_WINE = new Item(1995, 1); - - /** - * Constructs a new {@code WineFermentPlugin} {@code Object}. - */ - public WineFermentPlugin() { - super(1937); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(1987, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getSkills().getLevel(Skills.COOKING) < 35) { - player.getPacketDispatch().sendMessage("You need a cooking level of 35 to do this."); - return true; - } - if (player.getInventory().remove(GRAPES, JUG_OF_WATER)) { - player.getInventory().add(UNFERMENTED_WINE); - GameWorld.getPulser().submit(new WineFermentingPulse(1, player)); - } - return true; - } - -} 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/fermenting/AppleMushListener.kt b/Server/src/main/content/global/skill/cooking/fermenting/AppleMushListener.kt new file mode 100644 index 000000000..72ffeef3d --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/AppleMushListener.kt @@ -0,0 +1,187 @@ +package content.global.skill.cooking.fermenting + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.tools.ticksToCycles +import org.rs09.consts.Items +import org.rs09.consts.Scenery as SceneryIds + + +class AppleMushListener : InteractionListener { + + private data class BarrelLocations( + val start: Location, // Where the player begins the mushing animation from + val jump: Location, // Where the player ends the mushing animation at + val tap: Location, // Where the apple barrel's tap is located + val stand: Location // Where the player stands to collect from the tap + ) + + private val barrelLocations = mapOf( + 14747 to BarrelLocations( // Port Phasmatys + start = Location.create(3676, 9959, 0), + jump = Location.create(3676, 9957, 0), + tap = Location.create(3677, 9959, 0), + stand = Location.create(3678, 9959, 0) + ), + 11679 to BarrelLocations( // Keldagrim + start = Location.create(2914, 10193, 1), + jump = Location.create(2914, 10191, 1), + tap = Location.create(2915, 10193, 1), + stand = Location.create(2916, 10193, 1) + ), + 9780 to BarrelLocations( // Gnome Orchard + start = Location.create(2484, 3375, 0), + jump = Location.create(2484, 3373, 0), + tap = Location.create(2485, 3375, 0), + stand = Location.create(2486, 3375, 0) + ) + ) + + private val emptyAppleBarrels = intArrayOf( + SceneryIds.APPLE_BARREL_7403, + SceneryIds.APPLE_BARREL_8807 + ) + + private val fullAppleBarrels = intArrayOf( + SceneryIds.APPLE_BARREL_7404, + SceneryIds.APPLE_BARREL_8808 + ) + + private val appleTaps = intArrayOf( + SceneryIds.BARREL_TAP_7406, + SceneryIds.BARREL_TAP_8737 + ) + + private val MUSH_ANIM = Animation(2306) + + override fun defineListeners() { + + onUseWith(SCENERY, Items.COOKING_APPLE_1955, *emptyAppleBarrels) { player, _, with -> + if (getDynLevel(player, Skills.COOKING) < 14) { + sendDialogue(player, "You need a Cooking level of at least 14 in order to do this.") + return@onUseWith true + } + if (!inInventory(player, Items.COOKING_APPLE_1955, 4)) { + sendDialogue(player, "You need 4 cooking apples to make apple mush.") + return@onUseWith true + } + if (!inInventory(player, Items.BUCKET_1925)) { + sendDialogue(player, "You need a bucket to collect the apple mush.") + return@onUseWith true + } + if (inInventory(player, Items.COOKING_APPLE_1955, 4)) { + startMushing(player, with.asScenery()) + } + return@onUseWith true + } + + onUseWith(SCENERY, Items.BUCKET_1925, *appleTaps) { player, _, _ -> + if (getAttribute(player, "/save:cooking:brewing-apples", false)) { + if (removeItem(player, Item(Items.BUCKET_1925, 1))) { + addItem(player, Items.APPLE_MUSH_5992, 1) + sendMessage(player, "You fill a bucket with apple mush.") + } + removeAttribute(player, "/save:cooking:brewing-apples") + return@onUseWith true + } + sendMessage(player, "Nothing interesting happens.") + return@onUseWith false + } + } + + private fun startMushing(player: Player, barrel: Scenery) { + val initLoc = player.location + val barrelLoc = barrel.location + val locations = barrelLocations[player.location.regionId] + val startLoc = locations?.start + val jumpLoc = locations?.jump + val standLoc = locations?.stand + val tapLoc = locations?.tap + val fastStart: Boolean = initLoc == startLoc + + // https://www.youtube.com/watch?v=7I_Ec_tQ-tc + // Has to be soft or else you can log out and clog the barrel + queueScript(player, 1 ,QueueStrength.SOFT) { counter -> + when (counter) { + 0 -> { + stopWalk(player) + forceWalk(player, startLoc!!, "smart") + lock(player, MUSH_ANIM.duration + 7) + return@queueScript delayScript(player, if (fastStart) 1 else 4) + } + + 1 -> { + face(player, barrelLoc) + if (!removeItem(player, Item(Items.COOKING_APPLE_1955, 4))) { return@queueScript stopExecuting(player) } + setAttribute(player, "/save:cooking:brewing-apples", true) + return@queueScript delayScript(player, 0) + } + + 2 -> { + forceMove(player, startLoc!!, barrelLoc, 0, ticksToCycles(1), anim = MUSH_ANIM.id) + if (barrel.id == SceneryIds.APPLE_BARREL_7403) sendGraphics(414, barrelLoc) + else sendGraphics(413, barrelLoc) + swapBarrel(barrel) + return@queueScript delayScript(player, MUSH_ANIM.duration - 1) + } + + 3 -> { + forceMove(player, barrelLoc, jumpLoc!!, 0, ticksToCycles(1)) + return@queueScript delayScript(player, 2) + } + + 4 -> { + stopWalk(player) + return@queueScript delayScript(player, 1) + } + + 5 -> { + unlock(player) + stopWalk(player) + forceWalk(player, standLoc!!, "smart") + lock(player, 5) + return@queueScript delayScript(player, 4) + } + + 6 -> { + stopWalk(player) + face(player, tapLoc!!) + return@queueScript delayScript(player, 1) + } + + 7 -> { + if (removeItem(player, Item(Items.BUCKET_1925, 1))) { + addItem(player, Items.APPLE_MUSH_5992, 1) + sendMessage(player, "You fill a bucket with apple mush.") + } + removeAttribute(player, "/save:cooking:brewing-apples") + val currentBarrel = getScenery(barrelLoc) + currentBarrel?.let { swapBarrel(it) } + unlock(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + unlock(player) + } + + private fun swapBarrel(barrel: Scenery) { + val emptyIndex = emptyAppleBarrels.indexOf(barrel.id) + val fullIndex = fullAppleBarrels.indexOf(barrel.id) + val newId = when { + emptyIndex != -1 -> fullAppleBarrels[emptyIndex] + fullIndex != -1 -> emptyAppleBarrels[fullIndex] + else -> return + } + replaceScenery(barrel, newId, -1) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/fermenting/BrewGrowth.kt b/Server/src/main/content/global/skill/cooking/fermenting/BrewGrowth.kt new file mode 100644 index 000000000..a3baec569 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/BrewGrowth.kt @@ -0,0 +1,119 @@ +package content.global.skill.cooking.fermenting + +import core.api.log +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer +import core.tools.Log +import core.tools.secondsToTicks +import org.json.simple.JSONArray +import org.json.simple.JSONObject + +/** + * The timer for regular brewing operations + */ + +const val brewCycleTime = 6 /*hours*/ * 60 /*minutes per hour*/ * 100 /*ticks per minute*/ + +class BrewGrowth : PersistTimer(brewCycleTime, "cooking:brewing", isSoft = true) { + private val vatMap = HashMap() + lateinit var player: Player + + override fun onRegister(entity: Entity) { + player = (entity as? Player)!! + for (vat in vatMap.values.toList()){ + catchUp(vat.nextBrew) + } + } + + override fun save(root: JSONObject, entity: Entity) { + player = (entity as? Player)!! + val vats = JSONArray() + for ((key, vat) in vatMap.entries.toList()){ + val isBrewingStage = vat.stage in listOf( + BrewingStage.MIXED, + BrewingStage.BREWING1, + BrewingStage.BREWING2, + BrewingStage.DONE, + BrewingStage.MATURE, + BrewingStage.BAD + ) + + if (isBrewingStage) { + val v = JSONObject() + v["brew-ordinal"] = key + v["brew-stuff"] = vat.theStuff + v["brewing-nextBrew"] = vat.nextBrew + v["brewing-stage"] = vat.stage.ordinal + v["brewing-item"] = vat.brewingItem?.ordinal + vats.add(v) + } + } + root["brewing"] = vats + } + + override fun parse(root: JSONObject, entity: Entity) { + player = (entity as? Player)!! + val data = root["brewing"] as JSONArray + for (d in data){ + val v = d as JSONObject + val vatOrdinal = v["brew-ordinal"].toString().toInt() + val vatStuff = v["brew-stuff"].toString().toBoolean() + val vatNextBrew = v["brewing-nextBrew"].toString().toLong() + val vatStage = BrewingStage.values()[v["brewing-stage"].toString().toInt()] + val vatItem = v["brewing-item"]?.toString()?.toIntOrNull()?.let { + Brewable.values().getOrNull(it) + } + + if (vatMap[vatOrdinal] == null){ + val brewingVat = BrewingVat.values()[vatOrdinal] + val vat = Vat(player, brewingVat, vatStuff, vatNextBrew, vatStage, vatItem) + vatMap[vatOrdinal] = vat + vat.updateVat() + } + } + } + + fun getVat(brewingVat: BrewingVat, addVat: Boolean = true) : Vat { + return vatMap[brewingVat.ordinal] ?: (Vat(player, brewingVat).also { if (addVat) vatMap[brewingVat.ordinal] = it }) + } + + private fun catchUp(timeTillGrow: Long){ + if (timeTillGrow < System.currentTimeMillis()){ + val seconds = (System.currentTimeMillis() - timeTillGrow)/1000 + if (seconds > Int.MAX_VALUE){ + execute(1024) + } + else{ + val cyclesToGrow = secondsToTicks(seconds.toInt()) / brewCycleTime + execute(cyclesToGrow) + } + } + + } + + override fun run(entity: Entity): Boolean { + player = (entity as? Player)!! + return execute(0) + } + + private fun execute(count: Int) : Boolean { + for (vat in vatMap.entries.toList()){ + if (vat.value.canBrew()) + for (i in 0..count){ + val resp = vat.value.brew() + if (!resp){ + break + } + } + else { + vatMap.entries.removeIf { it.value.isVatEmpty() && it.value.isBarrelEmpty()} + } + } + return vatMap.isNotEmpty() + } + + fun getVats(): MutableCollection{ + return vatMap.values + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/fermenting/Brewable.kt b/Server/src/main/content/global/skill/cooking/fermenting/Brewable.kt new file mode 100644 index 000000000..9f6eab619 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/Brewable.kt @@ -0,0 +1,32 @@ +package content.global.skill.cooking.fermenting + +import org.rs09.consts.Items + + +enum class Brewable (val displayName: String, val itemID: Int, val level: Int, val product: IntArray, val levelXP: Double, val vatVarBitOffset: Int, + val barrelVarBitOffset: Int, val ingredientAmount: Int = 4){ + DWARVEN_STOUT("Dwarven Stout", Items.HAMMERSTONE_HOPS_5994, 19, intArrayOf(Items.DWARVEN_STOUT_1913, Items.DWARVEN_STOUT4_5777, Items.DWARVEN_STOUTM_5747, Items.DWARVEN_STOUTM4_5857), 215.0, 4, 8), + ASGARNIAN_ALE("Asgarnian Ale", Items.ASGARNIAN_HOPS_5996, 26, intArrayOf(Items.ASGARNIAN_ALE_1905, Items.ASGARNIAN_ALE4_5785, Items.ASGARNIAN_ALEM_5739, Items.ASGARNIAN_ALEM4_5865), 248.0, 10, 16), + GREENMANS_ALE("Greenman's Ale", Items.CLEAN_HARRALANDER_255, 29, intArrayOf(Items.GREENMANS_ALE_1909, Items.GREENMANS_ALE4_5793, Items.GREENMANS_ALEM_5743, Items.GREENMANS_ALEM4_5873), 281.0, 16, 24), + WIZARD_BOMB("Wizard's Mind Bomb", Items.YANILLIAN_HOPS_5998, 34, intArrayOf(Items.WIZARDS_MIND_BOMB_1907, Items.MIND_BOMB4_5801, Items.MATURE_WMB_5741, Items.MIND_BOMBM4_5881), 341.0, 22, 32), + DRAGON_BITTER("Dragon Bitter", Items.KRANDORIAN_HOPS_6000, 39, intArrayOf(Items.DRAGON_BITTER_1911, Items.DRAGON_BITTER4_5809, Items.DRAGON_BITTERM_5745, Items.DRAGON_BITTERM4_5889), 347.0, 28, 40), + MOONLIGHT_MEAD("Moonlight Mead", Items.MUSHROOM_6004, 44, intArrayOf(Items.MOONLIGHT_MEAD_2955, Items.MOONLIGHT_MEAD4_5817, Items.MOONLIGHT_MEADM_5749, Items.MLIGHT_MEADM4_5897), 380.0, 34, 48), + AXEMANS_FOLLY("Axeman's Folly", Items.OAK_ROOTS_6043, 49, intArrayOf(Items.AXEMANS_FOLLY_5751, Items.AXEMANS_FOLLY4_5825, Items.AXEMANS_FOLLYM_5753, Items.AXEMANS_FOLLYM4_5905), 413.0, 40, 56, 1), + CHEFS_DELIGHT("Chef's Delight", Items.CHOCOLATE_DUST_1975, 54, intArrayOf(Items.CHEFS_DELIGHT_5755, Items.CHEFS_DELIGHT4_5833, Items.CHEFS_DELIGHTM_5757, Items.CHEFS_DELIGHTM4_5913), 446.0, 46, 64), + SLAYERS_RESPITE("Slayer's Respite", Items.WILDBLOOD_HOPS_6002, 59, intArrayOf(Items.SLAYERS_RESPITE_5759, Items.SLAYERS_RESPITE4_5841, Items.SLAYERS_RESPITEM_5761, Items.SLAYER_RESPITEM4_5921), 479.0, 52, 72), + CIDER("cider", Items.APPLE_MUSH_5992, 14, intArrayOf(Items.CIDER_5763, Items.CIDER4_5849, Items.MATURE_CIDER_5765, Items.CIDERM4_5929), 182.0, 58, 80), + KELDA_STOUT("Kelda Stout", Items.KELDA_HOPS_6113, 22, intArrayOf(Items.KELDA_STOUT_6118), 0.0, 68, 3, 1); + + + companion object { + fun getIngredients() : IntArray{ + return values().map { it.itemID }.toIntArray() + } + + fun getBrewable(itemId: Int) : Brewable?{ + return values().find { it.itemID == itemId } + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/fermenting/BrewingListener.kt b/Server/src/main/content/global/skill/cooking/fermenting/BrewingListener.kt new file mode 100644 index 000000000..185550072 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/BrewingListener.kt @@ -0,0 +1,232 @@ +package content.global.skill.cooking.fermenting + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.Scenery + + +const val KELDAGRIM_LOC = 11679 +const val PORT_PHASMATYS_LOC = 14747 + +val varbits = mapOf( + PORT_PHASMATYS_LOC to BrewingVat.PORT_PHAS, + KELDAGRIM_LOC to BrewingVat.KELDAGRIM +) + +class BrewingListener : InteractionListener { + + private val aleIngredientsId = Brewable.getIngredients() + + private val vatsNeedingYeast = intArrayOf( + Scenery.FERMENTING_VAT_7444, // Hammerstone hops + Scenery.FERMENTING_VAT_7449, // Asgarnian hops + Scenery.FERMENTING_VAT_7454, // Harralander + Scenery.FERMENTING_VAT_7459, // Yanillian hops + Scenery.FERMENTING_VAT_7464, // Krandorian hops + Scenery.FERMENTING_VAT_7469, // Bittercap mushrooms + Scenery.FERMENTING_VAT_7474, // Oak roots + Scenery.FERMENTING_VAT_7479, // Chocolate dust + Scenery.FERMENTING_VAT_7484, // Wildblood hops + Scenery.FERMENTING_VAT_7489, // Apple mush + Scenery.FERMENTING_VAT_8871 // Kelda hops + ) + + // Const Lib is missing some of these + private val drainBarrels = intArrayOf( + Scenery.BARREL_7408, + Scenery.BARREL_7409, + Scenery.BARREL_7410, + + ) + private val barrels = intArrayOf( + Scenery.KELDA_STOUT_8870, + Scenery.DWARVEN_STOUT_7411, + Scenery.ASGARNIAN_ALE_7413, + Scenery.GREENMANS_ALE_7415, + Scenery.WIZARDS_MIND_BOMB_7417, + Scenery.DRAGON_BITTER_7419, + Scenery.MOONLIGHT_MEAD_7421, + Scenery.AXEMAN_S_FOLLY_7423, + Scenery.CHEF_S_DELIGHT_7425, + Scenery.SLAYER_S_RESPITE_7427, + Scenery.CIDER_7429, + Scenery.MATURE_DWARVEN_STOUT_7412, + Scenery.MATURE_ASGARNIAN_ALE_7414, + Scenery.MATURE_GREENMANS_ALE_7416, + Scenery.MATURE_WIZARDS_MIND_BOMB_7418, + Scenery.MATURE_DRAGON_BITTER_7420, + Scenery.MATURE_MOONLIGHT_MEAD_7422, + Scenery.MATURE_AXEMAN_S_FOLLY_7424, + Scenery.MATURE_CHEF_S_DELIGHT_7426, + Scenery.MATURE_SLAYER_S_RESPITE_7428, + Scenery.MATURE_CIDER_7430 + ) + + + override fun defineListeners() { + + // Fill an empty vat with water - self-contained operation + onUseWith(SCENERY, Items.BUCKET_OF_WATER_1929, Scenery.FERMENTING_VAT_7437) { player, _, _ -> + if (removeItem(player, Item(Items.BUCKET_OF_WATER_1929, 2))) { + queueScript(player, 0) { + addItem(player, Items.BUCKET_1925, 2) + + lock(player, Animation(2283).duration) + animate(player, Animation(2283)) + + sendMessage(player, "You add some water to the vat.") + + val vatVarbit = varbits.getValue(player.location.regionId).varbit + setVarbit(player, vatVarbit, 1, true) + return@queueScript (stopExecuting(player)) + } + } + else{ + sendDialogue(player, "You need 2 buckets of water.") + } + return@onUseWith true + } + + // Fill an empty vat with apple mush + onUseWith(SCENERY, Items.APPLE_MUSH_5992, Scenery.FERMENTING_VAT_7437) { player, used, _ -> + getVat(player).addIngredient(used as Item) + return@onUseWith true + } + + // Add barley malt to a vat with water - self-contained operation + onUseWith(SCENERY, Items.BARLEY_MALT_6008, Scenery.FERMENTING_VAT_7438) { player, _, _ -> + if (inInventory(player, Items.BARLEY_MALT_6008, 2)) { + queueScript(player, 0, QueueStrength.SOFT) { counter -> + when (counter) { + 0 -> { + if (!removeItem(player, Item(Items.BARLEY_MALT_6008, 1))) { return@queueScript stopExecuting(player) } + + lock(player, Animation(2295).duration * 2 + 2) + animate(player, Animation(2295)) + + sendMessage(player, "You add some barley malt to the vat.") + + return@queueScript delayScript(player, Animation(2295).duration + 1) + } + 1 -> { + if (!removeItem(player, Item(Items.BARLEY_MALT_6008, 1))) { return@queueScript stopExecuting(player) } + + stopWalk(player) + animate(player, Animation(2295)) + + val vatVarbit = varbits.getValue(player.location.regionId).varbit + setVarbit(player, vatVarbit, 2, true) + + return@queueScript delayScript(player, Animation(2295).duration + 1) + } + 2 -> { + stopWalk(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + else{ + sendDialogue(player, "You need 2 barley malt.") + } + return@onUseWith true + } + + // Add The Stuff to a vat with malt + onUseWith(SCENERY, Items.THE_STUFF_8988, Scenery.FERMENTING_VAT_7441) { player, _, _ -> + if (inInventory(player, Items.THE_STUFF_8988, 1)) { + getVat(player).addTheStuff() + } + return@onUseWith true + } + + // Add ingredients to a vat with malt + onUseWith(SCENERY, aleIngredientsId, Scenery.FERMENTING_VAT_7441) { player, used, _ -> + if (used.id == Items.APPLE_MUSH_5992) { + sendDialogue(player, "Apple mush needs to be added to an empty vat.") + return@onUseWith false + } + // If you try to make Kelda Stout in Port Phasmatys the vat deletes itself forever + if (used.id == Items.KELDA_HOPS_6113 && player.location.regionId != KELDAGRIM_LOC) { + sendMessage(player, "Nothing interesting happens.") + return@onUseWith false + } + // Branch off into Kelda Stout logic + if (used.id == Items.KELDA_HOPS_6113 && player.location.regionId == KELDAGRIM_LOC) { + KeldaBypasses.keldaAddHops(player) + return@onUseWith true + } + getVat(player).addIngredient(used as Item) + return@onUseWith true + } + + // Add yeast to a vat that's ready to brew + onUseWith(SCENERY, Items.ALE_YEAST_5767, *vatsNeedingYeast) { player, _, with -> + if (inInventory(player, Items.ALE_YEAST_5767, 1)) { + // Branch off into Kelda Stout logic + if (with.id == Scenery.FERMENTING_VAT_8871 && + player.location.regionId == KELDAGRIM_LOC && + getVarbit(player, 736) == 68) { // 68 = Kelda MIXED state + KeldaBypasses.keldaAddYeast(player) + return@onUseWith true + } + getVat(player).addYeast() + } + return@onUseWith true + } + + // Turn the valve to empty a vat into a barrel + on(intArrayOf(Scenery.VALVE_7442, Scenery.VALVE_7443), SCENERY, "turn") { player, _ -> + // Branch off into Kelda Stout logic + when { + (player.location.regionId == KELDAGRIM_LOC && KeldaBypasses.keldaIsBrewing(player)) -> + sendMessage(player, "The contents of the vat haven't finished fermenting yet.") + (player.location.regionId == KELDAGRIM_LOC && KeldaBypasses.keldaIsComplete(player)) -> + KeldaBypasses.keldaTurnValve(player) + else -> getVat(player).turnValve() + } + return@on true + } + + // Drain a barrel of bad ale + on(drainBarrels, SCENERY, "Drain") { player, _ -> + getVat(player).drainBarrel() + return@on true + } + + // Level a barrel (clicking on it) + on(barrels, SCENERY, "Level") { player, _ -> + queueScript(player, 1) { + val goAgain : Boolean + if (inInventory(player, Items.BEER_GLASS_1919)) { + goAgain = getVat(player).levelBarrel(Items.BEER_GLASS_1919) + } + else if (inInventory(player, Items.CALQUAT_KEG_5769)) { + goAgain = getVat(player).levelBarrel(Items.CALQUAT_KEG_5769) + } + else{ + goAgain = false + sendDialogue(player, "You need a container to level the barrel.") + } + return@queueScript if(goAgain) delayScript(player, 1) else stopExecuting(player) + } + return@on true + } + + // Level a barrel (using a container on it) + onUseWith(SCENERY, intArrayOf(Items.BEER_GLASS_1919, Items.CALQUAT_KEG_5769), *barrels) { player, used, _ -> + getVat(player).levelBarrel(used.id) + return@onUseWith true + } + } + + private fun getVat(player: Player) : Vat { + return varbits.getValue(player.location.regionId).getVat(player) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/fermenting/CalquatDecant.kt b/Server/src/main/content/global/skill/cooking/fermenting/CalquatDecant.kt new file mode 100644 index 000000000..c431bbd41 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/CalquatDecant.kt @@ -0,0 +1,217 @@ +package content.global.skill.cooking.fermenting + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Items + +object CalquatDecant { + + // Consumables.java hurts my eyes so I did this instead + data class CalquatKegs ( + val emptyKeg: Int = Items.CALQUAT_KEG_5769, + val fourDose: Int, val threeDose: Int = fourDose - 2, val twoDose: Int = threeDose - 2, val oneDose: Int = twoDose - 2, val glass: Int, + val fourMature: Int, val threeMature: Int = fourMature - 2, val twoMature: Int = threeMature - 2, val oneMature: Int = twoMature - 2, val matureGlass: Int + ) + + val calquatDrinks = + Brewable.values() + .filter { it.product.size == 4 } + .associateWith { CalquatKegs ( + fourDose = it.product[1], + glass = it.product[0], + fourMature = it.product[3], + matureGlass = it.product[2] + ) + } + + // Array of all kegs with nonzero contents + val allKegIds: IntArray by lazy { + calquatDrinks.values.flatMap { keg -> + listOf( + keg.fourDose, keg.threeDose, keg.twoDose, keg.oneDose, + keg.fourMature, keg.threeMature, keg.twoMature, keg.oneMature + ) + }.distinct().toIntArray() + } + + // Reverse lookup map + private val kegLookupMap: Map> by lazy { + calquatDrinks.entries.flatMap { (brewable, kegs) -> + listOf( + kegs.fourDose, kegs.threeDose, kegs.twoDose, kegs.oneDose, + kegs.fourMature, kegs.threeMature, kegs.twoMature, kegs.oneMature + ).map { itemId -> itemId to (brewable to kegs) } + }.toMap() + } + + // Check what a keg is so it can be related to other kegs + fun getKeg(itemId: Int): Pair? = kegLookupMap[itemId] + + // Check if an item is a keg + fun isKeg(itemId: Int): Boolean = kegLookupMap.containsKey(itemId) + + // Check how full a keg is + fun getDoseByKeg(itemId: Int, kegs: CalquatKegs): Int { + return when (itemId) { + kegs.fourDose, kegs.fourMature -> 4 + kegs.threeDose, kegs.threeMature -> 3 + kegs.twoDose, kegs.twoMature -> 2 + kegs.oneDose, kegs.oneMature -> 1 + else -> 0 + } + } + + // Check if a keg contains matured ale + fun isMature(itemId: Int, kegs: CalquatKegs): Boolean { + return itemId == kegs.fourMature || itemId == kegs.threeMature || + itemId == kegs.twoMature || itemId == kegs.oneMature + } + + // Identifies a keg's category by its contents + fun getKegByDose(kegs: CalquatKegs, dose: Int, mature: Boolean): Int { + return if (!mature) { + when (dose) { + 4 -> kegs.fourDose + 3 -> kegs.threeDose + 2 -> kegs.twoDose + 1 -> kegs.oneDose + else -> kegs.emptyKeg + } + } else { + when (dose) { + 4 -> kegs.fourMature + 3 -> kegs.threeMature + 2 -> kegs.twoMature + 1 -> kegs.oneMature + else -> kegs.emptyKeg + } + } + } + + // Decanting function (NPC) - should work the same as potions + fun decantCalquat(container: core.game.container.Container): Pair, List> { + val doseCount = HashMap, Int>() // (Brewable, isMature), Total Doses + val toRemove = ArrayList() + val toAdd = ArrayList() + + // Count up all doses grouped by keg type and maturity + for (item in container.toArray()) { + if (item == null) continue + val kegInfo = getKeg(item.id) ?: continue + val (brewable, kegs) = kegInfo + val dose = getDoseByKeg(item.id, kegs) + if (dose == 0) continue + val mature = isMature(item.id, kegs) + val key = brewable to mature + doseCount[key] = (doseCount[key] ?: 0) + dose + toRemove.add(item) + } + + // Convert doses back into kegs + for ((key, totalDoses) in doseCount) { + val (brewable, mature) = key + val kegs = calquatDrinks[brewable] ?: continue + + val fullKegs = totalDoses / 4 // Number of 4-dose kegs + val remainderDose = totalDoses % 4 // Leftover doses + + if (fullKegs > 0) + toAdd.add(Item(getKegByDose(kegs, 4, mature), fullKegs)) + if (remainderDose > 0) + toAdd.add(Item(getKegByDose(kegs, remainderDose, mature))) + } + + // Add empty kegs for removed containers + val emptyKegs = toRemove.size - toAdd.sumOf { it.amount } + if (emptyKegs > 0) { + val firstKegs = calquatDrinks.values.firstOrNull() + if (firstKegs != null) + toAdd.add(Item(firstKegs.emptyKeg, emptyKegs)) + } + + return Pair(toRemove, toAdd) + } + + // Decanting function (manual) - should work the same as potions + fun combineKegs(player: Player, used: Item, with: Item): Boolean { + // Identify both kegs + val (usedBrewable, kegs) = getKeg(used.id) ?: return false + val (withBrewable, _) = getKeg(with.id) ?: return false + + // Verify the same drink is in both kegs + if (usedBrewable != withBrewable) return false + + val usedMature = isMature(used.id, kegs) + val withMature = isMature(with.id, kegs) + + // Verify the same maturity status is in both kegs + if (usedMature != withMature) return false + + // Get doses + val usedDose = getDoseByKeg(used.id, kegs) + val withDose = getDoseByKeg(with.id, kegs) + + // Disallow combining full or empty kegs + if (usedDose == 4 || withDose == 4 || usedDose == 0 || withDose == 0) { + return false + } + + val totalDosage = usedDose + withDose + val fullDoses = totalDosage / 4 + val leftoverDose = totalDosage % 4 + + // Replace the targeted keg with a (4) dose keg + if (fullDoses != 0) { + replaceSlot(player, with.slot, Item(getKegByDose(kegs, 4, usedMature)), with, Container.INVENTORY) + } + + // Replace the targeted keg with the updated dosage amount + if (leftoverDose != 0 && fullDoses == 0) { + replaceSlot(player, with.slot, Item(getKegByDose(kegs, leftoverDose, usedMature)), with, Container.INVENTORY) + + // Replace the used with keg with the updated dosage amount + } else if (leftoverDose != 0) { + replaceSlot(player, used.slot, Item(getKegByDose(kegs, leftoverDose, usedMature)), used, Container.INVENTORY) + } + + // Replace the used with keg with an empty keg + if (leftoverDose == 0 || fullDoses == 0) { + replaceSlot(player, used.slot, Item(kegs.emptyKeg), used, Container.INVENTORY) + } + + // Send message/Play sound + val amountString = when { + totalDosage >= 4 -> "four" + totalDosage == 3 -> "three" + else -> "two" + } + + sendMessage(player, "You have combined the kegs into $amountString pints.") + playAudio(player, 2401) + + return true + } + + // Decant keg into beer glass + fun pourIntoGlass(player: Player, glass: Item, keg: Item): Boolean { + // Find keg info + val kegInfo = getKeg(keg.id) ?: return false + val (_, kegs) = kegInfo + val currentDose = getDoseByKeg(keg.id, kegs) + val mature = isMature(keg.id, kegs) + if (currentDose == 0) { + return false + } + + // Fill the glass + val glassItem = if (mature) kegs.matureGlass else kegs.glass + replaceSlot(player, glass.slot, Item(glassItem), glass, Container.INVENTORY) + + // Drain the keg + val newDose = currentDose - 1 + replaceSlot(player, keg.slot, Item(getKegByDose(kegs, newDose, mature)), keg, Container.INVENTORY) + + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/fermenting/KeldaStoutGrowth.kt b/Server/src/main/content/global/skill/cooking/fermenting/KeldaStoutGrowth.kt new file mode 100644 index 000000000..37d4de77e --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/KeldaStoutGrowth.kt @@ -0,0 +1,108 @@ +package content.global.skill.cooking.fermenting + +import core.api.getVarbit +import core.api.sendMessage +import core.game.node.entity.Entity +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer + +/** + * Independent brewing timer and functions exclusively for Kelda Stout + * Varbit progression: 68 (mixed) -> 69 (brewing1) -> 70 (brewing2) -> 71 (done) + */ + +class KeldaStoutGrowth : PersistTimer( 5 /*minutes*/ * 100 /*ticks per minute*/, "cooking:brewing-kelda", isSoft = true) { + lateinit var player: Player + + override fun run(entity: Entity): Boolean { + player = entity as Player + when (val currentVarbit = getVarbit(player, 736)) { + 69 -> { + setVarbit(player, 736, currentVarbit + 1) + return true + } + 70 -> { + setVarbit(player, 736, currentVarbit + 1) + sendMessage(player, "Perhaps I should have a look and see if my Kelda Stout has brewed...") + return false + } + else -> { + return false + } + } + } + + override fun getTimer(vararg args: Any): RSTimer { + return KeldaStoutGrowth() + } +} + + +object KeldaBypasses { + + fun keldaAddHops(player: Player) { + queueScript(player, 0) { + stopWalk(player) + if (!removeItem(player, Item(Items.KELDA_HOPS_6113, 1))) { return@queueScript stopExecuting(player) } + sendMessage(player, "You add some Kelda hops to the vat.") + lock(player, Animation(2295).duration + 1) + animate(player, Animation(2295)) + setVarbit(player, 736, getVarbit(player, 736) + 66) + return@queueScript stopExecuting(player) + } + } + + fun keldaAddYeast(player: Player) { + //addYeast() override + queueScript(player, 0) { + + if (!removeItem(player, Item(Items.ALE_YEAST_5767, 1))) { return@queueScript stopExecuting(player) } + addItem(player, Items.EMPTY_POT_1931) + + lock(player, Animation(2283).duration) + animate(player, Animation(2295)) + + sendMessage(player, "You add a pot of ale yeast to the vat.") + sendMessage(player, "The contents of the vat have begun to ferment.") + + setVarbit(player, 736, getVarbit(player, 736) + 1) + registerTimer(player, KeldaStoutGrowth()) + return@queueScript true + } + } + + fun keldaTurnValve(player: Player) { + queueScript(player, 0, QueueStrength.SOFT) { + + lock(player, Animation(780).duration) + animate(player, Animation(780)) + + sendMessage(player, "You turn the valve.") + + delayScript(player, 2) + + // Fill the barrel, then empty the vat + setVarbit(player, 738, 3, true) + setVarbit(player, 736, 0, true) + + sendMessage(player, "The barrel now contains 1 pint of Kelda Stout.") + return@queueScript stopExecuting(player) + } + } + + fun keldaIsBrewing(player: Player): Boolean { + val vatVarbit = getVarbit(player, 736) + return vatVarbit in 69..70 + } + + fun keldaIsComplete(player: Player): Boolean { + val vatVarbit = getVarbit(player, 736) + return vatVarbit == 71 + } +} diff --git a/Server/src/main/content/global/skill/cooking/fermenting/Vat.kt b/Server/src/main/content/global/skill/cooking/fermenting/Vat.kt new file mode 100644 index 000000000..e71f8850a --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/fermenting/Vat.kt @@ -0,0 +1,366 @@ +package content.global.skill.cooking.fermenting + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items + + +class Vat (var player: Player, private val brewingVat : BrewingVat, var theStuff: Boolean, + var nextBrew: Long, var stage: BrewingStage, + var brewingItem : Brewable?) { + + private var barrelItem: Brewable? = null + + constructor(player: Player, brewingVat: BrewingVat) : + this(player, brewingVat, false, 0, BrewingStage.EMPTY, null) + + fun addTheStuff() { + if (!theStuff && removeItem(player, Item(Items.THE_STUFF_8988, 1))) { + theStuff = true + + lock(player, Animation(2283).duration) + animate(player, Animation(2283)) + + sendMessage(player, "You add The Stuff to the mixture in the vat.") + + } else { sendMessage(player, "Nothing interesting happens.") } + } + + fun addIngredient(item: Item): Boolean { + val ingredient = Brewable.getBrewable(item.id) ?: return false + if (getDynLevel(player, Skills.COOKING) < ingredient.level) { + sendDialogue(player, "You need a Cooking level of at least ${ingredient.level} in order to brew ${ingredient.displayName}.") + return false + } + if (!inInventory(player, item.id, ingredient.ingredientAmount)) { + val ingredientWord = if (ingredient.itemID == Items.MUSHROOM_6004) item.name+"s" else item.name + sendDialogue(player, "You need 4 $ingredientWord.") + return false + } + queueScript(player, 0, QueueStrength.SOFT) { counter -> + when (counter) { + 0 -> { + if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) } + if (ingredient.itemID == Items.APPLE_MUSH_5992) { + addItem(player, Items.BUCKET_1925) + } + + stopWalk(player) + animate(player, Animation(2295)) + + val ingredientWord = if (item.id == Items.MUSHROOM_6004) item.name+"s" else item.name + sendMessage(player, "You add some $ingredientWord to the vat.") + + if (ingredient.ingredientAmount == 1) { + lock(player, Animation(2295).duration) + + brewingItem = ingredient + stage = BrewingStage.MIXED + updateVat() + + return@queueScript stopExecuting(player) + } + + lock(player, Animation(2295).duration * 4 + 3) + return@queueScript delayScript(player, Animation(2295).duration + 1) + } + 1, 2 -> { + if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) } + if (ingredient.itemID == Items.APPLE_MUSH_5992) { + addItem(player, Items.BUCKET_1925) + } + + stopWalk(player) + animate(player, Animation(2295)) + + return@queueScript delayScript(player, Animation(2295).duration + 1) + } + 3 -> { + if (!removeItem(player, Item(item.id, 1))) { return@queueScript stopExecuting(player) } + if (ingredient.itemID == Items.APPLE_MUSH_5992) { + addItem(player, Items.BUCKET_1925) + } + + stopWalk(player) + animate(player, Animation(2295)) + + brewingItem = ingredient + stage = BrewingStage.MIXED + updateVat() + + return@queueScript delayScript(player, Animation(2295).duration) + } + 4 -> { + stopWalk(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return true + } + + fun addYeast() { + if (removeItem(player, Item(Items.ALE_YEAST_5767, 1))) { + addItem(player, Items.EMPTY_POT_1931) + + lock(player, Animation(2283).duration) + animate(player, Animation(2295)) + + sendMessage(player, "You add a pot of ale yeast to the vat.") + sendMessage(player, "The contents of the vat have begun to ferment.") + + stage = BrewingStage.BREWING1 + nextBrew = System.currentTimeMillis() + (brewCycleTime * 600) // ticks to ms + updateVat() + } else { + sendMessage(player, "Nothing interesting happens.") + } + } + + fun brew(forceStep: Boolean = false, forceGood: Boolean = false, forceMature: Boolean = false, forceBad: Boolean = false) : Boolean { + val resp = internalBrew(forceStep, forceGood, forceMature, forceBad) + updateVat() + return resp + } + + fun canBrew() : Boolean { + return listOf(BrewingStage.BREWING1, BrewingStage.BREWING2).any { it == stage } + } + + /** The meat of the brewing mechanic lives in the following internalBrew function + * No one has clearly documented how this should work + * Period sources are vague, unaccountable, and/or vary wildly in their claims + * https://x.com/JagexAsh/status/994867369460813824 + * Ash states that it was challenging to follow when he was trying to do it in OSRS + * + * If a good source is uncovered, this section can easily be configured to match it + * + * Current config results in the following behavior: + * Cooking level does not play a factor + * Chance to advance a step (or go bad) - 20% + * Chance to go bad when advancing - 25% + * Chance to mature (w/o The Stuff) - 05% + * Chance to mature (w/ The Stuff) - 50% + * Time per cycle (in BrewGrowth.kt) - 6 hours, or 36000 ticks + */ + + private fun internalBrew(forceStep: Boolean, forceGood: Boolean, forceMature: Boolean, forceBad: Boolean):Boolean { + nextBrew = System.currentTimeMillis() + (brewCycleTime * 600) // Ticks to ms + if (listOf(BrewingStage.BREWING1, BrewingStage.BREWING2).all{ it != stage}) { + return false + } + + // Step advancement check + if (RandomFunction.random(5) == 0 || forceStep || forceGood || forceMature || forceBad) { + if ((RandomFunction.random(4) == 0 || forceBad) && !forceGood && !forceMature) { + stage = BrewingStage.BAD + return false + } + stage = if (stage == BrewingStage.BREWING1) BrewingStage.BREWING2 else BrewingStage.DONE + } + + // Maturity check + if (stage == BrewingStage.DONE) { + sendMessage(player, "Perhaps I should have a look and see if my ${brewingItem!!.displayName} has brewed...") + val roll = if (theStuff) 2 else 20 + if ((RandomFunction.random(roll) == 0 || forceMature) && !forceGood) { + stage = BrewingStage.MATURE + } + return false + } + return true + } + + fun turnValve(){ + if (stage == BrewingStage.EMPTY) { + queueScript(player, 1) { + sendMessage(player, "Nothing interesting happens.") + return@queueScript stopExecuting(player) + } + return + } + if (getVarbit(player,this.brewingVat.varbit + 2) > 0) { + queueScript(player, 1) { + sendMessage(player, "The barrel is already full.") + return@queueScript stopExecuting(player) + } + return + } + if (stage == BrewingStage.BREWING1 || stage == BrewingStage.BREWING2) { + queueScript(player,1) { + sendMessage(player, "The contents of the vat haven't finished fermenting yet.") + return@queueScript stopExecuting(player) + } + return + } + lock(player, Animation(780).duration) + animate(player, Animation(780)) + + sendMessage(player, "You turn the valve.") + + if (stage == BrewingStage.MATURE || stage == BrewingStage.DONE) { + rewardXP(player, Skills.COOKING, brewingItem!!.levelXP) + queueScript(player, 2, QueueStrength.SOFT) { + sendMessage(player, "The barrel now contains 8 pints of ${brewingItem!!.displayName}.") + fillBarrel() + emptyVat() + updateVat() + return@queueScript stopExecuting(player) + } + } else { + fillBarrel() + emptyVat() + updateVat() + } + } + + private fun emptyVat() { + brewingItem = null + theStuff = false + stage = BrewingStage.EMPTY + updateVat() + } + + private fun fillBarrel() { + var varbitValue = 0 + when (stage){ + BrewingStage.EMPTY -> Unit + BrewingStage.WATER, + BrewingStage.MALT, + BrewingStage.MIXED, + BrewingStage.BREWING1, + BrewingStage.BREWING2 -> { + varbitValue = 4 + } + BrewingStage.DONE, + BrewingStage.MATURE -> { + barrelItem = brewingItem + val barrelAmount = 8 + + varbitValue = barrelItem?.barrelVarBitOffset ?: 0 + + if (barrelItem != null){ + varbitValue += (barrelAmount - 1) + if (stage == BrewingStage.MATURE) { + varbitValue += 128 + } + } + } + BrewingStage.BAD ->{ + varbitValue = if(brewingItem == Brewable.CIDER) 2 else 1 + } + } + setVarbit(player, this.brewingVat.varbit + 2, varbitValue, true) + } + + fun levelBarrel(container: Int): Boolean { + var value = getVarbit(player, this.brewingVat.varbit + 2) + if (value == 0) return false + + // Check for mature bit (128) and strip it for calculations + val isMature = value >= 128 + val baseValue = if (isMature) value - 128 else value + var servingsLeft = if(baseValue == 3) 1 else (baseValue % 8) + 1 + + val brewable = + if (baseValue == 3) Brewable.KELDA_STOUT + else Brewable.values()[(baseValue/8) - 1] + + val product = brewable.product + + if (container == Items.BEER_GLASS_1919) { + if(removeItem(player, container)) { + animate(player, Animation(2285)) + value-- + servingsLeft-- + val productIndex = if (isMature) 2 else 0 + sendMessage(player, "You pour a glass of ${brewable.displayName}.") + addItem(player, product[productIndex]) + } + } + else if (brewable == Brewable.KELDA_STOUT) { + sendDialogue(player, "You must use a glass to level Kelda Stout.") + return false + } + else if (container == Items.CALQUAT_KEG_5769) { + if(removeItem(player, container)){ + animate(player, Animation(2284)) + val servingsToTake = minOf(servingsLeft, 4) + value -= servingsToTake + servingsLeft -= servingsToTake + val filledKeg = CalquatDecant.calquatDrinks[brewable]!! + addItem(player, CalquatDecant.getKegByDose(filledKeg, servingsToTake, isMature)) + } + } + if (servingsLeft == 0) { + sendMessage(player, "The barrel is now empty.") + value = 0 + } + setVarbit(player, this.brewingVat.varbit + 2, value, true) + return servingsLeft != 0 + } + + fun drainBarrel() { + sendMessage(player, "You drain the barrel.") + setVarbit(player, this.brewingVat.varbit + 2, 0, true) + + } + + fun isVatEmpty(): Boolean { + return stage == BrewingStage.EMPTY + } + + fun isBarrelEmpty(): Boolean { + return getVarbit(player, (this.brewingVat.varbit + 2)) == 0 + } + + private fun getVatDisplay(): Int { + return when (stage){ + BrewingStage.EMPTY -> 0 + BrewingStage.WATER -> 1 + BrewingStage.MALT -> 2 + BrewingStage.MIXED -> brewingItem?.vatVarBitOffset ?: run { 0 } + BrewingStage.BREWING1 -> (brewingItem?.vatVarBitOffset?.plus(1)) ?: run { 0 } + BrewingStage.BREWING2 -> (brewingItem?.vatVarBitOffset?.plus(2)) ?: run { 0 } + BrewingStage.DONE -> (brewingItem?.vatVarBitOffset?.plus(3)) ?: run { 0 } + BrewingStage.MATURE -> (brewingItem?.vatVarBitOffset?.plus(4)) ?: run { 0 } + BrewingStage.BAD -> if (brewingItem == Brewable.CIDER) 65 else 64 + } + } + + fun updateVat(){ + setVarbit(player, this.brewingVat.varbit, this.getVatDisplay(), true) + } + +} + + +enum class BrewingStage { + EMPTY, + WATER, + MALT, + MIXED, + BREWING1, + BREWING2, + DONE, + MATURE, + BAD; +} + + +enum class BrewingVat(val varbit: Int) { + KELDAGRIM(736), + PORT_PHAS(737); + + fun getVat(player: Player): Vat { + val vat = getOrStartTimer(player) + return vat.getVat(this, true) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/ChocolateCakeListener.kt b/Server/src/main/content/global/skill/cooking/recipe/ChocolateCakeListener.kt new file mode 100644 index 000000000..0661c9417 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/ChocolateCakeListener.kt @@ -0,0 +1,31 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class ChocolateCakeListener : InteractionListener { + + val chocolate = intArrayOf(Items.CHOCOLATE_BAR_1973, Items.CHOCOLATE_DUST_1975) + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.CAKE_1891, *chocolate) { player, used, with -> + val product = Items.CHOCOLATE_CAKE_1897 + val level = 50 + val experience = 30.0 + val message = "" + val failMessage = "You need a Cooking level of $level in order to do that." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/CurryListener.kt b/Server/src/main/content/global/skill/cooking/recipe/CurryListener.kt new file mode 100644 index 000000000..b6be43bb4 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/CurryListener.kt @@ -0,0 +1,37 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import org.rs09.consts.Items + +class CurryListener : InteractionListener { + + val added = intArrayOf(Items.CURRY_LEAF_5970, Items.SPICE_2007) + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.UNCOOKED_STEW_2001, *added) { player, used, with -> + val product = Items.UNCOOKED_CURRY_2009 + val amountAdded = when (with.id) { + Items.CURRY_LEAF_5970 -> 3 + Items.SPICE_2007 -> 1 + else -> 0 + } + val level = 60 + val experience = 0.0 + val message = "You mix the spice with the stew." + val failMessage = "You need a Cooking level of at least $level in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + Item(with.id, amountAdded), + product, + level, + experience, + message, + failMessage + ) + } + } +} \ No newline at end of file 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/MixHandler.kt b/Server/src/main/content/global/skill/cooking/recipe/MixHandler.kt new file mode 100644 index 000000000..aa3b8bc59 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/MixHandler.kt @@ -0,0 +1,72 @@ +package content.global.skill.cooking.recipe + +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +fun standardMix(player: Player, used: Item, with: Item, product: Int, level: Int, experience: Double, message: String, failMessage: String): Boolean { + // Avoids sending dialogue if not enough ingredients to create (relevant only for curry leaves) + if (amountInInventory(player, used.id) < used.amount || amountInInventory(player, with.id) < with.amount) { + // Not sure about the authentic message + sendMessage(player, "You don't have enough ingredients to make that.") + return true + } + + // Avoids sending dialogue if only one can be created + if (amountInInventory(player, used.id) == used.amount || amountInInventory(player, with.id) == with.amount) { + if (getDynLevel(player, Skills.COOKING) < level) { + sendDialogue(player, failMessage) + return true + } + + if (removeItem(player, used) && removeItem(player, with)) { + sendMessage(player, message) + rewardXP(player, Skills.COOKING, experience) + addItem(player, product) + containerIngredient(player, used.id) + containerIngredient(player, with.id) + } + return true + } + + mixHandler( + player, + used, + with, + product, + level, + experience, + message, + failMessage + ).open() + return true +} + +private fun mixHandler(player: Player, used: Item, with: Item, product: Int, level: Int, experience: Double, message: String, failMessage: String): SkillDialogueHandler { + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, product.asItem()) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + MixScript(player, used, with, product, amount, level, experience, message, failMessage).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.COOKING) < level) { + sendDialogue(player, failMessage) + return false + } + return true + } + + override fun getAll(index: Int): Int { + return min( + amountInInventory(player, used.id)/used.amount, + amountInInventory(player, with.id)/with.amount + ) + } + } + return handler +} diff --git a/Server/src/main/content/global/skill/cooking/recipe/MixScript.kt b/Server/src/main/content/global/skill/cooking/recipe/MixScript.kt new file mode 100644 index 000000000..245c078af --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/MixScript.kt @@ -0,0 +1,57 @@ +package content.global.skill.cooking.recipe + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items + +class MixScript( + private val player: Player, + private val used: Item, + private val with: Item, + private val product: Int, + private val sets: Int, + private val level: Int, + private val experience: Double, + private val message: String, + private val failMessage: String +) { + + private val delay = 2 + + fun invoke() { + queueScript(player, delay) { stage -> Int + if (getDynLevel(player, Skills.COOKING) < level) { + sendDialogue(player, failMessage) + return@queueScript stopExecuting(player) + } + + if (inInventory(player, used.id, used.amount) && inInventory(player, with.id, with.amount)) { + if(removeItem(player, used) && removeItem(player, with)) { + sendMessage(player, message) + rewardXP(player, Skills.COOKING, experience) + addItem(player, product) + containerIngredient(player, used.id) + containerIngredient(player, with.id) + } + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayScript(player, delay) + } + } +} + +fun containerIngredient(player: Player, ingredient: Int): Boolean{ + return when (ingredient) { + Items.BUCKET_OF_WATER_1929, Items.COMPOST_6032 -> addItem(player, Items.BUCKET_1925) + Items.FRIED_MUSHROOMS_7082 -> addItem(player, Items.BOWL_1923) + else -> false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/OomlieWrap.java b/Server/src/main/content/global/skill/cooking/recipe/OomlieWrap.java deleted file mode 100644 index d5e8ee5f7..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/OomlieWrap.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.global.skill.cooking.recipe; - -import org.rs09.consts.Items; -import core.game.node.entity.skill.Skills; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * @author afaroutdude - */ -public class OomlieWrap extends Recipe { - - private static final Item OOMLIE_WRAP = new Item(Items.WRAPPED_OOMLIE_2341); - private static final Item RAW_OOMLIE = new Item(Items.RAW_OOMLIE_2337); - private static final Item PALM_LEAF = new Item(Items.PALM_LEAF_2339); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 50) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of 50 in order to do that."); - return; - } - super.mix(player, event); - } - - @Override - public Item getBase() { - return RAW_OOMLIE; - } - - @Override - public Item getProduct() { - return OOMLIE_WRAP; - } - - @Override - public Item[] getIngredients() { - return new Item[] { PALM_LEAF }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You wrap the raw oomlie in the palm leaf."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/OomlieWrapListener.kt b/Server/src/main/content/global/skill/cooking/recipe/OomlieWrapListener.kt new file mode 100644 index 000000000..79024be71 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/OomlieWrapListener.kt @@ -0,0 +1,29 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class OomlieWrapListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.RAW_OOMLIE_2337, Items.PALM_LEAF_2339) { player, used, with -> + val product = Items.WRAPPED_OOMLIE_2341 + val level = 50 + val experience = 0.0 + val message = "You wrap the raw oomlie in the palm leaf." + val failMessage = "You need a Cooking level of at least $level in order to do that." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/PieListener.kt b/Server/src/main/content/global/skill/cooking/recipe/PieListener.kt new file mode 100644 index 000000000..8f6b1a841 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/PieListener.kt @@ -0,0 +1,79 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class PieListener : InteractionListener { + + val base = PieProduct.values().map { it.base }.toIntArray() + val added = PieProduct.values().map { it.added }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, base, *added) { player, used, with -> + val pie = PieProduct.productMap[used.id] ?: return@onUseWith true + + if (pie.added != with.id) { + return@onUseWith false + } + + val product = pie.product + val level = pie.minimumLevel + val experience = 0.0 + val message = "You fill the pie with ${pie.ingredientMessage}." + val failMessage = "You need a Cooking level of ${pie.minimumLevel} to make this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } + + enum class PieProduct( + val base: Int, + val added: Int, + val product: Int, + val minimumLevel: Int, + val ingredientMessage: String, + ) { + REDBERRY_PIE(Items.REDBERRIES_1951, Items.PIE_SHELL_2315, Items.UNCOOKED_BERRY_PIE_2321, 10, "redberries"), + MEAT_PIE(Items.COOKED_MEAT_2142, Items.PIE_SHELL_2315, Items.UNCOOKED_MEAT_PIE_2319, 20, "meat"), + MUD_PIE_1(Items.COMPOST_6032, Items.PIE_SHELL_2315, Items.PART_MUD_PIE_7164, 29, "compost"), + MUD_PIE_2(Items.PART_MUD_PIE_7164, Items.BUCKET_OF_WATER_1929, Items.PART_MUD_PIE_7166, 29, "water"), + MUD_PIE_3(Items.PART_MUD_PIE_7166, Items.CLAY_434, Items.RAW_MUD_PIE_7168, 29, "clay"), + APPLE_PIE(Items.COOKING_APPLE_1955, Items.PIE_SHELL_2315, Items.UNCOOKED_APPLE_PIE_2317, 30, "cooking apple"), + GARDEN_PIE_1(Items.TOMATO_1982, Items.PIE_SHELL_2315, Items.PART_GARDEN_PIE_7172, 34, "tomato"), + GARDEN_PIE_2(Items.PART_GARDEN_PIE_7172, Items.ONION_1957, Items.PART_GARDEN_PIE_7174, 34, "onion"), + GARDEN_PIE_3(Items.PART_GARDEN_PIE_7174, Items.CABBAGE_1965, Items.RAW_GARDEN_PIE_7176, 34, "cabbage"), + FISH_PIE_1(Items.TROUT_333, Items.PIE_SHELL_2315, Items.PART_FISH_PIE_7182, 47, "trout"), + FISH_PIE_2(Items.PART_FISH_PIE_7182, Items.COD_339, Items.PART_FISH_PIE_7184, 47, "cod"), + FISH_PIE_3(Items.PART_FISH_PIE_7184, Items.POTATO_1942, Items.RAW_FISH_PIE_7186, 47, "potato"), + ADMIRAL_PIE_1(Items.SALMON_329, Items.PIE_SHELL_2315, Items.PART_ADMIRAL_PIE_7192, 70, "salmon"), + ADMIRAL_PIE_2(Items.PART_ADMIRAL_PIE_7192, Items.TUNA_361, Items.PART_ADMIRAL_PIE_7194, 70, "tuna"), + ADMIRAL_PIE_3(Items.PART_ADMIRAL_PIE_7194, Items.POTATO_1942, Items.RAW_ADMIRAL_PIE_7196, 70, "potato"), + WILD_PIE_1(Items.RAW_BEAR_MEAT_2136, Items.PIE_SHELL_2315, Items.PART_WILD_PIE_7202, 85, "raw bear meat"), + WILD_PIE_2(Items.PART_WILD_PIE_7202, Items.RAW_CHOMPY_2876, Items.PART_WILD_PIE_7204, 85, "raw chompy"), + WILD_PIE_3(Items.PART_WILD_PIE_7204, Items.RAW_RABBIT_3226, Items.RAW_WILD_PIE_7206, 85, "raw rabbit"), + SUMMER_PIE_1(Items.STRAWBERRY_5504, Items.PIE_SHELL_2315, Items.PART_SUMMER_PIE_7212, 95, "strawberry"), + SUMMER_PIE_2(Items.PART_SUMMER_PIE_7212, Items.WATERMELON_5982, Items.PART_SUMMER_PIE_7214, 95, "watermelon"), + SUMMER_PIE_3(Items.PART_SUMMER_PIE_7214, Items.COOKING_APPLE_1955, Items.RAW_SUMMER_PIE_7216, 95, "apple"), + ; + + companion object { + val productMap = HashMap() + + init { + for (pie in PieProduct.values()) { + productMap[pie.base] = pie + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/PieShellListener.kt b/Server/src/main/content/global/skill/cooking/recipe/PieShellListener.kt new file mode 100644 index 000000000..b0e075575 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/PieShellListener.kt @@ -0,0 +1,29 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class PieShellListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.PIE_DISH_2313, Items.PASTRY_DOUGH_1953) { player, used, with -> + val product = Items.PIE_SHELL_2315 + val level = 1 + val experience = 0.0 + val message = "You put the pastry dough into the pie dish to make a pie shell." + val failMessage = "" + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/PizzaListener.kt b/Server/src/main/content/global/skill/cooking/recipe/PizzaListener.kt new file mode 100644 index 000000000..f70c32f35 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/PizzaListener.kt @@ -0,0 +1,66 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class PizzaListener : InteractionListener { + + val base = PizzaProduct.values().map { it.base }.toIntArray() + val added = PizzaProduct.values().map { it.added }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, base, *added) { player, used, with -> + val pizza = PizzaProduct.productMap[used.id] ?: return@onUseWith true + + if (pizza.added != with.id) { + return@onUseWith false + } + + val product = pizza.product + val level = pizza.minimumLevel + val experience = pizza.experience + val message = "You add the ${pizza.message} to the pizza." + val failMessage = "You need a Cooking level of at least ${pizza.minimumLevel} in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } + + enum class PizzaProduct( + val base: Int, + val added: Int, + val product: Int, + val minimumLevel: Int, + val experience: Double, + val message: String, + ) { + PIZZA_1(Items.TOMATO_1982, Items.PIZZA_BASE_2283, Items.INCOMPLETE_PIZZA_2285, 35, 0.0,"tomato"), + PIZZA_2(Items.CHEESE_1985, Items.INCOMPLETE_PIZZA_2285, Items.UNCOOKED_PIZZA_2287, 35, 0.0, "cheese"), + MEAT_PIZZA_1(Items.COOKED_MEAT_2142, Items.PLAIN_PIZZA_2289, Items.MEAT_PIZZA_2293, 45, 26.0, "meat"), + MEAT_PIZZA_2(Items.COOKED_CHICKEN_2140, Items.PLAIN_PIZZA_2289, Items.MEAT_PIZZA_2293, 45, 26.0, "cooked chicken"), + ANCHOVY_PIZZA(Items.ANCHOVIES_319, Items.PLAIN_PIZZA_2289, Items.ANCHOVY_PIZZA_2297, 55, 39.0, "anchovies"), + PINEAPPLE_PIZZA_1(Items.PINEAPPLE_CHUNKS_2116, Items.PLAIN_PIZZA_2289, Items.PINEAPPLE_PIZZA_2301, 65, 52.0, "pineapple"), + PINEAPPLE_PIZZA_2(Items.PINEAPPLE_RING_2118, Items.PLAIN_PIZZA_2289, Items.PINEAPPLE_PIZZA_2301, 65, 52.0, "pineapple"), + ; + + companion object { + val productMap = HashMap() + + init { + for (pizza in PizzaProduct.values()) { + productMap[pizza.base] = pizza + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/PotatoListener.kt b/Server/src/main/content/global/skill/cooking/recipe/PotatoListener.kt new file mode 100644 index 000000000..6e2cd5b5c --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/PotatoListener.kt @@ -0,0 +1,77 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class PotatoListener : InteractionListener { + + val added = PotatoProduct.values().map { it.added }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.BAKED_POTATO_6701, Items.PAT_OF_BUTTER_6697) { player, used, with -> + val product = Items.POTATO_WITH_BUTTER_6703 + val level = 39 + val experience = 40.5 + val message = "You add a pat of butter to the potato." + val failMessage = "You need a Cooking level of at least $level in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + + onUseWith(IntType.ITEM, added, Items.POTATO_WITH_BUTTER_6703) { player, used, with -> + val topping = PotatoProduct.productMap[used.id] ?: return@onUseWith true + + val product = topping.product + val level = topping.minimumLevel + val experience = topping.experience + val message = "" + val failMessage = "You need a Cooking level of at least $level in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } + + enum class PotatoProduct( + val added: Int, + val product: Int, + val minimumLevel: Int, + val experience: Double, + val message: String + ) { + CHEESE_POTATO(Items.CHEESE_1985, Items.POTATO_WITH_CHEESE_6705, 47, 10.0, ""), + CHILLI_POTATO(Items.CHILLI_CON_CARNE_7062, Items.CHILLI_POTATO_7054, 41, 15.0, ""), + EGG_POTATO(Items.EGG_AND_TOMATO_7064, Items.EGG_POTATO_7056, 51, 50.0, ""), + TUNA_POTATO(Items.TUNA_AND_CORN_7068, Items.TUNA_POTATO_7060, 68, 10.0, ""), + MUSHROOM_POTATO(Items.MUSHROOM_AND_ONION_7066, Items.MUSHROOM_POTATO_7058, 64, 55.0, ""), + ; + + companion object { + val productMap = HashMap() + + init { + for (topping in PotatoProduct.values()) { + productMap[topping.added] = topping + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/Recipe.java b/Server/src/main/content/global/skill/cooking/recipe/Recipe.java deleted file mode 100644 index 3d41926c4..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/Recipe.java +++ /dev/null @@ -1,131 +0,0 @@ -package content.global.skill.cooking.recipe; - -import content.global.skill.cooking.recipe.pie.impl.*; -import content.global.skill.cooking.recipe.pizza.impl.AnchovyPizza; -import content.global.skill.cooking.recipe.pizza.impl.MeatPizza; -import content.global.skill.cooking.recipe.pizza.impl.PineapplePizza; -import content.global.skill.cooking.recipe.pizza.impl.PlainPizza; -import content.global.skill.cooking.recipe.potato.impl.*; -import content.global.skill.cooking.recipe.stew.CurryRecipe; -import content.global.skill.cooking.recipe.stew.StewRecipe; -import content.global.skill.cooking.recipe.topping.impl.*; -import content.global.skill.cooking.recipe.cake.ChocolateCake; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents a cooking recipe, this is dynamic that can range from a pie to a - * pizza. - * @author 'Vexia - * @date 21/12/2013 - */ -public abstract class Recipe { - - /** - * Represents the array of active recipes in Keldagrim.. - */ - // TODO: - // - Making this an enum would drastically save on file/line count, since the recipes seem to mostly be plain-old-data classes - // - Making pie shells a recipe would make make-x for them just work - // - Making pineapple cutting a recipe would probably fix their make-x making all with any option - public static final Recipe[] RECIPES = new Recipe[] { - new RedberryPie(), new MeatPie(), new ApplePie(), new MudPie(), new GardenPie(), new FishPie(), new AdmiralPie(), new WildPie(), new SummerPie(), - new StewRecipe(), new CurryRecipe(), - new PlainPizza(), new MeatPizza(), new AnchovyPizza(), new PineapplePizza(), - new ChocolateCake(), - new ButterPotato(), new ChilliPotato(), new CheesePotato(), new EggPotato(), new MushroomPotato(), new TunaPotato(), - new SpicySauce(), new ChilliConCarne(), new UncookedEgg(), new EggAndTomato(), new MushroomAndOnion(), new ChoppedOnion(), new SlicedMushroom(), new ChoppedTuna(), new TunaAndCorn(), new OomlieWrap() - }; - - /** - * Method used to get the base item. - * @return the item. - */ - public abstract Item getBase(); - - /** - * Method used to get the product item. - * @return the product item. - */ - public abstract Item getProduct(); - - /** - * Method used to get the ingredients in this recipe. - * @return the ingredients. - */ - public abstract Item[] getIngredients(); - - /** - * Method used to get the part items made from ingredients. - * @return the part items. - */ - public abstract Item[] getParts(); - - /** - * Method used to get the mixing message. - * @param event the node usage event. - * @return the message used to mix. - */ - public abstract String getMixMessage(final NodeUsageEvent event); - - /** - * Method used to check if this is a singular one step recipe. - * @return True if so. - */ - public abstract boolean isSingular(); - - /** - * Method used to mix this recipes ingredients. - * @param player the player. - * @param event the event. - */ - public void mix(final Player player, final NodeUsageEvent event) { - if (getIngredients().length == 1) { - singleMix(player, event); - } else { - multipleMix(player, event); - } - } - - /** - * Method used to handle a single mixing. - * @param player the player. - * @param event the event. - */ - public void singleMix(final Player player, final NodeUsageEvent event) { - if (player.getInventory().remove(event.getBaseItem()) && player.getInventory().remove(event.getUsedItem())) { - player.getInventory().add(getProduct()); - String message = getMixMessage(event); - if (message != null) { - player.getPacketDispatch().sendMessage(message); - } - } - } - - /** - * Method used to handle mixing multiple item recipes. - * @param player the player. - * @param event the event. - */ - public void multipleMix(final Player player, final NodeUsageEvent event) { - Item item = null; - int index = -1; - for (int counter = 0; counter < getIngredients().length; counter++) { - item = getIngredients()[counter]; - if (item.getId() == event.getUsedItem().getId() || item.getId() == event.getBaseItem().getId()) { - index = counter; - break; - } - } - if (index != -1) { - if (player.getInventory().remove(event.getBaseItem()) && player.getInventory().remove(event.getUsedItem())) { - player.getInventory().add(getParts()[index + 1]); - String message = getMixMessage(event); - if (message != null) { - player.getPacketDispatch().sendMessage(message); - } - } - } - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/StewListener.kt b/Server/src/main/content/global/skill/cooking/recipe/StewListener.kt new file mode 100644 index 000000000..312aedcf0 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/StewListener.kt @@ -0,0 +1,62 @@ +package content.global.skill.cooking.recipe + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class StewListener : InteractionListener { + + val base = StewProduct.values().map { it.base }.toIntArray() + val added = StewProduct.values().map { it.added }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, base, *added) { player, used, with -> + val stew = StewProduct.productMap[used.id] ?: return@onUseWith true + + if (stew.added != with.id) { + return@onUseWith false + } + + val product = stew.product + val level = stew.minimumLevel + val experience = 0.0 + val message = "You cut up the ${stew.message} and put it into the stew." + val failMessage = "You need a Cooking level of at least ${stew.minimumLevel} in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } + + enum class StewProduct( + val base: Int, + val added: Int, + val product: Int, + val minimumLevel: Int, + val message: String, + ) { + STEW_1(Items.POTATO_1942, Items.BOWL_OF_WATER_1921, Items.INCOMPLETE_STEW_1997, 25, "potato"), + STEW_2(Items.INCOMPLETE_STEW_1997, Items.COOKED_MEAT_2142, Items.UNCOOKED_STEW_2001, 25, "meat"), + STEW_3(Items.COOKED_MEAT_2142, Items.BOWL_OF_WATER_1921, Items.INCOMPLETE_STEW_1999, 25, "meat"), + STEW_4(Items.INCOMPLETE_STEW_1999, Items.POTATO_1942, Items.UNCOOKED_STEW_2001, 25, "potato"), + ; + + companion object { + val productMap = HashMap() + + init { + for (stew in StewProduct.values()) { + productMap[stew.base] = stew + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/ToppingListener.kt b/Server/src/main/content/global/skill/cooking/recipe/ToppingListener.kt new file mode 100644 index 000000000..68e5c55bb --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/recipe/ToppingListener.kt @@ -0,0 +1,84 @@ +package content.global.skill.cooking.recipe + +import core.api.inInventory +import core.api.sendDialogue +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class ToppingListener : InteractionListener { + + val base = ToppingProduct.values().map { it.base }.toIntArray() + val added = ToppingProduct.values().map { it.added }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, base, *added) { player, used, with -> + val topping = ToppingProduct.productMap[used.id] ?: return@onUseWith true + val verb = if (with.id == Items.COOKED_MEAT_2142) "cut" else "slice" + + if (topping.added != with.id) { + return@onUseWith false + } + + if (topping.cut_name != "" && !inInventory(player, Items.KNIFE_946)) { + sendDialogue(player, "You need a knife in order to $verb up the ${topping.cut_name}.") + return@onUseWith true + } + + val product = topping.product + val level = topping.minimumLevel + val experience = topping.experience + val message = topping.message + val failMessage = "You need a Cooking level of at least ${topping.minimumLevel} in order to do this." + + return@onUseWith standardMix( + player, + used.asItem(), + with.asItem(), + product, + level, + experience, + message, + failMessage + ) + } + } + + enum class ToppingProduct( + val base: Int, + val added: Int, + val product: Int, + val minimumLevel: Int, + val experience: Double, + val message: String, + val cut_name: String + ) { + //Tuna and Corn: https://www.youtube.com/watch?v=wAavERc9p2c + //Uncooked Egg: https://www.youtube.com/watch?v=LiLq6PhCc2M + //Bowl of sweetcorn (OSRS source is only I could find) https://www.youtube.com/watch?v=pz8epXjkKYE + + UNCOOKED_EGG(Items.EGG_1944, Items.BOWL_1923, Items.UNCOOKED_EGG_7076, 1, 0.0, "You carefully break the egg into the bowl.", ""), + BOWL_OF_SWEETCORN(Items.COOKED_SWEETCORN_5988, Items.BOWL_1923, Items.SWEETCORN_7088, 1, 0.0, "You put the cooked sweetcorn into the bowl.", ""), + SLICED_MUSHROOM(Items.MUSHROOM_6004, Items.BOWL_1923, Items.SLICED_MUSHROOMS_7080, 1, 0.0, "You chop the mushrooms into the bowl.", "mushrooms"), + CHOPPED_TUNA(Items.TUNA_361, Items.BOWL_1923, Items.CHOPPED_TUNA_7086, 1, 0.0, "You chop the tuna into the bowl.", "tuna"), + CHOPPED_ONION(Items.ONION_1957, Items.BOWL_1923, Items.CHOPPED_ONION_1871, 1, 0.0, "You chop the onion into the bowl.", "onion"), + CHOPPED_GARLIC(Items.GARLIC_1550, Items.BOWL_1923, Items.CHOPPED_GARLIC_7074, 1, 0.0, "You chop the garlic into the bowl.", "garlic"), + SPICY_SAUCE(Items.CHOPPED_GARLIC_7074, Items.GNOME_SPICE_2169, Items.SPICY_SAUCE_7072, 9, 25.0, "You mix the ingredients to make the topping.", ""), //inauthentic, used generic mixing message + CHILLI_CON_CARNE(Items.SPICY_SAUCE_7072, Items.COOKED_MEAT_2142, Items.CHILLI_CON_CARNE_7062, 9, 0.0, "You mix the ingredients to make the topping.", "meat"), //inauthentic, used generic mixing message + EGG_AND_TOMATO(Items.TOMATO_1982, Items.SCRAMBLED_EGG_7078, Items.EGG_AND_TOMATO_7064, 23, 50.0, "You mix the ingredients to make the topping.", ""), + MUSHROOM_AND_ONION(Items.FRIED_MUSHROOMS_7082, Items.FRIED_ONIONS_7084, Items.MUSHROOM_AND_ONION_7066, 57, 120.0, "You mix the ingredients to make the topping.", ""), + TUNA_AND_CORN_1(Items.CHOPPED_TUNA_7086, Items.COOKED_SWEETCORN_5988, Items.TUNA_AND_CORN_7068, 67, 204.0, "You mix the ingredients to make the topping.", ""), + TUNA_AND_CORN_2(Items.SWEETCORN_7088, Items.TUNA_361, Items.TUNA_AND_CORN_7068, 67, 204.0, "You mix the ingredients to make the topping.", "tuna"), + ; + + companion object { + val productMap = HashMap() + + init { + for (topping in ToppingProduct.values()) { + productMap[topping.base] = topping + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/recipe/cake/ChocolateCake.java b/Server/src/main/content/global/skill/cooking/recipe/cake/ChocolateCake.java deleted file mode 100644 index 97ef11d88..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/cake/ChocolateCake.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.global.skill.cooking.recipe.cake; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.node.entity.skill.Skills; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the chocolate cake recipe. This recipe consists of adding a - * chocolate bar to a cake. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ChocolateCake extends Recipe { - - /** - * Represents the cake item. - */ - private static final Item CAKE = new Item(1891); - - /** - * Represents the chocolate cake item. - */ - private static final Item CHOCOLATE_CAKE = new Item(1897); - - /** - * Represents the chocolate bar item. - */ - private static final Item CHOCOLATE_BAR = new Item(1973); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 50) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of 50 in order to do that."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, 30, true); - } - - @Override - public Item getBase() { - return CAKE; - } - - @Override - public Item getProduct() { - return CHOCOLATE_CAKE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { CHOCOLATE_BAR }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You add chocolate to the cake."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/PieRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/pie/PieRecipe.java deleted file mode 100644 index 878a3333f..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/PieRecipe.java +++ /dev/null @@ -1,38 +0,0 @@ -package content.global.skill.cooking.recipe.pie; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.item.Item; - -/** - * Represents the generic recipe for a pie. - * @author 'Vexia - * @date 21/12/2013 - */ -public abstract class PieRecipe extends Recipe { - - /** - * Represents the pie shell item. - */ - protected static final Item PIE_SHELL = new Item(2315); - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public Item getBase() { - return PIE_SHELL; - } - - @Override - public String getMixMessage(final NodeUsageEvent event) { - return "You fill the pie with " + (event.getBaseItem().getId() == 2315 ? event.getUsedItem().getName().toLowerCase() : event.getBaseItem().getName().toLowerCase()) + "."; - } - - @Override - public boolean isSingular() { - return true; - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/AdmiralPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/AdmiralPie.java deleted file mode 100644 index 9cc6a98eb..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/AdmiralPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the admiral pie recipe. This recipe conists of pixing a salmon, - * tuna and potato together. - * @author 'Vexia - * @date 21/12/2013 - */ -public class AdmiralPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7196); - - /** - * Represents the salmon ingredient item. - */ - private static final Item SALMON = new Item(329); - - /** - * Represents the tuna ingredient item. - */ - private static final Item TUNA = new Item(361); - - /** - * Represents the potato item. - */ - private static final Item POTATO = new Item(1942); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7192); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7194); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { SALMON, TUNA, POTATO }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/ApplePie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/ApplePie.java deleted file mode 100644 index e44917445..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/ApplePie.java +++ /dev/null @@ -1,34 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the apple pie recipe. This recipe consists of cooking apples and a - * pie shell. - * @author 'Vexia - * @date 21/12/2013 - */ -public class ApplePie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(2317); - - /** - * Represents the cooking apple item. - */ - private static final Item COOKING_APPLE = new Item(1955); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COOKING_APPLE }; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/FishPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/FishPie.java deleted file mode 100644 index 7a1dad02f..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/FishPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the garden pie recipe. This pie consists of mixing, tomato, onion, - * and cabbage together. - * @author 'Vexia - * @date 21/12/2013 - */ -public class FishPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7186); - - /** - * Represents the trout item ingredient. - */ - private static final Item TROUT = new Item(333); - - /** - * Represents the cod item ingredient. - */ - private static final Item COD = new Item(339); - - /** - * Represents the potato item. - */ - private static final Item POTATO = new Item(1942); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7182); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7184); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TROUT, COD, POTATO }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/GardenPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/GardenPie.java deleted file mode 100644 index 90fbe7e23..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/GardenPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the garden pie recipe. This pie consists of mixing, tomato, onion, - * and cabbage together. - * @author 'Vexia - * @date 21/12/2013 - */ -public class GardenPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7176); - - /** - * Represents the tomato ingredient item. - */ - private static final Item TOMATO = new Item(1982); - - /** - * Represents the onion ingredient item. - */ - private static final Item ONION = new Item(1957); - - /** - * Represents the cabbage ingredient item. - */ - private static final Item CABBAGE = new Item(1965); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7172); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7174); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOMATO, ONION, CABBAGE }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MeatPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MeatPie.java deleted file mode 100644 index 1edb223fd..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MeatPie.java +++ /dev/null @@ -1,58 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the meat pie recipe. - * @author 'Vexia - * @date 21/12/2013 - */ -public class MeatPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(2319); - - /** - * Represents the cooked meat item. - */ - private static final Item COOKED_MEAT = new Item(2142); - - /** - * Represents the cooked chicken item. - */ - private static final Item COOKED_CHICKEN = new Item(2140); - - /** - * Represents the cooked rabbit. - */ - private static final Item COOKED_RABBIT = new Item(3228); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getInventory().remove(event.getUsedItem()) && player.getInventory().remove(event.getBaseItem())) { - player.getInventory().add(getProduct()); - player.getPacketDispatch().sendMessage(getMixMessage(event)); - return; - } - } - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COOKED_MEAT, COOKED_CHICKEN, COOKED_RABBIT }; - } - - @Override - public String getMixMessage(final NodeUsageEvent event) { - return "You fill the pie with meat."; - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MudPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MudPie.java deleted file mode 100644 index 64a542f46..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/MudPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the mud pie recipe. A mud pie consists of mixing compost, water - * and clay together. - * @author 'Vexia - * @date 21/12/2013 - */ -public class MudPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7168); - - /** - * Represents the compost item. - */ - private static final Item COMPOST = new Item(6032); - - /** - * Represents the bucket of water item. - */ - private static final Item BUCKET_OF_WATER = new Item(1929); - - /** - * Represents the clay item. - */ - private static final Item CLAY = new Item(434); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7164); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7166); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COMPOST, BUCKET_OF_WATER, CLAY }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/RedberryPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/RedberryPie.java deleted file mode 100644 index 6ebbd04f6..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/RedberryPie.java +++ /dev/null @@ -1,33 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents a redberry pie recipe. - * @author 'Vexia - * @date 21/12/2013 - */ -public class RedberryPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(2321); - - /** - * Represents the redberries pie. - */ - private static final Item REDBERRIES = new Item(1951); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { REDBERRIES }; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/SummerPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/SummerPie.java deleted file mode 100644 index 14996852a..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/SummerPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the summer pie recipe. This recipe consists of mixing stawberry, - * watermelon, and an apple. - * @author 'Vexia - * @date 21/12/2013 - */ -public class SummerPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7216); - - /** - * Represents the strawberry item. - */ - private static final Item STRAWBERRY = new Item(5504); - - /** - * Represents the watermelon item. - */ - private static final Item WATERMELON = new Item(5982); - - /** - * Represents the cooking apple item. - */ - private static final Item COOKING_APPLE = new Item(1955); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7212); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7214); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { STRAWBERRY, WATERMELON, COOKING_APPLE }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/WildPie.java b/Server/src/main/content/global/skill/cooking/recipe/pie/impl/WildPie.java deleted file mode 100644 index 3b4decaa2..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pie/impl/WildPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.global.skill.cooking.recipe.pie.impl; - -import content.global.skill.cooking.recipe.pie.PieRecipe; -import core.game.node.item.Item; - -/** - * Represents the wild pie recipe. This recipe consists of mixing raw beat meat, - * raw chomp, and raw rabbit into a pie shell. - * @author 'Vexia - * @date 21/12/2013 - */ -public class WildPie extends PieRecipe { - - /** - * Represents the uncooked redberry pie. - */ - private static final Item UNCOOKED_PIE = new Item(7206); - - /** - * Represents the raw bear meat item. - */ - private static final Item BEAR_MEAT = new Item(2136); - - /** - * Represents the raw chompy meat item. - */ - private static final Item CHOMPY_MEAT = new Item(2876); - - /** - * Represents the raw rabbit meat item. - */ - private static final Item RABBIT_MEAT = new Item(3226); - - /** - * Represents the part one pie item. - */ - private static final Item PART_ONE = new Item(7202); - - /** - * Represents the part two pie item. - */ - private static final Item PART_TWO = new Item(7204); - - @Override - public Item getProduct() { - return UNCOOKED_PIE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { BEAR_MEAT, CHOMPY_MEAT, RABBIT_MEAT }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIE_SHELL, PART_ONE, PART_TWO, UNCOOKED_PIE }; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pizza/PizzaRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/pizza/PizzaRecipe.java deleted file mode 100644 index 5a1385a35..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pizza/PizzaRecipe.java +++ /dev/null @@ -1,63 +0,0 @@ -package content.global.skill.cooking.recipe.pizza; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the generic recipe for a pizza. - * @author ceikry - */ -public abstract class PizzaRecipe extends Recipe { - - /** - * Represents the plain pizza. - */ - protected static final Item PLAIN_PIZZA = new Item(2289); - - /** - * Method used to get the experience gained from adding the final - * ingredient. - * @return the experience. - */ - public abstract double getExperience(); - - /** - * Method used to get the level required. - * @return the level required. - */ - public abstract int getLevel(); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + getLevel() + " in order to do this."); - return; - } - super.singleMix(player, event); - player.getSkills().addExperience(Skills.COOKING, getExperience(), true); - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public Item getBase() { - return PLAIN_PIZZA; - } - - @Override - public String getMixMessage(final NodeUsageEvent event) { - return "You add " + event.getBaseItem().getName().toLowerCase() + " to the pizza."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/AnchovyPizza.java b/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/AnchovyPizza.java deleted file mode 100644 index 4a70625ff..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/AnchovyPizza.java +++ /dev/null @@ -1,44 +0,0 @@ -package content.global.skill.cooking.recipe.pizza.impl; - -import content.global.skill.cooking.recipe.pizza.PizzaRecipe; -import core.game.node.item.Item; - -/** - * Represents the anchovy pizza. This recipe consists of adding anchovies to a - * plain pizza. - * @author 'Vexia - * @date 22/12/2013 - */ -public class AnchovyPizza extends PizzaRecipe { - - /** - * Represents the anchovy pizza item. - */ - private static final Item ANCHOVY_PIZZA = new Item(2297); - - /** - * Represents the anchovies item. - */ - private static final Item ANCHOVIES = new Item(319); - - @Override - public double getExperience() { - return 39; - } - - @Override - public Item getProduct() { - return ANCHOVY_PIZZA; - } - - @Override - public Item[] getIngredients() { - return new Item[] { ANCHOVIES }; - } - - @Override - public int getLevel() { - return 55; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/MeatPizza.java b/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/MeatPizza.java deleted file mode 100644 index b79096137..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/MeatPizza.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.pizza.impl; - -import content.global.skill.cooking.recipe.pizza.PizzaRecipe; -import core.game.node.item.Item; - -/** - * Represents the meat pizza recipe. This recipe consists of adding cooked meat - * to a plain pizza. - * @author 'Vexia - * @date 22/12/2013 - */ -public class MeatPizza extends PizzaRecipe { - - /** - * Represents the meat pizza item. - */ - private static final Item MEAT_PIZZA = new Item(2293); - - /** - * Represents the cooked meat item. - */ - private static final Item COOKED_MEAT = new Item(2142); - - /** - * Represents the cooked chicken item. - */ - private static final Item COOKED_CHICKEN = new Item(2140); - - @Override - public double getExperience() { - return 26; - } - - @Override - public int getLevel() { - return 45; - } - - @Override - public Item getProduct() { - return MEAT_PIZZA; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COOKED_MEAT, COOKED_CHICKEN }; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PineapplePizza.java b/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PineapplePizza.java deleted file mode 100644 index 7e4746e2f..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PineapplePizza.java +++ /dev/null @@ -1,55 +0,0 @@ -package content.global.skill.cooking.recipe.pizza.impl; - -import content.global.skill.cooking.recipe.pizza.PizzaRecipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.item.Item; - -/** - * Represents the pineapple pizza recipe. This recipe consists of mixing either - * pineapple chunks or pineapple rings. - * @author 'Vexia - * @date 22/12/2013 - */ -public class PineapplePizza extends PizzaRecipe { - - /** - * Represents the pineapple pizza item. - */ - private static final Item PINEAPPLE_PIZZA = new Item(2301); - - /** - * Represents the pineapple ring item. - */ - private static final Item PINEAPPLE_RING = new Item(2118); - - /** - * Represents the pineapple chunk item. - */ - private static final Item PINEAPPLE_CHUNKS = new Item(2116); - - @Override - public double getExperience() { - return 52; - } - - @Override - public int getLevel() { - return 65; - } - - @Override - public Item getProduct() { - return PINEAPPLE_PIZZA; - } - - @Override - public Item[] getIngredients() { - return new Item[] { PINEAPPLE_CHUNKS, PINEAPPLE_RING }; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You add the " + event.getBaseItem().getName().toLowerCase() + " to the pizza."; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PlainPizza.java b/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PlainPizza.java deleted file mode 100644 index 8d624dca3..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/pizza/impl/PlainPizza.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.global.skill.cooking.recipe.pizza.impl; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the plain pizza recipe. This recipe consists of mixing tomato, and - * cheese to a pizza base. - * @author 'Vexia - * @date 22/12/2013 - */ -public class PlainPizza extends Recipe { - - /** - * Represents the pizza base. - */ - private static final Item PIZZA_BASE = new Item(2283); - - /** - * Represents the uncooked pizza. - */ - private static final Item UNCOOKED_PIZZA = new Item(2287); - - /** - * Represents the incomplete pizza. - */ - private static final Item INCOMPLETE_PIZZA = new Item(2285); - - /** - * Represents the tomato ingredient item. - */ - private static final Item TOMATO = new Item(1982); - - /** - * Represents the cheese item. - */ - private static final Item CHEESE = new Item(1985); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 35) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 35 + " in order to do this."); - return; - } - super.mix(player, event); - } - - @Override - public Item getBase() { - return PIZZA_BASE; - } - - @Override - public Item getProduct() { - return UNCOOKED_PIZZA; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOMATO, CHEESE }; - } - - @Override - public Item[] getParts() { - return new Item[] { PIZZA_BASE, INCOMPLETE_PIZZA, UNCOOKED_PIZZA }; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You add the " + event.getBaseItem().getName().toLowerCase() + " to the pizza."; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/PotatoRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/potato/PotatoRecipe.java deleted file mode 100644 index c2640a8bc..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/PotatoRecipe.java +++ /dev/null @@ -1,76 +0,0 @@ -package content.global.skill.cooking.recipe.potato; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents a generic potato topping recipe. - * @author 'Vexia - * @date 22/12/2013 - */ -public abstract class PotatoRecipe extends Recipe { - - /** - * Represents the potato with butter. - */ - private static final Item POTATO_WITH_BUTTER = new Item(6703); - - /** - * Represents the bowl item. - */ - protected static final Item BOWL = new Item(1923); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + getLevel() + " in order to do this."); - return; - } - super.singleMix(player, event); - if (isTopping()) { - player.getInventory().add(BOWL); - } - player.getSkills().addExperience(Skills.COOKING, getExperience(), true); - } - - @Override - public Item getBase() { - return POTATO_WITH_BUTTER; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return true; - } - - /** - * Method used to check if it is a topping recipe. - * @return True if it is a topping. - */ - public abstract boolean isTopping(); - - /** - * Method used to get the level required. - * @return the level. - */ - public abstract int getLevel(); - - /** - * Method used to get the experience gained. - * @return the experience. - */ - public abstract double getExperience(); -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ButterPotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ButterPotato.java deleted file mode 100644 index b382a5029..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ButterPotato.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.node.entity.skill.Skills; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the butter potato recipe. This recipe consists of adding a pat of - * butter to a potato. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ButterPotato extends Recipe { - - /** - * Represents the baked potato. - */ - private static final Item BAKED_POTATO = new Item(6701); - - /** - * Represents the potato with butter. - */ - private static final Item POTATO_WITH_BUTTER = new Item(6703); - - /** - * Represents the pat of butter. - */ - private static final Item PAT_OF_BUTTER = new Item(6697); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 39) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 39 + " in order to do this."); - return; - } - super.singleMix(player, event); - player.getSkills().addExperience(Skills.COOKING, 40.5, true); - } - - @Override - public Item getBase() { - return BAKED_POTATO; - } - - @Override - public Item getProduct() { - return POTATO_WITH_BUTTER; - } - - @Override - public Item[] getIngredients() { - return new Item[] { PAT_OF_BUTTER }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You add a pat of butter to the potato."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/CheesePotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/CheesePotato.java deleted file mode 100644 index f111ed950..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/CheesePotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.potato.PotatoRecipe; -import core.game.node.item.Item; - -/** - * Represents the cheese potato. This recipe consists of mixing cheese witha - * baked potato. - * @author 'Vexia - * @date 22/12/2013 - */ -public class CheesePotato extends PotatoRecipe { - - /** - * Represents the cheese potato. - */ - private static final Item CHEESE_POTATO = new Item(6705); - - /** - * Represents the cheese item. - */ - private static final Item CHEESE = new Item(1985); - - @Override - public Item getProduct() { - return CHEESE_POTATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { CHEESE }; - } - - @Override - public boolean isTopping() { - return false; - } - - @Override - public int getLevel() { - return 47; - } - - @Override - public double getExperience() { - return 10; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ChilliPotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ChilliPotato.java deleted file mode 100644 index 88c5ed146..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/ChilliPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.potato.PotatoRecipe; -import core.game.node.item.Item; - -/** - * Represents the chilli potato recipe. This recipe consists of adding chilli - * con carne to a baked butter potato. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ChilliPotato extends PotatoRecipe { - - /** - * Represents the chilli potato item. - */ - private static final Item CHILLI_POTATO = new Item(7054); - - /** - * Represents the chilli con carne item/ - */ - private static final Item CHILLI_CON_CARNE = new Item(7062); - - @Override - public Item getProduct() { - return CHILLI_POTATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { CHILLI_CON_CARNE }; - } - - @Override - public boolean isTopping() { - return true; - } - - @Override - public int getLevel() { - return 41; - } - - @Override - public double getExperience() { - return 10; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/EggPotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/EggPotato.java deleted file mode 100644 index c616591fd..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/EggPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.potato.PotatoRecipe; -import core.game.node.item.Item; - -/** - * Represents the egg potato recipe. This recipe consists of mixing a egg and a - * tomato. - * @author 'Vexia - * @date 22/12/2013 - */ -public class EggPotato extends PotatoRecipe { - - /** - * Represents the egg potato. - */ - private static final Item EGG_POTATO = new Item(7056); - - /** - * Represents the topping item. - */ - private static final Item TOPPING = new Item(7064); - - @Override - public Item getProduct() { - return EGG_POTATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOPPING }; - } - - @Override - public boolean isTopping() { - return true; - } - - @Override - public int getLevel() { - return 51; - } - - @Override - public double getExperience() { - return 10; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/MushroomPotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/MushroomPotato.java deleted file mode 100644 index d7bfa1c2c..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/MushroomPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.potato.PotatoRecipe; -import core.game.node.item.Item; - -/** - * Represents the mushrrom potato. This recipe consists of mixing a baked potato - * with mushroom and onion toppings. - * @author 'Vexia - * @date 22/12/2013 - */ -public class MushroomPotato extends PotatoRecipe { - - /** - * Represents the egg potato. - */ - private static final Item MUSHROOM_POTATO = new Item(7058); - - /** - * Represents the topping item. - */ - private static final Item TOPPING = new Item(7066); - - @Override - public Item getProduct() { - return MUSHROOM_POTATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOPPING }; - } - - @Override - public boolean isTopping() { - return true; - } - - @Override - public int getLevel() { - return 64; - } - - @Override - public double getExperience() { - return 10; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/TunaPotato.java b/Server/src/main/content/global/skill/cooking/recipe/potato/impl/TunaPotato.java deleted file mode 100644 index 645d5ed2e..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/potato/impl/TunaPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package content.global.skill.cooking.recipe.potato.impl; - -import content.global.skill.cooking.recipe.potato.PotatoRecipe; -import core.game.node.item.Item; - -/** - * Represents the tuna potato recipe. This recipe consists of mixing tuna and - * corn toppings. - * @author 'Vexia - * @date 22/12/2013 - */ -public class TunaPotato extends PotatoRecipe { - - /** - * Represents the tuna potato. - */ - private static final Item TUNA_POTATO = new Item(7060); - - /** - * Represents the topping item. - */ - private static final Item TOPPING = new Item(7068); - - @Override - public Item getProduct() { - return TUNA_POTATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOPPING }; - } - - @Override - public boolean isTopping() { - return true; - } - - @Override - public int getLevel() { - return 68; - } - - @Override - public double getExperience() { - return 10; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/stew/CurryRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/stew/CurryRecipe.java deleted file mode 100644 index 4ea78825d..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/stew/CurryRecipe.java +++ /dev/null @@ -1,89 +0,0 @@ -package content.global.skill.cooking.recipe.stew; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the curry recipe. This recipe consists of mixing 3 curry leaves or - * a spice into a stew. - * @author 'Vexia - * @date 22/12/2013 - */ -public class CurryRecipe extends Recipe { - - /** - * Represents the uncooked curry item. - */ - private static final Item UNCOOKED_CURRY = new Item(2009); - - /** - * Represents the uncooked stew item. - */ - private static final Item UNCOOKED_STEW = new Item(2001); - - /** - * Represents the spice item. - */ - private static final Item SPICE = new Item(2007); - - /** - * Represents the curry leaf. - */ - private static final Item CURRY_LEAF = new Item(5970); - - @Override - public void mix(Player player, NodeUsageEvent event) { - if (event.getBaseItem().getId() == CURRY_LEAF.getId() || event.getUsedItem().getId() == CURRY_LEAF.getId()) { - Item stew = event.getBaseItem().getId() == UNCOOKED_STEW.getId() ? event.getBaseItem() : event.getUsedItem(); - if (stew.getCharge() == 1000) { - stew.setCharge(1); - } - int charge = stew.getCharge(); - if (charge < 3) { - player.getInventory().remove(CURRY_LEAF); - stew.setCharge(charge + 1); - } else { - if (player.getInventory().remove(stew) && player.getInventory().remove(CURRY_LEAF)) { - player.getInventory().add(UNCOOKED_CURRY); - } - } - return; - } - if (player.getInventory().remove(event.getBaseItem()) && player.getInventory().remove(event.getUsedItem())) { - player.getInventory().add(getProduct()); - } - } - - @Override - public Item getBase() { - return UNCOOKED_STEW; - } - - @Override - public Item getProduct() { - return UNCOOKED_CURRY; - } - - @Override - public Item[] getIngredients() { - return new Item[] { SPICE, CURRY_LEAF }; - } - - @Override - public Item[] getParts() { - return new Item[] { UNCOOKED_STEW }; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You mix the spice with the stew."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/stew/StewRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/stew/StewRecipe.java deleted file mode 100644 index 4073bcc0e..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/stew/StewRecipe.java +++ /dev/null @@ -1,92 +0,0 @@ -package content.global.skill.cooking.recipe.stew; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the stew recipe. This recipe consists of mixing meat and a raw - * potato in a bowl of water. - * @author 'Vexia - * @date 22/12/2013 - */ -public class StewRecipe extends Recipe { - - /** - * Represents the uncooked stew item. - */ - private static final Item UNCOOKED_STEW = new Item(2001); - - /** - * Represents the bowl of water item. - */ - private static final Item BOWL_OF_WATER = new Item(1921); - - /** - * Represents the meat item. - */ - private static final Item MEAT = new Item(2142); - - /** - * Represents the potato item. - */ - private static final Item POTATO = new Item(1942); - - /** - * Represents the incomplete stew. - */ - private static final Item INCOMPLETE_STEW = new Item(1997); - - /** - * Represents the second incomplete stew. - */ - private static final Item INCOMPLETE_STEW2 = new Item(1999); - - @Override - public void mix(Player player, NodeUsageEvent event) { - Item first = event.getUsedItem(); - Item second = event.getBaseItem(); - if(first != null && second != null) { - if (player.getInventory().remove(first) && player.getInventory().remove(second)) { - if (first.getId() == BOWL_OF_WATER.getId() || second.getId() == BOWL_OF_WATER.getId()) { - player.getInventory().add(first.getId() == POTATO.getId() ? INCOMPLETE_STEW : first.getId() == MEAT.getId() ? INCOMPLETE_STEW2 : second.getId() == POTATO.getId() ? INCOMPLETE_STEW : second.getId() == MEAT.getId() ? INCOMPLETE_STEW2 : null); - } else { - player.getInventory().add(UNCOOKED_STEW); - } - player.getPacketDispatch().sendMessage(getMixMessage(event)); - } - } - } - - @Override - public Item getBase() { - return BOWL_OF_WATER; - } - - @Override - public Item getProduct() { - return UNCOOKED_STEW; - } - - @Override - public Item[] getIngredients() { - return new Item[] { MEAT, POTATO, MEAT, POTATO }; - } - - @Override - public Item[] getParts() { - return new Item[] { BOWL_OF_WATER, BOWL_OF_WATER, INCOMPLETE_STEW, INCOMPLETE_STEW2 }; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You cut up the " + (event.getUsedItem().getName().toLowerCase().contains("incomplete") ? event.getBaseItem().getName().toLowerCase() : event.getUsedItem().getName().toLowerCase().replace("cooked", "").trim()) + " and put it into the stew."; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/ToppingRecipe.java b/Server/src/main/content/global/skill/cooking/recipe/topping/ToppingRecipe.java deleted file mode 100644 index 5c2cacb15..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/ToppingRecipe.java +++ /dev/null @@ -1,58 +0,0 @@ -package content.global.skill.cooking.recipe.topping; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents a generic topping recipe. - * @author 'Vexia - * @date 22/12/2013 - */ -public abstract class ToppingRecipe extends Recipe { - - /** - * Represents the bowl item. - */ - protected static final Item BOWL = new Item(1923); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + getLevel() + " in order to do this."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, getExperience(), true); - } - - @Override - public Item getBase() { - return BOWL; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return true; - } - - /** - * Method used to get the level required. - * @return the level. - */ - public abstract int getLevel(); - - /** - * Method used to get the experience gained. - * @return the experience. - */ - public abstract double getExperience(); - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChilliConCarne.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChilliConCarne.java deleted file mode 100644 index 4085d149b..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChilliConCarne.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the chilli con carne recipe. This recipe consists of using spicy - * sauce with cooked meat. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ChilliConCarne extends Recipe { - - /** - * Represents the bowl item. - */ - private static final Item SPICY_SAUCE = new Item(7072); - - /** - * Represents the chilli con carne item. - */ - private static final Item CHILLI_CON_CARNE = new Item(7062); - - /** - * Represents the cooked meat item. - */ - private static final Item COOKED_MEAT = new Item(2142); - - /** - * Represents the knife item. - */ - private static final Item KNIFE = new Item(946); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 9) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 9 + " in order to do this."); - return; - } - if (!player.getInventory().containsItem(KNIFE)) { - player.getDialogueInterpreter().sendDialogue("You need a knife in order to cut up the meat."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, 25, true); - } - - @Override - public Item getBase() { - return SPICY_SAUCE; - } - - @Override - public Item getProduct() { - return CHILLI_CON_CARNE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COOKED_MEAT }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return "You put the cut up meat into the bowl."; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedOnion.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedOnion.java deleted file mode 100644 index 5a30107c1..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedOnion.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import content.global.skill.cooking.recipe.topping.ToppingRecipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the chopped onion recipe. This recipe consists of using an onion - * on a bowl with a knife. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ChoppedOnion extends ToppingRecipe { - - /** - * Represents the chopped onion product item. - */ - private static final Item CHOPPED_ONION = new Item(1871); - - /** - * Represents the knife used to cut the onion. - */ - private static final Item KNIFE = new Item(946); - - /** - * Represents the onion item. - */ - private static final Item ONION = new Item(1957); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (!player.getInventory().containsItem(KNIFE)) { - player.getDialogueInterpreter().sendDialogue("You need a knife in order to slice up the onion."); - return; - } - super.mix(player, event); - } - - @Override - public int getLevel() { - return 1; - } - - @Override - public double getExperience() { - return 0; - } - - @Override - public Item getProduct() { - return CHOPPED_ONION; - } - - @Override - public Item[] getIngredients() { - return new Item[] { ONION }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public boolean isSingular() { - return true; - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedTuna.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedTuna.java deleted file mode 100644 index 4723c61ff..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/ChoppedTuna.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import content.global.skill.cooking.recipe.topping.ToppingRecipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the chopped tuna recipe. This recipe consists of adding tuna to a - * bowl with a knife. - * @author 'Vexia - * @date 22/12/2013 - */ -public class ChoppedTuna extends ToppingRecipe { - - /** - * Represents the chopped tuna item. - */ - private static final Item CHOPPED_TUNA = new Item(7086); - - /** - * Represents the tuna item. - */ - private static final Item TUNA = new Item(361); - - /** - * Represents the knife item. - */ - private static final Item KNIFE = new Item(946); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (!player.getInventory().containsItem(KNIFE)) { - player.getDialogueInterpreter().sendDialogue("You need a knife in order to slice up the tuna."); - return; - } - super.mix(player, event); - } - - @Override - public int getLevel() { - return 1; - } - - @Override - public double getExperience() { - return 1; - } - - @Override - public Item getProduct() { - return CHOPPED_TUNA; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TUNA }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public boolean isSingular() { - return true; - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/EggAndTomato.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/EggAndTomato.java deleted file mode 100644 index dabf2db2b..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/EggAndTomato.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import content.global.skill.cooking.recipe.Recipe; -import core.game.node.entity.skill.Skills; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the egg and tomato recipe. This recipe consists of mixing a tomato - * with a scrambled egg. - * @author 'Vexia - * @date 22/12/2013 - */ -public class EggAndTomato extends Recipe { - - /** - * Represents the egg and tomato. - */ - private static final Item EGG_AND_TOMATO = new Item(7064); - - /** - * Represents the scrambled egg item. - */ - private static final Item SCRAMBLED_EGG = new Item(7078); - - /** - * epresents the tomato item. - */ - private static final Item TOMATO = new Item(1982); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 23) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 23 + " in order to do this."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, 50, true); - } - - @Override - public Item getBase() { - return SCRAMBLED_EGG; - } - - @Override - public Item getProduct() { - return EGG_AND_TOMATO; - } - - @Override - public Item[] getIngredients() { - return new Item[] { TOMATO }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/MushroomAndOnion.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/MushroomAndOnion.java deleted file mode 100644 index 3b99200f7..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/MushroomAndOnion.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the mushroom and onion recipe. This recipe consists of using a - * fried mushroom with a friend onion. - * @author 'Vexia - * @date 22/12/2013 - */ -public class MushroomAndOnion extends Recipe { - - /** - * Represents the mushroom and onion item. - */ - private static final Item MUSHROOM_AND_ONION = new Item(7066); - - /** - * Represents the fried onions item. - */ - private static final Item FRIED_ONIONS = new Item(7084); - - /** - * Represents the fried mushrooms item. - */ - private static final Item FRIED_MUSHROOMS = new Item(7082); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 57) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 57 + " in order to do this."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, 120, true); - } - - @Override - public Item getBase() { - return FRIED_MUSHROOMS; - } - - @Override - public Item getProduct() { - return MUSHROOM_AND_ONION; - } - - @Override - public Item[] getIngredients() { - return new Item[] { FRIED_ONIONS }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SlicedMushroom.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SlicedMushroom.java deleted file mode 100644 index 46f9d09f8..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SlicedMushroom.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import content.global.skill.cooking.recipe.topping.ToppingRecipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the fried mushroom recipe. This recipe consists of using a - * mushroom on a bowl. - * @author 'Vexia - * @date 22/12/2013 - */ -public final class SlicedMushroom extends ToppingRecipe { - - /** - * Represents the sliced mushrooms item. - */ - private static final Item SLICED_MUSHROOMS = new Item(7080); - - /** - * Represents the mushroom item. - */ - private static final Item MUSHROOM = new Item(6004); - - /** - * Represents the knife item. - */ - private static final Item KNIFE = new Item(946); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (!player.getInventory().containsItem(KNIFE)) { - player.getDialogueInterpreter().sendDialogue("You need a knife in order to slice up the mushrooms."); - return; - } - super.mix(player, event); - } - - @Override - public int getLevel() { - return 1; - } - - @Override - public double getExperience() { - return 0; - } - - @Override - public Item getProduct() { - return SLICED_MUSHROOMS; - } - - @Override - public Item[] getIngredients() { - return new Item[] { MUSHROOM }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public boolean isSingular() { - return true; - } -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SpicySauce.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SpicySauce.java deleted file mode 100644 index 2b0a7e103..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/SpicySauce.java +++ /dev/null @@ -1,84 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the spicy sauce recipe. This recipe consists of mixing gnome spice - * and garlic together. - * @author 'Vexia - * @date 22/12/2013 - */ -public class SpicySauce extends Recipe { - - /** - * Represents the spicy sauce item. - */ - private static final Item SPICY_SAUCE = new Item(7072); - - /** - * Represents the bowl item. - */ - private static final Item BOWL = new Item(1923); - - /** - * Represents the garlic item. - */ - private static final Item GARLIC = new Item(1550); - - /** - * Represents the chopped garlic item. - */ - private static final Item CHOPPED_GARLIC = new Item(7074); - - /** - * Represents the gnome spice item. - */ - private static final Item GNOME_SPICE = new Item(2169); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 9) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 9 + " in order to do this."); - return; - } - super.mix(player, event); - if (event.getBaseItem().getId() == GNOME_SPICE.getId() || event.getUsedItem().getId() == GNOME_SPICE.getId()) { - player.getSkills().addExperience(Skills.COOKING, 25, true); - } - } - - @Override - public Item getBase() { - return BOWL; - } - - @Override - public Item getProduct() { - return SPICY_SAUCE; - } - - @Override - public Item[] getIngredients() { - return new Item[] { GARLIC, GNOME_SPICE }; - } - - @Override - public Item[] getParts() { - return new Item[] { BOWL, CHOPPED_GARLIC, SPICY_SAUCE }; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/TunaAndCorn.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/TunaAndCorn.java deleted file mode 100644 index 1e2c92a6f..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/TunaAndCorn.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import content.global.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the tuna and corn recipe. This recipe consists of adding cooked - * sweetcorn to a bowl of chopped tuna. - * @author 'Vexia - * @date 22/12/2013 - */ -public final class TunaAndCorn extends Recipe { - - /** - * Represents the chopped tuna item. - */ - private static final Item CHOPPED_TUNA = new Item(7086); - - /** - * Represents the cooked corn item. - */ - private static final Item COOKED_CORN = new Item(5988); - - /** - * Represents the tuna and corn item. - */ - private static final Item TUNA_AND_CORN = new Item(7068); - - @Override - public void mix(final Player player, final NodeUsageEvent event) { - if (player.getSkills().getLevel(Skills.COOKING) < 67) { - player.getDialogueInterpreter().sendDialogue("You need a Cooking level of at least " + 57 + " in order to do this."); - return; - } - super.mix(player, event); - player.getSkills().addExperience(Skills.COOKING, 204, true); - } - - @Override - public Item getBase() { - return CHOPPED_TUNA; - } - - @Override - public Item getProduct() { - return TUNA_AND_CORN; - } - - @Override - public Item[] getIngredients() { - return new Item[] { COOKED_CORN }; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - - @Override - public String getMixMessage(NodeUsageEvent event) { - return null; - } - - @Override - public boolean isSingular() { - return true; - } - -} diff --git a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/UncookedEgg.java b/Server/src/main/content/global/skill/cooking/recipe/topping/impl/UncookedEgg.java deleted file mode 100644 index 61ce50394..000000000 --- a/Server/src/main/content/global/skill/cooking/recipe/topping/impl/UncookedEgg.java +++ /dev/null @@ -1,54 +0,0 @@ -package content.global.skill.cooking.recipe.topping.impl; - -import content.global.skill.cooking.recipe.topping.ToppingRecipe; -import core.game.node.item.Item; - -/** - * Represents the uncooked egg recipe. This recipe consists of adding an - * uncooked egg into a bowl. - * @author 'Vexia - * @date 22/12/2013 - */ -public final class UncookedEgg extends ToppingRecipe { - - /** - * Represents the egg item. - */ - private static final Item EGG = new Item(1944); - - /** - * Represents the uncooked egg product. - */ - private static final Item UNCOOKED_EGG = new Item(7076); - - @Override - public int getLevel() { - return 1; - } - - @Override - public double getExperience() { - return 1; - } - - @Override - public Item getProduct() { - return UNCOOKED_EGG; - } - - @Override - public Item[] getIngredients() { - return new Item[] { EGG }; - } - - @Override - public boolean isSingular() { - return true; - } - - @Override - public Item[] getParts() { - return new Item[] {}; - } - -} 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..5c65d0850 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt @@ -0,0 +1,91 @@ +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 = Items.BATTLESTAFF_1391 + 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 + + fun getMaxAmount(_unused: Int = 0): Int { + return min(amountInInventory(player, with.id), amountInInventory(player, used.id)) + } + + if (!hasLevelDyn(player, Skills.CRAFTING, product.minimumLevel)) { + sendMessage(player, "You need a Crafting level of ${product.minimumLevel} to make this.") + return@onUseWith true + } + + // 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, Items.BATTLESTAFF_1391)) { + 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, min(amount, getMaxAmount())) { + if (amount < 1) return@runTask + + if (removeItem(player, product.requiredOrbItemId) && removeItem(player, Items.BATTLESTAFF_1391)) { + 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(::getMaxAmount) + } + + 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/JewelleryCraftPlugin.java b/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java index 72d19ef46..30c382180 100644 --- a/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java +++ b/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java @@ -5,6 +5,7 @@ import content.global.skill.crafting.jewellery.JewelleryCrafting; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.plugin.Plugin; +import org.rs09.consts.Scenery; /** * Represents the plugin used to craft jewellery. @@ -17,7 +18,12 @@ public final class JewelleryCraftPlugin extends UseWithHandler { /** * Represents the ids to use for this plugin. */ - private static final int[] IDS = new int[] { 4304, 6189, 11010, 11666, 12100, 12809, 18497, 26814, 30021, 30510, 36956, 37651 }; + private static final int[] IDS = new int[]{ + Scenery.FURNACE_4304, Scenery.FURNACE_6189, Scenery.FURNACE_11010, Scenery.FURNACE_11666, + Scenery.FURNACE_12100, Scenery.FURNACE_12809, Scenery.FURNACE_18497, Scenery.CLAY_FORGE_21303, + Scenery.FURNACE_26814, Scenery.FURNACE_30021, Scenery.FURNACE_30510, Scenery.FURNACE_36956, + Scenery.FURNACE_37651 + }; /** * Constructs a new {@code JewelleryCraftPlugin} {@code Object}. diff --git a/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java b/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java index 2a6086ac8..0692339a6 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]; @@ -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/PotteryPlugin.java b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java index f7fa44e3a..23ddac860 100644 --- a/Server/src/main/content/global/skill/crafting/PotteryPlugin.java +++ b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java @@ -56,7 +56,7 @@ public final class PotteryPlugin extends UseWithHandler { @Override public boolean handle(final NodeUsageEvent event) { final Player player = event.getPlayer(); - new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getPottery(false)) { + new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getPottery(false)) { @Override public void create(final int amount, int index) { @@ -145,7 +145,7 @@ public final class PotteryPlugin extends UseWithHandler { * @return the dialogue handler. */ public SkillDialogueHandler getSkillHandler(final Player player) { - return new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getPottery(true)) { + return new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getPottery(true)) { @Override public void create(final int amount, final int index) { diff --git a/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java index f4929248e..09c2ed605 100644 --- a/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java +++ b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java @@ -34,7 +34,7 @@ public class SnakeSkinPlugin extends UseWithHandler { @Override public boolean handle(final NodeUsageEvent event) { final Player player = event.getPlayer(); - new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, (Object[]) getSkins()) { + new SkillDialogueHandler(player, SkillDialogue.FIVE_OPTION, getSkins()) { @Override public void create(final int amount, int index) { diff --git a/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java b/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java index 1b1335c1c..f23d50b5f 100644 --- a/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java +++ b/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java @@ -20,7 +20,7 @@ public class SnelmCraftPlugin extends UseWithHandler { * The snelm data. */ private static final int[][] DATA = new int[][] { { 3345, 3327 }, { 3355, 3337 },// blamish - { 3349, 3341 }, { 3341, 3359 },// ochre + { 3349, 3331 }, { 3359, 3341 },// ochre { 3347, 3329 }, { 3357, 3339 },// blood { 3351, 3333 }, { 3361, 3343 },// blue { 3353, 3335 },// bark 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/JewelleryPulse.java b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java index 2959e1402..e69a7ee29 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); } } 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..0df72d1f2 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt @@ -1,14 +1,19 @@ package content.global.skill.crafting.lightsources +import content.data.LightSource +import core.api.log +import core.api.* import core.cache.def.impl.ItemDefinition import core.game.container.Container import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player import core.game.node.item.Item +import core.game.world.map.Location import core.tools.SystemLogger import core.plugin.Initializable import core.plugin.Plugin +import core.tools.Log /** @@ -28,7 +33,13 @@ 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}") } + + // For Temple of Ikov - if you are in the dark basement, do not let light source extinguish. + if(player.location.isInRegion(10648)) { + sendMessage(player, "Extinguishing the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.") + return true + } player.inventory.replace(node.asItem(), Item(lightSource.fullID)) return true diff --git a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt index d1e1bb009..b558ec38f 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt @@ -1,11 +1,16 @@ package content.global.skill.crafting.lightsources +import core.api.* +import core.api.teleport import core.game.container.Container +import core.game.event.LitLightSourceEvent import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler +import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.node.item.Item +import core.game.world.map.Location import core.plugin.Initializable import core.plugin.Plugin @@ -65,6 +70,20 @@ class LightSourceLighter : UseWithHandler(590,36,38){ lightSource ?: return false + // For Sea Slug Quest - No lighting of any torch on the fishing platform. + if(event.player.location.isInRegion(11059)) { + event.player.sendMessage("Your tinderbox is damp from the sea crossing. It won't work here.") + return true + } + + // For Temple of Ikov - if you are in the dark basement and light a light source, switch to the light basement. + // For the listener that covers the firemaking cape perk, see content.global.skill.skillcapeperks.SkillcapePerks.kt + if(event.player.location.isInRegion(10648) && event.player.location.withinDistance(Location(2639,9738,0), 8)) { + teleport(event.player, Location.create(event.player.getLocation().x, event.player.getLocation().y + 23, event.player.getLocation().z)) + closeDialogue(event.player) + // Dark basement is region 10648, min 2639 9738 0, max 2643 9744 0. Add 23 to the Y loc to tele to light basement + } + if(!light(event.player,used,lightSource)){ event.player.sendMessage("You need a Firemaking level of at least ${lightSource.levelRequired} to light this.") } @@ -85,6 +104,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/SilverCraftingListener.kt b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt index 346b61b3e..6ab0e7a45 100644 --- a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt +++ b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt @@ -31,7 +31,7 @@ class SilverCraftingListener : InterfaceListener, InteractionListener { Scenery.FURNACE_12100, Scenery.FURNACE_12809, Scenery.FURNACE_18497, Scenery.FURNACE_18525, Scenery.FURNACE_18526, Scenery.FURNACE_21879, Scenery.FURNACE_22721, Scenery.FURNACE_26814, Scenery.FURNACE_28433, Scenery.FURNACE_28434, Scenery.FURNACE_30021, Scenery.FURNACE_30510, - Scenery.FURNACE_36956, Scenery.FURNACE_37651 + Scenery.FURNACE_36956, Scenery.FURNACE_37651, Scenery.CLAY_FORGE_21303 ) private const val ATTRIBUTE_FURNACE_ID = "crafting:silver:furnace_id" 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..338b41b47 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,11 +29,10 @@ class SilverCraftingPulse( } animate(player, Animations.HUMAN_FURNACE_SMELTING_3243) + playAudio(player, Sounds.FURNACE_2725) - if (removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY)) { - addItem(player, product.producedItemId, product.amountProduced) + if (removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY) && addItem(player, product.producedItemId, product.amountProduced)) { rewardXP(player, Skills.CRAFTING, product.xpReward) - player.dispatch( ResourceProducedEvent( product.producedItemId, diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt index 83becbf16..98bcc014e 100644 --- a/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt +++ b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt @@ -27,7 +27,7 @@ enum class SilverProduct( val strungId: Int ) { HOLY(BUTTON_UNBLESSED, Items.HOLY_MOULD_1599, Items.UNSTRUNG_SYMBOL_1714, 1, 16, 50.0, Items.UNBLESSED_SYMBOL_1716), - UNHOLY(BUTTON_UNHOLY, Items.UNHOLY_MOULD_1594, Items.UNSTRUNG_EMBLEM_1720, 1, 17, 50.0, Items.UNHOLY_SYMBOL_1724), + UNHOLY(BUTTON_UNHOLY, Items.UNHOLY_MOULD_1594, Items.UNSTRUNG_EMBLEM_1720, 1, 17, 50.0, Items.UNPOWERED_SYMBOL_1722), SICKLE(BUTTON_SICKLE, Items.SICKLE_MOULD_2976, Items.SILVER_SICKLE_2961, 1, 18, 50.0, -1), TIARA(BUTTON_TIARA, Items.TIARA_MOULD_5523, Items.TIARA_5525, 1, 23, 52.5, -1), SILVTHRIL_CHAIN(BUTTON_SILVTHRIL_CHAIN, Items.CHAIN_LINK_MOULD_13153, Items.SILVTHRIL_CHAIN_13154, 1, 47, 100.0, -1), diff --git a/Server/src/main/content/global/skill/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..e47820bb3 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.HOPS_PATCH, PatchType.ALLOTMENT) @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,61 +34,84 @@ 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) { - 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 + + val familiar = player.familiarManager.familiar + if (familiar != null && familiar is GiantEntNPC) { + familiar.modifyFarmingReward(fPatch, reward) + } + + var requiredItem = when (fPatch.type) { + PatchType.TREE_PATCH -> Items.SECATEURS_5329 else -> Items.SPADE_952 } - 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 sendHarvestMessages = if (fPatch.type == PatchType.FLOWER_PATCH) false else true + if (sendHarvestMessages && firstHarvest) { + sendMessage(player, "You begin to harvest the $patchName.") + firstHarvest = false + } + animate(player, anim) + playAudio(player, sound) + // TODO: If a flower patch is being harvested, delay the clearing of the + // patch until after the animation has played - https://youtu.be/lg4GktlVNUY?t=75 delay = 2 - player.inventory.add(reward) - player.skills.addExperience(Skills.FARMING,plantable.harvestXP) - if(patch.patch.type in livesBased){ + addItemOrDrop(player, reward.id,reward.amount) + rewardXP(player, Skills.FARMING, plantable.harvestXP) + if (patch.patch.type in livesBased) { patch.rollLivesDecrement( getDynLevel(player, Skills.FARMING), - requiredItem == Items.MAGIC_SECATEURS_7409 + inInventory(player, Items.MAGIC_SECATEURS_7409) //add ||inEquipment() check when Fairy Tale pt 1 has been implemented ) } else { patch.harvestAmt-- - if(patch.harvestAmt <= 0 && crop == plantable.harvestItem){ + if (patch.harvestAmt <= 0 && crop == plantable.harvestItem) { patch.clear() } + else if (fPatch.type == PatchType.MUSHROOM_PATCH){ + patch.setCurrentState(patch.getCurrentState() + 1) + } + } + if (sendHarvestMessages && (patch.cropLives <= 0 || patch.harvestAmt <= 0)) { + sendMessage(player, "The $patchName is now empty.") } return patch.cropLives <= 0 || patch.harvestAmt <= 0 } @@ -107,13 +128,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..ab170aa74 100644 --- a/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt +++ b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt @@ -1,54 +1,83 @@ 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 + } + } + if (patch?.patch?.type == PatchType.FRUIT_TREE_PATCH) { + val isTreeStump = patch?.getCurrentState() == patch?.plantable!!.value + 25 + if (patch!!.isGrown() && !isTreeStump) { + sendMessage(player, "You need to chop this tree down first.") // this message is not authentic + stage = 1000 + return true + } + } + 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..e048914a1 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt @@ -1,68 +1,152 @@ package content.global.skill.farming -import core.game.node.item.Item -import org.rs09.consts.Items +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 FarmerPayOptionDialogue(val patch: Patch): DialogueFile() { +class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): DialogueFile() { var item: Item? = null - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - START_DIALOGUE -> { - item = patch.plantable?.protectionItem - val protectionText = when(item?.id){ - Items.COMPOST_6032 -> if(item?.amount == 1) "bucket of compost" else "buckets of compost" - Items.POTATOES10_5438 -> if(item?.amount == 1) "sack of potatoes" else "sacks of potatoes" - Items.ONIONS10_5458 -> if(item?.amount == 1) "sack of onions" else "sacks of onions" - Items.CABBAGES10_5478 -> if(item?.amount == 1) "sack of cabbages" else "sacks of cabbages" - Items.JUTE_FIBRE_5931 -> "jute fibres" - Items.APPLES5_5386 -> if(item?.amount == 1) "basket of apples" else "baskets of apples" - Items.MARIGOLDS_6010 -> "harvest of marigold" - Items.TOMATOES5_5968 -> if(item?.amount == 1) "basket of tomatoes" else "baskets of tomatoes" - Items.ORANGES5_5396 -> if(item?.amount == 1) "basket of oranges" else "baskets of oranges" - Items.COCONUT_5974 -> "coconuts" - Items.CACTUS_SPINE_6016 -> "cactus spines" - Items.STRAWBERRIES5_5406 -> if(item?.amount == 1) "basket of strawberries" else "baskets of strawberries" - Items.BANANAS5_5416 -> if(item?.amount == 1) "basket of bananas" else "baskets of bananas" - else -> item?.name?.toLowerCase() - } - if(item == null) npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } - else{ - npc("I would like ${item?.amount} $protectionText","to protect that patch.") - stage++ - } - } + var itemSecondary: Item? = null + var itemTertiary: Item? = null - 1 -> options("Sure!","No, thanks.").also { stage++ } - 2 -> { - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } + fun hasAllItems(): Boolean { + return (inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount)) + && (inInventory(player!!, itemSecondary!!.id, itemSecondary!!.amount) || inInventory(player!!, note(itemSecondary!!).id, note(itemSecondary!!).amount)) + && (inInventory(player!!, itemTertiary!!.id, itemTertiary!!.amount) || inInventory(player!!, note(itemTertiary!!).id, note(itemTertiary!!).amount)) + } + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> { + if (patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown()) { + // This is for the right-click "Pay" option; full dialogue is in GardenerDialoguePlugin + showTopics( + Topic("Yes, get rid of the tree.", 300, true), + Topic("No thanks.", END_DIALOGUE, true), + title = "Pay 200 gp to have the tree chopped down?" + ) + } else if (patch.protectionPaid) { + npc("I don't know what you're talking about - I'm already", "looking after that patch for you.").also { stage = 100 } + } else if (patch.isDead) { + npc("That patch is dead - it's too late for me to do", "anything about it now.").also { stage = END_DIALOGUE } + } else if (patch.isDiseased) { + npc("That patch is diseased - I can't look after it", "until it has been cured.").also { stage = END_DIALOGUE } // this dialogue is not authentic + } else if (patch.isWeedy() || patch.isEmptyAndWeeded()) { + npc(FacialExpression.NEUTRAL, "You don't have anything planted in that patch. Plant", "something and I might agree to look after it for you.").also { stage = END_DIALOGUE } + } else if (patch.isGrown()) { + npc("That patch is already fully grown!", "I don't know what you want me to do with it!").also { stage = END_DIALOGUE } } else { - item = Item(item!!.noteChange,item!!.amount) - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } + item = patch.plantable?.protectionItem + itemSecondary = patch.plantable?.protectionItemSecondary + itemTertiary = patch.plantable?.protectionItemTertiary + + val protectionText = when (item?.id) { + Items.COMPOST_6032 -> if (item?.amount == 1) "bucket of compost" else "buckets of compost" + Items.POTATOES10_5438 -> if (item?.amount == 1) "sack of potatoes" else "sacks of potatoes" + Items.ONIONS10_5458 -> if (item?.amount == 1) "sack of onions" else "sacks of onions" + Items.CABBAGES10_5478 -> if (item?.amount == 1) "sack of cabbages" else "sacks of cabbages" + Items.JUTE_FIBRE_5931 -> "jute fibres" + Items.APPLES5_5386 -> if (item?.amount == 1) "basket of apples" else "baskets of apples" + Items.MARIGOLDS_6010 -> "harvest of marigold" + Items.TOMATOES5_5968 -> if (item?.amount == 1) "basket of tomatoes" else "baskets of tomatoes" + Items.ORANGES5_5396 -> if (item?.amount == 1) "basket of oranges" else "baskets of oranges" + Items.COCONUT_5974 -> "coconuts" + Items.CACTUS_SPINE_6016 -> "cactus spines" + Items.STRAWBERRIES5_5406 -> if (item?.amount == 1) "basket of strawberries" else "baskets of strawberries" + Items.BANANAS5_5416 -> if (item?.amount == 1) "basket of bananas" else "baskets of bananas" + else -> item?.name?.lowercase() + } + if (item == null) { + npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay + && !hasAllItems()) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc(FacialExpression.HAPPY, "I want $amount $protectionText, one monkey bar,", "and one ground tooth for that.") + stage = 200 + } else if (patch.patch.type == PatchType.CALQUAT_TREE_PATCH && getQuestStage(player!!, Quests.JUNGLE_POTION) < 100) { + npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } // Cannot confirm authenticity of this line just that there should be a rejection here + } else if (quickPay && !(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc(FacialExpression.HAPPY, "I want $amount $protectionText for that.") + stage = 200 + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay) { + showTopics( + //Found a 2011 source for quick-pay, but no earlier dialogue sources (https://www.youtube.com/watch?v=RdIcNH50v7I) + Topic("Yes", 20, true), + Topic("No", END_DIALOGUE, true), + title = "Pay the gnome?" + ) + } else if (quickPay) { + val amount = if (item?.amount == 1) "one" else item?.amount + showTopics( + Topic("Yes", 20, true), + Topic("No", END_DIALOGUE, true), + title = "Pay $amount $protectionText?" + ) + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText,", "one monkey bar, and one ground tooth for that.") + stage++ } else { - player("I don't have that to give.").also { stage = 20 } + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText for that.") + stage++ } } } - 10 -> { - if(player!!.inventory.remove(item)){ - npc("Thank you! I'll keep an eye on this patch.").also { stage = END_DIALOGUE } - patch?.protectionPaid = true + 1 -> { + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && !hasAllItems()) { + player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } + } else if (!(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { + player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } } else { - npc("That stuff just... vanished....").also { stage = END_DIALOGUE } + showTopics( + Topic(FacialExpression.NEUTRAL, "Okay, it's a deal.", 20), + Topic(FacialExpression.NEUTRAL, "No, that's too much.", 10) + ) } } + 10 -> npc("Well, I'm not wasting my time for free.").also { stage = END_DIALOGUE } + 20 -> { - npc("Come back when you do.") - stage = END_DIALOGUE + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH + && (removeItem(player!!, item) || removeItem(player!!, note(item!!))) + && (removeItem(player!!, itemSecondary) || removeItem(player!!, note(itemSecondary!!))) + && (removeItem(player!!, itemTertiary) || removeItem(player!!, note(itemTertiary!!)))) { + patch.protectionPaid = true + npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "that tree grows up good and strong.").also { stage = END_DIALOGUE } + } else if (patch.patch.type != PatchType.SPIRIT_TREE_PATCH + && removeItem(player!!, item) || removeItem(player!!, note(item!!))) { + patch.protectionPaid = true + // Note: A slight change in this dialogue was seen in a December 2009 video - https://youtu.be/7gVh42ylQ48?t=138 + npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "those crops grow for you.").also { stage = END_DIALOGUE } + } else { + npc("This shouldn't be happening. Please report this.").also { stage = END_DIALOGUE } + } + } + + 100 -> player("Oh sorry, I forgot.").also { stage = END_DIALOGUE } + + // Right-click "Pay" - protect patch - player doesn't have payment + 200 -> player(FacialExpression.NEUTRAL, "Thanks, maybe another time.").also { stage = END_DIALOGUE } + + // Right-click "Pay" - chop down tree + 300 -> { + if (removeItem(player!!, Item(Items.COINS_995, 200))) { + patch.clear() + dialogue("The gardener obligingly removes your tree.").also { stage = END_DIALOGUE } + } else { + dialogue("You need 200 gp to pay for that.").also { stage = END_DIALOGUE } // not authentic + } } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt index 724ce6c73..52d7874ad 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt @@ -1,5 +1,6 @@ package content.global.skill.farming +import core.api.openDialogue import core.game.node.Node import core.game.node.entity.player.Player import core.game.interaction.InteractionListener @@ -8,35 +9,20 @@ import core.game.interaction.IntType class FarmerPayOptionHandler : InteractionListener { override fun defineListeners() { - on(IntType.NPC,"pay","pay (north)","pay (north-west)"){ player, node -> + on(IntType.NPC,"pay","pay (north)","pay (north-west)") { player, node -> return@on attemptPay(player,node,0) } - on(IntType.NPC,"pay (south)","pay (south-east)"){ player, node -> + on(IntType.NPC,"pay (south)","pay (south-east)") { player, node -> return@on attemptPay(player,node,1) } } - fun attemptPay(player: Player, node: Node, index: Int): Boolean{ + fun attemptPay(player: Player, node: Node, index: Int): Boolean { val farmer = Farmers.forId(node.id) ?: return false val patch = farmer.patches[index].getPatchFor(player) - if(patch.plantable == null){ - player.dialogueInterpreter.sendDialogue("I have nothing to protect in that patch.") - return true - } - - if(patch.protectionPaid){ - player.dialogueInterpreter.sendDialogue("I have already paid to protect that patch.") - return true - } - - if(patch.isGrown()){ - player.dialogueInterpreter.sendDialogue("This patch is already fully grown!") - return true - } - - player.dialogueInterpreter.open(FarmerPayOptionDialogue(patch),node.asNpc()) + openDialogue(player, FarmerPayOptionDialogue(patch, true), node.asNpc()) return true } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/Farmers.kt b/Server/src/main/content/global/skill/farming/Farmers.kt index 4a57feeef..360e583c5 100644 --- a/Server/src/main/content/global/skill/farming/Farmers.kt +++ b/Server/src/main/content/global/skill/farming/Farmers.kt @@ -21,7 +21,12 @@ enum class Farmers(val id: Int, val patches: Array) { FRANCIS(2327, arrayOf(FarmingPatch.ENTRANA_HOPS)), DREVEN(2335, arrayOf(FarmingPatch.CHAMPIONS_GUILD_BUSH)), TARIA(2336, arrayOf(FarmingPatch.RIMMINGTON_BUSH)), - TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)); + TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)), + FRIZZY(4560, arrayOf(FarmingPatch.PORT_SARIM_SPIRIT_TREE)), + YULF(4561, arrayOf(FarmingPatch.ETCETERIA_SPIRIT_TREE)), + PRAISTAN(4562, arrayOf(FarmingPatch.KARAMJA_SPIRIT_TREE)), + IMIAGO(8041, arrayOf(FarmingPatch.CALQUAT_TREE)) + ; companion object{ @JvmField diff --git a/Server/src/main/content/global/skill/farming/FarmingPatch.kt b/Server/src/main/content/global/skill/farming/FarmingPatch.kt index e6ab30f5e..d19e8871e 100644 --- a/Server/src/main/content/global/skill/farming/FarmingPatch.kt +++ b/Server/src/main/content/global/skill/farming/FarmingPatch.kt @@ -1,77 +1,110 @@ 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), + CALQUAT_TREE(726, PatchType.CALQUAT_TREE_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 + patchNodes.add(21950) //harmony island allotment + patchNodes.add(28919) //lletya fruit patch. Was usable in 2009: https://www.youtube.com/watch?v=7sXOW4CRZ3k + //patchNodes.add(37988) Wilderness flower patch. Can only plant Limpwurt Seeds. Intentionally left out, as it is a reward from Spirit of Summer. + + + for (patch in patchNodes) { + val def = SceneryDefinition.forId(patch) + nodeMap[def.varbitID] = def + } + } @JvmStatic fun forObject(obj: Scenery): FarmingPatch?{ @@ -81,17 +114,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..380334445 100644 --- a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt +++ b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt @@ -5,13 +5,16 @@ import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node import content.global.skill.summoning.familiar.GiantEntNPC +import content.global.skill.summoning.familiar.WolpertingerNPC import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse 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 +47,58 @@ 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) + + if (familiar != null && familiar is WolpertingerNPC && patch.patch.type == PatchType.BUSH_PATCH) { + addItemOrDrop(player, reward.id, reward.amount * 2) + rewardXP(player, Skills.FARMING, plantable.harvestXP * 2) + } + else { + addItemOrDrop(player, reward.id, reward.amount) + rewardXP(player, Skills.FARMING, plantable.harvestXP) + } patch.setCurrentState(patch.getCurrentState() - 1) + if (patch.patch.type == PatchType.CACTUS_PATCH) { + 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()}.") + } + + if (plantable == Plantable.POISON_IVY_SEED && patch.patch == FarmingPatch.CHAMPIONS_GUILD_BUSH){ + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 0) + } + return patch.getFruitOrBerryCount() == 0 } }) diff --git a/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt b/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt new file mode 100644 index 000000000..288bf1e01 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FruitTreeChopper.kt @@ -0,0 +1,62 @@ +package content.global.skill.farming + +import content.data.skill.SkillingTool +import core.api.* +import core.cache.def.impl.SceneryDefinition +import core.game.interaction.OptionHandler +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.plugin.Initializable +import core.plugin.Plugin +import core.tools.RandomFunction +import org.rs09.consts.Sounds + +@Initializable +class FruitTreeChopper : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + SceneryDefinition.setOptionHandler("chop-down",this) + SceneryDefinition.setOptionHandler("chop down",this) + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + player ?: return false + node ?: return false + + val fPatch = FarmingPatch.forObject(node.asScenery()) + fPatch ?: return false + + val patch = fPatch.getPatchFor(player) + + val plantable = patch.plantable + plantable ?: return false + + if (SkillingTool.getHatchet(player) == null) { + sendMessage(player, "You do not have an axe to use.") + return true + } + + val animation = SkillingTool.getHatchet(player).animation + + submitIndividualPulse(player, object : Pulse(animation.duration) { + override fun pulse(): Boolean { + animate(player, animation) + val soundIndex = RandomFunction.random(0, woodcuttingSounds.size) + playAudio(player, woodcuttingSounds[soundIndex]) + patch.setCurrentState(patch.getCurrentState() + 19) + sendMessage(player, "You chop down the ${plantable.displayName.lowercase().removeSuffix(" sapling")}.") + return true + } + }) + return true + } + + private val woodcuttingSounds = intArrayOf( + Sounds.WOODCUTTING_HIT_3038, + Sounds.WOODCUTTING_HIT_3039, + Sounds.WOODCUTTING_HIT_3040, + Sounds.WOODCUTTING_HIT_3041, + Sounds.WOODCUTTING_HIT_3042 + ) +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/HealthChecker.kt b/Server/src/main/content/global/skill/farming/HealthChecker.kt index 990f6b167..1813a5145 100644 --- a/Server/src/main/content/global/skill/farming/HealthChecker.kt +++ b/Server/src/main/content/global/skill/farming/HealthChecker.kt @@ -1,19 +1,21 @@ 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.player.link.diary.DiaryType 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 +27,47 @@ 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 (!patch.isCheckable()) { + 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) + if (fPatch == FarmingPatch.BRIMHAVEN_FRUIT_TREE) { + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 12) + } + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } + PatchType.SPIRIT_TREE_PATCH -> { + patch.setCurrentState(patch.getCurrentState() - 24) + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } + PatchType.BUSH_PATCH -> { + patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) + sendMessage(player, "You examine the bush for signs of disease and find that it's in perfect health.") + } + PatchType.CACTUS_PATCH -> { + patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3) + sendMessage(player, "You examine the cactus for signs of disease and find that it is in perfect health.") + } + PatchType.CALQUAT_TREE_PATCH -> { + patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 6) + sendMessage(player, "You examine the calquat tree for signs of disease and find that it is in perfect health.") + } + else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt") } - 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/LeprechaunNoter.kt b/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt index 3a3d47bbf..8cf6eff81 100644 --- a/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt +++ b/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt @@ -12,26 +12,29 @@ class LeprechaunNoter : InteractionListener { val LEPRECHAUNS = intArrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861) override fun defineListeners() { - onUseWith(IntType.NPC,CROPS,*LEPRECHAUNS){ player, used, with -> + onUseAnyWith(IntType.NPC,*LEPRECHAUNS) { player, used, with -> val usedItem = used.asItem() val npc = with.asNpc() val expr = when(npc.id){ - 3021 -> core.game.dialogue.FacialExpression.OLD_NORMAL + NPCs.TOOL_LEPRECHAUN_3021 -> core.game.dialogue.FacialExpression.OLD_NORMAL else -> core.game.dialogue.FacialExpression.FRIENDLY } - - if(usedItem.noteChange != usedItem.id){ - val amt = player.inventory.getAmount(usedItem.id) - if(player.inventory.remove(Item(usedItem.id,amt))){ - player.inventory.add(Item(usedItem.noteChange,amt)) + when { + (!usedItem.definition.isUnnoted) -> { // If the item is a banknote + player.dialogueInterpreter.sendDialogues(npc.id,expr,"That IS a banknote!") + } + (usedItem.id !in CROPS || usedItem.definition.noteId < 0) -> { // If the item is not a crop or cannot be noted + player.dialogueInterpreter.sendDialogues(npc.id,expr,"Nay, I can't turn that into a banknote.") + } + else -> { // Note the item + val amt = amountInInventory(player, usedItem.id) + if (removeItem(player, Item(usedItem.id, amt))) { + addItem(player, usedItem.noteChange, amt) + } + sendItemDialogue(player, usedItem.id, "The leprechaun exchanges your items for banknotes.") } - sendItemDialogue(player,usedItem.id,"The leprechaun exchanges your items for banknotes.") - } else { - // Unsure why the line below no longer functions, despite only changing the line above to be more correct. Using your note(NOT CROP) on the leprechaun no longer functions because of this. - Crash - player.dialogueInterpreter.sendDialogues(npc.id,expr,"That IS a banknote!") } - - return@onUseWith true + return@onUseAnyWith 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..b2235419e 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 @@ -15,12 +16,13 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl var compost = CompostType.NONE var protectionPaid = false var cropLives = 3 + val checkablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CACTUS_PATCH, PatchType.CALQUAT_TREE_PATCH) 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 +30,18 @@ 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 + && plantable?.applicablePatch != PatchType.BELLADONNA_PATCH + && plantable?.applicablePatch != PatchType.MUSHROOM_PATCH + && plantable?.applicablePatch != PatchType.EVIL_TURNIP_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 +74,8 @@ 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.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 +88,156 @@ 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 isChoppedFruitTree(): Boolean { + return (patch.type == PatchType.FRUIT_TREE_PATCH) + && getCurrentState() == (plantable?.value ?: 0) + 25 + } + + 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) + PatchType.SPIRIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 24) + PatchType.CALQUAT_TREE_PATCH -> setVarbit(player, patch.varbit, 34) + else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name + "at" + patch.name) } } else { 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.SPIRIT_TREE_PATCH -> { + if(isDead) setVisualState(getSpiritTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getSpiritTreeDiseaseValue()) } - 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.FRUIT_TREE_PATCH -> { + if(isDead) setVisualState(getFruitTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) } - 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.BELLADONNA_PATCH -> { + if(isDead) setVisualState(getBelladonnaDeathValue()) + else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue()) + else setVisualState((plantable?.value ?: 0) + currentGrowthStage) } - 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.CACTUS_PATCH -> { + if(isDead) setVisualState(getCactusDeathValue()) + else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue()) + } + PatchType.HERB_PATCH -> { + if(isDead) setVisualState(getHerbDeathValue()) + else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue()) + else { + var state = (plantable?.value ?: 0) + currentGrowthStage + // Spirit Weed contains states with bits 0x40 and 0x80 set, which the disease-free patch scenery interprets as watered/dead + // Visually transmogrifies Spirit Weed (204-208) into Guam (4-8) while it's planted in the disease-free patch + if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB && plantable == Plantable.SPIRIT_WEED_SEED) { + state = 4 + currentGrowthStage + } + setVisualState(state) + } + } + PatchType.MUSHROOM_PATCH -> { + if(isDead) setVisualState(getMushroomDeathValue()) + else if(isDiseased && !isDead) setVisualState(getMushroomDiseaseValue()) + } + PatchType.CALQUAT_TREE_PATCH -> { + if(isDead) setVisualState(getCalquatDeathValue()) + else if(isDiseased && !isDead) setVisualState(getCalquatDiseaseValue()) + } + else -> { + log(this::class.java, Log.WARN, "Invalid patch type attempted to be displayed: " + patch.type.name) } - 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,20 +246,43 @@ 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 - } else { + } + else if (plantable == Plantable.REDBERRY_SEED + || plantable == Plantable.CADAVABERRY_SEED){ + return (plantable?.value ?: 0) + currentGrowthStage + 65 + } + else { return (plantable?.value ?: 0) + currentGrowthStage + 64 } } private fun getBushDeathValue(): Int{ if(plantable == Plantable.POISON_IVY_SEED){ - return (plantable?.value ?: 0) + currentGrowthStage + 22 - } else { - return (plantable?.value ?: 0) + currentGrowthStage + 126 + return (plantable?.value ?: 0) + currentGrowthStage + 20 } + else if (plantable == Plantable.REDBERRY_SEED + || plantable == Plantable.CADAVABERRY_SEED + || plantable == Plantable.WHITEBERRY_SEED){ + return (plantable?.value ?: 0) + currentGrowthStage + 129 + } + else { + return (plantable?.value ?: 0) + currentGrowthStage + 128 + } + } + + private fun getSpiritTreeDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 12 + } + + private fun getSpiritTreeDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 24 } private fun getFruitTreeDiseaseValue(): Int { @@ -207,6 +309,22 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return (plantable?.value ?: 0) + currentGrowthStage + 16 } + private fun getMushroomDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 11 + } + + private fun getMushroomDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 16 + } + + private fun getCalquatDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 14 + } + + private fun getCalquatDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 21 + } + private fun getHerbDiseaseValue(): Int { return if (plantable?.value ?: -1 <= 103) { 128 + (((plantable?.ordinal ?: 0) - Plantable.GUAM_SEED.ordinal) * 3) + currentGrowthStage - 1 @@ -224,7 +342,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } private fun grow(){ - if(isWeedy() && getCurrentState() > 0) { + if((isWeedy() || isEmptyAndWeeded() || (plantable == Plantable.SCARECROW && !isGrown())) && getCurrentState() > 0) { nextGrowth = System.currentTimeMillis() + 60000 setCurrentState(getCurrentState() - 1) currentGrowthStage-- @@ -236,47 +354,56 @@ 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 + && plantable != Plantable.POISON_IVY_SEED + && currentGrowthStage != 0){ + isDiseased = true + // If we manually set disease mod reset it back to 0 so that crops can naturally grow after being treated/accidentally attempted to disease when they cannot be + if (diseaseMod < 0) diseaseMod = 0 + return } - if((patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable != null && plantable?.stages == currentGrowthStage + 1){ + if(isCheckable() && 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(arrayOf(PatchType.FRUIT_TREE_PATCH, PatchType.BUSH_PATCH, PatchType.CACTUS_PATCH, PatchType.CALQUAT_TREE_PATCH).any{ patch.type == it} && plantable?.stages == currentGrowthStage){ + if((patch.type == PatchType.BUSH_PATCH && getFruitOrBerryCount() < 4) || + (patch.type == PatchType.FRUIT_TREE_PATCH && getFruitOrBerryCount() < 6) || + (patch.type == PatchType.CACTUS_PATCH && getFruitOrBerryCount() < 3) || + (patch.type == PatchType.CALQUAT_TREE_PATCH && getFruitOrBerryCount() < 6) + ){ setCurrentState(getCurrentState() + 1) } } - if(patch.type == PatchType.TREE) { + if(patch.type == PatchType.TREE_PATCH) { // Willow branches if(harvestAmt < 6) { - harvestAmt++; + harvestAmt++ } } - if(plantable?.stages ?: 0 > currentGrowthStage && !isGrown()){ + if((plantable?.stages ?: 0) > currentGrowthStage && !isGrown()){ currentGrowthStage++ setCurrentState(getCurrentState() + 1) isWatered = false } - - regrowIfTreeStump() } 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) @@ -304,7 +431,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl 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,11 +446,15 @@ 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(arrayOf(PatchType.FRUIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH).any{ it == patch.type } && isGrown()) { // Fruit trees take 160 minutes per stage to grow, but // restocking their fruit should take 40 minutes per fruit minutes = 40 } + else if(plantable == Plantable.WILLOW_SAPLING && isGrown()) { + // Willow Branches grow back in only 5 minutes + minutes = 5 + } return minutes } @@ -336,10 +467,16 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl FarmingPatch.CATHERBY_ALLOTMENT_S,FarmingPatch.CATHERBY_ALLOTMENT_N -> FarmingPatch.CATHERBY_FLOWER_C FarmingPatch.PORT_PHAS_ALLOTMENT_SE,FarmingPatch.PORT_PHAS_ALLOTMENT_NW -> FarmingPatch.PORT_PHAS_FLOWER_C else -> return false - }.getPatchFor(player) + }.getPatchFor(player, false) - return (fpatch.plantable != null && + if (fpatch.plantable == Plantable.SCARECROW && fpatch.plantable == plantable?.protectionFlower){ + return true + } else return (fpatch.plantable != null && (fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589)) && fpatch.isGrown()) } + + fun isCheckable(): Boolean{ + return (patch.type in checkablePatches) + } } diff --git a/Server/src/main/content/global/skill/farming/PatchRaker.kt b/Server/src/main/content/global/skill/farming/PatchRaker.kt index ee4641925..21967671b 100644 --- a/Server/src/main/content/global/skill/farming/PatchRaker.kt +++ b/Server/src/main/content/global/skill/farming/PatchRaker.kt @@ -1,34 +1,48 @@ 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) //authentically destroys weeds if inventory was full + rewardXP(player, Skills.FARMING, 4.0) + } + if (patchStage >= 3) { + resetAnimator(player) } return patchStage >= 3 } }) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/PatchType.kt b/Server/src/main/content/global/skill/farming/PatchType.kt index 20a76ee97..fd0836126 100644 --- a/Server/src/main/content/global/skill/farming/PatchType.kt +++ b/Server/src/main/content/global/skill/farming/PatchType.kt @@ -2,15 +2,21 @@ 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), + CALQUAT_TREE_PATCH(160); + + /** + * 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..6d08d9386 100644 --- a/Server/src/main/content/global/skill/farming/Plantable.kt +++ b/Server/src/main/content/global/skill/farming/Plantable.kt @@ -3,88 +3,90 @@ package content.global.skill.farming import core.game.node.item.Item import org.rs09.consts.Items -enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null,val protectionFlower: Plantable? = null) { +enum class Plantable(val itemID: Int, val displayName: String, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null, val protectionFlower: Plantable? = null, val protectionItemSecondary: Item? = null, val protectionItemTertiary: Item? = null) { - //Flowers - MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER,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",36,-3,0.0,0.0,0.0,23,PatchType.FLOWER_PATCH,Items.SCARECROW_6059), - //Allotments - POTATO_SEED(5318, 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED), - ONION_SEED(5319, 13, 4, 9.5, 10.5,0.0, 5, PatchType.ALLOTMENT,Items.ONION_1957,Item(Items.POTATOES10_5438),MARIGOLD_SEED), - CABBAGE_SEED(5324, 20, 4, 10.0, 11.5, 0.0,7, PatchType.ALLOTMENT,Items.CABBAGE_1965,Item(Items.ONIONS10_5458),ROSEMARY_SEED), - TOMATO_SEED(5322,27,4,12.5,14.0,0.0,12,PatchType.ALLOTMENT,Items.TOMATO_1982,Item(Items.CABBAGES10_5478,2),MARIGOLD_SEED), - SWEETCORN_SEED(5320,34,6,17.0,19.0,0.0,20,PatchType.ALLOTMENT,Items.SWEETCORN_5986,Item(Items.JUTE_FIBRE_5931,10),SCARECROW), - STRAWBERRY_SEED(5323,43,6,26.0,29.0,0.0,31,PatchType.ALLOTMENT,Items.STRAWBERRY_5504,Item(Items.APPLES5_5386)), - WATERMELON_SEED(5321,52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED), + // Allotments + POTATO_SEED(Items.POTATO_SEED_5318, "potato seed", 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED), + ONION_SEED(Items.ONION_SEED_5319, "onion seed", 13, 4, 9.5, 10.5,0.0, 5, PatchType.ALLOTMENT,Items.ONION_1957,Item(Items.POTATOES10_5438),MARIGOLD_SEED), + CABBAGE_SEED(Items.CABBAGE_SEED_5324, "cabbage seed", 20, 4, 10.0, 11.5, 0.0,7, PatchType.ALLOTMENT,Items.CABBAGE_1965,Item(Items.ONIONS10_5458),ROSEMARY_SEED), + TOMATO_SEED(Items.TOMATO_SEED_5322,"tomato seed",27,4,12.5,14.0,0.0,12,PatchType.ALLOTMENT,Items.TOMATO_1982,Item(Items.CABBAGES10_5478,2),MARIGOLD_SEED), + SWEETCORN_SEED(Items.SWEETCORN_SEED_5320,"sweetcorn seed",34,6,17.0,19.0,0.0,20,PatchType.ALLOTMENT,Items.SWEETCORN_5986,Item(Items.JUTE_FIBRE_5931,10),SCARECROW), + STRAWBERRY_SEED(Items.STRAWBERRY_SEED_5323,"strawberry seed",43,6,26.0,29.0,0.0,31,PatchType.ALLOTMENT,Items.STRAWBERRY_5504,Item(Items.APPLES5_5386)), + WATERMELON_SEED(Items.WATERMELON_SEED_5321,"watermelon seed",52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED), - //Hops - BARLEY_SEED(5305,49,4,8.5,9.5,0.0,3,PatchType.HOPS,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,7,31.5,12.0,177.5,36,PatchType.BUSH_PATCH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)), + JANGERBERRY_SEED(Items.JANGERBERRY_SEED_5104,"jangerberry bush seed",38,8,50.5,19.0,284.5,48,PatchType.BUSH_PATCH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)), + WHITEBERRY_SEED(Items.WHITEBERRY_SEED_5105,"whiteberry bush seed",51,8,78.0,29.0,437.5,59,PatchType.BUSH_PATCH,Items.WHITE_BERRIES_239,Item(Items.MUSHROOM_6004,8)), + POISON_IVY_SEED(Items.POISON_IVY_SEED_5106,"poison ivy bush seed",197,8,120.0,45.0,675.0,70,PatchType.BUSH_PATCH,Items.POISON_IVY_BERRIES_6018,null), - //Herbs - GUAM_SEED(5291,4,4,11.0,12.5,0.0,9,PatchType.HERB,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", 4, 6, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004), + CACTUS_SEED(Items.CACTUS_SEED_5280, "cactus seed", 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016), + EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, "evil turnip seed", 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134), + CALQUAT_SAPLING(Items.CALQUAT_SAPLING_5503, "calquat tree sapling", 4, 8, 130.5, 48.5, 12096.0, 72, PatchType.CALQUAT_TREE_PATCH, Items.CALQUAT_FRUIT_5980, Item(Items.POISON_IVY_BERRIES_6018, 8)), + SPIRIT_SAPLING(Items.SPIRIT_SAPLING_5375, "spirit tree sapling", 8, 12, 199.5, 0.0, 19301.0, 83, PatchType.SPIRIT_TREE_PATCH, 0, Item(Items.MONKEY_NUTS_4012, 5), null, Item(Items.MONKEY_BAR_4014, 1), Item(Items.GROUND_TOOTH_9082, 1) ) ; - 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/SackBasketOptionHandler.kt b/Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt index ce7a4019a..b9a25c563 100644 --- a/Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt +++ b/Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt @@ -61,7 +61,7 @@ class SackBasketOptionHandler : OptionHandler() { appropriateProduce.amount = (max - specific) } - if(player.inventory.remove(item) && player.inventory.remove(appropriateProduce)) + if(player.inventory.remove(appropriateProduce) && player.inventory.remove(item)) player.inventory.add(Item(container.containers[specific + appropriateProduce.amount])) } diff --git a/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt b/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt index 42d921dd9..5f03a292f 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,40 +44,33 @@ 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 - Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359 - Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360 - Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361 - Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362 - Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480 - Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481 - Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482 - Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483 - Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484 - Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485 - Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486 - Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363 + Items.ACORN_5312 -> Items.OAK_SEEDLING_5358 + Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359 + Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360 + Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361 + Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362 + Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480 + Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481 + Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482 + Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483 + Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484 + Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485 + Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486 + Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363 + Items.CALQUAT_TREE_SEED_5290 -> Items.CALQUAT_SEEDLING_5487 else -> -1 } } @@ -94,7 +88,8 @@ class SeedlingListener : InteractionListener { Items.PINEAPPLE_SEED_5287, Items.PAPAYA_TREE_SEED_5288, Items.PALM_TREE_SEED_5289, - Items.SPIRIT_SEED_5317 + Items.SPIRIT_SEED_5317, + Items.CALQUAT_TREE_SEED_5290 ) val TREE_SEEDLINGS = intArrayOf( @@ -110,8 +105,9 @@ class SeedlingListener : InteractionListener { Items.PINEAPPLE_SEEDLING_5484, Items.PAPAYA_SEEDLING_5485, Items.PALM_SEEDLING_5486, - Items.SPIRIT_SEEDLING_5363 + Items.SPIRIT_SEEDLING_5363, + Items.CALQUAT_SEEDLING_5487, ) private val WATERING_CANS = intArrayOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333) -} \ 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/Stump.kt b/Server/src/main/content/global/skill/farming/Stump.kt new file mode 100644 index 000000000..60c9bab8a --- /dev/null +++ b/Server/src/main/content/global/skill/farming/Stump.kt @@ -0,0 +1,5 @@ +package content.global.skill.farming + +class Stump(val varbit: Int, val TTL: Long) { + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/ToolLeprechaunDialogue.kt b/Server/src/main/content/global/skill/farming/ToolLeprechaunDialogue.kt new file mode 100644 index 000000000..4ad7953f9 --- /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.Items +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) + } +} + +/** + * This Leprechaun Goth should appear once you have Spirit of Summer done, but is currently defaulted as not. + */ +@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 ToolLeprechaunGothDialogue(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..29e0d719d 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) 31 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) 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..e443ad061 100644 --- a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt +++ b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt @@ -5,82 +5,124 @@ 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() + val trimmablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH) + + if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) { + if (!hasRequirement(player, Quests.MY_ARMS_BIG_ADVENTURE)) + 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 in trimmablePatches) { + 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 +131,232 @@ 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 && patch.type !in trimmablePatches) { + 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()) { + 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 - } + + /*if (patch == FarmingPatch.WILDERNESS_FLOWER && plantable != Plantable.LIMPWURT_SEED){ + return@onUseWith true + }*/ + + val requiredItem = when (patch.type) { + PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CALQUAT_TREE_PATCH -> Items.SPADE_952 + PatchType.FLOWER_PATCH -> if (plantable == Plantable.SCARECROW) null else Items.SEED_DIBBER_5343 + else -> Items.SEED_DIBBER_5343 } - 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)) { + sendDialogue(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + return@onUseWith true + } + val spiritTreePlanted = (getVarbit(player, 720) > 3 || getVarbit(player, 722) > 3 || getVarbit(player, 724) > 3) + if (plantable == Plantable.SPIRIT_SAPLING && spiritTreePlanted){ + sendDialogue(player, "You already have a spirit tree. You can't plant another one.") + return@onUseWith true + } + + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + val (anim, sound) = when (requiredItem) { + Items.SPADE_952 -> Pair(spadeDigAnim, Sounds.DIGSPADE_1470) + else -> Pair(seedDibberAnim,Sounds.FARMING_DIBBING_2432) } - 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 || patch.type == PatchType.SPIRIT_TREE_PATCH || + patch.type == PatchType.CALQUAT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0 + return@queueScript delayScript(player,anim.duration + delay) } - }) + 1 -> { + if (removeItem(player, plantItem)) { + if (plantable == Plantable.JUTE_SEED && patch == FarmingPatch.MCGRUBOR_HOPS && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 0, 7)) { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 7) + } + p.plant(plantable) + rewardXP(player, Skills.FARMING, plantable.plantingXP) + p.setNewHarvestAmount() + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) { + addItem(player, Items.PLANT_POT_5350) + } + + val itemAmount = + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) "the" + else if (plantItem.amount == 1) "a" + else plantItem.amount + val itemName = + if (plantItem.amount == 1) plantable.displayName else StringUtils.plusS( + plantable.displayName + ) + val patchName = p.patch.type.displayName() + if (plantable == Plantable.SCARECROW) { + sendMessage(player, "You place the scarecrow in the $patchName.") + } else { + sendMessage(player, "You plant $itemAmount $itemName in the $patchName.") + } + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } } } } - return@onUseWith true } } - 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..3cc14cd55 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt @@ -0,0 +1,155 @@ +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.isChoppedFruitTree()){ + 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) || (patch.plantable == Plantable.WILLOW_SAPLING && patch.harvestAmt < 6) + if (shouldPlayCatchup && !patch.isDead && !patch.isChoppedFruitTree()) { + var stagesToSimulate = if (!patch.isGrown()) { + if (patch.isWeedy() || patch.isEmptyAndWeeded()) patch.currentGrowthStage % 4 + else patch.plantable!!.stages - patch.currentGrowthStage + } else 0 + + if (patch.plantable != null) { + if (type == PatchType.BUSH_PATCH) + stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) + if (type == PatchType.FRUIT_TREE_PATCH) + stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) + if (patch.plantable == Plantable.WILLOW_SAPLING) + stagesToSimulate += Math.min(6, 6 - patch.harvestAmt) + } + + val nowTime = System.currentTimeMillis() + var simulatedTime = patch.nextGrowth + + 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/farming/timers/StumpGrowth.kt b/Server/src/main/content/global/skill/farming/timers/StumpGrowth.kt new file mode 100644 index 000000000..616bde43a --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/StumpGrowth.kt @@ -0,0 +1,59 @@ +package content.global.skill.farming.timers + +import core.game.node.entity.Entity +import core.game.system.timer.* +import core.game.node.entity.player.Player +import content.global.skill.farming.* +import core.tools.ticksToSeconds +import java.util.concurrent.TimeUnit +import org.json.simple.* + +class StumpGrowth : PersistTimer (1, "farming:stump", isSoft = true) { + val stumps = ArrayList() + lateinit var player: Player + + fun addStump(varbit: Int, ttl: Int){ + stumps.add( + Stump( + varbit, + System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(ticksToSeconds(ttl).toLong()) + ) + ) + } + + override fun onRegister (entity: Entity) { + player = (entity as? Player)!! + } + + override fun run (entity: Entity) : Boolean { + val removeList = ArrayList() + for (stump in stumps) { + if (System.currentTimeMillis() > stump.TTL) { + FarmingPatch.patches[stump.varbit]?.getPatchFor(player)?.regrowIfTreeStump() + removeList.add(stump) + } + } + stumps.removeAll(removeList) + return stumps.isNotEmpty() + } + + override fun save(root: JSONObject, entity: Entity) { + val stumpArray = JSONArray() + for(s in stumps){ + val seed = JSONObject() + seed["patch"] = s.varbit + seed["ttl"] = s.TTL + stumpArray.add(seed) + } + root.put("stumps",stumpArray) + } + + override fun parse(root: JSONObject, entity: Entity) { + (root["stumps"] as JSONArray).forEach { + val s = it as JSONObject + val id = s["patch"].toString().toInt() + val ttl = s["ttl"].toString().toLong() + stumps.add(Stump(id,ttl)) + } + } +} diff --git a/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java index 6da6f4327..50b969efd 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,11 @@ 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; /** * Represents the pulse used to light a log. @@ -29,7 +33,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,7 +88,8 @@ 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); GroundItemManager.create(groundItem); } } @@ -129,7 +135,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 +146,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 +178,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 +197,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..b75ccf9b4 --- /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), "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/AchievementDiaryAttributeKeys.kt b/Server/src/main/content/global/skill/fletching/AchievementDiaryAttributeKeys.kt new file mode 100644 index 000000000..8e3d7fe33 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/AchievementDiaryAttributeKeys.kt @@ -0,0 +1,8 @@ +package content.global.skill.fletching + +object AchievementDiaryAttributeKeys { + /** + * This attribute signifies that the fletch of the magic shortbow is complete which is the first stage of getting the achievement + */ + const val FLETCHED_UNSTRUNG_MAGIC_SHORTBOW = "diary:seers:fletch-magic-short-bow" +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java b/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java deleted file mode 100644 index 711e9b626..000000000 --- a/Server/src/main/content/global/skill/fletching/BoltCreatePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import core.game.dialogue.SkillDialogueHandler; -import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.fletching.items.bolts.Bolt; -import core.game.node.entity.skill.fletching.items.bolts.BoltPulse; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.game.node.entity.player.Player; -import org.crandor.net.packet.PacketRepository; -import org.crandor.net.packet.context.ChildPositionContext; -import org.crandor.net.packet.out.RepositionChild; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; - -*/ -/** - * Represents the bolt creating plugin. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class BoltCreatePlugin extends UseWithHandler { - - */ -/** - * Constructs a new {@code BoltCreatePlugin} {@code Object}. - *//* - - public BoltCreatePlugin() { - super(314); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Bolt bolt : Bolt.values()) { - addHandler(bolt.getItem().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - - } - -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java b/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java deleted file mode 100644 index 70c38b30a..000000000 --- a/Server/src/main/content/global/skill/fletching/BoltGemPlugin.java +++ /dev/null @@ -1,45 +0,0 @@ -//package core.game.node.entity.skill.fletching; -// -//import core.game.dialogue.SkillDialogueHandler; -//import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -//import core.game.node.entity.skill.fletching.items.gem.GemBolt; -//import core.game.node.entity.skill.fletching.items.gem.GemBoltPulse; -//import org.crandor.game.interaction.NodeUsageEvent; -//import org.crandor.game.interaction.UseWithHandler; -//import org.crandor.game.node.entity.player.Player; -//import org.crandor.net.packet.PacketRepository; -//import org.crandor.net.packet.context.ChildPositionContext; -//import org.crandor.net.packet.out.RepositionChild; -//import org.crandor.plugin.InitializablePlugin; -//import org.crandor.plugin.Plugin; -// -///** -// * Represents the plugin used to handle gem bolt making. -// * @author 'Vexia -// * @version 1.0 -// */ -//@InitializablePlugin -//public class BoltGemPlugin extends UseWithHandler { -// -// /** -// * Constructs a new {@code BoltGemPlugin} {@code Object}. -// */ -// public BoltGemPlugin() { -// super(45, 46, 9187, 9188, 9189, 9190, 9191, 9192, 9193, 9194); -// } -// -// @Override -// public Plugin newInstance(Object arg) throws Throwable { -// for (GemBolt bolt : GemBolt.values()) { -// addHandler(bolt.getBase().getId(), ITEM_TYPE, this); -// } -// return this; -// } -// -// @Override -// public boolean handle(final NodeUsageEvent event) { -// final Player player = event.getPlayer(); -// -// } -// -//} diff --git a/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java b/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java deleted file mode 100644 index c43104095..000000000 --- a/Server/src/main/content/global/skill/fletching/DartCreatePlugin.java +++ /dev/null @@ -1,49 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import core.game.node.entity.skill.fletching.items.darts.Dart; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; -import core.game.content.quest.touristrap.TouristTrap; - -*/ -/** - * Represents the plugin used to create a dart. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class DartCreatePlugin extends UseWithHandler { - - */ -/** - * Constructs a new {@code DartCreatePlugin} {@code Object}. - *//* - - public DartCreatePlugin() { - super(314); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Dart dart : Dart.values()) { - addHandler(dart.getItem().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - if (event.getPlayer().getQuestRepository().getQuest(TouristTrap.NAME).getStage(event.getPlayer()) < 60) { - event.getPlayer().getPacketDispatch().sendMessage("You need to start Tourist Trap in order to do this."); - return true; - } - event.getPlayer().getDialogueInterpreter().open(328933, event.getUsedItem(), event.getBaseItem()); - return true; - } - -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/Feathers.kt b/Server/src/main/content/global/skill/fletching/Feathers.kt new file mode 100644 index 000000000..f76b00a72 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/Feathers.kt @@ -0,0 +1,16 @@ +package content.global.skill.fletching + +import org.rs09.consts.Items + +object Feathers { + val all = intArrayOf( + Items.FEATHER_314, + Items.STRIPY_FEATHER_10087, + Items.RED_FEATHER_10088, + Items.BLUE_FEATHER_10089, + Items.YELLOW_FEATHER_10090, + Items.ORANGE_FEATHER_10091 + ) + + const val STANDARD = Items.FEATHER_314 +} diff --git a/Server/src/main/content/global/skill/fletching/FletchItem.java b/Server/src/main/content/global/skill/fletching/FletchItem.java deleted file mode 100644 index fee42d6c1..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchItem.java +++ /dev/null @@ -1,137 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents a fletching item to make. - * @author 'Vexia - *//* - -public enum FletchItem { - ARROW_SHAFT(FletchType.LOG, new Item(52, 15), 1, 5), SHORTBOW(FletchType.LOG, new Item(50), 5, 5), - LONGBOW(FletchType.LOG, new Item(48), 10, 10), - OAK_SHORTBOW(FletchType.OAK, new Item(54), 20, 16.5), - OAK_LONGBOW(FletchType.OAK, new Item(56), 25, 25), - WILLOW_SHORTBOW(FletchType.WILLOW, new Item(60), 35, 33.3), - WILLOW_LONGBOW(FletchType.WILLOW, new Item(58), 40, 41.5), - MAPLE_SHORTBOW(FletchType.MAPLE, new Item(64), 50, 50), - MAPLE_LONGBOW(FletchType.MAPLE, new Item(62), 55, 58.3), - YEW_SHORTBOW(FletchType.YEW, new Item(68), 65, 55), - YEW_LONGBOW(FletchType.YEW, new Item(66), 70, 75), - MAGIC_SHORTBOW(FletchType.MAGIC, new Item(72), 80, 83.3), - MAGIC_LONGBOW(FletchType.MAGIC, new Item(70), 85, 91.5), - WOODEN_STOCK(FletchType.LOG, new Item(9440), 9, 6), - OAK_STOCK(FletchType.OAK, new Item(9442), 24, 16), - WILLOW_STOCK(FletchType.WILLOW, new Item(9444), 39, 22), - TEAK_STOCK(FletchType.TEAK, new Item(9446), 46, 27), - MAPLE_STOCK(FletchType.MAPLE, new Item(9448), 54, 32), - MAHOGANY_STOCK(FletchType.MAHOGANY, new Item(9450), 61, 41), - YEW_STOCK(FletchType.YEW, new Item(9452), 69, 50); - - */ -/** - * Constructs a new {@code FletchItem.java} {@code Object}. - * @param type the type. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - FletchItem(final FletchType type, final Item product, final int level, final double experience) { - this.type = type; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the type this item pertaint o. - *//* - - private FletchType type; - - */ -/** - * Represents the product of this item. - *//* - - private Item product; - - */ -/** - * Represents the level. - *//* - - private final int level; - - */ -/** - * Represents the experience. - *//* - - private final double experience; - - */ -/** - * Gets the type. - * @return The type. - *//* - - public FletchType getType() { - return type; - } - - */ -/** - * Sets the type. - * @param type The type to set. - *//* - - public void setType(FletchType type) { - this.type = type; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Sets the product. - * @param product The product to set. - *//* - - public void setProduct(Item product) { - this.product = product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/FletchType.java b/Server/src/main/content/global/skill/fletching/FletchType.java deleted file mode 100644 index 33509c1ab..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchType.java +++ /dev/null @@ -1,88 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import org.crandor.game.component.Component; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the multiple fletching types(log types) - * @author 'Vexia - *//* - -public enum FletchType { - LOG(new Item(1511), FletchItem.ARROW_SHAFT, FletchItem.SHORTBOW, FletchItem.LONGBOW, FletchItem.WOODEN_STOCK), OAK(new Item(1521), FletchItem.OAK_SHORTBOW, FletchItem.OAK_LONGBOW, FletchItem.OAK_STOCK), WILLOW(new Item(1519), FletchItem.WILLOW_SHORTBOW, FletchItem.WILLOW_LONGBOW, FletchItem.WILLOW_STOCK), MAPLE(new Item(1517), FletchItem.MAPLE_SHORTBOW, FletchItem.MAPLE_LONGBOW, FletchItem.MAPLE_STOCK), TEAK(new Item(6333), FletchItem.TEAK_STOCK), MAHOGANY(new Item(6332), FletchItem.MAHOGANY_STOCK), YEW(new Item(1515), FletchItem.YEW_SHORTBOW, FletchItem.YEW_LONGBOW, FletchItem.YEW_STOCK), MAGIC(new Item(1513), FletchItem.MAGIC_SHORTBOW, FletchItem.MAGIC_LONGBOW); - - */ -/** - * Constructs a new {@code FletchType} {@code Object}. - * @param log the log. - * @param items the item.s - *//* - - FletchType(final Item log, final FletchItem... items) { - this.log = log; - this.items = items; - } - - */ -/** - * Represents the log of this type. - *//* - - private final Item log; - - */ -/** - * Represents the fletching items of this type. - *//* - - private FletchItem[] items; - - */ -/** - * Gets the items. - * @return The items. - *//* - - public FletchItem[] getItems() { - return items; - } - - */ -/** - * Gets the log. - * @return The log. - *//* - - public Item getLog() { - return log; - } - - */ -/** - * Method used to get the component based on type. - * @return the component. - *//* - - public Component getComponent() { - return items.length > 1 ? new Component(301 + items.length) : new Component(309); - } - - */ -/** - * Method used to get the FletchType based on the log item. - * @param item the item. - * @return the fletch type. - *//* - - public static FletchType forItem(final Item item) { - for (FletchType type : FletchType.values()) { - if (type.getLog().getId() == item.getId()) { - return type; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/Fletching.java b/Server/src/main/content/global/skill/fletching/Fletching.java deleted file mode 100644 index 001bd1a82..000000000 --- a/Server/src/main/content/global/skill/fletching/Fletching.java +++ /dev/null @@ -1,353 +0,0 @@ -package content.global.skill.fletching; - -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; - -import java.util.HashMap; -public class Fletching { - public static HashMaplogMap = new HashMap<>(); - public static HashMap boltMap = new HashMap<>(); - public static HashMap dartMap = new HashMap<>(); - public static HashMap arrowHeadMap = new HashMap<>(); - public static HashMap gemMap = new HashMap<>(); - public static HashMap tipMap = new HashMap<>(); - public static HashMap stringMap = new HashMap<>(); - public static HashMap limbMap = new HashMap<>(); - static{ - Items[] itemsArray = Items.values(); - int thisLength = itemsArray.length; - for(int x = 0; x < thisLength; x++){ - Items item = itemsArray[x]; - logMap.putIfAbsent(item.id, item.items); - } - Bolts[] boltArray = Bolts.values(); - thisLength = boltArray.length; - for(int x = 0; x < thisLength; x++){ - Bolts bolt = boltArray[x]; - boltMap.putIfAbsent(bolt.unfinished,bolt); - } - Darts[] dartArray = Darts.values(); - thisLength = dartArray.length; - for(int x = 0; x < thisLength; x++){ - Darts dart = dartArray[x]; - dartMap.putIfAbsent(dart.unfinished,dart); - } - ArrowHeads[] ahArray = ArrowHeads.values(); - thisLength = ahArray.length; - for(int x = 0; x < thisLength; x++){ - ArrowHeads arrowhead = ahArray[x]; - arrowHeadMap.putIfAbsent(arrowhead.unfinished,arrowhead); - } - GemBolts[] gbArray = GemBolts.values(); - thisLength = gbArray.length; - for(int x = 0; x < thisLength; x++){ - GemBolts gem = gbArray[x]; - gemMap.putIfAbsent(gem.gem,gem); - tipMap.putIfAbsent(gem.tip,gem); - } - String[] stringArray = String.values(); - thisLength = stringArray.length; - for(int x = 0; x < thisLength; x++){ - String bow = stringArray[x]; - stringMap.putIfAbsent(bow.unfinished,bow); - } - Limb[] limbsArray = Limb.values(); - thisLength = limbsArray.length; - for(int x = 0; x < thisLength; x++){ - Limb limb = limbsArray[x]; - limbMap.putIfAbsent(limb.stock, limb); - } - } - public static FletchingItems[] getEntries(int id){ - return logMap.get(id); - } - public static boolean isLog(int id){ - return logMap.get(id) != null; - } - public static boolean isBolt(int id){ - return boltMap.get(id) != null; - } - public static boolean isDart(int id){ - return dartMap.get(id) != null; - } - public static boolean isArrowHead(int id){ - return arrowHeadMap.get(id) != null; - } - public static boolean isGemTip(int id){ - return tipMap.get(id) != null; - } - public static Item[] getItems(int id){ - FletchingItems[] entry = getEntries(id); - Item items[] = {}; - switch(entry.length){ - case 1: - items = new Item[] {new Item(entry[0].id)}; - break; - case 2: - items = new Item[] {new Item(entry[0].id), new Item(entry[1].id)}; - break; - case 3: - items = new Item[] {new Item(entry[0].id), new Item(entry[1].id), new Item(entry[2].id)}; - break; - case 4: - items = new Item[] {new Item(entry[0].id), new Item(entry[1].id), new Item(entry[2].id), new Item(entry[3].id)}; - break; - } - return items; - } - public enum Limb { - WOODEN_STOCK(9440,9420,9454,9, 12, new Animation(4436)), - OAK_STOCK(9442,9422,9176,24, 32, new Animation(4437)), - WILLOW_STOCK(9444,9423,9457,39, 44, new Animation(4438)), - TEAK_STOCK(9446,9425,9459,46, 54, new Animation(4439)), - MAPLE_STOCK(9448,9427,9461,54, 64, new Animation(4440)), - MAHOGANY_STOCK(9450,9429,9463,61, 82, new Animation(4441)), - YEW_STOCK(9452,9431,9465,69, 100, new Animation(4442)); - - - public int stock, limb, product,level; - public double experience; - public Animation animation; - - Limb(int stock, int limb, int product, int level, double experience, Animation animation) { - this.stock = stock; - this.limb = limb; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - } - public enum String{ - //Bows - SHORT_BOW((byte) 1,50,841,5, 5, new Animation(6678)), - LONG_BOW((byte) 1,48,839,10, 10, new Animation(6684)), - OAK_SHORTBOW((byte) 1,54,843,20, 16.5, new Animation(6679)), - OAK_LONGBOW((byte) 1,56,845,25, 25, new Animation(6685)), - WILLOW_SHORTBOW((byte) 1,60,849,35, 33.3, new Animation(6680)), - WILLOW_LONGBOW((byte) 1,58,847,40, 41.5, new Animation(6686)), - MAPLE_SHORTBOW((byte) 1,64,853,50, 50, new Animation(6681)), - MAPLE_LONGBOW((byte) 1,62,851,55, 58.3, new Animation(6687)), - YEW_SHORTBOW((byte) 1,68,857,65, 67.5, new Animation(6682)), - YEW_LONGBOW((byte) 1,66,855,70, 75, new Animation(6688)), - MAGIC_SHORTBOW((byte) 1,72,861,80, 83.3, new Animation(6683)), - MAGIC_LONGBOW((byte) 1,70,859,85, 91.5, new Animation(6689)), - - //crossbows - BRONZE_CBOW((byte) 2,9454,9174,9, 6, new Animation(6671)), - BLURITE_CBOW((byte) 2,9456,9176,24, 16, new Animation(6672)), - IRON_CBOW((byte) 2,9457,9177,39, 22, new Animation(6673)), - STEEL_CBOW((byte) 2,9459,9179,46, 27, new Animation(6674)), - MITHIRIL_CBOW((byte) 2,9461,9181,54, 32, new Animation(6675)), - ADAMANT_CBOW((byte) 2,9463,9183,61, 41, new Animation(6676)), - RUNITE_CBOW((byte) 2,9465,9185,69, 50, new Animation(6677)); - - - public int unfinished,product,string,level; - public final double experience; - public final Animation animation; - String(byte indicator, final int unfinished, final int product, final int level, final double experience, final Animation animation) { - this.unfinished = unfinished; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - switch(indicator & 0xFF){ - case 1: - this.string = org.rs09.consts.Items.BOW_STRING_1777; - break; - case 2: - this.string = org.rs09.consts.Items.CROSSBOW_STRING_9438; - break; - default: - break; - } - } - } - public enum GemBolts { - OPAL(877, org.rs09.consts.Items.OPAL_1609, 45, 879, 11, 1.6), - PEARL(9140, org.rs09.consts.Items.OYSTER_PEARL_411, 46, 880, 41, 3.2), - PEARLS(9140, org.rs09.consts.Items.OYSTER_PEARLS_413, 46, 880, 41, 3.2), - JADE(9139, org.rs09.consts.Items.JADE_1611, 9187, 9335, 26, 2.4), - RED_TOPAZ(9141, org.rs09.consts.Items.RED_TOPAZ_1613, 9188, 9336, 48, 3.9), - SAPPHIRE(9142, org.rs09.consts.Items.SAPPHIRE_1607, 9189, 9337, 56, 4.7), - EMERALD(9142, org.rs09.consts.Items.EMERALD_1605, 9190, 9338, 58, 5.5), - RUBY(9143, org.rs09.consts.Items.RUBY_1603, 9191, 9339, 63, 6.3), - DIAMOND(9143, org.rs09.consts.Items.DIAMOND_1601, 9192, 9340, 65, 7), - DRAGONSTONE(9144, org.rs09.consts.Items.DRAGONSTONE_1615, 9193, 9341, 71, 8.2), - ONYX(9144, org.rs09.consts.Items.ONYX_6573, 9194, 9342, 73, 9.4); - - public int gem,tip,base,product,level; - public double experience; - GemBolts(int base, int gem, int tip, int product, int level, double experience){ - this.gem = gem; - this.tip = tip; - this.base = base; - this.product = product; - this.level = level; - this.experience = experience; - } - - } - public enum ArrowHeads { - BRONZE_ARROW(39, 882, 1, 2.6), - IRON_ARROW(40, 884, 15, 3.8), - STEEL_ARROW(41, 886, 30, 6.3), - MITHRIL_ARROW(42, 888, 45, 8.8), - ADAMANT_ARROW(43, 890, 60, 11.3), - RUNE_ARROW(44, 892, 75, 13.8), - DRAGON_ARROW(11237, 11212, 90, 16.3), - BROAD_ARROW(13278, 4160, 52, 15); - - public int unfinished,finished,level; - public double experience; - ArrowHeads(int unfinished, int finished, int level, double experience){ - this.unfinished = unfinished; - this.finished = finished; - this.level = level; - this.experience = experience; - } - public Item getFinished(){ - return new Item(finished); - } - public Item getUnfinished(){ - return new Item(unfinished); - } - } - public enum Darts{ - BRONZE_DART(819, 806, 1, 1.8), - IRON_DART(820, 807, 22, 3.8), - STEEL_DART(821, 808, 37, 7.5), - MITHRIL_DART(822, 809, 52, 11.2), - ADAMANT_DART(823, 810, 67, 15), - RUNE_DART(824, 811, 81, 18.8), - DRAGON_DART(11232, 11230, 95, 25); - - public int unfinished, finished, level; - public double experience; - Darts(int unfinished, int finished, int level, double experience){ - this.unfinished = unfinished; - this.finished = finished; - this.level = level; - this.experience = experience; - } - public Item getFinished(){ - return new Item(finished); - } - public Item getUnfinished(){ - return new Item(unfinished); - } - } - public enum Bolts{ - BRONZE_BOLT(9375, 877, 9, 0.5), - BLURITE_BOLT(9376, 9139, 24, 1), - IRON_BOLT(9377, 9140, 39, 1.5), - SILVER_BOLT(9382, 9145, 43, 2.5), - STEEL_BOLT(9378, 9141, 46, 3.5), - MITHRIL_BOLT(9379, 9142, 54, 5), - ADAMANTITE_BOLT(9380, 9143, 61, 7), - RUNITE_BOLT(9381, 9144, 69, 10), - BROAD_BOLT(13279, 13280, 55, 3); - - public int unfinished, finished, level; - public double experience; - Bolts(int unfinished, int finished, int level, double experience){ - this.unfinished = unfinished; - this.finished = finished; - this.level = level; - this.experience = experience; - } - public Item getFinished(){ - return new Item(finished); - } - public Item getUnfinished(){ - return new Item(unfinished); - } - } - private enum Items{ - STANDARD(1511,FletchingItems.ARROW_SHAFT, FletchingItems.SHORT_BOW, FletchingItems.LONG_BOW, FletchingItems.WOODEN_STOCK), - ACHEY(2862, FletchingItems.OGRE_ARROW_SHAFT), - OAK(1521, FletchingItems.OAK_SHORTBOW, FletchingItems.OAK_LONGBOW, FletchingItems.OAK_STOCK), - WILLOW(1519, FletchingItems.WILLOW_SHORTBOW, FletchingItems.WILLOW_LONGBOW, FletchingItems.WILLOW_STOCK), - MAPLE(1517, FletchingItems.MAPLE_SHORTOW, FletchingItems.MAPLE_LONGBOW, FletchingItems.MAPLE_STOCK), - YEW(1515, FletchingItems.YEW_SHORTBOW, FletchingItems.YEW_LONGBOW, FletchingItems.YEW_STOCK), - MAGIC(1513, FletchingItems.MAGIC_SHORTBOW, FletchingItems.MAGIC_LONGBOW), - TEAK(6333, FletchingItems.TEAK_STOCK), - MAHOGANY(6332, FletchingItems.MAHOGANY_STOCK); - - - FletchingItems[] items; - int id; - Items(int id, FletchingItems item_1, FletchingItems item_2, FletchingItems item_3, FletchingItems item_4){ - items = new FletchingItems[] {item_1, item_2, item_3, item_4}; - this.id = id; - } - Items(int id, FletchingItems item_1, FletchingItems item_2, FletchingItems item_3){ - items = new FletchingItems[] {item_1, item_2, item_3}; - this.id = id; - } - Items(int id, FletchingItems item_1, FletchingItems item_2){ - items = new FletchingItems[] {item_1, item_2}; - this.id = id; - } - Items(int id, FletchingItems item_1){ - items = new FletchingItems[] {item_1}; - this.id = id; - } - } - public enum FletchingItems { - //Standard logs - ARROW_SHAFT(52, 5, 1, 15), - SHORT_BOW(50, 5, 5, 1), - LONG_BOW(48, 10, 10, 1), - WOODEN_STOCK(9440, 6, 9, 1), - - //Achey logs - OGRE_ARROW_SHAFT(2864, 6.4, 5, 4), - - //Oak logs - OAK_SHORTBOW(54, 16.5, 20, 1), - OAK_LONGBOW(56,25,25,1), - OAK_STOCK(9442, 16, 24, 1), - - //Willow logs - WILLOW_SHORTBOW(60, 33.3, 35, 1), - WILLOW_LONGBOW(58, 41.5, 40, 1), - WILLOW_STOCK(9444, 22, 39, 1), - - //Maple logs - MAPLE_SHORTOW(64, 50, 50, 1), - MAPLE_LONGBOW(62, 58.3, 55, 1), - MAPLE_STOCK(9448, 32, 54, 1), - - //Yew logs - YEW_SHORTBOW(68, 67.5, 65, 1), - YEW_LONGBOW(66, 75, 70, 1), - YEW_STOCK(9452, 50, 69, 1), - - //Magic logs - MAGIC_SHORTBOW(72, 83.3, 80,1), - MAGIC_LONGBOW(70, 91.5, 85, 1), - - //Teak - TEAK_STOCK(9446, 27, 46,1), - - //Mahogany - MAHOGANY_STOCK(9450, 41.0, 61, 1); - - - int id,level,amount,logId; - double experience; - FletchingItems(int id, double experience, int level, int amount){ - this.id = id; - this.level = level; - this.amount = amount; - this.experience = experience; - } - - public Item getItem(){ - return new Item(id); - } - } - -} diff --git a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt b/Server/src/main/content/global/skill/fletching/FletchingListeners.kt deleted file mode 100644 index 461df2d2c..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt +++ /dev/null @@ -1,141 +0,0 @@ -package content.global.skill.fletching - -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching -import content.global.skill.fletching.items.arrow.ArrowHeadPulse -import content.global.skill.fletching.items.arrow.HeadlessArrowPulse -import content.global.skill.fletching.items.bow.StringPulse -import content.global.skill.fletching.items.crossbow.LimbPulse -import core.game.node.item.Item -import core.net.packet.PacketRepository -import core.net.packet.context.ChildPositionContext -import core.net.packet.out.RepositionChild -import org.rs09.consts.Components -import org.rs09.consts.Items -import org.rs09.consts.Items.BLUE_FEATHER_10089 -import org.rs09.consts.Items.FEATHER_314 -import org.rs09.consts.Items.ORANGE_FEATHER_10091 -import org.rs09.consts.Items.RED_FEATHER_10088 -import org.rs09.consts.Items.STRIPY_FEATHER_10087 -import org.rs09.consts.Items.YELLOW_FEATHER_10090 -import core.game.dialogue.SkillDialogueHandler -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -class FletchingListeners : InteractionListener { - - val LIMBIDs = Fletching.Limb.values().map(Fletching.Limb::limb).toIntArray() - val STOCKIDs = Fletching.Limb.values().map(Fletching.Limb::stock).toIntArray() - val MITHRIL_BOLT = Items.MITHRIL_BOLTS_9142 - val MITH_GRAPPLE_TIP = Items.MITH_GRAPPLE_TIP_9416 - val ROPE = Items.ROPE_954 - val MITH_GRAPPLE = Items.MITH_GRAPPLE_9418 - val ROPE_GRAPPLE = Items.MITH_GRAPPLE_9419 - val ARROW_SHAFT = Items.ARROW_SHAFT_52 - val FLETCHED_SHAFT = Items.HEADLESS_ARROW_53 - val UNFINISHED_ARROWS = Fletching.ArrowHeads.values().map(Fletching.ArrowHeads::unfinished).toIntArray() - val FEATHERS = intArrayOf(FEATHER_314,STRIPY_FEATHER_10087,RED_FEATHER_10088,BLUE_FEATHER_10089,YELLOW_FEATHER_10090,ORANGE_FEATHER_10091) - val UNSTRUNG_BOWS = Fletching.String.values().map(Fletching.String::unfinished).toIntArray() - val STRINGS = intArrayOf(Items.BOW_STRING_1777,Items.CROSSBOW_STRING_9438) - - override fun defineListeners() { - - onUseWith(IntType.ITEM,STRINGS,*UNSTRUNG_BOWS){ player, string, bow -> - val enum = Fletching.stringMap[bow.id] ?: return@onUseWith false - if(enum.string != string.id){ - player.sendMessage("That's not the right kind of string for this.") - return@onUseWith true - } - val handler: SkillDialogueHandler = - object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(enum.product)) { - override fun create(amount: Int, index: Int) { - player.pulseManager.run(StringPulse(player, string.asItem(), enum, amount)) - } - - override fun getAll(index: Int): Int { - return player.inventory.getAmount(string.asItem()) - } - } - handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 215, 10)) - return@onUseWith true - } - - onUseWith(IntType.ITEM,ARROW_SHAFT,*FEATHERS){ player, shaft, feather -> - val handler: SkillDialogueHandler = - object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(FLETCHED_SHAFT)) { - override fun create(amount: Int, index: Int) { - player.pulseManager.run(HeadlessArrowPulse(player, shaft.asItem(), Item(feather.id), amount)) - } - - override fun getAll(index: Int): Int { - return player.inventory.getAmount(FLETCHED_SHAFT) - } - } - handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)) - 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()) { - override fun create(amount: Int, index: Int) { - player.pulseManager.run(ArrowHeadPulse(player, shaft.asItem(), head, amount)) - } - - override fun getAll(index: Int): Int { - return player.inventory.getAmount(head.getUnfinished()) - } - } - handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)) - return@onUseWith true - } - - onUseWith(IntType.ITEM,MITHRIL_BOLT,MITH_GRAPPLE_TIP){ player, bolt, tip -> - if(player.skills.getLevel(Skills.FLETCHING) < 59){ - player.sendMessage("You need a fletching level of 59 to make this.") - return@onUseWith true - } - if(player.inventory.remove(Item(MITHRIL_BOLT,1),tip.asItem())){ - player.inventory.add(Item(MITH_GRAPPLE)) - } - return@onUseWith true - } - - onUseWith(IntType.ITEM,ROPE,MITH_GRAPPLE){ player, rope, grapple -> - if(player.skills.getLevel(Skills.FLETCHING) < 59){ - player.sendMessage("You need a fletching level of 59 to make this.") - return@onUseWith true - } - if(player.inventory.remove(rope.asItem(),grapple.asItem())){ - player.inventory.add(Item(ROPE_GRAPPLE)) - } - return@onUseWith true - } - - onUseWith(IntType.ITEM,LIMBIDs,*STOCKIDs){ player, limb, stock -> - val limbEnum = Fletching.limbMap[stock.id] ?: return@onUseWith false - if(limbEnum.limb != limb.id){ - player.sendMessage("That's not the right limb to attach to that stock.") - return@onUseWith true - } - val handler: SkillDialogueHandler = object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(limbEnum.product)){ - override fun create(amount: Int, index: Int) { - player.pulseManager.run(LimbPulse(player, stock.asItem(), limbEnum, amount)) - } - - override fun getAll(index: Int): Int { - return player.inventory.getAmount(stock.asItem()) - } - } - handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)) - return@onUseWith true - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/FletchingPlugin.java b/Server/src/main/content/global/skill/fletching/FletchingPlugin.java deleted file mode 100644 index 94d898bef..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingPlugin.java +++ /dev/null @@ -1,133 +0,0 @@ -package content.global.skill.fletching; - -import content.global.skill.fletching.items.bolts.BoltPulse; -import content.global.skill.fletching.items.darts.DartPulse; -import org.rs09.consts.Items; -import core.game.dialogue.SkillDialogueHandler; -import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.net.packet.PacketRepository; -import core.net.packet.context.ChildPositionContext; -import core.net.packet.out.RepositionChild; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to open the fletching dialogue. - * @author Ceikry - */ -@Initializable -public class FletchingPlugin extends UseWithHandler { - - public FletchingPlugin() { - super(819,820,821,822,823,824,11232,9375,9376,9377,9382,9378,9379,9380,9381,13279,1511,1521,1519,1517,1515,1513,2862,6332,6333, Items.MAHOGANY_LOGS_6332); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - // Knife - addHandler(946, ITEM_TYPE, this); - - // Feathers plus colored feathers - addHandler(314, ITEM_TYPE,this); - addHandler(10087, ITEM_TYPE, this); - addHandler(10088, ITEM_TYPE, this); - addHandler(10089, ITEM_TYPE, this); - addHandler(10090, ITEM_TYPE, this); - addHandler(10091, ITEM_TYPE, this); - - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - - //handle darts - if(Fletching.isDart(event.getUsedItem().getId())){ - final Fletching.Darts dart = Fletching.dartMap.get(event.getUsedItem().getId()); - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, dart.getFinished()) { - @Override - public void create(final int amount, int index) { - player.getPulseManager().run(new DartPulse(player, event.getUsedItem(), dart, amount)); - } - @Override - public int getAll(int index) { - return player.getInventory().getAmount(event.getUsedItem()); - } - }; - handler.open(); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 309, 2, 230, 10)); - return true; - } - - //handle bolts - if(Fletching.isBolt(event.getUsedItem().getId()) || Fletching.isBolt(event.getUsedWith().getId())){ - // figure out which of the used items is a bolt, and which is potentially a feather - final Fletching.Bolts bolt - = Fletching.isBolt(event.getUsedItem().getId()) - ? Fletching.boltMap.get(event.getUsedItem().getId()) - : Fletching.boltMap.get(event.getUsedWith().getId()); - final int featherId - = Fletching.isBolt(event.getUsedItem().getId()) - ? event.getUsedWith().getId() - : event.getUsedItem().getId(); - final boolean hasFeather = (featherId == 314 || (featherId >= 10087 && featherId <= 10091)); - - if (hasFeather) { - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, bolt.getFinished()) { - @Override - public void create(final int amount, int index) { - player.getPulseManager().run(new BoltPulse(player, event.getUsedItem(), bolt, new Item(featherId), amount)); - } - @Override - public int getAll(int index) { - return player.getInventory().getAmount(event.getUsedItem()); - } - }; - handler.open(); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 309, 2, 210, 10)); - return true; - } - return false; - } - - //handle logs - if(Fletching.isLog(event.getUsedItem().getId())) { - final Item log = event.getUsedItem(); - Item[] items = Fletching.getItems(log.getId()); - SkillDialogue dialLength = SkillDialogue.ONE_OPTION; - switch (items.length) { - case 2: - dialLength = SkillDialogue.TWO_OPTION; - break; - case 3: - dialLength = SkillDialogue.THREE_OPTION; - break; - case 4: - dialLength = SkillDialogue.FOUR_OPTION; - break; - } - SkillDialogueHandler handler = new SkillDialogueHandler(player, dialLength, items) { - - @Override - public void create(final int amount, int index) { - final Fletching.FletchingItems item = Fletching.getEntries(log.getId())[index]; - player.getPulseManager().run(new FletchingPulse(player, log, amount, item)); - } - - @Override - public int getAll(int index) { - return player.getInventory().getAmount(log); - } - - }; - handler.open(); - return true; - } - return false; - } -} diff --git a/Server/src/main/content/global/skill/fletching/FletchingPulse.java b/Server/src/main/content/global/skill/fletching/FletchingPulse.java deleted file mode 100644 index 9c7d0d638..000000000 --- a/Server/src/main/content/global/skill/fletching/FletchingPulse.java +++ /dev/null @@ -1,117 +0,0 @@ -package content.global.skill.fletching; - -import core.tools.RandomFunction; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.zone.ZoneBorders; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.tools.StringUtils; - -/** - * fletching skill pulse - * @author ceik - */ -public final class FletchingPulse extends SkillPulse { - - /** - * Seers bank zone borders for the diary task - */ - private static final ZoneBorders bankZone = new ZoneBorders(2721,3493,2730,3487); - - /** - * Represents the animation used in this generic pulse. - */ - private static final Animation ANIMATION = new Animation(1248); - - /** - * Represents the item we are fletching. - */ - private Fletching.FletchingItems fletch; - - /** - * Represents the amount to fletch. - */ - private int amount = 0; - - /** - * Constructs a new {@code FletchingPulse.java} {@code Object}. - * @param player - * @param node - */ - public FletchingPulse(final Player player, final Item node, final int amount, final Fletching.FletchingItems fletch) { - super(player, node); - this.amount = amount; - this.fletch = fletch; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < fletch.level) { - player.getDialogueInterpreter().sendDialogue("You need a Fletching skill of " + fletch.level + " or above to make " + (StringUtils.isPlusN(fletch.getItem().getName().replace("(u)", "").trim()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim()); - return false; - } - if (amount > player.getInventory().getAmount(node)) { - amount = player.getInventory().getAmount(node); - } - if (fletch == Fletching.FletchingItems.OGRE_ARROW_SHAFT) { - if (player.getQuestRepository().getQuest("Big Chompy Bird Hunting").getStage(player) == 0) { - player.getPacketDispatch().sendMessage("You must have started Big Chompy Bird Hunting to make those."); - return false; - } - } - return true; - } - - @Override - public void animate() { - player.animate(ANIMATION); - } - - @Override - public boolean reward() { - if(bankZone.insideBorder(player) && fletch == Fletching.FletchingItems.MAGIC_SHORTBOW) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2); - } - if (getDelay() == 1) { - super.setDelay(4); - return false; - } - if (player.getInventory().remove(node)) { - final Item item = new Item(fletch.id,fletch.amount); - if ( fletch == Fletching.FletchingItems.OGRE_ARROW_SHAFT ) { - item.setAmount(RandomFunction.random(3,6)); - } - player.getInventory().add(item); - player.getSkills().addExperience(Skills.FLETCHING, fletch.experience, true); - String message = getMessage(); - player.getPacketDispatch().sendMessage(message); - - if (fletch.id == Fletching.FletchingItems.MAGIC_SHORTBOW.id - && (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player) - || new ZoneBorders(2727, 3487, 2730, 3490, 0).insideBorder(player)) - && !player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 2)) { - player.setAttribute("/save:diary:seers:fletch-magic-short-bow", true); - } - } else { - return true; - } - amount--; - return amount == 0; - } - - /** - * Method used to get the message of the fletch. - * @return the message. - */ - public String getMessage() { - switch (fletch) { - case ARROW_SHAFT: - return "You carefully cut the wood into 15 arrow shafts."; - default: - return "You carefully cut the wood into " + (StringUtils.isPlusN(fletch.getItem().getName()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim() + "."; - } - } -} diff --git a/Server/src/main/content/global/skill/fletching/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/Zones.kt b/Server/src/main/content/global/skill/fletching/Zones.kt new file mode 100644 index 000000000..8c10f0c4f --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/Zones.kt @@ -0,0 +1,21 @@ +package content.global.skill.fletching + +import core.game.node.Node +import core.game.world.map.zone.ZoneBorders + +object Zones { + val seersMagicShortbowAchievementZones = arrayOf( + ZoneBorders(2721, 3489, 2724, 3493, 0), + ZoneBorders(2727, 3487, 2730, 3490, 0), + ZoneBorders(2721, 3493, 2730, 3487) + ) + + fun inAnyZone(node: Node, zones: Array): Boolean { + for (zone in zones) { + if (zone.insideBorder(node)) { + return true + } + } + return false + } +} diff --git a/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt b/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt new file mode 100644 index 000000000..087c688a9 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/ArrowCraftInfo.kt @@ -0,0 +1,24 @@ +package content.global.skill.fletching.arrow + +import org.rs09.consts.Items + +enum class ArrowCraftInfo(val tipItemId: Int, val arrowItemId: Int, val level: Int, val experience: Double) { + BRONZE_ARROW(Items.BRONZE_ARROWTIPS_39, Items.BRONZE_ARROW_882, 1, 1.3), + IRON_ARROW(Items.IRON_ARROWTIPS_40, Items.IRON_ARROW_884, 15, 2.5), + STEEL_ARROW(Items.STEEL_ARROWTIPS_41, Items.STEEL_ARROW_886, 30, 5.0), + MITHRIL_ARROW(Items.MITHRIL_ARROWTIPS_42, Items.MITHRIL_ARROW_888, 45, 7.5), + ADAMANT_ARROW(Items.ADAMANT_ARROWTIPS_43, Items.ADAMANT_ARROW_890, 60, 10.0), + RUNE_ARROW(Items.RUNE_ARROWTIPS_44, Items.RUNE_ARROW_892, 75, 12.5), + DRAGON_ARROW(Items.DRAGON_ARROWTIPS_11237,Items.DRAGON_ARROW_11212, 90, 15.0), + BROAD_ARROW(Items.BROAD_ARROW_HEADS_13278, Items.BROAD_ARROW_4160, 52, 15.0); + + companion object { + private val arrowCraftInfoByTipId = values().associateBy { it.tipItemId } + + val arrowTipIds: IntArray = values().map { arrowCraftInfo: ArrowCraftInfo -> arrowCraftInfo.tipItemId }.toIntArray() + + fun fromTipId(tipId: Int) : ArrowCraftInfo? { + return arrowCraftInfoByTipId[tipId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt b/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt new file mode 100644 index 000000000..2c061b3cb --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/ArrowListeners.kt @@ -0,0 +1,112 @@ +package content.global.skill.fletching.arrow + +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items + +@Suppress("unused") // Reflectively loaded +class ArrowListeners : InteractionListener { + private val arrowShaft = Items.ARROW_SHAFT_52 + val headlessArrow = Items.HEADLESS_ARROW_53 + + companion object { + const val FLIGHTED_OGRE_ARROW_LEVEL = 5 + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level to do this." + ) + } + } + + override fun defineListeners() { + onUseWith(IntType.ITEM, arrowShaft, *Feathers.all) { player, shaft, feather -> + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(headlessArrow)) { + override fun create(amount: Int, index: Int) { + if (!hasSpaceFor(player, Item(headlessArrow))) { + sendDialogue(player, "You do not have enough inventory space.") + return + } + HeadlessArrowCraftScript(player, feather.id, amount).invoke() + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, headlessArrow) + } + } + handler.open() + return@onUseWith true + } + + onUseWith(IntType.ITEM, headlessArrow, *ArrowCraftInfo.arrowTipIds) { player, headlessArrow, arrowTip -> + val arrowCraftInfo = ArrowCraftInfo.fromTipId(arrowTip.id) ?: return@onUseWith false + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(arrowCraftInfo.arrowItemId)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + TippedArrowCraftScript(player, arrowCraftInfo, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (arrowCraftInfo == ArrowCraftInfo.BROAD_ARROW && !getSlayerFlags(player).isBroadsUnlocked()) { + sendDialogue(player, "You need to unlock the ability to create broad arrows.") + return false + } + + if (getDynLevel(player, Skills.FLETCHING) < arrowCraftInfo.level) { + sendLevelCheckFailDialog(player, arrowCraftInfo.level) + return false + } + + if (!hasSpaceFor(player, Item(arrowCraftInfo.arrowItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, arrowTip.id) + } + } + handler.open() + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.OGRE_ARROW_SHAFT_2864, *Feathers.all) { player, ogreArrowShaft, feather -> + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(Items.FLIGHTED_OGRE_ARROW_2865)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + FlightedOgreArrowCraftScript(player, feather.id, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < FLIGHTED_OGRE_ARROW_LEVEL) { + sendLevelCheckFailDialog(player, FLIGHTED_OGRE_ARROW_LEVEL) + return false + } + + if (!hasSpaceFor(player, Item(Items.FLIGHTED_OGRE_ARROW_2865))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, Items.OGRE_ARROW_SHAFT_2864) + } + } + handler.open() + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt new file mode 100644 index 000000000..126b72aee --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/FlightedOgreArrowCraftScript.kt @@ -0,0 +1,88 @@ +package content.global.skill.fletching.arrow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +/** + * Defines the queueScript for creating a flighted ogre arrow + * @author 'Vexia + * @param player the player. + * @param idOfFeatherUsed the feather to attach to the shaft. + * @param sets the amount of sets to complete. + */ +class FlightedOgreArrowCraftScript( + private val player: Player, + private val idOfFeatherUsed: Int, + private val sets: Int +) { + + private val flightedOgreArrow = Items.FLIGHTED_OGRE_ARROW_2865 + private val ogreArrowShaft = Items.OGRE_ARROW_SHAFT_2864 + private val maximumFlightedOgreArrowsCraftableInOneStage = 6 + + private val feathersPerArrow = 4 + + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisStage(shaftsInInventory: Int, feathersInInventory: Int): Int { + val limitOfCraftableArrowsDueToFeathers = feathersInInventory / feathersPerArrow + + val totalCraftableArrows = min(shaftsInInventory, limitOfCraftableArrowsDueToFeathers) + return if (totalCraftableArrows > maximumFlightedOgreArrowsCraftableInOneStage) { + maximumFlightedOgreArrowsCraftableInOneStage + } else { + totalCraftableArrows + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < ArrowListeners.FLIGHTED_OGRE_ARROW_LEVEL) { + ArrowListeners.sendLevelCheckFailDialog(player, ArrowListeners.FLIGHTED_OGRE_ARROW_LEVEL) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val featherAmount = amountInInventory(player, idOfFeatherUsed) + val shaftAmount = amountInInventory(player, ogreArrowShaft) + + val amountToCraft = getAmountToCraftForThisStage(shaftAmount, featherAmount) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(ogreArrowShaft, amountToCraft), + Item(idOfFeatherUsed, amountToCraft * feathersPerArrow) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach $feathersPerArrow feathers to a shaft.") + else -> { + sendMessage( + player, + "You attach ${amountToCraft * feathersPerArrow} feathers to $amountToCraft arrow shafts." + ) + } + } + + rewardXP(player, Skills.FLETCHING, amountToCraft.toDouble()) + addItem(player, flightedOgreArrow, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt new file mode 100644 index 000000000..40ef4c235 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/HeadlessArrowCraftScript.kt @@ -0,0 +1,76 @@ +package content.global.skill.fletching.arrow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +/** + * Defines the queueScript for creating a headless arrow + * @author 'Vexia + * @param player the player. + * @param idOfFeatherUsed the feather to attach to the shaft. + * @param sets the amount of sets to complete. + */ +class HeadlessArrowCraftScript( + private val player: Player, + private val idOfFeatherUsed: Int, + private val sets: Int +) { + + private val headlessArrow = Items.HEADLESS_ARROW_53 + private val arrowShaft = Items.ARROW_SHAFT_52 + private val maximumHeadlessArrowsCraftableInOneStage = 15 + + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisStage(shaftsInInventory: Int, feathersInInventory: Int): Int { + val smallerItemAmount = min(shaftsInInventory, feathersInInventory) + return if (smallerItemAmount > maximumHeadlessArrowsCraftableInOneStage) { + maximumHeadlessArrowsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + val featherAmount = amountInInventory(player, idOfFeatherUsed) + val shaftAmount = amountInInventory(player, arrowShaft) + + val amountToCraft = getAmountToCraftForThisStage(shaftAmount, featherAmount) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(arrowShaft, amountToCraft), + Item(idOfFeatherUsed, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach a feather to a shaft.") + else -> { + sendMessage(player, "You attach feathers to $amountToCraft arrow shafts.") + } + } + + rewardXP(player, Skills.FLETCHING, amountToCraft.toDouble()) + addItem(player, headlessArrow, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt b/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt new file mode 100644 index 000000000..d4249b660 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/arrow/TippedArrowCraftScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.arrow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +/** + * Represents the arrow head pulse to complete the headless arrow. + * @author 'Vexia + * @param player the player. + * @param arrowCraftInfo provides information about the arrow we're crafting. + * @param sets the amount of sets to complete. + */ +class TippedArrowCraftScript( + private val player: Player, + private val arrowCraftInfo: ArrowCraftInfo, + private val sets: Int +) { + + private val headlessArrow = Items.HEADLESS_ARROW_53 + private val maximumArrowsCraftableInOneStage = 15 + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisStage(headlessArrowsInInventory: Int, tipsInInventory: Int): Int { + val smallerItemAmount = min(headlessArrowsInInventory, tipsInInventory) + return if (smallerItemAmount > maximumArrowsCraftableInOneStage) { + maximumArrowsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < arrowCraftInfo.level) { + ArrowListeners.sendLevelCheckFailDialog(player, arrowCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val tipsInInventory = amountInInventory(player, arrowCraftInfo.tipItemId) + val headlessArrowsInInventory = amountInInventory(player, headlessArrow) + + val amountToCraft = getAmountToCraftForThisStage(headlessArrowsInInventory, tipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(headlessArrow, amountToCraft), + Item(arrowCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach an arrow head to an arrow shaft.") + else -> { + sendMessage(player, "You attach arrow heads to $amountToCraft arrow shafts.") + } + } + + rewardXP(player, Skills.FLETCHING, arrowCraftInfo.experience * amountToCraft) + addItem(player, arrowCraftInfo.arrowItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt new file mode 100644 index 000000000..497332a59 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftInfo.kt @@ -0,0 +1,39 @@ +package content.global.skill.fletching.bolts + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different bolts + * @property unfinishedBoltItemId the unfinished bolt (unf) Item id used to craft the dart. + * @property finishedItemId the resulting finished bolt Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class BoltCraftInfo( + val unfinishedBoltItemId: Int, + val finishedItemId: Int, + val level: Int, + val experience: Double +) { + BRONZE_BOLT(Items.BRONZE_BOLTS_UNF_9375, Items.BRONZE_BOLTS_877, 9, 0.5), + BLURITE_BOLT(Items.BLURITE_BOLTS_UNF_9376, Items.BLURITE_BOLTS_9139, 24, 1.0), + IRON_BOLT(Items.IRON_BOLTS_UNF_9377, Items.IRON_BOLTS_9140, 39, 1.5), + SILVER_BOLT(Items.SILVER_BOLTS_UNF_9382, Items.SILVER_BOLTS_9145, 43, 2.5), + STEEL_BOLT(Items.STEEL_BOLTS_UNF_9378, Items.STEEL_BOLTS_9141, 46, 3.5), + MITHRIL_BOLT(Items.MITHRIL_BOLTS_UNF_9379, Items.MITHRIL_BOLTS_9142, 54, 5.0), + ADAMANTITE_BOLT(Items.ADAMANT_BOLTSUNF_9380, Items.ADAMANT_BOLTS_9143, 61, 7.0), + RUNITE_BOLT(Items.RUNITE_BOLTS_UNF_9381, Items.RUNE_BOLTS_9144, 69, 10.0), + BROAD_BOLT(Items.UNFINISHED_BROAD_BOLTS_13279, Items.BROAD_TIPPED_BOLTS_13280, 55, 3.0); + + companion object { + private val boltCraftInfoByUnfinishedBoltIds = values().associateBy { it.unfinishedBoltItemId } + + val unfinishedBoltIds: IntArray = BoltCraftInfo.values() + .map { unfinishedBoltToBoltMapping: BoltCraftInfo -> unfinishedBoltToBoltMapping.unfinishedBoltItemId } + .toIntArray() + + fun fromUnfinishedBoltId(unfinishedBoltId: Int): BoltCraftInfo? { + return boltCraftInfoByUnfinishedBoltIds[unfinishedBoltId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt new file mode 100644 index 000000000..e525e7055 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltCraftScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.bolts + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the bolt pulse class to make bolts. + * @author ceik + * @param player the player. + * @param boltCraftInfo contains information about what bolt we're crafting + * @param feather the feather we're using to craft + * @param sets the amount of sets to craft + */ +class BoltCraftScript( + private val player: Player, + private val boltCraftInfo: BoltCraftInfo, + private val feather: Item, + private val sets: Int +) { + + private val maximumBoltsCraftableInOneStage = 10 + private val initialDelay = 1 + private val subsequentDelay = 3 + + private fun getAmountToCraftForThisSet(feathersInInventory: Int, unfinishedBoltsInInventory: Int): Int { + val smallerItemAmount = min(feathersInInventory, unfinishedBoltsInInventory) + return if (smallerItemAmount > maximumBoltsCraftableInOneStage) { + maximumBoltsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) { + BoltListeners.sendLevelCheckFailDialog(player, boltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val featherAmount = amountInInventory(player, feather.id) + val unfinishedBoltAmount = amountInInventory(player, boltCraftInfo.unfinishedBoltItemId) + + val amountToCraft = getAmountToCraftForThisSet(featherAmount, unfinishedBoltAmount) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(feather.id, amountToCraft), + Item(boltCraftInfo.unfinishedBoltItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach a feather to a bolt.") + else -> { + sendMessage(player, "You fletch $amountToCraft bolts") + } + } + + rewardXP(player, Skills.FLETCHING, boltCraftInfo.experience * amountToCraft) + addItem(player, boltCraftInfo.finishedItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt b/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt new file mode 100644 index 000000000..ad9d806d8 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/bolts/BoltListeners.kt @@ -0,0 +1,65 @@ +package content.global.skill.fletching.bolts + +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class BoltListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Feathers.all, *BoltCraftInfo.unfinishedBoltIds) { player, feather, unfinishedBolt -> + val boltCraftInfo = BoltCraftInfo.fromUnfinishedBoltId(unfinishedBolt.id) ?: return@onUseWith false + val handler: SkillDialogueHandler = + object : SkillDialogueHandler( + player, + SkillDialogue.MAKE_SET_ONE_OPTION, + Item(boltCraftInfo.finishedItemId) + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + BoltCraftScript(player, boltCraftInfo, feather.asItem(), amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (boltCraftInfo == BoltCraftInfo.BROAD_BOLT && !getSlayerFlags(player).isBroadsUnlocked()) { + sendDialogue(player, "You need to unlock the ability to create broad bolts.") + return false + } + if (getDynLevel(player, Skills.FLETCHING) < boltCraftInfo.level) { + sendLevelCheckFailDialog(player, boltCraftInfo.level) + return false + } + if (!hasSpaceFor(player, Item(boltCraftInfo.finishedItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + + return true + } + + override fun getAll(index: Int): Int { + return min( + amountInInventory(player, feather.id), + amountInInventory(player, unfinishedBolt.id) + ) + } + } + handler.open() + return@onUseWith true + } + } + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level in order to do this." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt b/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt new file mode 100644 index 000000000..0876d54c1 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/LimbingListener.kt @@ -0,0 +1,80 @@ +package content.global.skill.fletching.crossbow + +import core.api.getDynLevel +import core.api.hasSpaceFor +import core.api.sendDialogue +import core.api.sendMessage +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ChildPositionContext +import core.net.packet.out.RepositionChild +import org.rs09.consts.Components + +@Suppress("unused") // Reflectively loaded +class LimbingListener : InteractionListener { + override fun defineListeners() { + onUseWith( + IntType.ITEM, + UnfinishedCrossbowCraftInfo.limbIds, + *UnfinishedCrossbowCraftInfo.stockIds + ) { player, limb, stock -> + val unfinishedCrossbowCraftInfo = UnfinishedCrossbowCraftInfo.forStockId(stock.id) ?: return@onUseWith false + if (unfinishedCrossbowCraftInfo.limbItemId != limb.id) { + sendMessage(player, "That's not the right limb to attach to that stock.") + return@onUseWith true + } + val handler: SkillDialogueHandler = object : SkillDialogueHandler( + player, + SkillDialogue.ONE_OPTION, + Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId) + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + LimbingScript(player, unfinishedCrossbowCraftInfo, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) { + sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level) + return false + } + if (!hasSpaceFor(player, Item(unfinishedCrossbowCraftInfo.unstrungCrossbowItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + + return true + } + + override fun getAll(index: Int): Int { + return player.inventory.getAmount(stock.asItem()) + } + } + handler.open() + fixTextOverlappingTheCrossbowIcon(player) + return@onUseWith true + } + } + + + private fun fixTextOverlappingTheCrossbowIcon(player: Player) { + PacketRepository.send( + RepositionChild::class.java, + ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10) + ) + } + + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level to attach these limbs." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt b/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt new file mode 100644 index 000000000..07fb8ac43 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/LimbingScript.kt @@ -0,0 +1,56 @@ +package content.global.skill.fletching.crossbow + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * The queue script for attaching limbs. + * @author Ceikry + * @param player The player + * @param unfinishedCrossbowCraftInfo info about the unfinished (unstrung) crossbow we're crafting + * @param amount to create + */ +class LimbingScript( + private val player: Player, + private val unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 6 + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < unfinishedCrossbowCraftInfo.level) { + LimbingListener.sendLevelCheckFailDialog(player, unfinishedCrossbowCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(unfinishedCrossbowCraftInfo.stockItemId), + Item(unfinishedCrossbowCraftInfo.limbItemId) + ) + ) { + addItem(player, unfinishedCrossbowCraftInfo.unstrungCrossbowItemId, 1) + rewardXP(player, Skills.FLETCHING, unfinishedCrossbowCraftInfo.experience) + sendMessage(player, "You attach the metal limbs to the stock.") + animate(player, unfinishedCrossbowCraftInfo.animation) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt b/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt new file mode 100644 index 000000000..7250b02e4 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/crossbow/UnfinishedCrossbowCraftInfo.kt @@ -0,0 +1,55 @@ +package content.global.skill.fletching.crossbow + +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different unfinished (unstrung) crossbows + * @author 'Vexia + * @param stockItemId item id of the stock. + * @param limbItemId item id of the limb. + * @param unstrungCrossbowItemId item id of the product. + * @param level the level. + * @param experience the experience. + * @param animation the animation. + */ +enum class UnfinishedCrossbowCraftInfo + ( + val stockItemId: Int, + val limbItemId: Int, + val unstrungCrossbowItemId: Int, + val level: Int, + val experience: Double, + val animation: Animation +) { + WOODEN_STOCK(Items.WOODEN_STOCK_9440, Items.BRONZE_LIMBS_9420, Items.BRONZE_CBOW_U_9454, 9, 12.0, Animation(4436)), + OAK_STOCK(Items.OAK_STOCK_9442, Items.BLURITE_LIMBS_9422, Items.BLURITE_CROSSBOW_9176, 24, 32.0, Animation(4437)), + WILLOW_STOCK(Items.WILLOW_STOCK_9444, Items.IRON_LIMBS_9423, Items.IRON_CBOW_U_9457, 39, 44.0, Animation(4438)), + TEAK_STOCK(Items.TEAK_STOCK_9446, Items.STEEL_LIMBS_9425, Items.STEEL_CBOW_U_9459, 46, 54.0, Animation(4439)), + MAPLE_STOCK(Items.MAPLE_STOCK_9448, Items.MITHRIL_LIMBS_9427, Items.MITHRIL_CBOW_U_9461, 54, 64.0, Animation(4440)), + MAHOGANY_STOCK( + Items.MAHOGANY_STOCK_9450, + Items.ADAMANTITE_LIMBS_9429, + Items.ADAMANT_CBOW_U_9463, + 61, + 82.0, + Animation(4441) + ), + YEW_STOCK(Items.YEW_STOCK_9452, Items.RUNITE_LIMBS_9431, Items.RUNITE_CBOW_U_9465, 69, 100.0, Animation(4442)); + + companion object { + private val unfinishedCrossbowCraftInfoByStockId = values().associateBy { it.stockItemId } + + val limbIds: IntArray = + values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.limbItemId } + .toIntArray() + + val stockIds: IntArray = + values().map { unfinishedCrossbowCraftInfo: UnfinishedCrossbowCraftInfo -> unfinishedCrossbowCraftInfo.stockItemId } + .toIntArray() + + fun forStockId(stockId: Int): UnfinishedCrossbowCraftInfo? { + return unfinishedCrossbowCraftInfoByStockId[stockId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt b/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt new file mode 100644 index 000000000..488075b80 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartCraftInfo.kt @@ -0,0 +1,36 @@ +package content.global.skill.fletching.darts + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different darts + * @author 'Vexia + * @property tipItemId the tip Item id used to craft the dart. + * @property dartItemId the resulting dart Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class DartCraftInfo( + val tipItemId: Int, + val dartItemId: Int, + val level: Int, + val experience: Double) { + + BRONZE_DART(Items.BRONZE_DART_TIP_819, Items.BRONZE_DART_806, 1, 1.8), + IRON_DART(Items.IRON_DART_TIP_820, Items.IRON_DART_807, 22, 3.8), + STEEL_DART(Items.STEEL_DART_TIP_821, Items.STEEL_DART_808, 37, 7.5), + MITHRIL_DART(Items.MITHRIL_DART_TIP_822, Items.MITHRIL_DART_809, 52, 11.2), + ADAMANT_DART(Items.ADAMANT_DART_TIP_823, Items.ADAMANT_DART_810, 67, 15.0), + RUNE_DART(Items.RUNE_DART_TIP_824, Items.RUNE_DART_811, 81, 18.8), + DRAGON_DART(Items.DRAGON_DART_TIP_11232, Items.DRAGON_DART_11230, 95, 25.0); + + companion object { + private val dartCraftInfoByTipIds = values().associateBy { it.tipItemId } + + val tipIDs: IntArray = values().map { dartCraftInfo: DartCraftInfo -> dartCraftInfo.tipItemId }.toIntArray() + + fun fromTipID(dartTipId: Int): DartCraftInfo? { + return dartCraftInfoByTipIds[dartTipId] + } + } +} diff --git a/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt b/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt new file mode 100644 index 000000000..baecff08c --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartCraftScript.kt @@ -0,0 +1,78 @@ +package content.global.skill.fletching.darts + +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the queueScript to craft a dart. + * @author ceikry + * @param player the player. + * @param dartCraftInfo contains info about the dart we're crafting. + * @param sets count of sets to make + */ +class DartCraftScript( + private val player: Player, + private val dartCraftInfo: DartCraftInfo, + private var sets: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 3 + private val maximumDartsCraftableInOneStage = 10 + + private fun getAmountToCraftForThisStage(feathersInInventory: Int, dartTipsInInventory: Int): Int { + val smallerItemAmount = min(feathersInInventory, dartTipsInInventory) + return if (smallerItemAmount > maximumDartsCraftableInOneStage) { + maximumDartsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) { + DartListeners.sendLevelCheckFailDialog(player, dartCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val dartTipsInInventory = amountInInventory(player, dartCraftInfo.tipItemId) + val feathersInInventory = amountInInventory(player, Feathers.STANDARD) + + val amountToCraft = getAmountToCraftForThisStage(feathersInInventory, dartTipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(Feathers.STANDARD, amountToCraft), + Item(dartCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach a feather to a dart.") + else -> { + sendMessage(player, "You attach feathers to $amountToCraft darts.") + } + } + + rewardXP(player, Skills.FLETCHING, dartCraftInfo.experience * amountToCraft) + addItem(player, dartCraftInfo.dartItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt b/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt new file mode 100644 index 000000000..93b96d2ab --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/darts/DartListeners.kt @@ -0,0 +1,59 @@ +package content.global.skill.fletching.darts + +import content.data.Quests +import content.global.skill.fletching.Feathers +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class DartListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Feathers.STANDARD, *DartCraftInfo.tipIDs) { player, feather, dartTip -> + val dartCraftInfo = DartCraftInfo.fromTipID(dartTip.id) ?: return@onUseWith false + val handler: SkillDialogueHandler = + object : + SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(dartCraftInfo.dartItemId)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + DartCraftScript(player, dartCraftInfo, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < dartCraftInfo.level) { + sendLevelCheckFailDialog(player, dartCraftInfo.level) + return false + } + if (!isQuestComplete(player, Quests.THE_TOURIST_TRAP)) { + sendDialogue(player, "You need to have completed Tourist Trap to fletch darts.") + return false + } + if (!hasSpaceFor(player, Item(dartCraftInfo.dartItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return min( + amountInInventory(player, feather.id), + amountInInventory(player, dartTip.id) + ) + } + } + handler.open() + return@onUseWith true + } + } + companion object { + fun sendLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue(player, "You need a fletching level of $level to do this.") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt b/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt new file mode 100644 index 000000000..c6b1cfdfd --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/AttachGemTipToBoltScript.kt @@ -0,0 +1,77 @@ +package content.global.skill.fletching.gem + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import kotlin.math.min + +/** + * Represents the attaching of a gem tip to a premade bolt. + * @author Ceikry + * @param player the player. + * @param gemBoltCraftInfo crafting info for adding the gem to the bolt + * @param sets the number of sets to craft. + */ +class AttachGemTipToBoltScript( + private val player: Player, + private val gemBoltCraftInfo: GemBoltsCraftInfo, + private val sets: Int +) { + + private val initialDelay = 1 + private val subsequentDelay = 3 + private val maximumGemBoltsCraftableInOneStage = 10 + + private fun getAmountToCraftForThisPulse(untippedBoltsInInventory: Int, tipsInInventory: Int): Int { + val smallerItemAmount = min(untippedBoltsInInventory, tipsInInventory) + return if (smallerItemAmount > maximumGemBoltsCraftableInOneStage) { + maximumGemBoltsCraftableInOneStage + } else { + smallerItemAmount + } + } + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + GemBoltListeners.sendGemTipAttachLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val untippedBoltsInInventory = amountInInventory(player, gemBoltCraftInfo.untippedBoltItemId) + val tipsInInventory = amountInInventory(player, gemBoltCraftInfo.tipItemId) + + val amountToCraft = getAmountToCraftForThisPulse(untippedBoltsInInventory, tipsInInventory) + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(gemBoltCraftInfo.untippedBoltItemId, amountToCraft), + Item(gemBoltCraftInfo.tipItemId, amountToCraft) + ) + ) { + when (amountToCraft) { + 1 -> sendMessage(player, "You attach the tip to the bolt.") + else -> { + sendMessage(player, "You fletch $amountToCraft bolts.") + } + } + + rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience * amountToCraft) + addItem(player, gemBoltCraftInfo.tippedBoltItemId, amountToCraft) + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= sets - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt b/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt new file mode 100644 index 000000000..55ea058d0 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/CutGemsIntoBoltTipsScript.kt @@ -0,0 +1,79 @@ +package content.global.skill.fletching.gem + +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * Represents the queue script for cutting gems into bolt tips + * @author Ceikry + * @param player the player. + * @param gemBoltCraftInfo represents the crafting info for the gem we're cutting. + * @param amount the amount to make. + */ +class CutGemsIntoBoltTipsScript( + private val player: Player, + private val gemBoltCraftInfo: GemBoltsCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + private val craftDelay = 5 + private val animationDelay = 6 + + private var craftingFinished = false + + private fun invokeAnimationLoop() { + queueScript(player, 0) { + if (craftingFinished) { + return@queueScript stopExecuting(player) + } + animate(player, gemBoltCraftInfo.gemCutAnimationId) + return@queueScript delayScript(player, animationDelay) + } + } + + private fun invokeCraftLoop() { + queueScript(player, 0) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + craftingFinished = true + GemBoltListeners.sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + val amountOfTipsToCraft = when (gemBoltCraftInfo) { + GemBoltsCraftInfo.PEARLS -> 24 + GemBoltsCraftInfo.PEARL -> 6 + GemBoltsCraftInfo.ONYX -> 24 + else -> 12 + } + + if (removeItem(player, Item(gemBoltCraftInfo.gemItemId))) { + addItem(player, gemBoltCraftInfo.tipItemId, amountOfTipsToCraft) + rewardXP(player, Skills.FLETCHING, gemBoltCraftInfo.experience) + } else { + craftingFinished = true + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + craftingFinished = true + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, craftDelay, true) + } + } + + fun invoke() { + queueScript(player, initialDelay) { + invokeAnimationLoop() + invokeCraftLoop() + return@queueScript stopExecuting(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt b/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt new file mode 100644 index 000000000..15ef1d38f --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/GemBoltListeners.kt @@ -0,0 +1,90 @@ +package content.global.skill.fletching.gem + +import core.api.amountInInventory +import core.api.getDynLevel +import core.api.hasSpaceFor +import core.api.sendDialogue +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +@Suppress("unused") // Reflectively loaded +class GemBoltListeners : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.CHISEL_1755, *GemBoltsCraftInfo.gemIds) { player, _, gem -> + val gemBoltCraftInfo = GemBoltsCraftInfo.forGemId(gem.id) ?: return@onUseWith true + + object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(gemBoltCraftInfo.gemItemId)) { + override fun create(amount: Int, index: Int) { + if (getDynLevel(player, Skills.FLETCHING) < gemBoltCraftInfo.level) { + sendGemTipCutLevelCheckFailDialog(player, gemBoltCraftInfo.level) + return + } + CutGemsIntoBoltTipsScript(player, gemBoltCraftInfo, amount).invoke() + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, gemBoltCraftInfo.gemItemId) + } + }.open() + return@onUseWith true + } + + onUseWith( + IntType.ITEM, + GemBoltsCraftInfo.untippedBoltIds, + *GemBoltsCraftInfo.boltTipIds + ) { player, untippedBolt, boltTip -> + val bolt = GemBoltsCraftInfo.forTipId(boltTip.id) ?: return@onUseWith false + if (untippedBolt.id != bolt.untippedBoltItemId) return@onUseWith false + + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(bolt.tippedBoltItemId)) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements()) return + AttachGemTipToBoltScript(player, bolt, amount).invoke() + } + + private fun playerMeetsInitialRequirements(): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < bolt.level) { + sendGemTipAttachLevelCheckFailDialog(player, bolt.level) + return false + } + if (!hasSpaceFor(player, Item(bolt.tippedBoltItemId))) { + sendDialogue(player, "You do not have enough inventory space.") + return false + } + return true + } + + override fun getAll(index: Int): Int { + return min(amountInInventory(player, untippedBolt.id), amountInInventory(player, boltTip.id)) + } + } + handler.open() + return@onUseWith true + } + } + + companion object { + fun sendGemTipCutLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a Fletching level of $level or above to do that." + ) + } + + fun sendGemTipAttachLevelCheckFailDialog(player: Player, level: Int) { + sendDialogue( + player, + "You need a Fletching level of $level or above to do that." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt b/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt new file mode 100644 index 000000000..d0e2b0f11 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/gem/GemBoltsCraftInfo.kt @@ -0,0 +1,106 @@ +package content.global.skill.fletching.gem + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different gem tipped bolts + * @property untippedBoltItemId the base untipped bolt item id + * @property gemItemId the gem item id used to craft the bolt tips + * @property gemCutAnimationId the animation to play when the player cuts the gem + * @property tipItemId the item id of the resulting bolt tips + * @property tippedBoltItemId the item id of the finished gem-tipped bolt + * @property level the required level to craft the gem tipped bolt or tips themselves + * @property experience gained creating one set of gem tips, or per bolt tipped + */ +enum class GemBoltsCraftInfo( + var untippedBoltItemId: Int, + var gemItemId: Int, + var gemCutAnimationId: Int, + var tipItemId: Int, + var tippedBoltItemId: Int, + var level: Int, + var experience: Double +) { + OPAL(Items.BRONZE_BOLTS_877, Items.OPAL_1609, 890, Items.OPAL_BOLT_TIPS_45, Items.OPAL_BOLTS_879, 11, 1.6), + JADE(Items.BLURITE_BOLTS_9139, Items.JADE_1611, 891, Items.JADE_BOLT_TIPS_9187, Items.JADE_BOLTS_9335, 26, 2.4), + PEARL(Items.IRON_BOLTS_9140, Items.OYSTER_PEARL_411, 4470, Items.PEARL_BOLT_TIPS_46, Items.PEARL_BOLTS_880, 41, 3.2), + PEARLS( + Items.IRON_BOLTS_9140, + Items.OYSTER_PEARLS_413, + 4470, + Items.PEARL_BOLT_TIPS_46, + Items.PEARL_BOLTS_880, + 41, + 3.2 + ), + RED_TOPAZ( + Items.STEEL_BOLTS_9141, + Items.RED_TOPAZ_1613, + 892, + Items.TOPAZ_BOLT_TIPS_9188, + Items.TOPAZ_BOLTS_9336, + 48, + 3.9 + ), + SAPPHIRE( + Items.MITHRIL_BOLTS_9142, + Items.SAPPHIRE_1607, + 888, + Items.SAPPHIRE_BOLT_TIPS_9189, + Items.SAPPHIRE_BOLTS_9337, + 56, + 4.7 + ), + EMERALD( + Items.MITHRIL_BOLTS_9142, + Items.EMERALD_1605, + 889, + Items.EMERALD_BOLT_TIPS_9190, + Items.EMERALD_BOLTS_9338, + 58, + 5.5 + ), + RUBY(Items.ADAMANT_BOLTS_9143, Items.RUBY_1603, 887, Items.RUBY_BOLT_TIPS_9191, Items.RUBY_BOLTS_9339, 63, 6.3), + DIAMOND( + Items.ADAMANT_BOLTS_9143, + Items.DIAMOND_1601, + 886, + Items.DIAMOND_BOLT_TIPS_9192, + Items.DIAMOND_BOLTS_9340, + 65, + 7.0 + ), + DRAGONSTONE( + Items.RUNE_BOLTS_9144, + Items.DRAGONSTONE_1615, + 885, + Items.DRAGON_BOLT_TIPS_9193, + Items.DRAGON_BOLTS_9341, + 71, + 8.2 + ), + ONYX(Items.RUNE_BOLTS_9144, Items.ONYX_6573, 2717, Items.ONYX_BOLT_TIPS_9194, Items.ONYX_BOLTS_9342, 73, 9.4); + + companion object { + private val gemBoltCraftInfoByGemId = values().associateBy { it.gemItemId } + private val gemBoltCraftInfoByBoltTipId = values().associateBy { it.tipItemId } + + val untippedBoltIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.untippedBoltItemId }.distinct() + .toIntArray() + + val gemIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.gemItemId }.toIntArray() + + val boltTipIds: IntArray = + values().map { gemBoltsCraftInfo: GemBoltsCraftInfo -> gemBoltsCraftInfo.tipItemId }.toIntArray() + + fun forGemId(gemId: Int): GemBoltsCraftInfo? { + return gemBoltCraftInfoByGemId[gemId] + } + + fun forTipId(tipId: Int): GemBoltsCraftInfo? { + return gemBoltCraftInfoByBoltTipId[tipId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt b/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt new file mode 100644 index 000000000..46373cda0 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/grapple/GrappleListeners.kt @@ -0,0 +1,67 @@ +package content.global.skill.fletching.grapple + +import core.api.* +import core.game.interaction.Clocks +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items + +@Suppress("unused") // Reflectively loaded +class GrappleListeners : InteractionListener { + private val mithrilBolt = Items.MITHRIL_BOLTS_9142 + private val mithrilGrappleTip = Items.MITH_GRAPPLE_TIP_9416 + private val rope = Items.ROPE_954 + private val mithrilGrapple = Items.MITH_GRAPPLE_9418 + private val mithrilGrappleWithRope = Items.MITH_GRAPPLE_9419 + + override fun defineListeners() { + onUseWith(IntType.ITEM, mithrilBolt, mithrilGrappleTip) { player, bolt, tip -> + if (getDynLevel(player, Skills.FLETCHING) < 59) { + sendMessage(player, "You need a fletching level of 59 to make this.") + return@onUseWith true + } + queueScript(player, 0) { _ -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + if (removeItemsIfPlayerHasEnough( + player, + Item(mithrilBolt, 1), + tip.asItem() + ) + ) { + addItem(player, mithrilGrapple, 1) + } + + delayClock(player, Clocks.SKILLING, 3) + return@queueScript stopExecuting(player) + } + + + return@onUseWith true + } + + onUseWith(IntType.ITEM, rope, mithrilGrapple) { player, rope, grapple -> + if (getDynLevel(player, Skills.FLETCHING) < 59) { + sendMessage(player, "You need a fletching level of 59 to make this.") + return@onUseWith true + } + queueScript(player, 0) { _ -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + if (removeItemsIfPlayerHasEnough( + player, + rope.asItem(), + grapple.asItem() + ) + ) { + addItem(player, mithrilGrappleWithRope, 1) + } + delayClock(player, Clocks.SKILLING, 3) + return@queueScript stopExecuting(player) + } + return@onUseWith true + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java b/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java deleted file mode 100644 index c7ab5ec7c..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHead.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.arrow; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the enum storing the arrow head information. - * @author 'Vexia - * @note brutal arrows after quest. - *//* - -public enum ArrowHead { - BRONZE_ARROW(new Item(39), new Item(882), 1, 2.6), - IRON_ARROW(new Item(40), new Item(884), 15, 3.8), - STEEL_ARROW(new Item(41), new Item(886), 30, 6.3), - MITHRIL_ARROW(new Item(42), new Item(888), 45, 8.8), - ADAMANT_ARROW(new Item(43), new Item(890), 60, 10), - RUNE_ARROW(new Item(44), new Item(892), 75, 13.8), - DRAGON_ARROW(new Item(11237), new Item(11212), 90, 16.3), - BROAD_ARROW(new Item(13278), new Item(4160), 52, 10); - - */ -/** - * Constructs a new {@code ArrowHead.java} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - ArrowHead(Item item, Item product, int level, double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the arrow tip. - *//* - - private final Item item; - - */ -/** - * Represents the product item. - *//* - - private final Item product; - - */ -/** - * Represents the level required. - *//* - - private final int level; - - */ -/** - * Represents the experience gained. - *//* - - private final double experience; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getTips() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Gets the arrow head. - * @param item the item. - * @return the arrow head. - *//* - - public static ArrowHead forItem(final Item item) { - for (ArrowHead arrow : ArrowHead.values()) { - if (arrow.getTips().getId() == item.getId()) { - return arrow; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java b/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java deleted file mode 100644 index 927f16d10..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java +++ /dev/null @@ -1,104 +0,0 @@ -package content.global.skill.fletching.items.arrow; - -import content.global.skill.slayer.SlayerManager; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the arrow head pulse to complete the headless arrow. - * @author 'Vexia - */ -public class ArrowHeadPulse extends SkillPulse { - - /** - * Represents the headless arrow item. - */ - private static final Item HEADLESS_ARROW = new Item(53); - - /** - * Represents the arrow head. - */ - private final Fletching.ArrowHeads arrow; - - /** - * Represents the sets to do. - */ - private int sets; - - /** - * Constructs a new {@code ArrowHeadPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - * @param arrow the arrow. - * @param sets the sets. - */ - public ArrowHeadPulse(final Player player, final Item node, final Fletching.ArrowHeads arrow, final int sets) { - super(player, node); - this.arrow = arrow; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (arrow.unfinished == 4160) { - if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) { - player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad arrows."); - return false; - } - } - if (player.getSkills().getLevel(Skills.FLETCHING) < arrow.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + arrow.level + " to do this."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(3); - } - Item tip = arrow.getUnfinished(); - int tipAmount = player.getInventory().getAmount(arrow.unfinished); - int shaftAmount = player.getInventory().getAmount(HEADLESS_ARROW); - if (tipAmount >= 15 && shaftAmount >= 15) { - HEADLESS_ARROW.setAmount(15); - tip.setAmount(15); - player.getPacketDispatch().sendMessage("You attach arrow heads to 15 arrow shafts."); - } else { - int amount = tipAmount > shaftAmount ? shaftAmount : tipAmount; - HEADLESS_ARROW.setAmount(amount); - tip.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach an arrow head to an arrow shaft." : "You attach arrow heads to " + amount + " arrow shafts."); - } - if (player.getInventory().remove(HEADLESS_ARROW, tip)) { - player.getSkills().addExperience(Skills.FLETCHING, arrow.experience * tip.getAmount(), true); - Item product = arrow.getFinished(); - product.setAmount(tip.getAmount()); - player.getInventory().add(product); - } - HEADLESS_ARROW.setAmount(1); - tip.setAmount(1); - if (!player.getInventory().containsItem(HEADLESS_ARROW)) { - return true; - } - if (!player.getInventory().containsItem(tip)) { - return true; - } - sets--; - return sets == 0; - } - - @Override - public void message(int type) { - - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java b/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java deleted file mode 100644 index 3ac1ab593..000000000 --- a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java +++ /dev/null @@ -1,139 +0,0 @@ -package content.global.skill.fletching.items.arrow; - -import org.rs09.consts.Items; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the arrow pulse for creating unfinished arrows. - * @author 'Vexia - */ -public final class HeadlessArrowPulse extends SkillPulse { - - /** - * Represents the headless arrow item. - */ - private final Item HEADLESS_ARROW = new Item(Items.HEADLESS_ARROW_53); - - /** - * Represents the arrow shaft item. - */ - private final Item ARROW_SHAFT = new Item(Items.ARROW_SHAFT_52); - - /** - * Represents the feather items. - */ - private static final Item[] FEATHER = new Item[] { - new Item(Items.FEATHER_314), - new Item(Items.STRIPY_FEATHER_10087), - new Item(Items.RED_FEATHER_10088), - new Item(Items.BLUE_FEATHER_10089), - new Item(Items.YELLOW_FEATHER_10090), - new Item(Items.ORANGE_FEATHER_10091) - }; - - /** - * The feather being used. - */ - private Item feather; - - /** - * Represents the amount to make. - */ - private int sets; - - /** - * Represents if we should use sets, meaning we have 15 & 15 arrow shafts and feathers. - */ - private boolean useSets = false; - - /** - * Constructs a new {@code ArrowPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public HeadlessArrowPulse(Player player, Item node, Item feather, int sets) { - super(player, node); - this.sets = sets; - this.feather = feather; - } - - @Override - public boolean checkRequirements() { - if (!player.getInventory().containsItem(ARROW_SHAFT)) { - player.getDialogueInterpreter().sendDialogue("You don't have any arrow shafts."); - return false; - } - if (feather == null || !player.getInventory().containsItem(feather)) { - player.getDialogueInterpreter().sendDialogue("You don't have any feathers."); - return false; - } - if (player.getInventory().contains(ARROW_SHAFT.getId(), 15) && player.getInventory().contains(feather.getId(), 15)) { - useSets = true; - } else { - useSets = false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - int featherAmount = player.getInventory().getAmount(feather); - int shaftAmount = player.getInventory().getAmount(ARROW_SHAFT); - if (getDelay() == 1) { - super.setDelay(3); - } - if (featherAmount >= 15 && shaftAmount >= 15) { - feather.setAmount(15); - ARROW_SHAFT.setAmount(15); - player.getPacketDispatch().sendMessage("You attach feathers to 15 arrow shafts."); - } else { - int amount = Math.min(featherAmount, shaftAmount); - feather.setAmount(amount); - ARROW_SHAFT.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 - ? "You attach a feathers to a shaft." : "You attach feathers to " + amount + " arrow shafts."); - } - if (player.getInventory().remove(feather, ARROW_SHAFT)) { - HEADLESS_ARROW.setAmount(feather.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, HEADLESS_ARROW.getAmount(), true); - player.getInventory().add(HEADLESS_ARROW); - } - HEADLESS_ARROW.setAmount(1); - feather.setAmount(1); - ARROW_SHAFT.setAmount(1); - if (!player.getInventory().containsItem(ARROW_SHAFT)) { - return true; - } - if (!player.getInventory().containsItem(feather)) { - return true; - } - sets--; - return sets <= 0; - } - - @Override - public void message(int type) { - } - - /** - * Gets the feather item. - * @return the item. - */ - private Item getFeather() { - int length = FEATHER.length; - for (int i = 0; i < length; i++) { - Item f = FEATHER[i]; - if (player.getInventory().containsItem(f)) { - return f; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java b/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java deleted file mode 100644 index f877050db..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bolts/Bolt.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.bolts; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents an enum of bolts. - * @author 'Vexia - *//* - -public enum Bolt { - BRONZE_BOLT(new Item(9375), new Item(877), 9, 0.5), - BLURITE_BOLT(new Item(9376), new Item(9139), 24, 1), - IRON_BOLT(new Item(9377), new Item(9140), 39, 1.5), - SILVER_BOLT(new Item(9382), new Item(9145), 43, 2.5), - STEEL_BOLT(new Item(9378), new Item(9141), 46, 3.5), - MITHRIL_BOLT(new Item(9379), new Item(9142), 54, 5), - ADAMANTITE_BOLT(new Item(9380), new Item(9143), 61, 7), - RUNITE_BOLT(new Item(9381), new Item(9144), 69, 10), - BROAD_BOLT(new Item(13279), new Item(13280), 55, 3); - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The product recieved. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience gained. - *//* - - private final double experience; - - */ -/** - * Constructs a new {@code Bolt} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experienece. - *//* - - Bolt(Item item, Item product, int level, double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the bolt for the item. - * @param item the item. - * @return the bolt. - *//* - - public static Bolt forItem(final Item item) { - for (Bolt bolt : Bolt.values()) { - if (bolt.getItem().getId() == item.getId()) { - return bolt; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java b/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java deleted file mode 100644 index 26c974603..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java +++ /dev/null @@ -1,125 +0,0 @@ -package content.global.skill.fletching.items.bolts; - -import content.global.skill.slayer.SlayerManager; -import org.rs09.consts.Items; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the bolt pulse class to make bolts. - * @author ceik - */ -public final class BoltPulse extends SkillPulse { - - /** - * Represents the feather item. - */ - private Item feather; - - /** - * Represents possible feather Items - */ - private static final Item[] FEATHER = new Item[] { - new Item(Items.FEATHER_314), - new Item(Items.STRIPY_FEATHER_10087), - new Item(Items.RED_FEATHER_10088), - new Item(Items.BLUE_FEATHER_10089), - new Item(Items.YELLOW_FEATHER_10090), - new Item(Items.ORANGE_FEATHER_10091) - }; - - /** - * Represents the bolt. - */ - private final Fletching.Bolts bolt; - - /** - * Represents the sets to do. - */ - private int sets; - - /** - * Represents if we're using sets. - */ - private boolean useSets = false; - - /** - * Constructs a new {@code BoltPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public BoltPulse(Player player, Item node, final Fletching.Bolts bolt, final Item feather, final int sets) { - super(player, node); - this.bolt = bolt; - this.sets = sets; - this.feather = feather; - } - - @Override - public boolean checkRequirements() { - if (bolt.getUnfinished().getId() == 13279) { - if (!SlayerManager.getInstance(player).flags.isBroadsUnlocked()) { - player.getDialogueInterpreter().sendDialogue("You need to unlock the ability to create broad bolts."); - return false; - } - } - if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bolt.level + " in order to do this."); - return false; - } - if (!player.getInventory().containsItem(feather)) { - return false; - } - if (!player.getInventory().containsItem(bolt.getUnfinished())) { - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - int featherAmount = player.getInventory().getAmount(feather); - int boltAmount = player.getInventory().getAmount(bolt.unfinished); - if (getDelay() == 1) { - super.setDelay(3); - } - final Item unfinished = bolt.getUnfinished(); - if (featherAmount >= 10 && boltAmount >= 10) { - feather.setAmount(10); - unfinished.setAmount(10); - player.getPacketDispatch().sendMessage("You fletch 10 bolts."); - } else { - int amount = featherAmount > boltAmount ? boltAmount : featherAmount; - feather.setAmount(amount); - unfinished.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a bolt." : "You fletch " + amount + " bolts"); - } - if (player.getInventory().remove(feather, unfinished)) { - Item product = bolt.getFinished(); - product.setAmount(feather.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, product.getAmount() * bolt.experience, true); - player.getInventory().add(product); - } - feather.setAmount(1); - if (!player.getInventory().containsItem(feather)) { - return true; - } - if (!player.getInventory().containsItem(bolt.getUnfinished())) { - return true; - } - sets--; - return sets <= 0; - } - - @Override - public void message(int type) { - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java b/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java deleted file mode 100644 index 0187f17bb..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bow/StringBow.java +++ /dev/null @@ -1,136 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.bow; - -import org.crandor.game.node.item.Item; -import org.crandor.game.world.update.flag.context.Animation; - -*/ -/** - * Represents the enum of stringing bows. - * @author 'Vexia - *//* - -public enum StringBow { - SHORT_BOW(new Item(50), new Item(841), 5, 5, new Animation(6678)), - LONG_BOW(new Item(48), new Item(839), 10, 10, new Animation(6684)), - OAK_SHORTBOW(new Item(54), new Item(843), 20, 16.5, new Animation(6679)), - OAK_LONGBOW(new Item(56), new Item(845), 25, 25, new Animation(6685)), - WILLOW_SHORTBOW(new Item(60), new Item(849), 35, 33.3, new Animation(6680)), - WILLOW_LONGBOW(new Item(58), new Item(847), 40, 41.5, new Animation(6686)), - MAPLE_SHORTBOW(new Item(64), new Item(853), 50, 50, new Animation(6681)), - MAPLE_LONGBOW(new Item(62), new Item(851), 55, 58.3, new Animation(6687)), - YEW_SHORTBOW(new Item(68), new Item(857), 65, 66, new Animation(6682)), - YEW_LONGBOW(new Item(66), new Item(855), 70, 75, new Animation(6688)), - MAGIC_SHORTBOW(new Item(72), new Item(861), 80, 83.3, new Animation(6683)), - MAGIC_LONGBOW(new Item(70), new Item(859), 85, 91.5, new Animation(6689)); - - - StringBow(final Item item, final Item product, final int level, final double experience, final Animation animation) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The item product. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience required. - *//* - - private final double experience; - - */ -/** - * The animation of stringing. - *//* - - private final Animation animation; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the animation. - * @return the animation. - *//* - - public Animation getAnimation() { - return animation; - } - - */ -/** - * Method used to get the string bow for the item. - * @param item the item. - * @return the string bow. - *//* - - public static StringBow forItem(final int id) { - for (StringBow bw : StringBow.values()) { - if (bw.getItem().getId() == id) { - return bw; - } - } - return null; - } -}*/ diff --git a/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java b/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java deleted file mode 100644 index fdae3a1bb..000000000 --- a/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java +++ /dev/null @@ -1,87 +0,0 @@ -package content.global.skill.fletching.items.bow; - -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.zone.ZoneBorders; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the skill pulse of stringing. - * - * @author Ceikry - */ -public class StringPulse extends SkillPulse { - - /** - * Represents the string bow. - */ - private final Fletching.String bow; - - /** - * The amount. - */ - private int amount; - - /** - * Constructs a new {@code StringbowPlugin.java} {@code Object}. - * - * @param player the player. - * @param node the node. - */ - public StringPulse(Player player, Item node, final Fletching.String bow, int amount) { - super(player, node); - this.bow = bow; - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - if (getDelay() == 1) { - setDelay(2); - } - if (player.getSkills().getLevel(Skills.FLETCHING) < bow.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.level + " to string this bow."); - return false; - } - if (!player.getInventory().containsItem(new Item(bow.string))) { - player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings."); - return false; - } - animate(); - return true; - } - - @Override - public void animate() { - player.animate(bow.animation); - } - - @Override - public boolean reward() { - if (player.getInventory().remove(new Item(bow.unfinished), new Item(bow.string))) { - player.getInventory().add(new Item(bow.product)); - player.getSkills().addExperience(Skills.FLETCHING, bow.experience, true); - player.getPacketDispatch().sendMessage("You add a string to the bow."); - - if (bow == Fletching.String.MAGIC_SHORTBOW - && (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player) - || new ZoneBorders(2727, 3487, 2730, 3490, 0).insideBorder(player)) - && player.getAttribute("diary:seers:fletch-magic-short-bow", false)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2); - } - } - if (!player.getInventory().containsItem(new Item(bow.string)) || !player.getInventory().containsItem(new Item(bow.unfinished))) { - return true; - } - amount--; - return amount == 0; - } - - @Override - public void message(int type) { - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java b/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java deleted file mode 100644 index cfe0cf53b..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/CrossbowPulse.java +++ /dev/null @@ -1,97 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.crossbow; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the skill pulse of stringing. - * @author 'Vexia - *//* - -public class CrossbowPulse extends SkillPulse { - - */ -/** - * Represents the bow string item. - *//* - - private final Item BOW_STRING = new Item(9438); - - */ -/** - * Represents the string bow. - *//* - - private final StringCross bow; - - */ -/** - * Represents the amount. - *//* - - private int amount; - - */ -/** - * Constructs a new {@code StringcrossbowPlugin.java} {@code Object}. - * @param player the player. - * @param node the node. - *//* - - public CrossbowPulse(Player player, Item node, final StringCross bow, int amount) { - super(player, node); - this.bow = bow; - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < bow.getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.getLevel() + " to string this crossbow."); - return false; - } - if (!player.getInventory().containsItem(BOW_STRING)) { - player.getDialogueInterpreter().sendDialogue("You seem to have run out of bow strings."); - return false; - } - return true; - } - - @Override - public void animate() { - player.animate(bow.getAnimation()); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(5); - return false; - } - if (player.getInventory().remove(bow.getItem(), BOW_STRING)) { - player.getInventory().add(bow.getProduct()); - player.getSkills().addExperience(Skills.FLETCHING, bow.getExperience(), true); - player.getPacketDispatch().sendMessage("You add a string to the crossbow."); - } - if (!player.getInventory().containsItem(BOW_STRING) || !player.getInventory().containsItem(bow.getItem())) { - return true; - } - amount--; - return amount == 0; - } - - @Override - public void message(int type) { - switch (type) { - case 0: - break; - case 1: - break; - } - } - -}*/ diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java b/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java deleted file mode 100644 index 6ffc01413..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/Limb.java +++ /dev/null @@ -1,128 +0,0 @@ -package content.global.skill.fletching.items.crossbow; - -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; - -/** - * Represents the enum for limbs. - * @author 'Vexia - */ -public enum Limb { - WOODEN_STOCK(new Item(9440), new Item(9420), new Item(9454), 9, 12, new Animation(4436)), - OAK_STOCK(new Item(9442), new Item(9422), new Item(9176), 24, 32, new Animation(4437)), - WILLOW_STOCK(new Item(9444), new Item(9423), new Item(9457), 39, 44, new Animation(4438)), - TEAK_STOCK(new Item(9446), new Item(9425), new Item(9459), 46, 54, new Animation(4439)), - MAPLE_STOCK(new Item(9448), new Item(9427), new Item(9461), 54, 64, new Animation(4440)), - MAHOGANY_STOCK(new Item(9450), new Item(9429), new Item(9463), 61, 82, new Animation(4441)), - YEW_STOCK(new Item(9452), new Item(9431), new Item(9465), 69, 100, new Animation(4442)); - - /** - * Constructs a new {@code StringcrosbowPlugin.java} {@code Object}. - * @param stock the stock. - * @param limb the limb. - * @param product the product. - * @param level the level. - * @param experience the experience. - * @param animation the animation. - */ - Limb(Item stock, Item limb, Item product, int level, double experience, Animation animation) { - this.stock = stock; - this.limb = limb; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - /** - * The stock. - */ - private final Item stock; - - /** - * The limb. - */ - private final Item limb; - - /** - * The product. - */ - private final Item product; - - /** - * The level. - */ - private final int level; - - /** - * The experience. - */ - private final double experience; - - /** - * The animation. - */ - private final Animation animation; - - /** - * Gets the stock. - * @return The stock. - */ - public Item getStock() { - return stock; - } - - /** - * Gets the limb. - * @return The limb. - */ - public Item getLimb() { - return limb; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the animation. - * @return The animation. - */ - public Animation getAnimation() { - return animation; - } - - /** - * Method used to get the {@link Limb} for the item. - * @param item the item. - * @return the limb. - */ - public static Limb forItems(final Item item, final Item second) { - for (Limb l : Limb.values()) { - if (l.getLimb().getId() == item.getId() && l.getStock().getId() == second.getId() || l.getLimb().getId() == second.getId() && l.getStock().getId() == item.getId()) { - return l; - } - } - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt b/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt deleted file mode 100644 index 2d6d35562..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/LimbPulse.kt +++ /dev/null @@ -1,52 +0,0 @@ -package content.global.skill.fletching.items.crossbow - -import core.game.node.entity.player.Player -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching -import core.game.node.item.Item - -/** - * Represents the skill pulse of attaching limbs. - * @author Ceikry - */ -class LimbPulse(player: Player?, node: Item, private val limb: Fletching.Limb, private var amount: Int) : SkillPulse(player, node) { - override fun checkRequirements(): Boolean { - if (player.skills.getLevel(Skills.FLETCHING) < limb.level) { - player.dialogueInterpreter.sendDialogue("You need a fletching level of " + limb.level + " to attach these limbs.") - return false - } - if (!player.inventory.containsItem(Item(limb.limb))) { - player.dialogueInterpreter.sendDialogue("That's not the correct limb to attach.") - return false - } - if(!player.inventory.containsItem(Item(limb.stock))){ - player.dialogueInterpreter.sendDialogue("That's not the correct stock for that limb.") - return false - } - return player.inventory.containsItem(Item(limb.stock)) - } - - override fun animate() { - player.animate(limb.animation) - } - - override fun reward(): Boolean { - if (delay == 1) { - super.setDelay(6) - return false - } - if (player.inventory.remove(Item(limb.stock), Item(limb.limb))) { - player.inventory.add(Item(limb.product)) - player.skills.addExperience(Skills.FLETCHING, limb.experience, true) - player.packetDispatch.sendMessage("You attach the metal limbs to the stock.") - } - if (!player.inventory.containsItem(Item(limb.limb))) { - return true - } - amount-- - return amount == 0 - } - - override fun message(type: Int) {} -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java b/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java deleted file mode 100644 index 32671c480..000000000 --- a/Server/src/main/content/global/skill/fletching/items/crossbow/StringCross.java +++ /dev/null @@ -1,139 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.crossbow; - -import org.crandor.game.node.item.Item; -import org.crandor.game.world.update.flag.context.Animation; - -*/ -/** - * Represents the enum of stringing crossbows. - * @author 'Vexia - *//* - -public enum StringCross { - BRONZE_CBOW(new Item(9454), new Item(9174), 9, 6, new Animation(6671)), - BLURITE_CBOW(new Item(9456), new Item(9176), 24, 16, new Animation(6672)), - IRON_CBOW(new Item(9457), new Item(9177), 39, 22, new Animation(6673)), - STEEL_CBOW(new Item(9459), new Item(9179), 46, 27, new Animation(6674)), - MITHIRIL_CBOW(new Item(9461), new Item(9181), 54, 32, new Animation(6675)), - ADAMANT_CBOW(new Item(9463), new Item(9183), 61, 41, new Animation(6676)), - RUNITE_CBOW(new Item(9465), new Item(9185), 69, 50, new Animation(6677)); - */ -/** - * Constructs a new {@code StringcrossbowPlugin.java} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - *//* - - StringCross(final Item item, final Item product, final int level, final double experience, final Animation animation) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - this.animation = animation; - } - - */ -/** - * The item required. - *//* - - private final Item item; - - */ -/** - * The item product. - *//* - - private final Item product; - - */ -/** - * The level required. - *//* - - private final int level; - - */ -/** - * The experience required. - *//* - - private final double experience; - - */ -/** - * The animation of stringing. - *//* - - private final Animation animation; - - */ -/** - * Gets the item. - * @return The item. - *//* - - public Item getItem() { - return item; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the animation. - * @return the animation. - *//* - - public Animation getAnimation() { - return animation; - } - - */ -/** - * Method used to get the string bow for the item. - * @param item the item. - * @return the string bow. - *//* - - public static StringCross forItem(final Item item) { - for (StringCross bw : StringCross.values()) { - if (bw.getItem().getId() == item.getId()) { - return bw; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/darts/Dart.java b/Server/src/main/content/global/skill/fletching/items/darts/Dart.java deleted file mode 100644 index 235849496..000000000 --- a/Server/src/main/content/global/skill/fletching/items/darts/Dart.java +++ /dev/null @@ -1,96 +0,0 @@ -package content.global.skill.fletching.items.darts; - -import core.game.node.item.Item; - -/** - * Represents the enum to hold dart info. - * @author 'Vexia - */ -public enum Dart { - BRONZE_DART(new Item(819), new Item(806), 1, 1.8), - IRON_DART(new Item(820), new Item(807), 22, 3.8), - STEEL_DART(new Item(821), new Item(808), 37, 7.5), - MITHRIL_DART(new Item(822), new Item(809), 52, 11.2), - ADAMANT_DART(new Item(823), new Item(810), 67, 15), - RUNE_DART(new Item(824), new Item(811), 81, 18.8), - DRAGON_DART(new Item(11232), new Item(11230), 95, 25); - /** - * Constructs a new {@code Dart} {@code Object}. - * @param item the item. - * @param product the product. - * @param level the level. - * @param experience the experience. - */ - Dart(final Item item, final Item product, final int level, final double experience) { - this.item = item; - this.product = product; - this.level = level; - this.experience = experience; - } - - /** - * Represents the item required. - */ - private final Item item; - - /** - * Represents the product gained. - */ - private final Item product; - - /** - * Represents the level required. - */ - private final int level; - - /** - * Represents the experience gained. - */ - private final double experience; - - /** - * Gets the item. - * @return The item. - */ - public Item getItem() { - return item; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Method used to get the dart for the item. - * @param item the item. - * @return the dart. - */ - public static Dart forItem(final Item item) { - for (Dart dart : Dart.values()) { - if (dart.getItem().getId() == item.getId()) { - return dart; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java b/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java deleted file mode 100644 index ec2e31e0e..000000000 --- a/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java +++ /dev/null @@ -1,98 +0,0 @@ -package content.global.skill.fletching.items.darts; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the dart pulse. - * @author ceikry - */ -public final class DartPulse extends SkillPulse { - - /** - * Represents the feather item. - */ - private static final Item FEATHER = new Item(314); - - /** - * Represents the dart. - */ - private final Fletching.Darts dart; - - /** - * Represents the sets to make. - */ - private int sets; - - /** - * Constructs a new {@code DartPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public DartPulse(Player player, Item node, Fletching.Darts dart, int sets) { - super(player, node); - this.dart = dart; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < dart.level) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + dart.level + " to do this."); - return false; - } - if (!player.getQuestRepository().isComplete("The Tourist Trap")){ - player.getDialogueInterpreter().sendDialogue("You need to have completed Tourist Trap to fletch darts."); - return false; - } - return true; - } - - @Override - public void animate() { - - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(3); - } - final Item unfinished = dart.getUnfinished(); - final int dartAmount = player.getInventory().getAmount(unfinished); - final int featherAmount = player.getInventory().getAmount(FEATHER); - if (dartAmount >= 10 && featherAmount >= 10) { - FEATHER.setAmount(10); - unfinished.setAmount(10); - player.getPacketDispatch().sendMessage("You attach feathers to 10 darts."); - } else { - int amount = featherAmount > dartAmount ? dartAmount : featherAmount; - FEATHER.setAmount(amount); - unfinished.setAmount(amount); - player.getPacketDispatch().sendMessage(amount == 1 ? "You attach a feather to a dart." : "You attach feathers to " + amount + " darts."); - } - if (player.getInventory().remove(FEATHER, unfinished)) { - Item product = dart.getFinished(); - product.setAmount(FEATHER.getAmount()); - player.getSkills().addExperience(Skills.FLETCHING, dart.experience * product.getAmount(), true); - player.getInventory().add(product); - } - FEATHER.setAmount(1); - if (!player.getInventory().containsItem(FEATHER)) { - return true; - } - if (!player.getInventory().containsItem(dart.getUnfinished())) { - return true; - } - sets--; - return sets == 0; - } - - @Override - public void message(int type) { - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/gem/Gem.java b/Server/src/main/content/global/skill/fletching/items/gem/Gem.java deleted file mode 100644 index 6a3377aa4..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/Gem.java +++ /dev/null @@ -1,101 +0,0 @@ -package content.global.skill.fletching.items.gem; - -import core.game.node.item.Item; - -/** - * Represents gems to cut into bolt tips. - * @author 'Vexia - * @date 01/12/2013 - */ -public enum Gem { - OPAL(new Item(1609), new Item(45, 12), 11, 1.5), - JADE(new Item(1611), new Item(9187, 12), 26, 2.4), - RED_TOPAZ(new Item(1613), new Item(9188, 12), 48, 3.9), - SAPPHIRE(new Item(1607), new Item(9189, 12), 56, 4), - EMERALD(new Item(1605), new Item(9190, 12), 58, 5.5), - RUBY(new Item(1603), new Item(9191, 12), 63, 6.3), - DIAMOND(new Item(1601), new Item(9192, 12), 65, 7), - DRAGONSTONE(new Item(1615), new Item(9193, 12), 71, 8.2), - ONYX(new Item(6573), new Item(9194, 24), 73, 9.4); - - /** - * Constructs a new {@code Gem.java} {@code Object}. - * @param gem the gem. - * @param bolt the bolt. - * @param level the level. - * @param experience the experience. - */ - Gem(Item gem, Item bolt, int level, double experience) { - this.gem = gem; - this.bolt = bolt; - this.level = level; - this.experience = experience; - } - - /** - * Represents the gem. - */ - private final Item gem; - - /** - * Represents the bolt. - */ - private final Item bolt; - - /** - * Represents the level required. - */ - private final int level; - - /** - * Represents the experience gained. - */ - private final double experience; - - /** - * Gets the gem. - * @return The gem. - */ - public Item getGem() { - return gem; - } - - /** - * Gets the bolt. - * @return The bolt. - */ - public Item getBolt() { - return bolt; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Method used to get a gem for the item. - * @param item the item. - * @return the gem. - */ - public static Gem forItem(final Item item) { - for (Gem gem : values()) { - if (gem.getGem().getId() == item.getId()) { - return gem; - } - } - return null; - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java b/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java deleted file mode 100644 index b21e6a164..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBolt.java +++ /dev/null @@ -1,144 +0,0 @@ -/* -package core.game.node.entity.skill.fletching.items.gem; - -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents a gem bolt. - * @author 'Vexia - * @date 01/12/2013 - *//* - -public enum GemBolt { - OPAL(new Item(877, 10), new Item(45, 10), new Item(879, 10), 11, 1.5), - PEARL(new Item(9140, 10), new Item(46, 10), new Item(880, 10), 41, 3.2), - JADE(new Item(9139, 10), new Item(9187, 10), new Item(9335, 10), 26, 2.4), - RED_TOPAZ(new Item(9141, 10), new Item(9188, 10), new Item(9336, 10), 48, 3.9), - SAPPHIRE(new Item(9142, 10), new Item(9189, 10), new Item(9337, 10), 56, 4), - EMERALD(new Item(9142, 10), new Item(9190, 10), new Item(9338, 10), 58, 5.5), - RUBY(new Item(9143, 10), new Item(9191, 10), new Item(9339, 10), 63, 6.3), - DIAMOND(new Item(9143, 10), new Item(9192, 10), new Item(9340, 10), 65, 7), - DRAGONSTONE(new Item(9144, 10), new Item(9193, 10), new Item(9341, 10), 71, 8.2), - ONYX(new Item(9144, 10), new Item(9194, 10), new Item(9342, 10), 73, 9.4); - - */ -/** - * Constructs a new {@code GemBolt} {@code Object}. - * @param base the base. - * @param tip the tip. - * @param level the level. - * @param experience the experience. - *//* - - GemBolt(Item base, Item tip, Item product, int level, double experience) { - this.base = base; - this.tip = tip; - this.product = product; - this.level = level; - this.experience = experience; - } - - */ -/** - * Represents the base item. - *//* - - private final Item base; - - */ -/** - * Represents the tip to attach. - *//* - - private final Item tip; - - */ -/** - * Represents the product. - *//* - - private final Item product; - - */ -/** - * Represents the level. - *//* - - private final int level; - - */ -/** - * Represents the experience. - *//* - - private final double experience; - - */ -/** - * Gets the base. - * @return The base. - *//* - - public Item getBase() { - return base; - } - - */ -/** - * Gets the tip. - * @return The tip. - *//* - - public Item getTip() { - return tip; - } - - */ -/** - * Gets the product. - * @return The product. - *//* - - public Item getProduct() { - return product; - } - - */ -/** - * Gets the level. - * @return The level. - *//* - - public int getLevel() { - return level; - } - - */ -/** - * Gets the experience. - * @return The experience. - *//* - - public double getExperience() { - return experience; - } - - */ -/** - * Method used to get the gem bolt from the id. - * @param boltt the boltt. - * @param tip the tip. - * @return the bolt. - *//* - - public static GemBolt forItems(final Item boltt, final Item tip) { - for (GemBolt bolt : values()) { - if (bolt.getBase().getId() == boltt.getId() && bolt.getTip().getId() == tip.getId() || bolt.getBase().getId() == tip.getId() && bolt.getTip().getId() == boltt.getId()) { - return bolt; - } - } - return null; - } -} -*/ diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt b/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt deleted file mode 100644 index e03781529..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltCutPulse.kt +++ /dev/null @@ -1,68 +0,0 @@ -package content.global.skill.fletching.items.gem - -import core.game.node.entity.player.Player -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching.GemBolts -import core.game.node.item.Item -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items - -/** - * Represents the gem cutting pulse(gem to bolt). - * @author Ceikry - */ -class GemBoltCutPulse -/** - * Constructs a new `GemCutPulse.java` `Object`. - * @param player the player. - * @param node the node. - * @param amount the amount. - */(player: Player?, node: Item?, - /** - * Represents the gem we're cutting. - */ - private val gem: GemBolts, - /** - * Represents the amount to make. - */ - private var amount: Int) : SkillPulse(player, node) { - /** - * Represents the ticks passed. - */ - private var ticks = 0 - - override fun checkRequirements(): Boolean { - if (player.skills.getLevel(Skills.FLETCHING) < gem.level) { - player.dialogueInterpreter.sendDialogue("You need a Fletching level of " + gem.level + " or above to do that.") - return false - } - return player.inventory.containsItem(Item(gem.gem)) - } - - override fun animate() { - if (ticks % 6 == 0) { - player.animate(ANIMATION) - } - } - - override fun reward(): Boolean { - if (++ticks % 5 != 0) { - return false - } - val reward = if (gem.gem == Items.OYSTER_PEARLS_413) Item(gem.tip, 24) else Item(gem.tip, 12) - if (player.inventory.remove(Item(gem.gem))) { - player.inventory.add(reward) - player.skills.addExperience(Skills.FLETCHING, gem.experience, true) - } - amount-- - return amount <= 0 - } - - companion object { - /** - * Represents the cutting animation. - */ - private val ANIMATION = Animation(6702) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java b/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java deleted file mode 100644 index 266d18dad..000000000 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java +++ /dev/null @@ -1,87 +0,0 @@ -package content.global.skill.fletching.items.gem; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.fletching.Fletching; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the attaching of a gem bolt to a premade bolt. - * @author Ceikry - */ -public final class GemBoltPulse extends SkillPulse { - - /** - * Represents the gem bolt being made. - */ - private Fletching.GemBolts bolt; - - /** - * Represents the sets to make. - */ - private int sets = 0; - - /** - * Represents the ticks passed. - */ - private int ticks; - - /** - * Constructs a new {@code GemBoltPulse} {@code Object}. - * @param player the player. - * @param node the node. - * @param sets the sets. - */ - public GemBoltPulse(Player player, Item node, Fletching.GemBolts bolt, int sets) { - super(player, node); - this.bolt = bolt; - this.sets = sets; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.FLETCHING) < bolt.level) { - player.getDialogueInterpreter().sendDialogue("You need a Fletching level of " + bolt.level + " or above to do that."); - return false; - } - if (!player.getInventory().containsItem(new Item(bolt.base)) || !player.getInventory().containsItem(new Item(bolt.tip))) { - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (++ticks % 3 != 0) { - return false; - } - int baseAmount = player.getInventory().getAmount(bolt.base); - int tipAmount = player.getInventory().getAmount(bolt.tip); - Item base = new Item(bolt.base); - Item tip = new Item(bolt.tip); - Item product = new Item(bolt.product); - if(baseAmount >= 10 && tipAmount >= 10){ - base.setAmount(10); - tip.setAmount(10); - product.setAmount(10); - } else { - int amount = baseAmount > tipAmount ? tipAmount : baseAmount; - base.setAmount(amount); - tip.setAmount(amount); - product.setAmount(amount); - } - if (player.getInventory().remove(base,tip)) { - player.getInventory().add(product); - player.getSkills().addExperience(Skills.FLETCHING, bolt.experience * product.getAmount(), true); - player.getPacketDispatch().sendMessage(product.getAmount() == 1 ? "You attach the tip to the bolt." : "You fletch " + product.getAmount() + " bolts."); - } - sets--; - return sets <= 0; - } - -} diff --git a/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java b/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java deleted file mode 100644 index cbba3820f..000000000 --- a/Server/src/main/content/global/skill/fletching/items/grapple/GrapplePulse.java +++ /dev/null @@ -1,85 +0,0 @@ -package content.global.skill.fletching.items.grapple; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the skill pulse used to create a mith grapple. - * @author 'Vexia - * @date 21/12/2013 - */ -public final class GrapplePulse extends SkillPulse { - - /** - * Represents the mith grapple tip. - */ - private static final Item MITH_GRAPPLE = new Item(9418); - - /** - * Represents the mithril grapple tio. - */ - private static final Item GRAPPLE_TIP = new Item(9416); - - /** - * Represents the mithril bolt. - */ - private static final Item MITHRIL_BOLT = new Item(9142); - - /** - * Represents the amount of the grapple to make. - */ - private int amount; - - /** - * Constructs a new {@code GrapplePulse} {@code Object}. - * @param player the player. - * @param node the node. - * @param amount the amount. - */ - public GrapplePulse(Player player, Item node, int amount) { - super(player, node); - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - int inventoryAmount = player.getInventory().getAmount(GRAPPLE_TIP); - if (amount > inventoryAmount) { - amount = inventoryAmount; - } - if (!player.getInventory().containsItem(GRAPPLE_TIP) || !player.getInventory().containsItem(MITHRIL_BOLT)) { - return false; - } - if (player.getSkills().getLevel(Skills.FLETCHING) < 59) { - player.getDialogueInterpreter().sendDialogue("You need a fletching level of at least 59 in order to do this."); - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - setDelay(3); - return false; - } - if (player.getInventory().remove(GRAPPLE_TIP) && player.getInventory().remove(MITHRIL_BOLT)) { - player.getInventory().add(MITH_GRAPPLE); - player.getSkills().addExperience(Skills.FLETCHING, 5, true); - } - amount--; - return amount < 1; - } - - @Override - public void message(int type) { - - } - -} diff --git a/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt b/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt new file mode 100644 index 000000000..33e6c5524 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/CraftItemWithLogScript.kt @@ -0,0 +1,113 @@ +package content.global.skill.fletching.log + +import content.global.skill.fletching.AchievementDiaryAttributeKeys +import content.global.skill.fletching.Zones +import content.global.skill.fletching.log.GrammarHelpers.aOrAn +import content.global.skill.fletching.log.GrammarHelpers.makeFriendlyName +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items + +/** + * Represents the queueScript used to craft things by carving a log + * @author ceik + * @param player the player. + * @param logCraftInfo crafting info about what we're making out of the log + * @param amount iterations of craft to run + */ +class CraftItemWithLogScript( + private val player: Player, + private val logCraftInfo: LogCraftInfo, + private val amount: Int +) { + + private val initialDelay = 1 + + // src https://gitlab.com/2009scape/2009scape/-/merge_requests/1960#note_2702231552 + private val subsequentDelay = 3 + + private val carveLogAnimation = Animation(1248) + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < logCraftInfo.level) { + LogCraftableListeners.sendLevelCheckFailDialog(player, logCraftInfo) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if (removeItem(player, logCraftInfo.logItemId.asItem())) { + player.animate(carveLogAnimation) + + val finishedItemName = makeFriendlyName(Item(logCraftInfo.finishedItemId).name) + + val amountToCraft = when (logCraftInfo) { + LogCraftInfo.OGRE_ARROW_SHAFT -> RandomFunction.random(2, 6) + LogCraftInfo.ARROW_SHAFT -> 15 + else -> 1 + } + + if (logCraftInfo == LogCraftInfo.OGRE_COMP_BOW) { + if (!removeItem(player, Items.WOLF_BONES_2859)) { + return@queueScript stopExecuting(player) + } + } + + sendCraftMessageToPlayer(amountToCraft, finishedItemName) + + addItem(player, logCraftInfo.finishedItemId, amountToCraft) + rewardXP(player, Skills.FLETCHING, logCraftInfo.experience) + + if (logCraftInfo == LogCraftInfo.MAGIC_SHORTBOW) { + handleSeersMagicShortbowAchievement() + } + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } + + private fun handleSeersMagicShortbowAchievement() { + if (Zones.inAnyZone(player, Zones.seersMagicShortbowAchievementZones) && + !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 2) + ) { + setAttribute( + player, + "/save:${AchievementDiaryAttributeKeys.FLETCHED_UNSTRUNG_MAGIC_SHORTBOW}", + true + ) + } + } + + private fun sendCraftMessageToPlayer(amountToCraft: Int, finishedItemName: String) { + when (logCraftInfo) { + LogCraftInfo.ARROW_SHAFT -> sendMessage( + player, + "You carefully cut the wood into $amountToCraft arrow shafts." + ) + + LogCraftInfo.OGRE_ARROW_SHAFT -> sendMessage( + player, + "You carefully cut the wood into $amountToCraft arrow shafts." + ) + + else -> sendMessage( + player, + "You carefully cut the wood into ${aOrAn(finishedItemName)} $finishedItemName." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt b/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt new file mode 100644 index 000000000..5863c51ea --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/GrammarHelpers.kt @@ -0,0 +1,20 @@ +package content.global.skill.fletching.log + +import core.tools.StringUtils + +object GrammarHelpers { + fun makeFriendlyName(name: String): String { + return name.replace("(u)", "").trim { it <= ' ' } + } + + /** + * Returns "a" or "an" depending on whether the word begins with a vowel + */ + @Suppress("GrazieInspection") + fun aOrAn(word: String): String { + return when (StringUtils.isPlusN(word)) { + true -> "an" + false -> "a" + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt b/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt new file mode 100644 index 000000000..2111606dc --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/LogCraftInfo.kt @@ -0,0 +1,63 @@ +package content.global.skill.fletching.log + +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different items from logs + * @property logItemId the log item id that can be used to craft this item + * @property finishedItemId the resulting finished bolt Item id. + * @property level the level required to craft. + * @property experience the experience gained by crafting. + */ +enum class LogCraftInfo(val logItemId: Int, val finishedItemId: Int, val experience: Double, val level: Int) { + ARROW_SHAFT(Items.LOGS_1511, Items.ARROW_SHAFT_52, 5.0, 1), + SHORT_BOW(Items.LOGS_1511, Items.SHORTBOW_U_50, 5.0, 5), + LONG_BOW(Items.LOGS_1511, Items.LONGBOW_U_48, 10.0, 10), + WOODEN_STOCK(Items.LOGS_1511, Items.WOODEN_STOCK_9440, 6.0, 9), + + OGRE_ARROW_SHAFT(Items.ACHEY_TREE_LOGS_2862, Items.OGRE_ARROW_SHAFT_2864, 6.4, 5), + OGRE_COMP_BOW(Items.ACHEY_TREE_LOGS_2862, Items.UNSTRUNG_COMP_BOW_4825, 45.0, 30), + + OAK_SHORTBOW(Items.OAK_LOGS_1521, Items.OAK_SHORTBOW_U_54, 16.5, 20), + OAK_LONGBOW(Items.OAK_LOGS_1521, Items.OAK_LONGBOW_U_56, 25.0, 25), + OAK_STOCK(Items.OAK_LOGS_1521, Items.OAK_STOCK_9442, 16.0, 24), + + WILLOW_SHORTBOW(Items.WILLOW_LOGS_1519, Items.WILLOW_SHORTBOW_U_60, 33.3, 35), + WILLOW_LONGBOW(Items.WILLOW_LOGS_1519, Items.WILLOW_LONGBOW_U_58, 41.5, 40), + WILLOW_STOCK(Items.WILLOW_LOGS_1519, Items.WILLOW_STOCK_9444, 22.0, 39), + + MAPLE_SHORTBOW(Items.MAPLE_LOGS_1517, Items.MAPLE_SHORTBOW_U_64, 50.0, 50), + MAPLE_LONGBOW(Items.MAPLE_LOGS_1517, Items.MAPLE_LONGBOW_U_62, 58.3, 55), + MAPLE_STOCK(Items.MAPLE_LOGS_1517, Items.MAPLE_STOCK_9448, 32.0, 54), + + YEW_SHORTBOW(Items.YEW_LOGS_1515, Items.YEW_SHORTBOW_U_68, 67.5, 65), + YEW_LONGBOW(Items.YEW_LOGS_1515, Items.YEW_LONGBOW_U_66, 75.0, 70), + YEW_STOCK(Items.YEW_LOGS_1515, Items.YEW_STOCK_9452, 50.0, 69), + + MAGIC_SHORTBOW(Items.MAGIC_LOGS_1513, Items.MAGIC_SHORTBOW_U_72, 83.3, 80), + MAGIC_LONGBOW(Items.MAGIC_LOGS_1513, Items.MAGIC_LONGBOW_U_70, 91.5, 85), + + TEAK_STOCK(Items.TEAK_LOGS_6333, Items.TEAK_STOCK_9446, 27.0, 46), + + MAHOGANY_STOCK(Items.MAHOGANY_LOGS_6332, Items.MAHOGANY_STOCK_9450, 41.0, 61); + + + companion object { + val logIds: IntArray = LogCraftInfo.values().map { logCraftInfo: LogCraftInfo -> logCraftInfo.logItemId }.distinct().toIntArray() + + private val logCraftablesByLogId = associateCraftInfoByLogIds() + private fun associateCraftInfoByLogIds(): Map> { + val map = HashMap>() + + for (logId in logIds) { + map[logId] = LogCraftInfo.values().filter { logCraftInfo: LogCraftInfo -> logCraftInfo.logItemId == logId } + } + + return map + } + + fun forLogId(logId: Int): List? { + return logCraftablesByLogId[logId] + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt b/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt new file mode 100644 index 000000000..fde0d5ff4 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/log/LogCraftableListeners.kt @@ -0,0 +1,83 @@ +package content.global.skill.fletching.log + +import content.data.Quests +import content.global.skill.fletching.log.GrammarHelpers.aOrAn +import content.global.skill.fletching.log.GrammarHelpers.makeFriendlyName +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items + +@Suppress("unused") // Reflectively loaded +class LogCraftableListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.KNIFE_946, *LogCraftInfo.logIds) { player, knife, log -> + val applicableCraftInfosForLog = LogCraftInfo.forLogId(log.id) ?: return@onUseWith false + val craftableItems = + applicableCraftInfosForLog.map { logCraftable -> Item(logCraftable.finishedItemId) }.toTypedArray() + + val handler: SkillDialogueHandler = object : SkillDialogueHandler( + player, + SkillDialogue.forLength(applicableCraftInfosForLog.size), + *craftableItems + ) { + override fun create(amount: Int, index: Int) { + if (!playerMeetsInitialRequirements(applicableCraftInfosForLog[index])) return + CraftItemWithLogScript(player, applicableCraftInfosForLog[index], amount).invoke() + } + + private fun playerMeetsInitialRequirements(logCraftInfo: LogCraftInfo): Boolean { + if (getDynLevel(player, Skills.FLETCHING) < logCraftInfo.level) { + sendLevelCheckFailDialog(player, logCraftInfo) + return false + } + if (logCraftInfo == LogCraftInfo.OGRE_ARROW_SHAFT && + !isQuestStarted(player, Quests.BIG_CHOMPY_BIRD_HUNTING) + ) { + sendDialogue(player, "You must have started ${Quests.BIG_CHOMPY_BIRD_HUNTING} to make those.") + return false + } + if (logCraftInfo == LogCraftInfo.OGRE_COMP_BOW) { + if (getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) { + sendMessage( + player, + "You must have started Zogre Flesh Eaters and asked Grish to string this." + ) + return false + } + if (amountInInventory(player, Items.WOLF_BONES_2859) < 1) { + sendMessage(player, "You need to have Wolf Bones in order to make this.") + return false + } + } + return true + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, log.id) + } + } + handler.open() + return@onUseWith true + } + } + + companion object { + fun sendLevelCheckFailDialog(player: Player, logCraftInfo: LogCraftInfo) { + val finishedItem = Item(logCraftInfo.finishedItemId) + val friendlyCraftedItemName = makeFriendlyName(finishedItem.name) + sendDialogue( + player, + "You need a Fletching skill of ${logCraftInfo.level} or above to make ${ + aOrAn( + friendlyCraftedItemName + ) + } $friendlyCraftedItemName." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt b/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt new file mode 100644 index 000000000..633e29667 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringItemScript.kt @@ -0,0 +1,71 @@ +package content.global.skill.fletching.stringing + +import content.global.skill.fletching.AchievementDiaryAttributeKeys +import content.global.skill.fletching.Zones +import content.global.skill.fletching.stringing.StringableCraftInfo.Companion.applicableStringId +import core.api.* +import core.game.interaction.Clocks +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * Represents queueScript to string a bow/crossbow + * @author Ceikry + * @param player the player. + * @param stringableCraftInfo contains crafting information about what we're stringing + * @param amount the amount of items to string + */ +class StringItemScript( + private val player: Player, + private val stringableCraftInfo: StringableCraftInfo, + private var amount: Int +) { + private val initialDelay = 1 + private val subsequentDelay = 2 + + fun invoke() { + queueScript(player, initialDelay) { stage -> + if (!clockReady(player, Clocks.SKILLING)) return@queueScript keepRunning(player) + + if (getDynLevel(player, Skills.FLETCHING) < stringableCraftInfo.level) { + StringingListeners.sendLevelCheckFailDialogue(player, stringableCraftInfo.level) + return@queueScript stopExecuting(player) // Check each iteration since dynLevel can change (status effects ending, skill assist session end...) + } + + if ( + removeItemsIfPlayerHasEnough( + player, + Item(stringableCraftInfo.unstrungItemId), + Item(stringableCraftInfo.applicableStringId) + ) + ) { + player.animate(stringableCraftInfo.animation) + + addItem(player, stringableCraftInfo.strungItemId, 1) + rewardXP(player, Skills.FLETCHING, stringableCraftInfo.experience) + sendMessage(player, "You add a string to the bow.") + if (stringableCraftInfo == StringableCraftInfo.MAGIC_SHORTBOW) { + handleSeersMagicShortbowAchievement() + } + } else { + return@queueScript stopExecuting(player) + } + + if (stage >= amount - 1) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayClock(player, Clocks.SKILLING, subsequentDelay, true) + } + } + + private fun handleSeersMagicShortbowAchievement() { + if (Zones.inAnyZone(player, Zones.seersMagicShortbowAchievementZones) && + getAttribute(player, AchievementDiaryAttributeKeys.FLETCHED_UNSTRUNG_MAGIC_SHORTBOW, false) + ) { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 2) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt b/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt new file mode 100644 index 000000000..38c089621 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringableCraftInfo.kt @@ -0,0 +1,66 @@ +package content.global.skill.fletching.stringing + +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +/** + * Provides information pertaining to crafting different strung items + * @property isCrossbow is this enum for a crossbow? + * @property unstrungItemId the unstrung item Id + * @property strungItemId the resulting strung item Id + * @property level the level required to string the item + * @property experience experience gained from stringing + * @property animation the stringing animation to play + */ +enum class StringableCraftInfo( + val isCrossbow: Boolean, + val unstrungItemId: Int, + val strungItemId: Int, + val level: Int, + val experience: Double, + val animation: Animation +) { + SHORT_BOW(false, Items.SHORTBOW_U_50, Items.SHORTBOW_841, 5, 5.0, Animation(6678)), + LONG_BOW(false, Items.LONGBOW_U_48, Items.LONGBOW_839, 10, 10.0, Animation(6684)), + OAK_SHORTBOW(false, Items.OAK_SHORTBOW_U_54, Items.OAK_SHORTBOW_843, 20, 16.5, Animation(6679)), + OAK_LONGBOW(false, Items.OAK_LONGBOW_U_56, Items.OAK_LONGBOW_845, 25, 25.0, Animation(6685)), + OGRE_COMP_BOW(false, Items.UNSTRUNG_COMP_BOW_4825, Items.COMP_OGRE_BOW_4827, 30, 45.0, Animation(-1)), + WILLOW_SHORTBOW(false, Items.WILLOW_SHORTBOW_U_60, Items.WILLOW_SHORTBOW_849, 35, 33.3, Animation(6680)), + WILLOW_LONGBOW(false, Items.WILLOW_LONGBOW_U_58, Items.WILLOW_LONGBOW_847, 40, 41.5, Animation(6686)), + MAPLE_SHORTBOW(false, Items.MAPLE_SHORTBOW_U_64, Items.MAPLE_SHORTBOW_853, 50, 50.0, Animation(6681)), + MAPLE_LONGBOW(false, Items.MAPLE_LONGBOW_U_62, Items.MAPLE_LONGBOW_851, 55, 58.3, Animation(6687)), + YEW_SHORTBOW(false, Items.YEW_SHORTBOW_U_68, Items.YEW_SHORTBOW_857, 65, 67.5, Animation(6682)), + YEW_LONGBOW(false, Items.YEW_LONGBOW_U_66, Items.YEW_LONGBOW_855, 70, 75.0, Animation(6688)), + MAGIC_SHORTBOW(false, Items.MAGIC_SHORTBOW_U_72, Items.MAGIC_SHORTBOW_861, 80, 83.3, Animation(6683)), + MAGIC_LONGBOW(false, Items.MAGIC_LONGBOW_U_70, Items.MAGIC_LONGBOW_859, 85, 91.5, Animation(6689)), + + BRONZE_CBOW(true, Items.BRONZE_CBOW_U_9454, Items.BRONZE_CROSSBOW_9174, 9, 6.0, Animation(6671)), + BLURITE_CBOW(true, Items.BLURITE_CBOW_U_9456, Items.BLURITE_CROSSBOW_9176, 24, 16.0, Animation(6672)), + IRON_CBOW(true, Items.IRON_CBOW_U_9457, Items.IRON_CROSSBOW_9177, 39, 22.0, Animation(6673)), + STEEL_CBOW(true, Items.STEEL_CBOW_U_9459, Items.STEEL_CROSSBOW_9179, 46, 27.0, Animation(6674)), + MITHIRIL_CBOW(true, Items.MITHRIL_CBOW_U_9461, Items.MITH_CROSSBOW_9181, 54, 32.0, Animation(6675)), + ADAMANT_CBOW(true, Items.ADAMANT_CBOW_U_9463, Items.ADAMANT_CROSSBOW_9183, 61, 41.0, Animation(6676)), + RUNITE_CBOW(true, Items.RUNITE_CBOW_U_9465, Items.RUNE_CROSSBOW_9185, 69, 50.0, Animation(6677)); + + companion object { + val unstrungBowIds: IntArray = values().map { stringableCraftInfo: StringableCraftInfo -> stringableCraftInfo.unstrungItemId }.toIntArray() + val stringItemIds: IntArray = intArrayOf(Items.BOW_STRING_1777,Items.CROSSBOW_STRING_9438) + + private val stringableCraftInfoByUnstrungItemId = values().associateBy { it.unstrungItemId } + + fun forUnstrungBowItemId(unstrungItemId: Int): StringableCraftInfo? { + return stringableCraftInfoByUnstrungItemId[unstrungItemId] + } + + /** + * Returns the item id that should be used for stringing this enums unstrung bow + */ + val StringableCraftInfo.applicableStringId: Int + get() { + return when { + this.isCrossbow -> Items.CROSSBOW_STRING_9438 + else -> Items.BOW_STRING_1777 + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt b/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt new file mode 100644 index 000000000..051f8102b --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/stringing/StringingListeners.kt @@ -0,0 +1,69 @@ +package content.global.skill.fletching.stringing + +import content.data.Quests +import content.global.skill.fletching.stringing.StringableCraftInfo.Companion.applicableStringId +import core.api.* +import core.game.dialogue.SkillDialogueHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ChildPositionContext +import core.net.packet.out.RepositionChild +import org.rs09.consts.Components + +@Suppress("unused") // Reflectively loaded +class StringingListeners : InteractionListener { + override fun defineListeners() { + onUseWith( + IntType.ITEM, + StringableCraftInfo.stringItemIds, + *StringableCraftInfo.unstrungBowIds + ) { player, string, unstrungBow -> + val stringableCraftInfo = StringableCraftInfo.forUnstrungBowItemId(unstrungBow.id) ?: return@onUseWith false + if (string.id != stringableCraftInfo.applicableStringId) { + sendMessage(player, "That's not the right kind of string for this.") + return@onUseWith true + } + if (stringableCraftInfo == StringableCraftInfo.OGRE_COMP_BOW && getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) { + sendMessage(player, "You must have started Zogre Flesh Eaters and asked Grish to string this.") + return@onUseWith true + } + val handler: SkillDialogueHandler = + object : + SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(stringableCraftInfo.strungItemId)) { + override fun create(amount: Int, index: Int) { + if (getDynLevel(player, Skills.FLETCHING) < stringableCraftInfo.level) { + sendLevelCheckFailDialogue(player, stringableCraftInfo.level) + return + } + StringItemScript(player, stringableCraftInfo, amount).invoke() + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, string.id) + } + } + handler.open() + fixSpacingBetweenTextAndCraftedItemIcon(player) + return@onUseWith true + } + } + + private fun fixSpacingBetweenTextAndCraftedItemIcon(player: Player) { + PacketRepository.send( + RepositionChild::class.java, + ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 215, 10) + ) + } + companion object { + fun sendLevelCheckFailDialogue(player: Player, level: Int) { + sendDialogue( + player, + "You need a fletching level of $level to string this bow." + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/gather/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..2f23e264c --- /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.withinMaxnormDistance(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..3e309fb65 100644 --- a/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt @@ -1,27 +1,28 @@ 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 +53,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 +118,17 @@ 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!!)) - SkillingPets.checkPetDrop(player, SkillingPets.HERON) - val item = fish!!.item + player.dispatch(ResourceProducedEvent(fish!!.id, 1, node!!)) + val item = fish!! if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { - player.inventory.add(item) + addItemOrDrop(player, item.id) player.sendMessage(colorize("%RYour expert aim catches you a second fish.")) } - player.inventory.add(item) + addItemOrDrop(player, item.id) var fishCaught = player.getAttribute(STATS_BASE + ":" + STATS_FISH, 0) player.setAttribute("/save:$STATS_BASE:$STATS_FISH", ++fishCaught) player.skills.addExperience(Skills.FISHING, fish!!.experience, true) @@ -169,20 +177,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 +233,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 +244,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..ea9de8c49 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/mining/MiningListener.kt @@ -0,0 +1,268 @@ +package content.global.skill.gather.mining + +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 == MiningNode.RUNE_ESSENCE + val isGems = resource.identifier == MiningNode.GEM_ROCK_0.identifier + + if (!finishedMoving(player)) + return true + + if (state == 0) { + if (!checkRequirements(player, resource, node)) { + player.scripts.reset() + return true + } + anim(player, tool) + sendMessage(player, "You swing your pickaxe at the rock.") + return delayScript(player, getDelay(resource, tool)) + } + + anim(player, tool) + if (!checkReward(player, resource, tool)) + return delayScript(player, getDelay(resource, tool)) + + // 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)) + + // 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 + } + + // Prepare reward name - gems stay lowercase without "Uncut", others are lowercase + val rewardName = if (isGems) { + getItemName(reward).replace("Uncut ", "").lowercase() + } else { + getItemName(reward).substringBefore(" (").lowercase().removeSuffix(" ore") + } + + // Send the message for the resource reward + if (isGems) { + val withArticle = prependArticle(rewardName) // "an emerald" or "a red topaz" + val parts = withArticle.split(" ", limit = 2) // ["an", "emerald"] or ["a", "red topaz"] + val article = parts[0] // "an" or "a" + val gemName = parts[1].replaceFirstChar { it.uppercase() } // "Emerald" or "Red topaz" + sendMessage(player, "You just mined $article $gemName!") + } else if (!isEssence) { + sendMessage(player, "You manage to mine some ${rewardName}.") + } + + // Give the mining reward, increment 'rocks mined' attribute + 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 just found ${prependArticle(gem.name.replace("Uncut ", "").lowercase())}!") + if (freeSlots(player) == 0) { + sendMessage(player,"You do not have enough space in your inventory, so you drop the gem on the floor.") + } + addItemOrDrop(player, gem.id) + } + } + + // Transform ore to depleted version + if (!isEssence && resource!!.respawnRate != 0) { + SceneryBuilder.replace(node as Scenery, Scenery(resource!!.emptyId, node.getLocation(), node.type, node.rotation), resource!!.respawnDuration) + node.setActive(false) + return true + } + + // For essence, check inventory and continue mining + if (isEssence) { + if (freeSlots(player) == 0) { + return true + } + return delayScript(player, getDelay(resource, tool)) + } + } + return true + } + + 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 { + // Essence mining always succeeds + if (resource == MiningNode.RUNE_ESSENCE) { + return true + } + val level = 1 + getDynLevel(player, Skills.MINING) + getFamiliarBoost(player, Skills.MINING) + val hostRatio = Math.random() * (100.0 * resource!!.rate) + var toolRatio = tool.ratio + 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, tool: SkillingTool) : Int { + if (resource == MiningNode.RUNE_ESSENCE) { // Essence mining - speed varies by pickaxe + return when (tool) { + SkillingTool.BRONZE_PICKAXE -> 7 + SkillingTool.IRON_PICKAXE -> 6 + SkillingTool.STEEL_PICKAXE -> 5 + SkillingTool.MITHRIL_PICKAXE -> 4 + SkillingTool.ADAMANT_PICKAXE -> 3 + SkillingTool.RUNE_PICKAXE -> 2 + SkillingTool.INFERNO_ADZE2 -> if (RandomFunction.random(2) == 0) 1 else 2 //https://www.youtube.com/watch?v=9XhjSdJ4qro + else -> 4 // fallback + } + } + return 4 // normal rocks + } + + fun anim(player: Player, tool: SkillingTool) { + 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 + } + if (resource == MiningNode.RUNE_ESSENCE) { + sendDialogue(player,"Your inventory is too full to hold any more essence.") + return false + } + sendDialogue(player,"Your inventory is too full to hold any more ${ItemDefinition.forId(resource!!.reward).name.lowercase()}.") + return false + } + return node.isActive + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/gather/mining/MiningNode.java b/Server/src/main/content/global/skill/gather/mining/MiningNode.java index 301382938..b271fdee2 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); @@ -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..4c26fac4d 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt +++ b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt @@ -3,7 +3,6 @@ package content.global.skill.gather.mining import core.api.* import core.game.event.ResourceProducedEvent import core.cache.def.impl.ItemDefinition -import content.data.skill.SkillingPets import core.game.node.Node import core.game.node.entity.impl.Animator import core.game.node.entity.npc.drop.DropFrequency @@ -11,6 +10,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills import content.data.skill.SkillingTool +import content.global.activity.shootingstar.StarBonus import content.global.skill.skillcapeperks.SkillcapePerks import core.game.node.item.ChanceItem import core.game.node.scenery.Scenery @@ -131,7 +131,6 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul rewardAmount = calculateRewardAmount(reward) // calculate amount player.dispatch(ResourceProducedEvent(reward, rewardAmount, node)) - SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet // Reward mining experience val experience = resource!!.experience * rewardAmount @@ -141,7 +140,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 +163,9 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul } // Give the mining reward, increment 'rocks mined' attribute - if(addItem(player, reward, rewardAmount)) { - var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) - setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", ++rocksMined) - } + addItemOrDrop(player, reward, rewardAmount) + var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) + setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", rocksMined + rewardAmount) // Calculate bonus gem chance while mining if (!isMiningEssence) { @@ -178,7 +176,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 +223,7 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul } // If player has mining boost from Shooting Star, roll chance at extra ore - if (player.hasActiveState("shooting-star")) { + if (hasTimerActive(player)) { if (RandomFunction.getRandom(5) == 3) { sendMessage(player, "...you manage to mine a second ore thanks to the Star Sprite.") amount += 1 diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt new file mode 100644 index 000000000..5a4a137b8 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt @@ -0,0 +1,293 @@ +package content.global.skill.gather.woodcutting + +import content.data.Quests +import content.data.skill.SkillingTool +import content.data.tables.BirdNest +import content.global.skill.farming.FarmingPatch.Companion.forObject +import content.global.skill.farming.timers.StumpGrowth +import content.global.skill.firemaking.Log +import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive +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", "cut-branch", + 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 (resource in arrayOf(WoodcuttingNode.DRAMEN_TREE, WoodcuttingNode.SWAYING_TREE)) { + // Reward after one chop and then abort chopping (this is authentic) + queueScript(player, 1, QueueStrength.STRONG) { + sendMessage(player, "You cut a branch from the ${if (resource == WoodcuttingNode.DRAMEN_TREE) "Dramen" else "strangely musical"} tree.") + addItem(player, resource.getReward()) + return@queueScript clearScripts(player) + } + return delayClock(player, Clocks.SKILLING, 1) + } + + if (!checkReward(player, resource, tool) && !getAttribute(player, "instachop", false)) + return delayClock(player, Clocks.SKILLING, 3) + + val reward = resource.getReward() + 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 + rewardAmount = calculateRewardAmount(player, reward) // calculate amount + val experience: Double = calculateExperience(player, resource, rewardAmount) + player.getSkills().addExperience(Skills.WOODCUTTING, experience, true) + + //nullcheck the fire, and only if it exists award the firemaking XP + val fire = Log.forId(reward) + 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 + + //add experience + val experience: Double = calculateExperience(player, resource, rewardAmount) + player.getSkills().addExperience(Skills.WOODCUTTING, experience, true) + + //send the message for the resource reward + 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(1, 2, 3, 4, 6).contains(resource.identifier.toInt()) || getAttribute(player, "instachop", false)){ + 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) + } + val stumps = getOrStartTimer (player) + stumps.addStump(fPatch.varbit, resource.respawnDuration) + } + 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 (node.id == org.rs09.consts.Scenery.DRAMEN_TREE_1292 && getQuestStage(player, Quests.LOST_CITY) <= 20) { + //TODO: find out if there is any authentic message to be shown + return false + } + if (player.inventory.freeSlots() < 1 && node.isActive) { + player.sendMessage("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).name.lowercase(Locale.getDefault()) + ".") + return false + } + 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().regionId == 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..d69c7fea1 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -8,7 +8,6 @@ import core.game.event.ResourceProducedEvent; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; import core.game.dialogue.FacialExpression; -import content.data.skill.SkillingPets; import core.game.node.entity.impl.Animator; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; @@ -29,6 +28,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 +123,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]); + } } } @@ -158,7 +156,6 @@ public class WoodcuttingSkillPulse extends Pulse { if (reward > 0) { reward = calculateReward(reward); // calculate rewards rewardAmount = calculateRewardAmount(reward); // calculate amount - SkillingPets.checkPetDrop(player, SkillingPets.BEAVER); // roll for pet //add experience double experience = calculateExperience(resource.reward, rewardAmount); @@ -166,11 +163,7 @@ public class WoodcuttingSkillPulse extends Pulse { player.getSkills().addExperience(Skills.WOODCUTTING, experience, true); //send the message for the resource reward - if (resource == WoodcuttingNode.DRAMEN_TREE) { - player.getPacketDispatch().sendMessage("You cut a branch from the Dramen tree."); - } else { - player.getPacketDispatch().sendMessage("You get some " + ItemDefinition.forId(reward).getName().toLowerCase() + "."); - } + player.getPacketDispatch().sendMessage("You get some " + ItemDefinition.forId(reward).getName().toLowerCase() + "."); //give the reward player.getInventory().add(new Item(reward, rewardAmount)); player.dispatch(new ResourceProducedEvent(reward, rewardAmount, node, -1)); @@ -204,7 +197,7 @@ public class WoodcuttingSkillPulse extends Pulse { if (resource.isFarming()) { FarmingPatch fPatch = FarmingPatch.forObject(node.asScenery()); if(fPatch != null) { - Patch patch = fPatch.getPatchFor(player); + Patch patch = fPatch.getPatchFor(player, true); patch.setCurrentState(patch.getCurrentState() + 1); } return true; @@ -216,7 +209,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..ce8e7202e --- /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), MAGIC_ESSENCE_POTION(9023, 61, 11489, 43.0, false), SUPER_RESTORE(3028, 67, 11493, 48.0, false), SUPER_DEFENCE_POTION(165, 71, 11497, 50.0, false), ANTIDOTE_PLUS(5947, 74, 11501, 52.0, false), ANTIFIRE_POTION(2456, 75, 11505, 53.0, false), RANGING_POTION(171, 80, 11509, 54.0, false), MAGIC_POTION(3044, 83, 11513, 57.0, false), ZAMORAK_BREW(191, 85, 11521, 58.0, false); + + /** + * Constructs a new {@code BarbarianPotion} {@Code Object}. + * @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/GrindItemPlugin.kt b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt index 3d7184c06..b87ead3f4 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,19 @@ 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) { + val quantity: Int = 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 +82,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 +95,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..6807e5d9e --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt @@ -0,0 +1,35 @@ +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 + +/** + * 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 + 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..247f69f62 100644 --- a/Server/src/main/content/global/skill/herblore/HerbTarPulse.java +++ b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java @@ -5,6 +5,7 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the pulse used to create herb tars. @@ -52,7 +53,7 @@ public final class HerbTarPulse extends SkillPulse { @Override public boolean checkRequirements() { - if (!player.getQuestRepository().isComplete("Druidic Ritual")) { + if (!player.getQuestRepository().isComplete(Quests.DRUIDIC_RITUAL)) { player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); return false; } diff --git a/Server/src/main/content/global/skill/herblore/HerblorePulse.java b/Server/src/main/content/global/skill/herblore/HerblorePulse.java index 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..4226fb436 100644 --- a/Server/src/main/content/global/skill/herblore/Herbs.java +++ b/Server/src/main/content/global/skill/herblore/Herbs.java @@ -7,7 +7,7 @@ import core.game.node.item.Item; * @author 'Vexia */ public enum Herbs { - GUAM(new Item(199), 2.5, 3, new Item(249)), MARRENTILL(new Item(201), 3.8, 5, new Item(251)), TARROMIN(new Item(203), 5, 11, new Item(253)), HARRALANDER(new Item(205), 6.3, 20, new Item(255)), RANARR(new Item(207), 7.5, 25, new Item(257)), TOADFLAX(new Item(3049), 8, 30, new Item(2998)), SPIRIT_WEED(new Item(12174), 7.8, 35, new Item(12172)), IRIT(new Item(209), 8.8, 40, new Item(259)), AVANTOE(new Item(211), 10, 48, new Item(261)), KWUARM(new Item(213), 11.3, 54, new Item(263)), SNAPDRAGON(new Item(3051), 11.8, 59, new Item(3000)), CADANTINE(new Item(215), 12.5, 65, new Item(265)), LANTADYME(new Item(2485), 13.1, 67, new Item(2481)), DWARF_WEED(new Item(217), 13.8, 70, new Item(267)), TORSTOL(new Item(219), 15, 75, new Item(269)), SNAKE_WEED(new Item(1525), 2.5, 3, new Item(1526)), ARDRIGAL(new Item(1527), 2.5, 3, new Item(1528)), SITO_FOIL(new Item(1529), 2.5, 3, new Item(1530)), VOLENCIA_MOSS(new Item(1531), 2.5, 3, new Item(1532)), ROGUES_PUSE(new Item(1533), 2.5, 3, new Item(1534)); + GUAM(new Item(199), 2.5, 3, new Item(249)), MARRENTILL(new Item(201), 3.8, 5, new Item(251)), TARROMIN(new Item(203), 5, 11, new Item(253)), HARRALANDER(new Item(205), 6.3, 20, new Item(255)), RANARR(new Item(207), 7.5, 25, new Item(257)), TOADFLAX(new Item(3049), 8, 30, new Item(2998)), SPIRIT_WEED(new Item(12174), 7.8, 35, new Item(12172)), IRIT(new Item(209), 8.8, 40, new Item(259)), AVANTOE(new Item(211), 10, 48, new Item(261)), KWUARM(new Item(213), 11.3, 54, new Item(263)), SNAPDRAGON(new Item(3051), 11.8, 59, new Item(3000)), CADANTINE(new Item(215), 12.5, 65, new Item(265)), LANTADYME(new Item(2485), 13.1, 67, new Item(2481)), DWARF_WEED(new Item(217), 13.8, 70, new Item(267)), TORSTOL(new Item(219), 15, 75, new Item(269)), SNAKE_WEED(new Item(1525), 2.5, 3, new Item(1526)), ARDRIGAL(new Item(1527), 2.5, 3, new Item(1528)), SITO_FOIL(new Item(1529), 2.5, 3, new Item(1530)), VOLENCIA_MOSS(new Item(1531), 2.5, 3, new Item(1532)), ROGUES_PURSE(new Item(1533), 2.5, 3, new Item(1534)); /** * Represents the herb item. diff --git a/Server/src/main/content/global/skill/herblore/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/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/NetTrapNode.kt b/Server/src/main/content/global/skill/hunter/NetTrapNode.kt new file mode 100644 index 000000000..43b9fdad5 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/NetTrapNode.kt @@ -0,0 +1,25 @@ +package content.global.skill.hunter + +import core.api.getStatLevel +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.node.item.Item + +/** + * Handles the net trap node. + */ +class NetTrapNode( + npcIds: IntArray, + level: Int, + experience: Double, + rewards: Array, + private val summoningLevel: Int +) : TrapNode(npcIds, level, experience, intArrayOf(), rewards) { + + override fun canCatch(wrapper: TrapWrapper, npc: NPC): Boolean { + if (getStatLevel(wrapper.player, Skills.SUMMONING) < summoningLevel) { + return false + } + return super.canCatch(wrapper, npc) + } +} \ No newline at end of file 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..8d3493c28 100644 --- a/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java @@ -1,10 +1,12 @@ package content.global.skill.hunter; -import content.data.skill.SkillingPets; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the dismantling of a trap. @@ -39,6 +41,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 @@ -71,13 +94,6 @@ public final class TrapDismantlePulse extends SkillPulse { if (wrapper.getType().getSettings().clear(wrapper, 1)) { instance.deregister(wrapper); if (wrapper.isCaught()) { - if (wrapper.getType().equals(Traps.BOX_TRAP)) { - for (int i : wrapper.getReward().getNpcIds()) { - if (i == 5080 || i == 5079) { - SkillingPets.checkPetDrop(player, i == 5080 ? SkillingPets.BABY_RED_CHINCHOMPA : SkillingPets.BABY_GREY_CHINCHOMPA); - } - } - } player.getSkills().addExperience(Skills.HUNTER, wrapper.getReward().getExperience(), true); } player.getPacketDispatch().sendMessage("You dismantle the trap."); diff --git a/Server/src/main/content/global/skill/hunter/TrapNode.java b/Server/src/main/content/global/skill/hunter/TrapNode.java index ad8608779..d2cc48e01 100644 --- a/Server/src/main/content/global/skill/hunter/TrapNode.java +++ b/Server/src/main/content/global/skill/hunter/TrapNode.java @@ -1,6 +1,5 @@ package content.global.skill.hunter; -import content.data.skill.SkillingPets; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; @@ -123,8 +122,4 @@ public class TrapNode { return objectIds; } - public SkillingPets getPet() { - return null; - } - } diff --git a/Server/src/main/content/global/skill/hunter/TrapSetting.java b/Server/src/main/content/global/skill/hunter/TrapSetting.java index f20daa17e..ec0f87ba3 100644 --- a/Server/src/main/content/global/skill/hunter/TrapSetting.java +++ b/Server/src/main/content/global/skill/hunter/TrapSetting.java @@ -16,6 +16,9 @@ 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; /** * A setting for a trap type. @@ -306,6 +309,22 @@ public class TrapSetting { wrapper.setTicks(GameWorld.getTicks() + 100); 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/Traps.java b/Server/src/main/content/global/skill/hunter/Traps.java index 0555254a6..f0cec6d54 100644 --- a/Server/src/main/content/global/skill/hunter/Traps.java +++ b/Server/src/main/content/global/skill/hunter/Traps.java @@ -5,13 +5,18 @@ 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; +import org.rs09.consts.Items; +import org.rs09.consts.NPCs; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.log; + /** * Represents a trap type. * @author Vexia @@ -30,16 +35,37 @@ public enum Traps { } }), BOX_TRAP(new TrapSetting(10008, new int[] { 19187 }, new int[] { 1963, 12579, 1869, 9996, 5972, 12535 }, "lay", 19192, Animation.create(5208), new Animation(9726), 27), - new BoxTrapNode(new int[] { 5081 }, 27, 100, new Item[] { new Item(10092) }, 1), - new BoxTrapNode(new int[] { 6918, 7289, 7290, 7291, 7292 }, 27, 100, new Item[] { new Item(12184) }, 10), - new BoxTrapNode(new int[] { 1487 }, 27, 100, new Item[] { new Item(4033, 1) }, 95), - new BoxTrapNode(new int[] { 7021, 7022, 7023 }, 48, 150, new Item[] { new Item(12551, 1) }, 1), - new BoxTrapNode(new int[] { 5079 }, 53, 198, new Item[] { new Item(10033, 1) }, 1), - new BoxTrapNode(new int[] { 5428, 5430, 5449, 5450, 5451 }, 56, 150, new Item[] { new Item(12188) }, 1), - 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[] { 5081 }, 27, 100, new Item[] { new Item(10092) }, 1), // Ferret + new BoxTrapNode(new int[] {NPCs.BABY_GECKO_6917 }, 27, 100, new Item[] { new Item(Items.BABY_GECKO_12488) }, 10), // Gecko Orange + new BoxTrapNode(new int[] {NPCs.BABY_GECKO_7285 }, 27, 100, new Item[] { new Item(Items.BABY_GECKO_12738) }, 10), // Gecko Speckled + new BoxTrapNode(new int[] {NPCs.BABY_GECKO_7286 }, 27, 100, new Item[] { new Item(Items.BABY_GECKO_12739) }, 10), // Gecko Green + new BoxTrapNode(new int[] {NPCs.BABY_GECKO_7287 }, 27, 100, new Item[] { new Item(Items.BABY_GECKO_12740) }, 10), // Gecko Blue + new BoxTrapNode(new int[] {NPCs.BABY_GECKO_7288 }, 27, 100, new Item[] { new Item(Items.BABY_GECKO_12741) }, 10), // Gecko Red + new BoxTrapNode(new int[] {NPCs.BABY_RACCOON_6997 }, 27, 100, new Item[] { new Item(Items.BABY_RACCOON_12486) }, 80), // Raccoon Gray + new BoxTrapNode(new int[] {NPCs.BABY_RACCOON_7275 }, 27, 100, new Item[] { new Item(Items.BABY_RACCOON_12734) }, 80), // Raccoon Tan + new BoxTrapNode(new int[] {NPCs.BABY_RACCOON_7276 }, 27, 100, new Item[] { new Item(Items.BABY_RACCOON_12736) }, 80), // Raccoon Red + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_6944 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12496) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7228 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12682) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7229 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12684) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7230 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12686) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7231 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12688) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7232 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12690) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7233 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12692) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7234 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12694) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7235 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12696) }, 95), // Monkey + new BoxTrapNode(new int[] { NPCs.BABY_MONKEY_7236 }, 27, 100, new Item[] { new Item(Items.BABY_MONKEY_12698) }, 95), // Monkey + new BoxTrapNode(new int[] { 7021, 7022, 7023 }, 48, 150, new Item[] { new Item(12551, 1) }, 1), // Platypus + new BoxTrapNode(new int[] { 5079 }, 53, 198, new Item[] { new Item(10033, 1) }, 1), // Chinchompa + new BoxTrapNode(new int[] { 5080 }, 63, 265, new Item[] { new Item(10034, 1) }, 1), // Red Chinchompa + new BoxTrapNode(new int[] { 7012, 7014 }, 66, 400, new Item[] { new Item(12535) }, 1), // Pawya + new BoxTrapNode(new int[] { 7010, 7011 }, 77, 0, new Item[] { new Item(12539, 1) }, 1) { // Grenwall + @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) })), @@ -49,11 +75,16 @@ public enum Traps { new TrapNode(new int[] { 5086 }, 37, 204, new int[] { 19208, 19208, 19217 }, new Item[] { new Item(10105), new Item(526) }), new TrapNode(new int[] { 7039 }, 44, 200, new int[] { 28939, 28940, 28941 }, new Item[] { new Item(12567), new Item(526) }), new TrapNode(new int[] { 5087 }, 51, 200, new int[] { 19209, 19210, 19216 }, new Item[] { new Item(10109), new Item(526) })), - NET_TRAP(new NetTrapSetting(), new TrapNode(new int[] { 5117 }, 29, 152, new int[] {}, new Item[] { new Item(10149) }), - new TrapNode(new int[] { 5114 }, 47, 224, new int[] {}, new Item[] { new Item(10146) }), - new TrapNode(new int[] { 6921 }, 29, 152, new int[] {}, new Item[] { new Item(12130) }), - new TrapNode(new int[] { 5115 }, 59, 272, new int[] {}, new Item[] { new Item(10147) }), - new TrapNode(new int[] { 5116 }, 67, 304, new int[] {}, new Item[] { new Item(10148) })); + NET_TRAP(new NetTrapSetting(), + new NetTrapNode(new int[] { 5117 }, 29, 152, new Item[] { new Item(10149) }, 1), // Swamp Lizard + new NetTrapNode(new int[] { 5114 }, 47, 224, new Item[] { new Item(10146) }, 1), // Orange Salamander + new NetTrapNode(new int[] { NPCs.BABY_SQUIRREL_6921 }, 29, 152, new Item[] { new Item(Items.BABY_SQUIRREL_12490) }, 60), // Squirrel Gray + new NetTrapNode(new int[] { NPCs.BABY_SQUIRREL_7309 }, 29, 152, new Item[] { new Item(Items.BABY_SQUIRREL_12754) }, 60), // Squirrel Tan + new NetTrapNode(new int[] { NPCs.BABY_SQUIRREL_7310 }, 29, 152, new Item[] { new Item(Items.BABY_SQUIRREL_12756) }, 60), // Squirrel White + new NetTrapNode(new int[] { NPCs.BABY_SQUIRREL_7311 }, 29, 152, new Item[] { new Item(Items.BABY_SQUIRREL_12758) }, 60), // Squirrel Grayish/Brown w/e + new NetTrapNode(new int[] { NPCs.BABY_SQUIRREL_7312 }, 29, 152, new Item[] { new Item(Items.BABY_SQUIRREL_12760) }, 60), // Squirrel Brown + new NetTrapNode(new int[] { 5115 }, 59, 272, new Item[] { new Item(10147) }, 1), // Red Salamander + new NetTrapNode(new int[] { 5116 }, 67, 304, new Item[] { new Item(10148) }, 1)); // Black Salamander /** * The location hooks for this node. @@ -101,7 +132,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/falconry/FalconryZone.kt b/Server/src/main/content/global/skill/hunter/falconry/FalconryZone.kt new file mode 100644 index 000000000..6256604de --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/falconry/FalconryZone.kt @@ -0,0 +1,13 @@ +package content.global.skill.hunter.falconry + +import core.game.node.Node +import core.game.world.map.zone.ZoneBorders + +/** + * Used to define the zoneBorders for an MapArea listener + */ +object FalconryZone { + val piscatorisFalconryZones = arrayOf( + ZoneBorders(2364, 3573, 2394, 3620, 0) + ) +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/hunter/falconry/PiscFalconryListener.kt b/Server/src/main/content/global/skill/hunter/falconry/PiscFalconryListener.kt new file mode 100644 index 000000000..c226a4fab --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/falconry/PiscFalconryListener.kt @@ -0,0 +1,36 @@ +package content.global.skill.hunter.falconry + +import core.api.* +import core.api.MapArea +import core.game.activity.ActivityManager +import core.game.node.entity.Entity +import core.game.world.map.zone.ZoneBorders +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class PiscFalconryListener: MapArea { + override fun defineAreaBorders(): Array { + return FalconryZone.piscatorisFalconryZones + } + + /** + * When player leaves by any method other than logout/disconnect. + */ + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + if (!logout) { + // Legacy compatibility of FalconryActivityPlugin.java + removeAttribute(entity, "falconry") + + // Compatibility with StileShortcut.kt + ActivityManager.getActivity("falconry").leave(entity, false) + + removeAll(entity, Items.FALCONERS_GLOVE_10023) + removeAll(entity, Items.FALCONERS_GLOVE_10023, Container.EQUIPMENT) + + removeAll(entity, Items.FALCONERS_GLOVE_10024) + removeAll(entity, Items.FALCONERS_GLOVE_10024, Container.EQUIPMENT) + } + } + } +} \ No newline at end of file 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..bdc9611a9 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 @@ -21,7 +21,9 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.node.entity.player.link.diary.DiaryType import core.game.world.GameWorld +import org.rs09.consts.Sounds /*@Initializable class HunterPitfall : OptionHandler() { @@ -60,28 +62,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 +168,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 +189,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 +205,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 +226,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 +240,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 +249,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 +258,18 @@ 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) + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 13) + 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 +284,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 +292,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 +310,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/HomeTeleportHelper.kt b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt new file mode 100644 index 000000000..b7085b62c --- /dev/null +++ b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt @@ -0,0 +1,48 @@ +package content.global.skill.magic + +import core.api.delayScript +import core.api.playGlobalAudio +import core.api.queueScript +import core.api.sendMessage +import core.api.stopExecuting +import core.game.event.TeleportEvent +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.world.map.Location +import core.game.world.map.zone.ZoneRestriction + +val HOME_ANIMATIONS = arrayOf(1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535) +val HOME_GRAPHICS = arrayOf(775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535) +fun getAudio(count: Int): Int { + return when (count) { + 0 -> 193 + 4 -> 194 + 11 -> 195 + else -> -1 + } +} + +fun homeTeleport(player: Player, dest: Location) { + player.walkingQueue.reset() + player.pulseManager.clear() + if (player.timers.getTimer("teleblock") != null) { + sendMessage(player, "A magical force prevents you from teleporting.") + return + } + if (player.locks.isTeleportLocked || player.zoneMonitor.isRestricted(ZoneRestriction.TELEPORT)) { + sendMessage(player, "A magical force has stopped you from teleporting.") + return + } + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 18) { + player.properties.teleportLocation = dest + player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.SPELL, -1, dest)) + return@queueScript stopExecuting(player) + } + playGlobalAudio(player.location, getAudio(stage)) + player.packetDispatch.sendGraphic(HOME_GRAPHICS[stage]) + player.packetDispatch.sendAnimation(HOME_ANIMATIONS[stage]) + return@queueScript delayScript(player, 1) + } +} 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..70ec07e25 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/MagicAltarListener.kt @@ -0,0 +1,82 @@ +package content.global.skill.magic + +import content.data.Quests +import core.api.animate +import core.api.hasLevelStat +import core.api.hasRequirement +import core.api.lock +import core.api.playAudio +import core.api.sendMessage +import core.game.event.SpellbookChangeEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +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.player.link.SpellBookManager +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 + +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) + } + val weaponInterface = player.getExtension(WeaponInterface::class.java) + if (weaponInterface != null && player.properties.autocastSpell != null) { + weaponInterface.selectAutoSpell(-1, true) + } + if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) { + player.dispatch(SpellbookChangeEvent( + SpellBook.forInterface(player.spellBookManager.spellBook), + SpellBook.MODERN, + SpellBookManager.SpellbookChangeSource.ALTAR)) + 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 { + player.dispatch(SpellbookChangeEvent( + SpellBook.forInterface(player.spellBookManager.spellBook), + if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR, + SpellBookManager.SpellbookChangeSource.ALTAR)) + 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..e938f3ef0 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()) { @@ -45,11 +47,10 @@ abstract class SpellListener(val bookName: String) : Listener { player.sendMessage("You need a magic level of $magicLevel to cast this spell.") throw IllegalStateException() } - for(rune in runes){ - if(!SpellUtils.hasRune(player,rune)){ - player.sendMessage("You don't have enough ${rune.definition.name.lowercase()}s to cast this spell.") - throw IllegalStateException() - } + val missing = SpellUtils.hasRunes(player, runes) + if (missing != null) { + player.sendMessage("You don't have enough ${missing.definition.name.lowercase()}s to cast this spell.") + throw IllegalStateException() } for(item in specialEquipment){ if(!player.equipment.contains(item,1)){ @@ -72,11 +73,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 +123,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..e390516e3 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.api.hasLineOfSight +import core.api.log +import core.game.event.SpellCastEvent +import core.game.interaction.MovementPulse import core.game.node.Node import core.game.node.entity.player.Player -import core.tools.SystemLogger +import core.game.node.entity.player.link.SpellBookManager +import core.game.world.map.path.Pathfinder +import core.tools.Log 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 + } + player.scripts.removeWeakScripts() + if (type in intArrayOf (SpellListener.NPC, SpellListener.OBJECT, SpellListener.PLAYER, SpellListener.GROUND_ITEM)) { + player.pulseManager.run (object : MovementPulse (player, node, Pathfinder.SMART) { + override fun pulse() : Boolean { + 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..57e8b4c6d 100644 --- a/Server/src/main/content/global/skill/magic/SpellUtils.kt +++ b/Server/src/main/content/global/skill/magic/SpellUtils.kt @@ -1,13 +1,25 @@ package content.global.skill.magic -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player 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.entity.player.Player import core.game.node.item.Item +import kotlin.math.min object SpellUtils { + /** + * Validates that the spell packet's interface matches the player's actual server-side spellbook. + * This prevents exploits where the client has a stale spellbook interface (e.g., after Spellbook Swap reverts). + * @param player The player casting the spell + * @param packetInterfaceId The interface ID from the client's spell packet + * @return true if the interfaces match (valid), false if there's a client/server desync + */ + @JvmStatic + fun validateSpellbookInterface(player : Player, packetInterfaceId : Int) : Boolean { + val actualSpellbook = player.spellBookManager.spellBook + return packetInterfaceId == actualSpellbook + } fun usingStaff(p: Player, rune: Int): Boolean { val weapon = p.equipment[3] ?: return false val staff = MagicStaff.forId(rune) ?: return false @@ -20,70 +32,57 @@ object SpellUtils { return false } - 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)){ - removeItems.add(rune) - p.setAttribute("spell:runes",removeItems) + /** + * Validates if the player has the necessary runes to cast a spell. + * + * If the player is able to cast the spell, the "spell:runes" attribute will be set to the list of items that should + * be removed from the player's inventory after successfully casting the spell. This accounts for staves and + * combination runes. + * + * @param p The player casting the spell + * @param runes The runes and other items required to cast the spell + * @return null if the player can cast the spell or an Item representing at least one of the runes the player is missing + */ + @JvmStatic + fun hasRunes(p: Player, runes: Array): Item? { + val cost = HashMap() + // `runes` are mostly actual runes but occasionally other items like staves or unpowered orbs + for (rune in runes) { + if (usingStaff(p, rune.id)) continue + cost[rune.id] = cost.getOrDefault(rune.id, 0) + rune.amount } - val baseAmt = p.inventory.getAmount(rune.id) - var amtRemaining = rune.amount - baseAmt - val possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(rune.id)) - for (r in possibleComboRunes) { - if (p.inventory.containsItem(Item(r.id)) && amtRemaining > 0) { - val amt = p.inventory.getAmount(r.id) - if (amtRemaining <= amt) { - removeItems.add(Item(r.id,amtRemaining)) - amtRemaining = 0 - break - } - removeItems.add(Item(r.id,p.inventory.getAmount(r.id))) - amtRemaining -= p.inventory.getAmount(r.id) + val toRemove = ArrayList() + + // Combination runes are used before elemental runes. + // https://runescape.wiki/w/Runecrafting?oldid=2618332#Function_and_Usage_of_Combination_Runes: + for (combo in CombinationRune.values()) { + val available = p.inventory.getAmount(combo.id) + val maxUsage = combo.types.mapNotNull { cost[it.id] }.maxOrNull() ?: 0 + if (maxUsage > 0 && available >= 0) { + val usage = min(maxUsage, available) + + toRemove.add(Item(combo.id, usage)) + // Even if a spell uses both parts of a combo rune, it should only consume a single rune. For example, + // a spell that requires an air rune and an earth rune should only consume a single dust rune. + // https://youtu.be/9gAiqEmF-Hc?t=67 + combo.types.forEach { cost[it.id] = cost.getOrDefault(it.id, 0) - usage } } } - p.setAttribute("spell:runes",removeItems) - return amtRemaining <= 0 - } - 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) - if (!hasBaseRune) { - val baseAmt = p.inventory.getAmount(item.id) - if (baseAmt > 0) { - toRemove.add(Item(item.id, p.inventory.getAmount(item.id))) - } - var amtRemaining = item.amount - baseAmt - val possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(item.id)) - for (r in possibleComboRunes) { - if (p.inventory.containsItem(Item(r.id)) && amtRemaining > 0) { - val amt = p.inventory.getAmount(r.id) - if (amtRemaining < amt) { - toRemove.add(Item(r.id, amtRemaining)) - amtRemaining = 0 - continue - } - amtRemaining -= p.inventory.getAmount(r.id) - toRemove.add(Item(r.id, p.inventory.getAmount(r.id))) - } - } - return if (amtRemaining <= 0) { - true - } else { - p.packetDispatch.sendMessage("You don't have enough " + item.name + "s to cast this spell.") - false - } + for ((runeId, amount) in cost) { + if (amount <= 0) continue + + val available = p.inventory.getAmount(runeId) + if (available < amount) { + return Item(runeId, amount) } - toRemove.add(item) - return true - } - return true - } - fun attackableNPC(npc: NPC): Boolean{ - return npc.definition.hasAction("attack") + toRemove.add(Item(runeId, amount)) + } + + p.setAttribute("spell:runes", toRemove) + return null } @JvmStatic diff --git a/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java b/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java index 8f9a62c8e..b794a87e0 100644 --- a/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java +++ b/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java @@ -1,10 +1,21 @@ package content.global.skill.magic; +import content.data.Quests; import core.game.component.Component; import core.game.dialogue.DialoguePlugin; +import core.game.event.SpellbookChangeEvent; +import core.game.interaction.Listener; import core.game.node.entity.player.Player; -import core.plugin.Initializable; import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource; +import core.game.node.entity.skill.Skills; +import core.game.node.item.Item; +import core.plugin.Initializable; + +import java.util.ArrayList; + +import static core.api.ContentAPIKt.hasRequirement; + /** * Handles the SpellbookSwapDialogue dialogue. @@ -73,8 +84,27 @@ public class SpellbookSwapDialogue extends DialoguePlugin { break; } final SpellBook book = type == 1 ? SpellBook.ANCIENT : SpellBook.MODERN; + if (book == SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) { + player.getPacketDispatch().sendMessage("You need to complete Desert Treasure to use Ancient Magicks."); + end(); + return true; + } + // Remove runes for Spellbook Swap + ArrayList runes = player.getAttribute("spell:runes", new ArrayList<>()); + if (!runes.isEmpty() && player.getInventory().remove(runes.toArray(new Item[0]))) { + player.removeAttribute("spell:runes"); + player.removeAttribute("tablet-spell"); + } + // Award XP + player.skills.addExperience(Skills.MAGIC, 130); + // Change book + player.dispatch(new SpellbookChangeEvent( + SpellBook.LUNAR, + book, + SpellbookChangeSource.SPELLBOOK_SWAP_CAST)); player.getSpellBookManager().setSpellBook(book); player.getInterfaceManager().openTab(new Component(book.getInterfaceId())); + player.getPacketDispatch().sendMessage("You have 2 minutes before your spellbook changes back to the Lunar Spellbook!"); end(); break; } 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..a9c95ab5e 100644 --- a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java +++ b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java @@ -1,20 +1,26 @@ package content.global.skill.magic.ancient; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.combat.spell.Runes; +import core.game.event.SpellCastEvent; import core.game.node.Node; import core.game.node.entity.Entity; +import core.game.node.entity.combat.spell.MagicSpell; +import core.game.node.entity.combat.spell.Runes; 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.player.link.diary.DiaryType; import core.game.node.item.Item; import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import core.tools.Log; import core.tools.RandomFunction; +import static content.global.skill.magic.HomeTeleportHelperKt.homeTeleport; +import static core.api.ContentAPIKt.log; + /** * Represents the plugin used to handle all ancient teleporting plugins. * @author 'Vexia @@ -51,10 +57,19 @@ public final class AncientTeleportPlugin extends MagicSpell { @Override public boolean cast(Entity entity, Node target) { - if (entity.getLocks().isTeleportLocked() || !super.meetsRequirements(entity, true, false)) { + 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)) { + boolean isHomeTeleport = getSpellId() == 28; + if (isHomeTeleport) { + if (!entity.isPlayer()) { + log(this.getClass(), Log.ERR, "Why the fuck is a non-player entity trying to cast ancient-magick home teleport?!"); + return false; + } + homeTeleport((Player) entity, Location.create(3087, 3495, 0)); + entity.dispatch(new SpellCastEvent(SpellBook.ANCIENT, getSpellId(), target)); + } else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) { if (!super.meetsRequirements(entity, true, true)) { entity.getTeleporter().getCurrentTeleport().stop(); return false; @@ -85,7 +100,7 @@ public final class AncientTeleportPlugin extends MagicSpell { // dareeyak teleport SpellBook.ANCIENT.register(24, new AncientTeleportPlugin(78, 88, Location.create(2966, 3696, 0), new Item(Runes.LAW_RUNE.getId(), 2), new Item(Runes.FIRE_RUNE.getId(), 3), new Item(Runes.AIR_RUNE.getId(), 2))); // carralangar teleport - SpellBook.ANCIENT.register(25, new AncientTeleportPlugin(84, 82, Location.create(3163, 3664, 0), new Item(Runes.SOUL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); + SpellBook.ANCIENT.register(25, new AncientTeleportPlugin(84, 82, Location.create(3217, 3676, 0), new Item(Runes.SOUL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); // annakarl teleport SpellBook.ANCIENT.register(26, new AncientTeleportPlugin(90, 100, Location.create(3287, 3883, 0), new Item(Runes.BLOOD_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2))); // ghorrock teleport diff --git a/Server/src/main/content/global/skill/magic/ancient/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..4b0e29ff4 100644 --- a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java @@ -1,163 +1,160 @@ -package content.global.skill.magic.ancient; - -import java.util.List; - -import core.game.node.entity.combat.spell.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.spell.CombatSpell; -import core.game.node.entity.combat.spell.SpellType; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.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; - -/** - * Handles the Ice spells from the Ancient spellbook. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class IceSpells extends CombatSpell { - - /** - * The barrage orb GFX. - */ - private static final Graphics BARRAGE_ORB = new Graphics(1677, 96); //119 - - /** - * The projectile for Ice rush. - */ - private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 360, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice rush. - */ - private static final Graphics RUSH_END = new Graphics(361, 96); - - /** - * The projectile for Ice barrage. - */ - private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 362, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice barrage. - */ - private static final Graphics BURST_END = new Graphics(363, 0); - - /** - * The start graphic for Ice rush. - */ - private static final Graphics BLITZ_START = new Graphics(366, 96); - - /** - * The end graphic for Ice rush. - */ - private static final Graphics BLITZ_END = new Graphics(367, 96); - - /** - * The projectile for Ice barrage. - */ - private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 368, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Ice barrage. - */ - private static final Graphics BARRAGE_END = new Graphics(369, 0); - - /** - * Constructs a new {@code IceSpells} {@code Object}. - */ - public IceSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code IceSpells} {@Code Object} - * @param type The spell type. - * @param impactSound The impact sound id. - * @param anim The animation. - * @param start The start graphics. - * @param projectile The projectile. - * @param end The end graphics. - */ - private IceSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.ANCIENT, level, baseExperience, 171, impactSound, anim, start, projectile, end, runes); - } - - @Override - public void visualize(Entity entity, Node target) { - entity.graphics(graphic); - if (projectile != null) { - projectile.transform(entity, (Entity) target, false, 58, 10).send(); - } - entity.animate(animation); - sendAudio(entity, audio); - } - - @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)); - } - target.graphics(BARRAGE_ORB); - return; - } - super.visualizeImpact(entity, target, state); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, 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))); - return this; - } - - @Override - public void fireEffect(Entity entity, Entity victim, BattleState state) { - if (state.getEstimatedHit() == -1) { - return; - } - int ticks = (1 + (type.ordinal() - SpellType.RUSH.ordinal())) * 8; - if (state.getEstimatedHit() > -1) { - if (victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks()) { - victim.getStateManager().set(EntityState.FROZEN, ticks); - } else if (type == SpellType.BARRAGE) { - state.setFrozen(true); - } - } - } - - @Override - public BattleState[] getTargets(Entity entity, Entity target) { - if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { - return super.getTargets(entity, target); - } - List list = getMultihitTargets(entity, target, 9); - BattleState[] targets = new BattleState[list.size()]; - int index = 0; - for (Entity e : list) { - targets[index++] = new BattleState(entity, e); - } - return targets; - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return getType().getImpactAmount(entity, victim, 4); - } - -} +package content.global.skill.magic.ancient; + +import java.util.List; + +import core.game.node.entity.combat.spell.Runes; +import core.game.node.Node; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.spell.CombatSpell; +import core.game.node.entity.combat.spell.SpellType; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.node.item.Item; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; +import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; + +/** + * Handles the Ice spells from the Ancient spellbook. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class IceSpells extends CombatSpell { + + /** + * The barrage orb GFX. + */ + private static final Graphics BARRAGE_ORB = new Graphics(1677, 96); //119 + + /** + * The projectile for Ice rush. + */ + private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 360, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics RUSH_END = new Graphics(361, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 362, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BURST_END = new Graphics(363, 0); + + /** + * The start graphic for Ice rush. + */ + private static final Graphics BLITZ_START = new Graphics(366, 96); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics BLITZ_END = new Graphics(367, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 368, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(369, 0); + + /** + * Constructs a new {@code IceSpells} {@code Object}. + */ + public IceSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code IceSpells} {@Code Object} + * @param type The spell type. + * @param impactSound The impact sound id. + * @param anim The animation. + * @param start The start graphics. + * @param projectile The projectile. + * @param end The end graphics. + */ + private IceSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, Sounds.ICE_CAST_171, impactSound, anim, start, projectile, end, runes); + } + + @Override + public void visualize(Entity entity, Node target) { + entity.graphics(graphic); + if (projectile != null) { + projectile.transform(entity, (Entity) target, false, 58, 10).send(); + } + entity.animate(animation); + playGlobalAudio(entity.getLocation(), audio.id, 20); + + } + + @Override + public void visualizeImpact(Entity entity, Entity target, BattleState state) { + if (state.isFrozen()) { + playGlobalAudio(target.getLocation(), impactAudio, 20); + target.graphics(BARRAGE_ORB); + return; + } + super.visualizeImpact(entity, target, state); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, Sounds.ICE_RUSH_IMPACT_173, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.WATER_RUNE.getItem(2))); + SpellBook.ANCIENT.register(2, new IceSpells(SpellType.BURST, 70, 40.0, Sounds.ICE_BURST_IMPACT_170, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); + SpellBook.ANCIENT.register(1, new IceSpells(SpellType.BLITZ, 82, 46.0, Sounds.ICE_BLITZ_IMPACT_169, new Animation(1978, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); + SpellBook.ANCIENT.register(3, new IceSpells(SpellType.BARRAGE, 94, 52.0, Sounds.ICE_BARRAGE_IMPACT_168, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(6))); + return this; + } + + @Override + public void fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() == -1) { + return; + } + int ticks = (type.ordinal() - 4) * 8; + if (hasTimerActive(victim, "frozen") || hasTimerActive(victim, "frozen:immunity")) { + if (type == SpellType.BARRAGE) { state.setFrozen(true); } + return; + } + registerTimer(victim, spawnTimer("frozen", ticks, true)); + } + + @Override + public BattleState[] getTargets(Entity entity, Entity target) { + if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { + return super.getTargets(entity, target); + } + List list = getMultihitTargets(entity, target, 9); + BattleState[] targets = new BattleState[list.size()]; + int index = 0; + for (Entity e : list) { + targets[index++] = new BattleState(entity, e); + } + return targets; + } + + @Override + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return getType().getImpactAmount(entity, victim, 4); + } + +} diff --git a/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java index d2c331366..5ead4131e 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(18, new MiasmicSpells(SpellType.BURST, 73, 42.0, 5366, 5372, new Animation(10516, Priority.HIGH), BURST_START, null, BURST_END, Runes.EARTH_RUNE.getItem(2), Runes.SOUL_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4))); + SpellBook.ANCIENT.register(17, 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..fd8dc5ccd 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -5,301 +5,302 @@ import content.global.skill.farming.CompostBins import content.global.skill.farming.CompostType import content.global.skill.farming.FarmingPatch import content.global.skill.magic.SpellListener +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Lunar import core.api.* +import core.game.component.CloseEvent import core.game.component.Component +import core.game.interaction.QueueStrength import core.game.node.Node +import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.npc.NPC 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 import core.game.system.config.NPCConfigParser import core.game.system.task.Pulse +import core.game.system.timer.impl.PoisonImmunity +import core.game.system.timer.impl.SkillRestore +import core.game.system.timer.impl.SpellbookSwap 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.Animations import org.rs09.consts.Components +import org.rs09.consts.Graphics import org.rs09.consts.Items +import org.rs09.consts.Sounds +import kotlin.math.floor + +private const val DREAM_HEAL_INTERVAL_TICKS = 50 +private const val DREAM_HARD_RESET_INTERVAL_TICKS = 90 +private const val DREAM_HARD_RESET_DELAY_TICKS = 0 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)) + homeTeleport(player, Location.create(2100, 3914, 0)) + 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)) + } + + // 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 + onCast(Lunar.SPELLBOOK_SWAP, NONE) { player, _ -> + requires(player, 96, arrayOf( + Item(Items.LAW_RUNE_563, 1), + Item(Items.COSMIC_RUNE_564, 2), + Item(Items.ASTRAL_RUNE_9075, 3) + )) + spellbookSwap(player) } } - // 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() @@ -313,19 +314,20 @@ class LunarListeners : SpellListener("lunar"), Commands { if(playerPies.isEmpty()){ player.sendMessage("You have no pies which you have the level to cook.") - return + throw IllegalStateException() } player.pulseManager.run(object : Pulse(){ 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 +338,226 @@ 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.") - return + // Level 66 + fun curePlant(player: Player, obj: Scenery) { + if (CompostBins.forObject(obj) != null) { + sendMessage(player, "Bins don't often get diseased.") + throw IllegalStateException() } - 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!") + throw IllegalStateException() + } + val patch = fPatch.getPatchFor(player) + if (patch.isWeedy()) { + sendMessage(player, "The weeds are healthy enough already.") + throw IllegalStateException() + } + if (patch.isEmptyAndWeeded()) { + sendMessage(player, "There's nothing there to cure.") + throw IllegalStateException() + } + if (patch.isGrown()) { + sendMessage(player, "That's not diseased.") + throw IllegalStateException() + } + if (patch.isDead) { + sendMessage(player, "It says 'Cure' not 'Resurrect'. Although death may arise from disease, it is not in itself a disease and hence cannot be cured. So there.") + throw IllegalStateException() + } + if (!patch.isDiseased) { + sendMessage(player, "It is growing just fine.") + throw IllegalStateException() + } + + patch.cureDisease() + removeRunes(player) + addXP(player,60.0) + 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.") + throw IllegalStateException() + } + face(player, npc) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_STATSPY_6293, Graphics.LUNAR_SPELLBOOK_STAT_SPY_OVER_PLAYER_1060, soundID = Sounds.LUNAR_STAT_SPY_3620) + removeRunes(player) + addXP(player,66.0) + + setDelay(player, false) + openSingleTab(player, Components.DREAM_MONSTER_STAT_522) + setInterfaceText(player, "Monster name : ${npc.definition.name}", Components.DREAM_MONSTER_STAT_522, 0) + setInterfaceText(player, "Combat Level : ${npc.definition.combatLevel}", Components.DREAM_MONSTER_STAT_522, 1) + setInterfaceText(player, "Hitpoints : ${npc.definition.handlers[NPCConfigParser.LIFEPOINTS] ?: 0}", Components.DREAM_MONSTER_STAT_522, 2) + setInterfaceText(player, "Max hit : ${npc.getSwingHandler(false).calculateHit(npc, player, 1.0)}", Components.DREAM_MONSTER_STAT_522, 3) + + val poisonStatus = if(npc.isPoisonImmune){ + "This creature is immune to poison." + } else "This creature is not immune to poison." + + setInterfaceText(player, poisonStatus, 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.") + throw IllegalStateException() } - sendTeleport(player,xp,loc) + val p = target.asPlayer() + if(!p.isActive || p.locks.isInteractionLocked) { + sendMessage(player, "This player is busy.") + throw IllegalStateException() + } + if(!p.settings.isAcceptAid) { + sendMessage(player, "This player is not accepting any aid.") + throw IllegalStateException() + } + if(!isPoisoned(p)) { + sendMessage(player, "This player is not poisoned.") + throw IllegalStateException() + } + player.face(p) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_OTHER_4411, Graphics.LUNAR_SPELLBOOK_CURE_OTHER_736, 130, Sounds.LUNAR_CURE_OTHER_2886) + 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.") + throw IllegalStateException() + } + + 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.") + throw IllegalStateException() + } + 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.") + throw IllegalStateException() + } + 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 +574,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,106 +588,307 @@ class LunarListeners : SpellListener("lunar"), Commands { if(index == size && size != 0) { removeRunes(player, true) - visualizeSpell(player, SUPERGLASS_MAKE_ANIM, SUPERGLASS_MAKE_GFX, 2896) - rewardXP(player, Skills.CRAFTING, 10.0) + 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 * index) 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.") + throw IllegalStateException() + } + // https://runescape.wiki/w/Dream?oldid=880976 claims Dream makes you heal 1 hp every 20 seconds + // https://oldschool.runescape.wiki/w/Dream claims that Dream has its own timer, so the Dream heals don't need + // to align with the natural heals + val timer = getOrStartTimer(player) + val dreamStartAnimationTicks = animationDuration(Animation(Animations.LUNAR_SPELLBOOK_DREAM_START_6295)) + var nextHealTick = getWorldTicks() + dreamStartAnimationTicks + DREAM_HEAL_INTERVAL_TICKS + var nextHardResetTick = -1 + var waitingForHardReset = false + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_START_6295) + removeRunes(player, true) + addXP(player, 82.0) + delayEntity(player, dreamStartAnimationTicks) + queueScript(player, dreamStartAnimationTicks, QueueStrength.WEAK) { stage: Int -> + if (stage == 0) { + sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) + playAudio(player, Sounds.LUNAR_SLEEP_3619) + return@queueScript delayScript(player, 5) + } + if (waitingForHardReset) { + waitingForHardReset = false + nextHardResetTick = getWorldTicks() + DREAM_HARD_RESET_INTERVAL_TICKS + } else if (nextHardResetTick != -1 && getWorldTicks() >= nextHardResetTick) { + // The 530 client eventually drops Dream back to idle if it only sees repeated 6296 updates + // Sending an explicit reset makes the next 6296 a full restart instead of a same-sequence refresh + resetAnimator(player) + waitingForHardReset = true + return@queueScript delayScript(player, DREAM_HARD_RESET_DELAY_TICKS) + } else if (nextHardResetTick == -1) { + nextHardResetTick = getWorldTicks() + DREAM_HARD_RESET_INTERVAL_TICKS + } + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_MID_6296) + sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) + if (getWorldTicks() >= nextHealTick) { + val amt = timer.getHealAmount(player) //accounts for regen brace + heal(player, amt) + nextHealTick += DREAM_HEAL_INTERVAL_TICKS + 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.") - return + throw IllegalStateException() } val fPatch = FarmingPatch.forObject(target) if(fPatch == null) { sendMessage(player, "Um... I don't want to fertilize that!") - return + throw IllegalStateException() } val patch = fPatch.getPatchFor(player) if (patch.isGrown()) { sendMessage(player, "Composting isn't going to make it get any bigger.") - return + throw IllegalStateException() } if (patch.isFertilized()) { sendMessage(player, "This patch has already been composted.") - return + throw IllegalStateException() } - 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.") - return + // 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.") + throw IllegalStateException() } - 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.") + 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.") + throw IllegalStateException() + } + 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 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 - } - 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) - } - addXP(player, 74.0) - } - - 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 + throw IllegalStateException() } - 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 + throw IllegalStateException() } - if(!p.settings.isAcceptAid) { + if(!targetPlayer.settings.isAcceptAid) { sendMessage(player, "This player is not accepting any aid.") - return + throw IllegalStateException() } - if(!hasState(p, EntityState.POISONED)) { - sendMessage(player, "This player is not poisoned.") - return + if(10 >= player.skills.lifepoints) { + sendMessage(player, "You need more hitpoints to cast this spell.") + throw IllegalStateException() } - 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 + private fun spellbookSwap(player : Player) { + // Runes are removed on successful swap to another spellbook in SpellbookSwapDialogue.java + // removeRunes(player, true) + lock(player, 4) + visualizeSpell(player, 6299, 1062) + player.scripts.removeWeakScripts() + player.dialogueInterpreter.open(3264731) + registerTimer(player, SpellbookSwap()) + // XP is awarded in SpellbookSwapDialogue.java similarly to rune removal handling + // addXP(player, 130.0) + setDelay(player, false) + } + + // 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, usage = "::poison player damage", description = "Applies poison to player for damage initial damage.") { 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 { + removeTimer(it) + 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, description = "Gives you the runes and items needed to test Humidify.") { 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/SpellbookSwapSpell.java b/Server/src/main/content/global/skill/magic/lunar/SpellbookSwapSpell.java deleted file mode 100644 index 4c152b0af..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/SpellbookSwapSpell.java +++ /dev/null @@ -1,84 +0,0 @@ -package content.global.skill.magic.lunar; - -import core.game.component.Component; -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.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; -import core.tools.RandomFunction; - -/** - * The spellbook swap spell. - * @author 'Vexia - */ -@Initializable -public class SpellbookSwapSpell extends MagicSpell { - - /** - * Represents the animation of this spell. - */ - private final Animation ANIMATION = new Animation(6299); - - /** - * Represents the graphics of this spell. - */ - private final Graphics GRAPHIC = new Graphics(1062); - - /** - * Constructs a new {@code SpellbookSwapSpell} {@code Object}. - */ - public SpellbookSwapSpell() { - super(SpellBook.LUNAR, 96, 130, null, null, null, new Item[] { new Item(Runes.LAW_RUNE.getId(), 1), new Item(Runes.COSMIC_RUNE.getId(), 2), new Item(Runes.ASTRAL_RUNE.getId(), 3) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(12, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player player = (Player) entity; - if (!super.meetsRequirements(player, true, true)) { - return false; - } - player.lock(9); - player.animate(ANIMATION); - player.graphics(GRAPHIC); - player.getDialogueInterpreter().open(3264731); - final int id = RandomFunction.random(1, 500000); - player.setAttribute("spell:swap", id); - GameWorld.getPulser().submit(new Pulse(20, player) { - @Override - public boolean pulse() { - if (player.getAttribute("spell:swap", 0) == id) { - removeTemporarySpell(player); - } - return true; - } - - }); - return true; - } - - /** - * Method used to remove the temp spell swap. - * @param player the player. - */ - public static void removeTemporarySpell(final Player player) { - player.removeAttribute("spell:swap"); - player.getSpellBookManager().setSpellBook(SpellBook.LUNAR); - player.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId())); - } -} 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..582795a81 100644 --- a/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java +++ b/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java @@ -1,5 +1,7 @@ package content.global.skill.magic.modern; +import content.region.kandarin.feldip.quest.zogreflesheaters.SkogreBehavior; +import content.region.kandarin.feldip.quest.zogreflesheaters.ZogreBehavior; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; import core.game.node.entity.Entity; @@ -15,6 +17,11 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.NPCs; +import org.rs09.consts.Sounds; + +import java.util.Arrays; +import java.util.List; /** * Handles the crumble undead spell. @@ -28,12 +35,21 @@ 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; + // Exception for Zogres and Skogres because they are classified as OGRES in npc.getTask() as not undead, + // so you couldn't use crumble undead on it. This bypasses that. + if ( + Arrays.stream(ZogreBehavior.zogreIds).anyMatch(i -> i == npc.getId()) || + Arrays.stream(SkogreBehavior.skogreIds).anyMatch(i -> i == npc.getId()) || + npc.getId() == NPCs.SLASH_BASH_2060 + ) { + return super.cast(entity, target); + } if (npc == null || npc.getTask() == null || !npc.getTask().undead) { ((Player) entity).getPacketDispatch().sendMessage("This spell only affects the undead."); return false; diff --git a/Server/src/main/content/global/skill/magic/modern/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..793a757b5 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/ModernData.kt @@ -0,0 +1,63 @@ +package content.global.skill.magic.modern + +import core.game.node.item.Item +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +enum class ChargeOrbData( + val obelisk: Int, + val requiredRunes: Array, + val level: Int, + val experience: Double, + val graphics: Graphics, + val sound: Int, + val chargedOrb: Int +) { + CHARGE_WATER_ORB( + Scenery.OBELISK_OF_WATER_2151, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.WATER_RUNE_555, 30), Item(Items.UNPOWERED_ORB_567)), + 56, + 66.0, + Graphics(149, 90), + Sounds.CHARGE_WATER_ORB_118, + Items.WATER_ORB_571 + ), + CHARGE_EARTH_ORB( + Scenery.OBELISK_OF_EARTH_29415, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.EARTH_RUNE_557, 30), Item(Items.UNPOWERED_ORB_567)), + 60, + 70.0, + Graphics(151, 90), + Sounds.CHARGE_EARTH_ORB_115, + Items.EARTH_ORB_575 + ), + CHARGE_FIRE_ORB( + Scenery.OBELISK_OF_FIRE_2153, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.FIRE_RUNE_554, 30), Item(Items.UNPOWERED_ORB_567)), + 63, + 73.0, + Graphics(152, 90), + Sounds.CHARGE_FIRE_ORB_117, + Items.FIRE_ORB_569 + ), + CHARGE_AIR_ORB( + Scenery.OBELISK_OF_AIR_2152, + arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.AIR_RUNE_556, 30), Item(Items.UNPOWERED_ORB_567)), + 66, + 76.0, + Graphics(150, 90), + Sounds.CHARGE_AIR_ORB_116, + Items.AIR_ORB_573 + ); + companion object{ + val spellMap = HashMap() + + init { + for (spell in ChargeOrbData.values()) { + spellMap[spell.obelisk] = spell + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt index 3a09289f2..777beee18 100644 --- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -1,72 +1,47 @@ package content.global.skill.magic.modern +import content.data.Quests import content.global.skill.magic.SpellListener +import content.global.skill.magic.SpellUtils.hasRunes import content.global.skill.magic.TeleportMethod +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Modern -import core.game.event.ItemAlchemizationEvent -import core.game.event.TeleportEvent -import 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 content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners +import core.ServerConstants +import core.api.* +import core.game.diary.DiaryLevel +import core.game.event.ItemAlchemizationEvent +import core.game.event.ResourceProducedEvent +import core.game.event.TeleportEvent import core.game.interaction.MovementPulse +import core.game.node.Node import core.game.node.entity.Entity +import core.game.node.entity.combat.spell.MagicStaff +import core.game.node.entity.impl.Animator import core.game.node.entity.impl.Projectile import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills -import content.global.skill.smithing.smelting.Bar -import content.global.skill.smithing.smelting.SmeltingPulse import core.game.node.item.Item import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items -import core.ServerConstants -import core.api.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 class ModernListeners : SpellListener("modern"){ - - private val CONFUSE_START = Graphics(102, 96) - private val CONFUSE_PROJECTILE = Projectile.create(null as Entity?, null, 103, 40, 36, 52, 75, 15, 11) - private val CONFUSE_END = Graphics(104, 96) - private val WEAKEN_START = Graphics(105, 96) - private val WEAKEN_PROJECTILE = Projectile.create(null as Entity?, null, 106, 40, 36, 52, 75, 15, 11) - private val WEAKEN_END = Graphics(107, 96) - private val CURSE_START = Graphics(108, 96) - private val CURSE_PROJECTILE = Projectile.create(null as Entity?, null, 109, 40, 36, 52, 75, 15, 11) - private val CURSE_END = Graphics(110, 96) - private val VULNER_START = Graphics(167, 96) - private val VULNER_PROJECTILE = Projectile.create(null as Entity?, null, 168, 40, 36, 52, 75, 15, 11) - private val VULNER_END = Graphics(169, 96) - private val ENFEEBLE_START = Graphics(170, 96) - private val ENFEEBLE_PROJECTILE = Projectile.create(null as Entity?, null, 171, 40, 36, 52, 75, 15, 11) - private val ENFEEBLE_END = Graphics(172, 96) - private val STUN_START = Graphics(173, 96) - private val STUN_PROJECTILE = Projectile.create(null as Entity?, null, 174, 40, 36, 52, 75, 15, 11) - private val STUN_END = Graphics(107, 96) - private val LOW_ANIMATION = Animation(716, Animator.Priority.HIGH) - private val HIGH_ANIMATION = Animation(729, Animator.Priority.HIGH) - private val LOW_ALCH_ANIM = Animation(712) - private val LOW_ALCH_GFX = Graphics(112,5) - private val HIGH_ALCH_ANIM = Animation(713) - private val HIGH_ALCH_GFX = Graphics(113,5) - private val BONE_CONVERT_GFX = Graphics(141, 96) - private val BONE_CONVERT_ANIM = Animation(722) - - override fun defineListeners() { - onCast(Modern.HOME_TELEPORT, NONE){ player, _ -> if (!getAttribute(player, "tutorial:complete", false)) { return@onCast } requires(player) - player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME) + homeTeleport(player, ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0)) setDelay(player,true) } @@ -89,28 +64,45 @@ 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); - sendTeleport(player,55.5, Location.create(2758, 3478, 0)) + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 5) + if (player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE).level == 2 + && (getItemFromEquipment(player, EquipmentSlot.HEAD)?.id ?: 0) == Items.SEERS_HEADBAND_14631) + sendTeleport(player,55.5,Location.create(2725, 3485, 0)) + else + 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)) + sendTeleport(player,74.0, Location.create(2795, 2798, 1)) } onCast(Modern.TELEPORT_TO_HOUSE, NONE){ player, _ -> @@ -145,6 +137,11 @@ class ModernListeners : SpellListener("modern"){ requires(player,60, arrayOf(Item(Items.EARTH_RUNE_557,4), Item(Items.WATER_RUNE_555,4), Item(Items.NATURE_RUNE_561,2))) boneConvert(player,false) } + + onCast(Modern.CHARGE_WATER_ORB, OBJECT, Scenery.OBELISK_OF_WATER_2151, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_EARTH_ORB, OBJECT, Scenery.OBELISK_OF_EARTH_29415, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_FIRE_ORB, OBJECT, Scenery.OBELISK_OF_FIRE_2153, 3, method = ::chargeOrb) + onCast(Modern.CHARGE_AIR_ORB, OBJECT, Scenery.OBELISK_OF_AIR_2152, 3, method = ::chargeOrb) } private fun boneConvert(player: Player,bananas: Boolean){ @@ -181,14 +178,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 +196,79 @@ 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 } + 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 } 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)) + if (explorersRing) { + visualize(player, LOW_ALCH_ANIM, EXPLORERS_RING_GFX) } else { - player.animate(Animation(if (high) 713 else 712)) - player.graphics(Graphics(if (high) 113 else 112)) + val weapon = getItemFromEquipment(player, EquipmentSlot.WEAPON) + if (weapon != null && weapon.id in MagicStaff.FIRE_RUNE.staves) { + visualize(player, if (high) HIGH_ALCH_STAFF_ANIM else LOW_ALCH_STAFF_ANIM, if (high) HIGH_ALCH_STAFF_GFX else LOW_ALCH_STAFF_GFX) + } else { + visualize(player, if (high) HIGH_ALCH_ANIM else LOW_ALCH_ANIM, if (high) HIGH_ALCH_GFX else LOW_ALCH_GFX) + } } - - if (coins.amount > 0) - player.inventory.add(coins) - + playAudio(player, if (high) Sounds.HIGH_ALCHEMY_97 else Sounds.LOW_ALCHEMY_98) player.dispatch(ItemAlchemizationEvent(item.id, high)) - player.inventory.remove(Item(item.id, 1)) + if (player.inventory.remove(Item(item.id, 1)) && coins.amount > 0) { + player.inventory.add(coins) + } removeRunes(player) addXP(player, if (high) 65.0 else 31.0) showMagicTab(player) setDelay(player, 5) + 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 +286,101 @@ 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.") + val hasHouse = player.houseManager.location.exitLocation != null + if(!hasHouse){ + player.sendMessage("You do not have a house you can teleport to.") return } + player.houseManager.preEnter(player, false) val teleType = TeleportManager.TeleportType.NORMAL - + val loc = player.houseManager.getEnterLocation() player.teleporter.send(loc, teleType) + player.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.MAGIC, spell.level)) { + sendMessage(player, "You need a magic level of ${spell.level} to cast this spell.") + return@queueScript stopExecuting(player) + } + val missing = hasRunes(player, spell.requiredRunes) + if (missing != null) { + sendMessage(player, "You don't have enough ${missing.name.lowercase()}s to cast this spell.") + return@queueScript stopExecuting(player) + } + visualizeSpell(player, CHARGE_ORB_ANIM, spell.graphics, spell.sound) + removeRunes(player) + val success = addItem(player, spell.chargedOrb) + if (!success) { + return@queueScript stopExecuting(player) + } + addXP(player, spell.experience) + setDelay(player, 3) + crafted++ + + if (crafted == 5 && spell.chargedOrb == Items.WATER_ORB_571) { + player.dispatch(ResourceProducedEvent(spell.chargedOrb, crafted, node)) + } + if (amount == crafted) { return@queueScript stopExecuting(player) } + setCurrentScriptState(player, 0) + return@queueScript delayScript(player, 6) + } + } + } + return + } + companion object { + private val CONFUSE_START = Graphics(102, 96) + private val CONFUSE_PROJECTILE = Projectile.create(null as Entity?, null, 103, 40, 36, 52, 75, 15, 11) + private val CONFUSE_END = Graphics(104, 96) + private val WEAKEN_START = Graphics(105, 96) + private val WEAKEN_PROJECTILE = Projectile.create(null as Entity?, null, 106, 40, 36, 52, 75, 15, 11) + private val WEAKEN_END = Graphics(107, 96) + private val CURSE_START = Graphics(108, 96) + private val CURSE_PROJECTILE = Projectile.create(null as Entity?, null, 109, 40, 36, 52, 75, 15, 11) + private val CURSE_END = Graphics(110, 96) + private val VULNER_START = Graphics(167, 96) + private val VULNER_PROJECTILE = Projectile.create(null as Entity?, null, 168, 40, 36, 52, 75, 15, 11) + private val VULNER_END = Graphics(169, 96) + private val ENFEEBLE_START = Graphics(170, 96) + private val ENFEEBLE_PROJECTILE = Projectile.create(null as Entity?, null, 171, 40, 36, 52, 75, 15, 11) + private val ENFEEBLE_END = Graphics(172, 96) + private val STUN_START = Graphics(173, 96) + private val STUN_PROJECTILE = Projectile.create(null as Entity?, null, 174, 40, 36, 52, 75, 15, 11) + private val STUN_END = Graphics(107, 96) + private val LOW_ANIMATION = Animation(716, Animator.Priority.HIGH) + private val HIGH_ANIMATION = Animation(729, Animator.Priority.HIGH) + private val LOW_ALCH_ANIM = Animation(9623) + private val LOW_ALCH_STAFF_ANIM = Animation(9625) + private val HIGH_ALCH_ANIM = Animation(9631) + private val HIGH_ALCH_STAFF_ANIM = Animation(9633) + private val LOW_ALCH_GFX = Graphics(763) + private val HIGH_ALCH_GFX = Graphics(1691) + private val LOW_ALCH_STAFF_GFX = Graphics(1692) + private val HIGH_ALCH_STAFF_GFX = Graphics(1693) + private val EXPLORERS_RING_GFX = Graphics(1698) + private val BONE_CONVERT_GFX = Graphics(141, 96) + private val BONE_CONVERT_ANIM = Animation(722) + private val CHARGE_ORB_ANIM = Animation(726) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/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..755a8062b 100644 --- a/Server/src/main/content/global/skill/prayer/Bones.kt +++ b/Server/src/main/content/global/skill/prayer/Bones.kt @@ -1,73 +1,64 @@ 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?, +) { + 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 +74,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..487d61e6f 100644 --- a/Server/src/main/content/global/skill/runecrafting/Altar.java +++ b/Server/src/main/content/global/skill/runecrafting/Altar.java @@ -4,6 +4,9 @@ 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; + /** * Represents an altar an it's relative information(corresponding ruin, etc) * @author 'Vexia @@ -69,13 +72,25 @@ 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 == 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/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..22cc1865f 100644 --- a/Server/src/main/content/global/skill/runecrafting/PouchManager.kt +++ b/Server/src/main/content/global/skill/runecrafting/PouchManager.kt @@ -1,5 +1,6 @@ package content.global.skill.runecrafting +import core.api.* import core.game.container.Container import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills @@ -7,109 +8,131 @@ import core.game.node.item.Item import org.json.simple.JSONArray import org.json.simple.JSONObject import org.rs09.consts.Items -import core.tools.colorize /** * A class for managing rune pouches. * @param player the player this manager instance belongs to. - * @author Ceikry + * @author Ceikry, Player Name */ class PouchManager(val player: Player) { - val pouches = mapOf( - Items.SMALL_POUCH_5509 to RCPouch(3,1), - Items.MEDIUM_POUCH_5510 to RCPouch(6,25), - Items.LARGE_POUCH_5512 to RCPouch(9,50), - Items.GIANT_POUCH_5514 to RCPouch(12,75) + Items.SMALL_POUCH_5509 to RCPouch(3, 3, 1), + Items.MEDIUM_POUCH_5510 to RCPouch(6, 264, 25), + Items.LARGE_POUCH_5512 to RCPouch(9, 186, 50), + Items.GIANT_POUCH_5514 to RCPouch(12,140, 75) ) /** * Method to add essence to a pouch - * @param pouchId the id of the pouch we are adding to + * @param itemId the item ID of the pouch we are adding to * @param amount the amount of essence to add * @param essence the ID of the essence item we are trying to add - * @author Ceikry + * @author Ceikry, Player Name */ - fun addToPouch(pouchId: Int, amount: Int, essence: Int){ - if(!checkRequirement(pouchId)){ - player.sendMessage(colorize("%RYou lack the required level to use this pouch.")) + fun addToPouch(itemId: Int, amount: Int, essence: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId + if (!checkRequirement(pouchId)) { + sendMessage(player, "You lack the required level to use this pouch.") return } var amt = amount val pouch = pouches[pouchId] - val otherEssence = when(essence){ + val otherEssence = when(essence) { Items.RUNE_ESSENCE_1436 -> Items.PURE_ESSENCE_7936 Items.PURE_ESSENCE_7936 -> Items.RUNE_ESSENCE_1436 else -> 0 } pouch ?: return - if(amount > pouch.container.freeSlots()){ + if (amount > pouch.container.freeSlots()) { amt = pouch.container.freeSlots() } - if(amt == 0){ - player.sendMessage("This pouch is already full.") + if (amt == pouch.container.freeSlots()) { + sendMessage(player, "Your pouch is full.") //https://www.youtube.com/watch?v=wbYtRwODKTo } - if(pouch.container.contains(otherEssence,1)){ - player.sendMessage("You can only store one type of essence in each pouch.") + if (pouch.container.contains(otherEssence,1)) { + sendMessage(player, "You can only store one type of essence in each pouch.") return } - player.inventory.remove(Item(essence,amt)) - pouch.container.add(Item(essence,amt)) - } + var disappeared = false + if (itemId != Items.SMALL_POUCH_5509) { + pouch.charges -= amt + } + if (pouch.charges <= 0) { + pouch.currentCap -= when (pouchId) { + Items.MEDIUM_POUCH_5510 -> 1 + Items.LARGE_POUCH_5512 -> 2 + Items.GIANT_POUCH_5514 -> 3 + else /*small pouch*/ -> 0 + } + if (pouch.currentCap <= 0) { + // The pouch will disappear: https://runescape.wiki/w/Runecrafting_pouches?oldid=708494, https://oldschool.runescape.wiki/w/Essence_pouch + // "Degraded pouches will continue to degrade and lose essence capacity until they disappear or are repaired." implies that this is the end result of a gradual decay process + if (removeItem(player, itemId)) { + disappeared = true + sendMessage(player, "Your pouch has degraded completely.") + // Reset the pouch for when the player obtains a new one + pouch.currentCap = pouch.capacity + pouch.charges = pouch.maxCharges + pouch.remakeContainer() + replaceAllItems(player, itemId, itemId - 1) //in case the player had more copies + } + } else { + if (!isDecayedPouch(itemId)) { + replaceAllItems(player, itemId, itemId + 1) + } + sendMessage(player, "Your pouch has decayed through use.") //https://www.youtube.com/watch?v=FUcPYrgPUlQ + pouch.charges = 9 * pouch.currentCap //implied by multiple contemporaneous sources, quantified only by https://oldschool.runescape.wiki/w/Large_pouch + pouch.remakeContainer() + if (amt > pouch.currentCap) { + amt = pouch.currentCap + } + } + } + val essItem = Item(essence, amt) + if (!disappeared && removeItem(player, essItem)) { + pouch.container.add(essItem) + } + } /** * Method to withdraw rune essence from a pouch. - * @param pouchId the item ID of the pouch to withdraw from - * @author Ceikry + * @param itemId the item ID of the pouch to withdraw from + * @author Ceikry, Player Name */ - fun withdrawFromPouch(pouchId: Int){ + fun withdrawFromPouch(itemId: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId val pouch = pouches[pouchId] pouch ?: return - val playerFree = player.inventory.freeSlots() + val playerFree = freeSlots(player) var amount = pouch.currentCap - pouch.container.freeSlots() - if (amount > playerFree) amount = playerFree - player.debug("$amount") - if(amount == 0) return - val essence = Item(pouch.container.get(0).id,amount) - pouch.container.remove(essence) - pouch.container.shift() - player.inventory.add(essence) - if(pouch.charges-- <= 0){ - pouch.currentCap -= when(pouchId){ - 5510 -> 1 - 5512 -> 2 - 5514 -> 3 - else -> 0 - } - if(pouch.currentCap <= 0){ - player.inventory.remove(Item(pouchId)) - player.inventory.add(Item(pouchId + 1)) - player.sendMessage(colorize("%RYour ${Item(pouchId).name} has degraded completely.")) - } - pouch.remakeContainer() - pouch.charges = 10 - if(pouchId != 5509) { - player.sendMessage(colorize("%RYour ${Item(pouchId).name.toLowerCase()} has degraded slightly from use.")) + if (amount > playerFree) { + amount = playerFree + } else { + sendMessage(player, "Your pouch has no essence left in it.") //https://www.youtube.com/watch?v=wbYtRwODKTo + if (amount == 0) { + return } } - + val essence = Item(pouch.container.get(0).id, amount) + pouch.container.remove(essence) + pouch.container.shift() + addItem(player, essence.id, essence.amount) } - /** * Method to save pouches to a root JSONObject * @param root the JSONObject we are adding the "pouches" JSONArray to * @author Ceikry */ - fun save(root: JSONObject){ + fun save(root: JSONObject) { val pouches = JSONArray() - for(i in this.pouches){ + for(i in this.pouches) { val pouch = JSONObject() pouch.put("id",i.key.toString()) val items = JSONArray() - for(item in i.value.container.toArray()){ + for(item in i.value.container.toArray()) { item ?: continue val it = JSONObject() it.put("itemId",item.id.toString()) @@ -124,14 +147,13 @@ class PouchManager(val player: Player) { root.put("pouches",pouches) } - /** * Method to parse save data from a JSONArray * @param data the JSONArray that contains the data to parse * @author Ceikry */ - fun parse(data: JSONArray){ - for(e in data){ + fun parse(data: JSONArray) { + for (e in data){ val pouch = e as JSONObject val id = pouch["id"].toString().toInt() val p = pouches[id] @@ -141,7 +163,7 @@ class PouchManager(val player: Player) { p.charges = charges p.currentCap = currentCap p.remakeContainer() - for(i in pouch["container"] as JSONArray){ + for (i in pouch["container"] as JSONArray) { val it = i as JSONObject it["itemId"] ?: continue val item = it["itemId"].toString().toInt() @@ -151,33 +173,31 @@ class PouchManager(val player: Player) { } } - /** * Method for checking the level requirement for a given pouch. * @param pouchId the item ID of the pouch to check * @author Ceikry */ - fun checkRequirement(pouchId: Int): Boolean{ + fun checkRequirement(pouchId: Int): Boolean { val p = pouches[pouchId] p ?: return false return player.skills.getLevel(Skills.RUNECRAFTING) >= p.levelRequirement } - /** * Method for sending the player a message about how much space is left in a pouch - * @param pouchId the item ID of the pouch to check - * @author Ceikry + * @param itemId the item ID of the pouch to check + * @author Ceikry, Player Name */ - fun checkAmount(pouchId: Int){ + fun checkAmount(itemId: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId val p = pouches[pouchId] p ?: return player.sendMessage("This pouch has space for ${p.container.freeSlots()} more essence.") } - - fun isDecayedPouch(pouchId: Int): Boolean{ - if(pouchId == 5510) return false + fun isDecayedPouch(pouchId: Int): Boolean { + if (pouchId == Items.MEDIUM_POUCH_5510) return false return pouches[pouchId - 1] != null } @@ -185,12 +205,12 @@ class PouchManager(val player: Player) { * A class that represents a runecrafting pouch. * @author Ceikry */ - class RCPouch(val capacity: Int, val levelRequirement: Int){ + class RCPouch(val capacity: Int, val maxCharges: Int, val levelRequirement: Int) { var container = Container(capacity) var currentCap = capacity - var charges = 10 - fun remakeContainer(){ + var charges = maxCharges + fun remakeContainer() { this.container = Container(currentCap) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/runecrafting/Rune.java b/Server/src/main/content/global/skill/runecrafting/Rune.java index 79762bf96..7736c579b 100644 --- a/Server/src/main/content/global/skill/runecrafting/Rune.java +++ b/Server/src/main/content/global/skill/runecrafting/Rune.java @@ -9,19 +9,19 @@ 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), + 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(), 5, 6, new int[] { 1, 19, 38, 57, 76, 95, 114 }), + EARTH(Runes.EARTH_RUNE.transform(), 9, 6.5, new int[] { 1, 26, 52, 78, 104 }), + FIRE(Runes.FIRE_RUNE.transform(), 14, 7, new int[] { 1, 35, 70, 105 }), + 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); /** diff --git a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java index d333dc443..c9fa23ef5 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 { @@ -92,36 +102,48 @@ 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) { + sendMessage(player, "You need pure essence to craft this rune."); return false; } - if (!altar.isOurania() && rune.isNormal() && !player.getInventory().containsItem(PURE_ESSENCE) && !player.getInventory().containsItem(RUNE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need rune essence or pure essence in order to craft this rune."); + if (!altar.isOurania() && rune.isNormal() && amountInInventory(player, PURE_ESSENCE.getId()) == 0 && amountInInventory(player, RUNE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need rune essence or pure essence in order to craft this rune."); return false; } - if (altar.isOurania() && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("You need pure essence to craft this rune."); + if (altar.isOurania() && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); return false; } - if (combination && player.getSkills().getLevel(Skills.RUNECRAFTING) < combo.getLevel()) { - player.getPacketDispatch().sendMessage("You need a Runecrafting level of at least " + combo.getLevel() + " to combine this rune."); + if (combination && getDynLevel(player, Skills.RUNECRAFTING) < combo.getLevel()) { + sendMessage(player, "You need a Runecrafting level of at least " + combo.getLevel() + " to combine this rune."); return false; } if (node != null) { if (node.getName().contains("rune") && !hasSpellImbue()) { final Rune r = Rune.forItem(node); final Talisman t = Talisman.forName(r.name()); - if (!player.getInventory().containsItem(t.getTalisman())) { - player.getPacketDispatch().sendMessage("You don't have the correct talisman to combine this rune."); + if (amountInInventory(player, t.getTalisman().getId()) == 0) { + sendMessage(player, "You don't have the correct talisman to combine this rune."); return false; } talisman = t; @@ -135,7 +157,7 @@ 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 @@ -148,43 +170,71 @@ public final class RuneCraftPulse extends SkillPulse { return true; } - @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()); + private void craft() { + final Item item = getEssenceItem(); int amount = player.getInventory().getAmount(item); - if (!altar.isOurania()) { + if (altar.isOurania()) { + if (removeItem(player, item, Container.INVENTORY)) { + sendMessage(player, "You bind the temple's power into runes."); + player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); + + int[] OuraniaValues; + if (getDynLevel(player, Skills.RUNECRAFTING) == 99) { + OuraniaValues = OuraniaTable[10]; + } else { + int index = getDynLevel(player, Skills.RUNECRAFTING) / 10; + OuraniaValues = OuraniaTable[index]; + } + for (int i = 0; i < amount; i++) { + int roll = RandomFunction.random(10000); + Rune rune = null; + for (int j = 0; j < 14; j++) { + if (roll < OuraniaValues[j]) { + rune = Rune.values()[13 - j]; + break; + } + } + rewardXP(player, Skills.RUNECRAFTING, rune.getExperience() * 2); + addItemOrDrop(player, rune.getRune().getId(), 1); + } + } + } else { int total = 0; for(int j = 0; j < amount; j++) { // since getMultiplier is stochastic, roll `amount` independent copies total += getMultiplier(); } - Item i = new Item(rune.getRune().getId(), total); - if (player.getInventory().remove(item) && player.getInventory().hasSpaceFor(i)) { - player.getInventory().add(i); + if (removeItem(player, item, Container.INVENTORY)) { + sendMessage(player, "You bind the temple's power into " + (combination ? combo.getRune().getName().toLowerCase() : rune.getRune().getName().toLowerCase()) + "s."); + addItemOrDrop(player, rune.getRune().getId(), total); player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); - 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 +242,7 @@ public final class RuneCraftPulse extends SkillPulse { player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 3); } // Craft 196 or more air runes simultaneously - if (altar == Altar.AIR && i.getAmount() >= 196) { + if (altar == Altar.AIR && total >= 196) { player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 2); } // Craft a water rune at the Water Altar @@ -201,59 +251,34 @@ public final class RuneCraftPulse extends SkillPulse { } } - } else { - if (player.getInventory().remove(item)) { - player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); - for (int i = 0; i < amount; i++) { - Rune rune = null; - while (rune == null) { - final Rune temp = Rune.values()[RandomFunction.random(Rune.values().length)]; - if (player.getSkills().getLevel(Skills.RUNECRAFTING) >= temp.getLevel()) { - rune = temp; - } else { - if (RandomFunction.random(3) == 1) { - rune = temp; - } - } - } - player.getSkills().addExperience(Skills.RUNECRAFTING, rune.getExperience() * 2, true); - Item runeItem = rune.getRune(); - player.getInventory().add(runeItem); - } - } } - - } /** * Method used to combine runes. */ - private final void combine() { + private void combine() { final Item remove = node.getName().contains("talisman") ? node : talisman != null ? talisman.getTalisman() : Talisman.forName(Rune.forItem(node).name()).getTalisman(); boolean imbued = hasSpellImbue(); - if (!imbued ? player.getInventory().remove(remove) : imbued) { + if (!imbued ? removeItem(player, remove, Container.INVENTORY) : imbued) { int amount = 0; int essenceAmt = player.getInventory().getAmount(PURE_ESSENCE); final Item rune = node.getName().contains("rune") ? Rune.forItem(node).getRune() : Rune.forName(Talisman.forItem(node).name()).getRune(); int runeAmt = player.getInventory().getAmount(rune); - if (essenceAmt > runeAmt) { - amount = runeAmt; - } else { - amount = essenceAmt; - } - if (player.getInventory().remove(new Item(PURE_ESSENCE.getId(), amount)) && player.getInventory().remove(new Item(rune.getId(), amount))) { + amount = Math.min(essenceAmt, runeAmt); + if (removeItem(player, new Item(PURE_ESSENCE.getId(), amount), Container.INVENTORY) && removeItem(player, new Item(rune.getId(), amount), Container.INVENTORY)) { for (int i = 0; i < amount; i++) { if (RandomFunction.random(1, 3) == 1 || hasBindingNecklace()) { - player.getInventory().add(new Item(combo.getRune().getId(), 1)); - player.getSkills().addExperience(Skills.RUNECRAFTING, combo.getExperience(), true); + addItemOrDrop(player, combo.getRune().getId(), 1); + rewardXP(player, Skills.RUNECRAFTING, combo.getExperience()); } } if (hasBindingNecklace()) { player.getEquipment().get(EquipmentContainer.SLOT_AMULET).setCharge(player.getEquipment().get(EquipmentContainer.SLOT_AMULET).getCharge() - 1); if (1000 - player.getEquipment().get(EquipmentContainer.SLOT_AMULET).getCharge() > 14) { - player.getEquipment().remove(BINDING_NECKLACE, true); - player.getPacketDispatch().sendMessage("Your binding necklace crumbles into dust."); + if (player.getEquipment().remove(BINDING_NECKLACE, true)) { + sendMessage(player, "Your binding necklace crumbles into dust."); + } } } } @@ -270,39 +295,21 @@ public final class RuneCraftPulse extends SkillPulse { } /** - * Gets the essence amount. + * Gets the rune essence item. * - * @return the amount of essence. + * @return the rune essence item. */ - private int getEssenceAmount() { - if (altar.isOurania() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); + private Item getEssenceItem() { + if (altar.isOurania() && amountInInventory(player, PURE_ESSENCE.getId()) > 0) { + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } - if (!rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); - } else if (rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); - } else { - return player.getInventory().getAmount(RUNE_ESSENCE); + if (!rune.isNormal() && amountInInventory(player, PURE_ESSENCE.getId()) > 0) { + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } - } - - /** - * Gets the rune essence that needs to be defined. - * - * @return the item. - */ - private Item getEssence() { - if (altar.isOurania() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } - if (!rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } else if (rune.isNormal() && player.getInventory().containsItem(PURE_ESSENCE)) { - return PURE_ESSENCE; - } else { - return RUNE_ESSENCE; + if (rune.isNormal() && amountInInventory(player, RUNE_ESSENCE.getId()) > 0) { + return new Item(RUNE_ESSENCE.getId(), amountInInventory(player, RUNE_ESSENCE.getId())); } + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); } /** @@ -314,7 +321,13 @@ public final class RuneCraftPulse extends SkillPulse { if (altar.isOurania()) { return 1; } - int rcLevel = player.getSkills().getLevel(Skills.RUNECRAFTING); + int rcLevel = getDynLevel(player, Skills.RUNECRAFTING); + int runecraftingFormulaRevision = ServerConstants.RUNECRAFTING_FORMULA_REVISION; + boolean lumbridgeDiary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1); + return RuneCraftPulse.getMultiplier(rcLevel, rune, runecraftingFormulaRevision, lumbridgeDiary); + } + + 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 +336,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 +373,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/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/SkillcapeEquipmentPlugin.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt index 0ad090fcd..7de63cfd9 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt @@ -1,27 +1,56 @@ package content.global.skill.skillcapeperks +import core.api.sendMessage +import core.game.container.Container import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.game.container.impl.EquipmentContainer +import content.data.skill.SkillcapePerks as DataSkillcapes +import content.global.skill.slayer.SlayerEquipmentFlags +import org.rs09.consts.Items +import java.util.ArrayList class SkillcapeEquipmentPlugin : InteractionListener { override fun defineListeners() { val capeIds = ArrayList() - for(cape in content.data.skill.SkillcapePerks.values()){ + for(cape in DataSkillcapes.values()){ cape.skillcapeIds.forEach { capeIds.add(it) } } val capes = capeIds.toIntArray() - - onEquip(capes){player, node -> - val skillcape = Skillcape.forId(node.id) - val perk = SkillcapePerks.forSkillcape(skillcape) - perk.activate(player) - return@onEquip true - } - + onEquip(capes){player, node -> true} onUnequip(capes){player, node -> val skillcape = Skillcape.forId(node.id) + // For Temple of Ikov. Do not let player unequip firemaking skillcape in the dark basement (need to keep an active light source). + if(player.location.isInRegion(10648) && (node.id == Items.FIREMAKING_CAPE_9804 || node.id == Items.FIREMAKING_CAPET_9805) && GameWorld.settings?.skillcape_perks == true) { + sendMessage(player, "Unequipping that skillcape would leave you without a light source.") + return@onUnequip false + } val perk = SkillcapePerks.forSkillcape(skillcape) perk.deactivate(player) return@onUnequip true } } -} \ No newline at end of file + companion object { + @JvmStatic + fun updateCapePerks(player: Player, c: Container, slot: Int) { + if (slot == EquipmentContainer.SLOT_CAPE) { + val cape = c.get(slot) + for (perk in SkillcapePerks.values()) { + if (SkillcapePerks.isActive(perk, player)) { + perk.deactivate(player) + } + } + if (cape != null) { + val skillcape = Skillcape.forId(cape.id) + val perk = SkillcapePerks.forSkillcape(skillcape) + if (perk != SkillcapePerks.NONE) { + perk.activate(player) + } + } + } + EquipmentContainer.updateBonuses(player) + SlayerEquipmentFlags.updateFlags(player) + } + } +} diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt index b30636136..148696543 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt @@ -1,16 +1,29 @@ package content.global.skill.skillcapeperks +import content.data.Quests +import content.global.skill.farming.PatchType +import content.global.skill.farming.Plantable +import content.global.skill.runecrafting.Altar +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import core.ServerStore.Companion.getInt +import core.api.closeDialogue +import core.api.getAttribute +import core.api.hasRequirement +import core.api.sendDialogue +import core.api.sendMessage +import core.api.teleport +import core.cache.def.impl.ItemDefinition import core.game.component.Component +import core.game.event.SpellbookChangeEvent +import core.game.node.entity.combat.equipment.WeaponInterface 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.GameWorld +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 enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) { BAREFISTED_SMITHING("cape_perks:barefisted-smithing"), @@ -26,32 +39,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 +74,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 +86,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 +109,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 +123,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 } } @@ -122,8 +137,15 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? player.setAttribute("/save:$attribute",true) } player.debug("Activated ${this.name}") - if(this == CONSTANT_GLOW) + if(this == CONSTANT_GLOW) { DarkZone.checkDarkArea(player) + // For Temple of Ikov - if you are in the dark basement and put on the firemaking cape with its 2009Scape light source perk, switch to the light basement. + // For the listener that teleports if you light a normal light source, see content.global.skill.crafting.lightsources.LightSourceLighter.kt + if(player.location.isInRegion(10648) && player.location.withinDistance(Location(2639,9738,0), 8)) { + teleport(player, Location.create(player.getLocation().x, player.getLocation().y + 23, player.getLocation().z)) + closeDialogue(player) + } + } } fun operate(player: Player){ @@ -141,62 +163,74 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } @Initializable - class MagicCapeDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return MagicCapeDialogue(player) - } + class MagicCapeDialogue(player : Player? = null) : core.game.dialogue.DialoguePlugin(player) { + override fun newInstance(player : Player?) : core.game.dialogue.DialoguePlugin { + return MagicCapeDialogue(player) + } - override fun open(vararg args: Any?): Boolean { - when(player.spellBookManager.spellBook){ - SpellBookManager.SpellBook.ANCIENT.interfaceId -> options("Modern","Lunar") - SpellBookManager.SpellBook.MODERN.interfaceId -> options("Ancient","Lunar") - SpellBookManager.SpellBook.LUNAR.interfaceId -> options ("Modern","Ancient") - } - return true - } + override fun open(vararg args : Any?) : Boolean { + when (player.spellBookManager.spellBook) { + SpellBookManager.SpellBook.ANCIENT.interfaceId -> options("Modern", "Lunar") + SpellBookManager.SpellBook.MODERN.interfaceId -> options("Ancient", "Lunar") + SpellBookManager.SpellBook.LUNAR.interfaceId -> options("Modern", "Ancient") + } + return true + } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - val spellbook = when(player.spellBookManager.spellBook){ - SpellBookManager.SpellBook.ANCIENT.interfaceId -> { - when(buttonId){ - 1 -> SpellBookManager.SpellBook.MODERN - 2 -> SpellBookManager.SpellBook.LUNAR - else -> null - } - } + override fun handle(interfaceId : Int, buttonId : Int) : Boolean { + val spellbook = when (player.spellBookManager.spellBook) { + SpellBookManager.SpellBook.ANCIENT.interfaceId -> { + when (buttonId) { + 1 -> SpellBookManager.SpellBook.MODERN + 2 -> SpellBookManager.SpellBook.LUNAR + else -> null + } + } - SpellBookManager.SpellBook.MODERN.interfaceId -> { - when(buttonId){ - 1 -> SpellBookManager.SpellBook.ANCIENT - 2 -> SpellBookManager.SpellBook.LUNAR - else -> null - } - } + SpellBookManager.SpellBook.MODERN.interfaceId -> { + when (buttonId) { + 1 -> SpellBookManager.SpellBook.ANCIENT + 2 -> SpellBookManager.SpellBook.LUNAR + else -> null + } + } - SpellBookManager.SpellBook.LUNAR.interfaceId -> { - when(buttonId){ - 1 -> SpellBookManager.SpellBook.MODERN - 2 -> SpellBookManager.SpellBook.ANCIENT - else -> null - } - } + SpellBookManager.SpellBook.LUNAR.interfaceId -> { + when (buttonId) { + 1 -> SpellBookManager.SpellBook.MODERN + 2 -> SpellBookManager.SpellBook.ANCIENT + else -> null + } + } - else -> null - } + else -> null + } - if(spellbook != null){ - player.spellBookManager.setSpellBook(spellbook) - player.interfaceManager.openTab(Component(spellbook.interfaceId)) - player.incrementAttribute("/save:cape_perks:librarian-magus-charges",-1) - } - end() - return true - } - - override fun getIds(): IntArray { - return intArrayOf(509871234) - } + 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 + } + val weaponInterface = player.getExtension(WeaponInterface::class.java) + if (weaponInterface != null && player.properties.autocastSpell != null) { + weaponInterface.selectAutoSpell(-1, true) + } + player.dispatch(SpellbookChangeEvent( + SpellBookManager.SpellBook.forInterface(player.spellBookManager.spellBook), + spellbook, + SpellBookManager.SpellbookChangeSource.MAGIC_CAPE_PERK)) + player.spellBookManager.setSpellBook(spellbook) + player.interfaceManager.openTab(Component(spellbook.interfaceId)) + player.incrementAttribute("/save:cape_perks:librarian-magus-charges", -1) + } + return true + } + override fun getIds() : IntArray { + return intArrayOf(509871234) + } } @Initializable @@ -206,45 +240,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..0ce93eb54 100644 --- a/Server/src/main/content/global/skill/slayer/Master.java +++ b/Server/src/main/content/global/skill/slayer/Master.java @@ -10,270 +10,240 @@ import java.util.List; /** * A non-garbage way of representing slayer masters + * Source for task amounts: ... + * Need to add a source for weights * @author ceik + * @author gregf */ public enum Master { - TURAEL(8273, 0, 0, new int[]{15, 50}, new int[]{0, 0, 0}, + TURAEL(8273, 3, 0, new int[]{15, 50}, new int[]{0, 0, 0}, new Task(Tasks.BANSHEE, 8), new Task(Tasks.BATS, 7), - new Task(Tasks.BIRDS, 6), new Task(Tasks.BEARS, 7), - new Task(Tasks.CAVE_BUG, 8), + new Task(Tasks.BIRDS, 6), + new Task(Tasks.CAVE_BUG, 8), new Task(Tasks.CAVE_CRAWLERS, 8), new Task(Tasks.CAVE_SLIMES, 8), - new Task(Tasks.COWS, 8), + new Task(Tasks.COWS, 8), new Task(Tasks.CRAWLING_HAND, 8), + new Task(Tasks.DESERT_LIZARDS, 8), new Task(Tasks.DOG, 7), new Task(Tasks.DWARF, 7), new Task(Tasks.GHOSTS, 7), new Task(Tasks.GOBLINS, 7), new Task(Tasks.ICE_FIENDS, 8), new Task(Tasks.KALPHITES, 6), - new Task(Tasks.DESERT_LIZARDS, 8), new Task(Tasks.MINOTAURS, 7), new Task(Tasks.MONKEYS, 6), - new Task(Tasks.RATS, 7), - new Task(Tasks.SCORPIONS, 7), + new Task(Tasks.SCORPIONS, 7), new Task(Tasks.SKELETONS, 7), - new Task(Tasks.SPIDERS, 6), + new Task(Tasks.SPIDERS, 6), new Task(Tasks.WOLVES, 7), new Task(Tasks.ZOMBIES, 7)), - MAZCHNA(8274, 20, 0, new int[]{30, 70}, new int[]{2, 5, 15}, + MAZCHNA(8274, 20, 0, new int[]{40, 70}, new int[]{2, 5, 15}, new Task(Tasks.BANSHEE, 8), - new Task(Tasks.BATS,7), - new Task(Tasks.BEARS,6), - new Task(Tasks.CATABLEPONS,8), - new Task(Tasks.CAVE_BUG,8), + new Task(Tasks.BATS, 7), + new Task(Tasks.BEARS, 6), + new Task(Tasks.CATABLEPONS, 8), + new Task(Tasks.CAVE_BUG, 8), new Task(Tasks.CAVE_CRAWLERS, 8), new Task(Tasks.CAVE_SLIMES, 8), new Task(Tasks.COCKATRICES, 8), new Task(Tasks.CRAWLING_HAND, 8), new Task(Tasks.DESERT_LIZARDS, 8), - new Task(Tasks.DOG,7), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.FLESH_CRAWLERS,7), - new Task(Tasks.GHOSTS,7), + new Task(Tasks.DOG, 7), + new Task(Tasks.EARTH_WARRIORS, 6), + new Task(Tasks.FLESH_CRAWLERS, 7), + new Task(Tasks.GHOSTS, 7), new Task(Tasks.GHOULS, 7), - new Task(Tasks.HILL_GIANTS,7), + new Task(Tasks.HILL_GIANTS, 7), new Task(Tasks.HOBGOBLINS, 7), new Task(Tasks.ICE_WARRIOR, 7), - new Task(Tasks.KALPHITES,6), - //new Task(Tasks.KILLERWATTS, 6), - //new Task(Tasks.MOGRES, 8), + new Task(Tasks.KALPHITES, 6), + new Task(Tasks.MOGRES, 8), new Task(Tasks.PYREFIENDS, 8), - new Task(Tasks.ROCK_SLUGS,8), - new Task(Tasks.SCORPIONS,7), - new Task(Tasks.SHADE,8), - new Task(Tasks.SKELETONS, 7), + new Task(Tasks.ROCK_SLUGS, 8), + new Task(Tasks.SHADE, 8), + new Task(Tasks.SKELETONS, 7), new Task(Tasks.VAMPIRES, 6), - //new Task(Tasks.WALL_BEASTS,7), + // new Task(Tasks.WALL_BEASTS, 7), new Task(Tasks.WOLVES, 7), - new Task(Tasks.ZOMBIES,7)), + new Task(Tasks.ZOMBIES, 7)), - VANNAKA(1597, 40, 0, new int[]{30, 80}, new int[]{4, 20, 60}, + VANNAKA(1597, 40, 0, new int[]{60, 120}, new int[]{4, 20, 60}, new Task(Tasks.ABERRANT_SPECTRES, 8), - new Task(Tasks.ABYSSAL_DEMONS, 5), - new Task(Tasks.ANKOU,7), - new Task(Tasks.BANSHEE,6), - new Task(Tasks.BASILISKS,8), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,7), - new Task(Tasks.BRINE_RATS,7), - new Task(Tasks.BRONZE_DRAGONS,7), - new Task(Tasks.CAVE_BUG,7), - new Task(Tasks.CAVE_CRAWLERS,7), - new Task(Tasks.CAVE_SLIMES,7), - new Task(Tasks.COCKATRICES,8), - new Task(Tasks.CRAWLING_HAND,6), - new Task(Tasks.CROCODILES,6), - new Task(Tasks.DAGANNOTHS, 7), - new Task(Tasks.DUST_DEVILS,8), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.ELVES, 7), - //new Task(Tasks.FEVER_SPIDERS,7), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.GARGOYLES, 5), - new Task(Tasks.GHOULS,7), - new Task(Tasks.GREEN_DRAGON,6), - new Task(Tasks.HARPIE_BUG_SWARMS,8), - new Task(Tasks.HELLHOUNDS,7), - new Task(Tasks.HILL_GIANTS,7), - new Task(Tasks.HOBGOBLINS,7), - new Task(Tasks.ICE_GIANTS,7), - new Task(Tasks.ICE_WARRIOR,7), - new Task(Tasks.INFERNAL_MAGES,8), - new Task(Tasks.JELLIES,8), + new Task(Tasks.ANKOU, 7), + new Task(Tasks.BANSHEE, 6), + new Task(Tasks.BASILISKS, 8), + new Task(Tasks.BLUE_DRAGONS, 7), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.BRINE_RATS, 7), + new Task(Tasks.CAVE_BUG, 7), + new Task(Tasks.CAVE_CRAWLERS, 7), + new Task(Tasks.CAVE_SLIMES, 7), + new Task(Tasks.COCKATRICES, 8), + new Task(Tasks.CRAWLING_HAND, 6), + new Task(Tasks.CROCODILES, 6, new Integer[]{30, 60}), + new Task(Tasks.DAGANNOTHS, 7), + new Task(Tasks.DESERT_LIZARDS, 7, new Integer[]{30, 60}), + new Task(Tasks.DUST_DEVILS, 8), + new Task(Tasks.EARTH_WARRIORS, 6, new Integer[]{30, 60}), + new Task(Tasks.ELVES, 7, new Integer[]{30, 60}), + //new Task(Tasks.FEVER_SPIDERS, 7), + new Task(Tasks.FIRE_GIANTS, 7), + new Task(Tasks.GHOULS, 7), + new Task(Tasks.GREEN_DRAGONS, 6, new Integer[]{30, 60}), + new Task(Tasks.HARPIE_BUG_SWARMS, 8), + new Task(Tasks.HELLHOUNDS, 7), + new Task(Tasks.HILL_GIANTS, 7), + new Task(Tasks.ICE_GIANTS, 7, new Integer[]{30, 60}), + new Task(Tasks.ICE_WARRIOR, 7), + new Task(Tasks.INFERNAL_MAGES, 8), + new Task(Tasks.JELLIES, 8), new Task(Tasks.JUNGLE_HORRORS, 8), - new Task(Tasks.KALPHITES,7), - //new Task(Tasks.KILLERWATTS,6), - new Task(Tasks.KURASKS,7), - new Task(Tasks.DESERT_LIZARDS,7), - new Task(Tasks.LESSER_DEMONS,7), - //new Task(Tasks.MOGRES,7), - //new Task(Tasks.MOLANISKS,7), - new Task(Tasks.MOSS_GIANTS,7), - new Task(Tasks.NECHRYAELS, 5), - new Task(Tasks.OGRES,7), - new Task(Tasks.OTHERWORDLY_BEING,8), - new Task(Tasks.PYREFIENDS,8), - new Task(Tasks.ROCK_SLUGS,7), - new Task(Tasks.SHADE,8), - //new Task(Tasks.SEA_SNAKES,6), - //new Task(Tasks.SHADOW_WARRIORS, 8), - new Task(Tasks.SPIRTUAL_MAGES,3), - new Task(Tasks.SPIRTUAL_RANGERS, 3), - new Task(Tasks.SPIRTUAL_WARRIORS,3), - //new Task(Tasks.TERROR_DOGS,6), - new Task(Tasks.TROLLS,7), + new Task(Tasks.KALPHITES, 7), + // new Task(Tasks.KILLERWATTS, 6), + new Task(Tasks.KURASKS, 7), + new Task(Tasks.LESSER_DEMONS, 7), + new Task(Tasks.MOGRES, 7), + // new Task(Tasks.MOLANISKS, 7), + new Task(Tasks.MOSS_GIANTS, 7), + new Task(Tasks.OGRES, 7), + new Task(Tasks.OTHERWORDLY_BEING, 8), + new Task(Tasks.PYREFIENDS, 8), + new Task(Tasks.ROCK_SLUGS, 7), + // new Task(Tasks.SEA_SNAKES, 6), + new Task(Tasks.SHADE, 8), + // new Task(Tasks.SHADOW_WARRIORS, 8), + new Task(Tasks.TROLLS, 7), new Task(Tasks.TUROTHS, 8), - new Task(Tasks.VAMPIRES,7), - //new Task(Tasks.WALL_BEAST,6), - new Task(Tasks.WEREWOLFS,7)), + new Task(Tasks.VAMPIRES, 7), + // new Task(Tasks.WALL_BEASTS, 6, , new Integer[]{10, 20}), + new Task(Tasks.WEREWOLVES, 7)), CHAELDAR(1598, 70, 0, new int[]{110, 170}, new int[]{10, 50, 150}, - new Task(Tasks.ABERRANT_SPECTRES,8), - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.AVIANSIES,9), + new Task(Tasks.ABERRANT_SPECTRES, 8), + new Task(Tasks.ABYSSAL_DEMONS, 12), new Task(Tasks.BANSHEE, 5), - new Task(Tasks.BASILISKS,7), - new Task(Tasks.BLACK_DEMONS,10), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,8), - new Task(Tasks.BRINE_RATS,7), - new Task(Tasks.BRONZE_DRAGONS,11), - new Task(Tasks.CAVE_CRAWLERS, 5), - new Task(Tasks.CAVE_HORRORS,10), - new Task(Tasks.CAVE_SLIMES,6), - new Task(Tasks.COCKATRICES,6), - new Task(Tasks.DAGANNOTHS,11), - new Task(Tasks.DUST_DEVILS,9), - new Task(Tasks.ELVES,8), - //new Task(Tasks.FEVER_SPIDERS,7), + new Task(Tasks.BASILISKS, 7), + new Task(Tasks.BLUE_DRAGONS, 8), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.BRINE_RATS, 7), + new Task(Tasks.BRONZE_DRAGONS, 11, new Integer[]{30, 60}), + new Task(Tasks.CAVE_BUG, 5), + new Task(Tasks.CAVE_CRAWLERS, 5), + new Task(Tasks.CAVE_HORRORS, 10), + new Task(Tasks.CAVE_SLIMES, 6), + new Task(Tasks.COCKATRICES, 6), + new Task(Tasks.CRAWLING_HAND, 5), + new Task(Tasks.CROCODILES, 5, new Integer[]{30, 60}), + new Task(Tasks.DAGANNOTHS, 11), + new Task(Tasks.DESERT_LIZARDS, 5, new Integer[]{30, 60}), + new Task(Tasks.DUST_DEVILS, 9), + new Task(Tasks.ELVES, 8, new Integer[]{60, 90}), + //new Task(Tasks.FEVER_SPIDERS, 7), new Task(Tasks.FIRE_GIANTS, 12), - new Task(Tasks.GARGOYLES,11), - new Task(Tasks.GREATER_DEMONS,9), - new Task(Tasks.HARPIE_BUG_SWARMS,6), - new Task(Tasks.HELLHOUNDS,9), - new Task(Tasks.INFERNAL_MAGES,7), - new Task(Tasks.IRON_DRAGONS,12), + new Task(Tasks.GARGOYLES, 11), + new Task(Tasks.GREATER_DEMONS, 9), + new Task(Tasks.HARPIE_BUG_SWARMS, 6), + new Task(Tasks.HELLHOUNDS, 9), + new Task(Tasks.IRON_DRAGONS, 12, new Integer[]{30, 60}), + new Task(Tasks.INFERNAL_MAGES, 7), new Task(Tasks.JELLIES, 10), - new Task(Tasks.JUNGLE_HORRORS,10), - new Task(Tasks.KALPHITES,11), + new Task(Tasks.JUNGLE_HORRORS, 10), + new Task(Tasks.KALPHITES, 11), new Task(Tasks.KURASKS, 12), - new Task(Tasks.LESSER_DEMONS,9), - new Task(Tasks.DESERT_LIZARDS, 5), - //new Task(Tasks.MOGRES,6), - //new Task(Tasks.MOLANISKS,6), - //new Task(Tasks.MUTATED_ZYGOMITES,7), + new Task(Tasks.LESSER_DEMONS, 9), + new Task(Tasks.MOGRES, 6), + // new Task(Tasks.MOLANISKS, 6), + //new Task(Tasks.MUTATED_ZYGOMITES, 7, new Integer[]{30, 60}), new Task(Tasks.NECHRYAELS, 12), - new Task(Tasks.PYREFIENDS,6), + new Task(Tasks.PYREFIENDS, 6), new Task(Tasks.ROCK_SLUGS, 5), - //new Task(Tasks.SHADOW_WARRIORS,8), - new Task(Tasks.SKELETAL_WYVERN,7), - new Task(Tasks.SPIRTUAL_WARRIORS,4), - new Task(Tasks.SPIRTUAL_RANGERS,4), - new Task(Tasks.SPIRTUAL_MAGES,4), - new Task(Tasks.STEEL_DRAGONS,9), - new Task(Tasks.TROLLS,11), + // new Task(Tasks.SHADOW_WARRIORS, 8), + new Task(Tasks.SPIRTUAL_WARRIORS, 4), + new Task(Tasks.SPIRTUAL_RANGERS, 4), + new Task(Tasks.SPIRTUAL_MAGES, 4), + new Task(Tasks.TROLLS, 11), new Task(Tasks.TUROTHS, 10)), - //new Task(Tasks.WALL_BEASTS,6), + // new Task(Tasks.WALL_BEASTS, 6, new Integer[]{10, 20}), + // new Task(Tasks.WARPED_TERROR_BIRD, 5), + // new Task(Tasks.WARPED_TORTOISE, 5) - SUMONA(7780, 90, 35, new int[]{50, 185}, new int[]{12, 60, 180}, + SUMONA(7780, 85, 35, new int[]{120, 185}, new int[]{12, 60, 180}, new Task(Tasks.ABERRANT_SPECTRES, 15), new Task(Tasks.ABYSSAL_DEMONS, 10), - new Task(Tasks.AVIANSIES, 7), new Task(Tasks.BANSHEE, 15), new Task(Tasks.BASILISKS, 15), new Task(Tasks.BLACK_DEMONS, 10), - new Task(Tasks.BLOODVELDS, 10), new Task(Tasks.BLUE_DRAGONS, 5), + new Task(Tasks.BLOODVELDS, 10), new Task(Tasks.CAVE_CRAWLERS, 15), new Task(Tasks.CAVE_HORRORS, 15), - new Task(Tasks.CROCODILES, 4), new Task(Tasks.DAGANNOTHS, 10), - new Task(Tasks.DESERT_LIZARDS, 4), new Task(Tasks.DUST_DEVILS, 15), - new Task(Tasks.ELVES, 10), + new Task(Tasks.ELVES, 10, new Integer[]{60, 90}), new Task(Tasks.FIRE_GIANTS, 10), new Task(Tasks.GARGOYLES, 10), new Task(Tasks.GREATER_DEMONS, 10), new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.IRON_DRAGONS, 7), + new Task(Tasks.IRON_DRAGONS, 7, new Integer[]{30, 60}), new Task(Tasks.KALPHITES, 10), new Task(Tasks.KURASKS, 15), new Task(Tasks.NECHRYAELS, 10), - // newTask(Tasks.RED_DRAGON, 5), - new Task(Tasks.SCORPIONS, 4), + new Task(Tasks.RED_DRAGONS, 5, new Integer[]{30, 60}), + // new Task(Tasks.SCABARITES, 9, new Integer[]{30, 60}), new Task(Tasks.SPIRTUAL_MAGES, 10), new Task(Tasks.SPIRTUAL_WARRIORS, 10), - // new Task(Tasks.TERROR_DOGS, 10), + // new Task(Tasks.TERROR_DOGS, 10, new Integer[]{30, 60}), new Task(Tasks.TROLLS, 10), - new Task(Tasks.TUROTHS, 15), - new Task(Tasks.VAMPIRES, 10)), - //new Task(Tasks.WARPED_TORTOISE, 15)), + new Task(Tasks.TUROTHS, 15)), + // new Task(Tasks.WARPED_TORTOISE, 15)), - DURADEL(8275, 100, 50, new int[]{50, 199}, new int[]{15, 75, 225}, - new Task(Tasks.ABERRANT_SPECTRES,7), - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.ANKOU,5), - new Task(Tasks.AVIANSIES,8), - new Task(Tasks.BLACK_DEMONS,8), - new Task(Tasks.BLACK_DRAGONS,9), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.BLUE_DRAGONS,4), - new Task(Tasks.CAVE_HORRORS,4), - new Task(Tasks.DAGANNOTHS,9), - new Task(Tasks.DARK_BEASTS,11), - new Task(Tasks.DUST_DEVILS,5), - new Task(Tasks.ELVES,4), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.GARGOYLES,8), - new Task(Tasks.GREATER_DEMONS,9), + DURADEL(8275, 100, 50, new int[]{130, 200}, new int[]{15, 75, 225}, + new Task(Tasks.ABERRANT_SPECTRES, 7), + new Task(Tasks.ABYSSAL_DEMONS, 12), + new Task(Tasks.BLACK_DEMONS, 8), + new Task(Tasks.BLACK_DRAGONS, 9, new Integer[]{40, 80}), + new Task(Tasks.BLOODVELDS, 8), + new Task(Tasks.DAGANNOTHS, 9), + new Task(Tasks.DARK_BEASTS, 11), + new Task(Tasks.DUST_DEVILS, 5), + new Task(Tasks.FIRE_GIANTS, 7), + new Task(Tasks.GARGOYLES, 8), + new Task(Tasks.GORAKS, 9, new Integer[]{40, 80}), + new Task(Tasks.GREATER_DEMONS, 9), new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.IRON_DRAGONS,5), - new Task(Tasks.KALPHITES,9), - new Task(Tasks.KURASKS,4), - new Task(Tasks.MITHRIL_DRAGON,9), - new Task(Tasks.NECHRYAELS,9), - //new Task(Tasks.RED_DRAGONS,8), - new Task(Tasks.SKELETAL_WYVERN,7), - new Task(Tasks.SPIRTUAL_MAGES,2), - new Task(Tasks.SPIRTUAL_RANGERS,2), - new Task(Tasks.SPIRTUAL_WARRIORS,2), - new Task(Tasks.STEEL_DRAGONS,7), - //new Task(Tasks.SUQAHS,8), - new Task(Tasks.TROLLS,6), - new Task(Tasks.TZHAAR, 10), - new Task(Tasks.VAMPIRES,8), - // new Task(Tasks.WARPED_TERRORBIRD,8), - new Task(Tasks.WATERFIENDS,2)); - //new Task(Tasks.MUTATED_ZYGOMITES,2), - //Boss Tasks below this point - Crash - //new Task(Tasks.JAD, 1), - //new Task(Tasks.COMMANDER_ZILYANA,1), - //new Task(Tasks.CHAOS_ELEMENTAL, 1), - //new Task(Tasks.GENERAL_GRARDOOR,1), - //new Task(Tasks.GIANT_MOLE,1), - //new Task(Tasks.KING_BLACK_DRAGON,1), - //new Task(Tasks.KRIL_TSUTSAROTH,1), - //new Task(Tasks.KREE_ARRA,1)); + new Task(Tasks.IRON_DRAGONS, 5, new Integer[]{40, 80}), + new Task(Tasks.KALPHITES, 9), + new Task(Tasks.MITHRIL_DRAGONS, 9, new Integer[]{4, 8}), + new Task(Tasks.NECHRYAELS, 9), + // new Task(Tasks.SCABARITES, 9, new Integer[]{40, 80}), + new Task(Tasks.SKELETAL_WYVERN, 7, new Integer[]{40, 80}), + new Task(Tasks.SPIRTUAL_MAGES, 2), + new Task(Tasks.STEEL_DRAGONS, 7, new Integer[]{40, 80}), + new Task(Tasks.SUQAHS, 8, new Integer[]{40, 80}), + // new Task(Tasks.WARPED_TERROR_BIRD, 8), + new Task(Tasks.WATERFIENDS, 2)); - private static HashMap idMap = new HashMap<>(); + private static final HashMap idMap = new HashMap<>(); static{ Arrays.stream(Master.values()).forEach(m -> idMap.putIfAbsent(m.npc_id, m)); } - int npc_id,required_combat,required_slayer; - public int[] assignment_range; - int[] streakPoints; - public List tasks; - Master(int npc_id, int required_combat, int required_slayer, int[] assignment_range, int[] streakPoints, Task... tasks) { + final int npc_id; + final int required_combat; + final int required_slayer; + public final int[] default_assignment_range; + final int[] streakPoints; + public final List tasks; + Master(int npc_id, int required_combat, int required_slayer, int[] default_assignment_range, int[] streakPoints, Task... tasks) { this.npc_id = npc_id; this.required_combat = required_combat; this.required_slayer = required_slayer; - this.assignment_range = assignment_range; + this.default_assignment_range = default_assignment_range; this.streakPoints = streakPoints; this.tasks = new ArrayList<>(Arrays.asList(tasks)); } @@ -301,9 +271,17 @@ public enum Master { public static class Task{ public Tasks task; public Integer weight; - Task(Tasks task, Integer weight){ + public Integer[] task_range; + public Task(Tasks task, Integer weight){ this.task = task; this.weight = weight; + this.task_range = new Integer[]{null, null}; + } + + Task(Tasks task, Integer weight, Integer[] task_range){ + this.task = task; + this.weight = weight; + this.task_range = task_range; } } -} \ 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..0f95eb9cb 100644 --- a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java +++ b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java @@ -1,8 +1,7 @@ package content.global.skill.slayer; import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.*; import core.game.node.entity.combat.equipment.SwitchAttack; import content.global.handlers.item.equipment.special.DragonfireSwingHandler; import core.game.node.entity.impl.Animator.Priority; @@ -13,8 +12,6 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.tools.RandomFunction; -import core.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.MultiSwingHandler; /** * Handles a mithril dragon npc. @@ -23,15 +20,23 @@ import core.game.node.entity.combat.MultiSwingHandler; @Initializable public final class MithrilDragonNPC extends AbstractNPC { - /** - * The dragonfire attack. - */ - private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), Graphics.create(1), null, null); - /** * Handles the combat. */ - private final CombatSwingHandler combatAction = new MultiSwingHandler(true, new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)), new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 20, 20, 41, 40, 18, 255)), DRAGONFIRE, new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 20, 20, 41, 40, 18, 255))); + private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), Graphics.create(1), null, null); + private static final SwitchAttack MELEE = new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(80, Priority.HIGH)); + private static final SwitchAttack MAGIC = new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 20, 20, 41, 40, 18, 255)); + private static class InRangeSwitchAttack extends SwitchAttack { + public InRangeSwitchAttack(CombatSwingHandler swingHandler, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { + super(swingHandler, animation, startGraphic, endGraphic, projectile); + } + @Override + public boolean canSelect(Entity entity, Entity victim, BattleState state) { + return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim) == InteractionType.NO_INTERACT; //only select range if not in melee distance + } + }; + private static final SwitchAttack RANGE = new InRangeSwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 20, 20, 41, 40, 18, 255)); + private final CombatSwingHandler combatAction = new MultiSwingHandler(true, MELEE, MAGIC, DRAGONFIRE, RANGE); /** * Constructs a new {@code MithrilDragonNPC} {@code Object}. @@ -92,6 +97,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..5542a7777 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerManager.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerManager.kt @@ -1,19 +1,17 @@ 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 /** * Manages the players slayer data. @@ -105,7 +103,12 @@ class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, val flags = slayer.flags if (slayer.hasTask() && npc.id in slayer.task!!.npcs) { - rewardXP(player, Skills.SLAYER, npc.skills.maximumLifepoints.toDouble()) + var xp = 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 +173,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 +209,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 +248,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..c8a57c44a 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java +++ b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java @@ -15,7 +15,9 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; + import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** * Represents the dialogue plugin used for a slayer master. @@ -40,11 +42,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { */ private static final Item HOLY_SYMBOL = new Item(1718); - /** - * Represents the items to use. - */ - private static final Item[] ITEMS = new Item[]{new Item(9813), new Item(9814)}; - /** * Represents the coins item. */ @@ -67,8 +64,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { private final int level = 2; - private int rerolls = 0; - /** * Constructs a new {@code SlayerMasterDialogue} {@code Object}. */ @@ -98,7 +93,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { npc = (NPC) args[0]; } master = Master.forId(args[0] instanceof NPC ? ((NPC) args[0]).getId() : (int) args[0]); - quest = player.getQuestRepository().getQuest("Animal Magnetism"); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); if (master == Master.DURADEL) { if (Skillcape.isMaster(player, Skills.SLAYER)) { @@ -118,7 +113,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - rerolls = ServerStore.getInt(getStoreFile(), player.getUsername().toLowerCase()); if (isDiary) { switch (stage) { case 999: @@ -335,7 +329,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 +343,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { if (player.getInventory().freeSlots() != 0) { player.getInventory().add(GEM); SlayerManager.getInstance(player).generate(master); - interpreter.sendDialogues(master.getNpc(), getExpression(master), "We'll start you off hunting " + SlayerManager.getInstance(player).getTaskName() + "'s, you'll need to", "kill " + SlayerManager.getInstance(player).getAmount() + " of them."); + interpreter.sendDialogues(master.getNpc(), getExpression(master), "We'll start you off hunting " + SlayerUtils.pluralise(SlayerManager.getInstance(player).getTaskName()) + ", you'll need to", "kill " + SlayerManager.getInstance(player).getAmount() + " of them."); stage = 510; } else if (player.getInventory().freeSlots() == 0) { player("Sorry, I don't have enough inventory space."); @@ -477,39 +471,22 @@ 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()) { + boolean hasTask = SlayerManager.getInstance(player).hasTask(); + boolean reroll = hasTask && master == Master.TURAEL && !Master.hasSameTask(master, player); + if (!hasTask || reroll) { SlayerManager.getInstance(player).generate(master); - if (SlayerManager.getInstance(player).getTask() == Tasks.JAD) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to", "defeat the almighty TzTok-Jad."); - } else { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerManager.getInstance(player).getTaskName() + "s."); - } + interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerUtils.pluralise(SlayerManager.getInstance(player).getTaskName()) + "."); stage = 844; break; } - if (Master.hasSameTask(master, player)) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "You're still hunting something. But let me check something..."); - stage = 847; - } else { - SlayerManager.getInstance(player).flags.setTaskStreak(0); - SlayerManager.getInstance(player).generate(master); - if (SlayerManager.getInstance(player).getTask() == Tasks.JAD) { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to", "defeat the almighty TzTok-Jad."); - } else { - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to kill", "" + SlayerManager.getInstance(player).getAmount() + " " + SlayerManager.getInstance(player).getTaskName() + "'s."); - } - stage = 844; - } + interpreter.sendDialogues(master.getNpc(), getExpression(master), "You're still hunting something. Come back when you've","finished your task."); + stage = END_DIALOGUE; break; case 844: - if (GameWorld.getSettings().getAllow_slayer_reroll()) { - options("Got any tips for me?", "Okay, great!", "I'd like to re-roll that task."); - } else { - options("Got any tips for me?", "Okay, great!"); - } + options("Got any tips for me?", "Okay, great!"); stage++; break; case 845: @@ -522,47 +499,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { player("Okay, great!"); stage = 999; break; - case 3: - player("I'd like to re-roll this task."); - if(rerolls == 10){ - stage++; - } else { - SlayerManager.getInstance(player).clear(); - getStoreFile().put(player.getUsername().toLowerCase(), rerolls + 1); - stage = 701; - } - } - break; - case 846: - npcl(FacialExpression.NEUTRAL, "Actually, you're out of free rerolls. You can buy a reroll from my reward store, though."); - stage = END_DIALOGUE; - break; - case 847: - if(rerolls < 10){ - npcl(FacialExpression.NEUTRAL, "You do have " + (10 - rerolls) + " rerolls left today, would you like to use one?"); - stage++; - } - else { - npcl(FacialExpression.NEUTRAL, "And it also seems you're out of rerolls for today. That's unfortunate."); - stage = END_DIALOGUE; - } - break; - case 848: - options("Yes, please.", "No, thanks."); - stage++; - break; - case 849: - switch(buttonId){ - case 1: - playerl(FacialExpression.FRIENDLY, "Yes, please."); - SlayerManager.getInstance(player).clear(); - getStoreFile().put(player.getUsername().toLowerCase(), rerolls + 1); - stage = 701; - break; - case 2: - playerl(FacialExpression.NEUTRAL, "No, thanks."); - stage = END_DIALOGUE; - break; } break; case 860: @@ -606,56 +542,6 @@ public final class SlayerMasterDialogue extends DialoguePlugin { } stage = 999; break; - case 906: - switch (buttonId) { - case 1: - player("May I buy a Quest Point cape?"); - stage = 907; - break; - case 2: - interpreter.sendDialogues(master.getNpc(), FacialExpression.HALF_GUILTY, "'Ello, and what are you after, then?"); - stage = 0; - break; - } - break; - case 907: - npc("You bet, " + player.getUsername() + "! Right when you give me 99000 coins."); - stage = 908; - break; - case 908: - options("Okay, here you go.", "No, thanks."); - stage = 909; - break; - case 909: - switch (buttonId) { - case 1: - player("Okay, here you go."); - stage = 910; - break; - case 2: - end(); - break; - } - break; - case 910: - if (player.getInventory().freeSlots() < 2) { - player("I don't seem to have enough inventory space."); - stage = 999; - return true; - } - if (!player.getInventory().containsItem(COINS)) { - player("I don't seem to have enough coins with", "me at this time."); - stage = 999; - return true; - } - if (player.getInventory().remove(COINS) && player.getInventory().add(ITEMS)) { - npc("Have fun with it."); - stage = 999; - } else { - player("I don't seem to have enough coins with", "me at this time."); - stage = 999; - } - break; } return true; } @@ -696,9 +582,4 @@ public final class SlayerMasterDialogue extends DialoguePlugin { public int[] getIds() { return new int[]{70, 1598, 1596, 1597, 1599, 7780, 8275, 8273, 8274, 8649}; } - - private JSONObject getStoreFile() { - return ServerStore.getArchive("daily-slayer-rerolls"); - } - } diff --git a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java index ec6940b15..eb02797d6 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java @@ -1,7 +1,6 @@ package content.global.skill.slayer; import core.cache.def.impl.SceneryDefinition; -import core.game.global.action.ClimbActionHandler; import core.game.global.action.DigAction; import core.game.global.action.DigSpadeHandler; import core.game.interaction.OptionHandler; @@ -11,6 +10,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,15 +27,12 @@ public class SlayerPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(8783).getHandlers().put("option:open", this); SceneryDefinition.forId(8785).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(23158).getHandlers().put("option:exit", this); SceneryDefinition.forId(23157).getHandlers().put("option:exit", this); SceneryDefinition.forId(15767).getHandlers().put("option:enter", this); SceneryDefinition.forId(15811).getHandlers().put("option:exit", this); SceneryDefinition.forId(15812).getHandlers().put("option:exit", this); - SceneryDefinition.forId(96).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(35121).getHandlers().put("option:climb-down", this); for (Location loc : BRYNE_DIGS) { DigSpadeHandler.register(loc, new DigAction() { @Override @@ -50,29 +49,26 @@ public class SlayerPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { switch (node.getId()) { - case 8783: - player.teleport(new Location(2044, 4649, 0)); - break; case 8785: + // Outside of trap door in East Ardy player.teleport(new Location(2543, 3327, 0)); break; case 23158: case 23157: + // Outside brine rat cave player.teleport(new Location(2729, 3733, 0)); break; case 15767: + if (!hasRequirement(player, Quests.CABIN_FEVER)) + return true; player.teleport(new Location(3748, 9373, 0)); + // Cave horrors - inside of cave break; case 15811: case 15812: + // Cave horrors - outside of cave player.teleport(new Location(3749, 2973, 0)); break; - case 96: - ClimbActionHandler.climb(player, null, new Location(2649, 9804, 0)); - break; - case 35121: - ClimbActionHandler.climb(player, null, new Location(2641, 9763, 0)); - break; } return true; } @@ -80,11 +76,9 @@ public class SlayerPlugin extends OptionHandler { @Override public Location getDestination(Node node, Node n) { if (n.getId() == 23158 || n.getId() == 23157) { + // Inside brine rate cave return new Location(2690, 10124, 0); } - if (n.getId() == 96) { - return new Location(2641, 9763, 0); - } return null; } diff --git a/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java index 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/SlayerTowerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java index aa8f0e38f..682ed5bdf 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java @@ -39,6 +39,8 @@ public final class SlayerTowerPlugin extends OptionHandler { SceneryDefinition.forId(4490).getHandlers().put("option:open", this); SceneryDefinition.forId(4487).getHandlers().put("option:open", this); SceneryDefinition.forId(4492).getHandlers().put("option:close", this); + SceneryDefinition.forId(10527).getHandlers().put("option:open", this); + SceneryDefinition.forId(10529).getHandlers().put("option:open", this); return this; } @@ -47,6 +49,8 @@ public final class SlayerTowerPlugin extends OptionHandler { switch (node.getId()) { case 4490: case 4487: + case 10527: + case 10529: DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); switchStatue(); 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..15b905dba 100644 --- a/Server/src/main/content/global/skill/slayer/Tasks.java +++ b/Server/src/main/content/global/skill/slayer/Tasks.java @@ -5,103 +5,123 @@ 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), + 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), + 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), + 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), + ; static final HashMap taskMap = new HashMap<>(); static{ @@ -111,23 +131,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 +159,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..a1d216746 --- /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(302, Priority.HIGH), // 299 = attack animation, 302 = idle animation. (authentic bug) + null, + null, + Projectile.create( + null as Entity?, + null, + 500, + 15, + 30, + 50, + 50, + 14, + 255 + ) + ), + SwitchAttack( + CombatStyle.RANGE.swingHandler, + Animation(302, Priority.HIGH), // 299 = attack animation, 302 = idle animation. (authentic bug) + 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/AncientCavern.java b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java index 7e8e55cd4..f86de623f 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java @@ -152,7 +152,7 @@ public final class AncientCavern extends MapZone implements Plugin { } }); - player.getInterfaceManager().open(c); + player.getInterfaceManager().openOverlay(c); break; case 3: PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); diff --git a/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java index c47cfff3c..af34faff2 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java @@ -28,7 +28,6 @@ import java.util.Map; * Handles the lumbridge dungeon. * @author Vexia */ -@Initializable public final class LumbridgeDungeon extends MapZone implements Plugin { /** diff --git a/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java index edce0a909..3dec3dd92 100644 --- a/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java @@ -132,7 +132,7 @@ public final class SmokeDungeon extends MapZone implements Plugin { * @param player the player. */ private static void effect(Player player) { - int hit = 2; + int hit = 10; setDelay(player); if (RandomFunction.random(2) == 1) { player.sendChat(CHATS[RandomFunction.random(CHATS.length)]); diff --git a/Server/src/main/content/global/skill/slayer/unused-tasks b/Server/src/main/content/global/skill/slayer/unused-tasks deleted file mode 100644 index 03501f20e..000000000 --- a/Server/src/main/content/global/skill/slayer/unused-tasks +++ /dev/null @@ -1,92 +0,0 @@ - /*WALL_BEASTS(new Task(new int[] { 7823 }, new String[] { "A spiny helmet or equivalent is required to start fighting one " }, 35, new Master[] { Master.MAZCHNA }, false, Equipment.SPINY_HELMET) { - @Override - public boolean isDisabled() { - return true; - } - }), - WARPED_TERRORBIRDS(new Task(new int[] { 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6322, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, 6331, 6332, }, new String[] { "They require 56 Slayer and a Crystal chime to be killed. " }, 56, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - WARPED_TORTOISES(new Task(new int[] { 6296, 6297 }, new String[] { "A warped tortoise is not as slow as they look." }, 56, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*CERBERUS(new Task(new int[] {8632, 8634}, new String[] {"Cerberus is about the best guard dog there is!"}, 91, new Master[] {Master.VANNAKA, Master.CHAELDAR, Master.NIEVE, Master.DURADEL}, false, 20 | 25 << 16) { - @Override - public boolean isDisabled() { - return true; - } - })*/ -/*ZYGOMITES(new Task(new int[] { 3346, 3347 }, new String[] { "Mutated zygomites are monsters that appear as Fungi until provoked." }, 57, new Master[] { Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*SUQAHS(new Task(new int[] { 4527, 4528, 4529, 4530, 4531, 4532, 4533 }, new String[] {}, 1, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - TERROR_DOG(new Task(new int[] { 5417, 5418 }, new String[] { "Terror Dogs are much like Wolves, they are pack creatures which will hunt in groups." }, 1, new Master[] { Master.VANNAKA, Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*SHADOW_WARRIORS(new Task(new int[] { 158 }, new String[] { "Shadow warriors are dark and mysterious, they hide in the shadows so be wary of ambushes." }, 1, new Master[] { Master.VANNAKA, Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -//MOGRES(new Task(new int[] { 114 }, new String[] { "Mogres are Slayer monsters and require level 32 Slayer to kill. " }, 32, new Master[] { Master.MAZCHNA, Master.VANNAKA }, false)), - /*MOLANISKS(new Task(new int[] { 5751 }, new String[] { "Molanisks are found attached to cave walls and must be lured", "off with a Slayer bell" }, 39, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - - /*RED_DRAGONS(new Task(new int[] { 53, 4669, 4670, 4671, 4672, 1589, 3588, 4667, 4668, }, new String[] { "Red dragons aren't as strong as other dragons but they're still", "very powerful, watch out for their firey breath." }, 1, new Master[] { Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*SCABARITES(new Task(new int[] { 2009 }, new String[] { "The Scabarites are fairly strong monsters, and can hit pretty hard." }, 1, new Master[] { Master.CHAELDAR }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*SEA_SNAKES(new Task(new int[] { 3943, 3939, }, new String[] { "They are quick, make sure you don't let your", "eye off of them for 1 second." }, 1, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ -/*ELVES(new Task(new int[] { 1184, 1183, 1185 }, new String[] { "Elves are quick, agile and vicious fighters which", "often favour bows and polearms." }, 1, new Master[] { Master.VANNAKA, Master.CHAELDAR, Master.NIEVE }, false) { - @Override - public boolean isDisabled() { - return true; - } - }), - FEVER_SPIDERS(new Task(new int[] { 2850 }, new String[] { "Fever Spiders are giant spiders that carry the deadly Spider Fever.", "If you don't want to catch it I suggest you wear Slayer Gloves to fight them." }, 1, new Master[] { Master.CHAELDAR, Master.DURADEL }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ - /*KILLERWATTS(new Task(new int[] { 3201, 3202 }, new String[] { "Vexia halfassed this... (this was shadow knight text)" }, 37, new Master[] { Master.VANNAKA }, false) { - @Override - public boolean isDisabled() { - return true; - } - }),*/ \ No newline at end of file diff --git a/Server/src/main/content/global/skill/smithing/Bars.java b/Server/src/main/content/global/skill/smithing/Bars.java index 2a8b9f493..7514693fe 100644 --- a/Server/src/main/content/global/skill/smithing/Bars.java +++ b/Server/src/main/content/global/skill/smithing/Bars.java @@ -360,8 +360,6 @@ public enum Bars { /** * Steel Lantern */ - STEEL_LANTERN(BarType.STEEL, SmithingType.TYPE_LANTERN, 4527, 49), - STEEL_BULLSEYE(BarType.STEEL, SmithingType.TYPE_BULLSEYE, 4544, 49), /** diff --git a/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java b/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java index 84d4e7edf..64833097c 100644 --- a/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java +++ b/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java @@ -1,55 +1,23 @@ package content.global.skill.smithing; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.api.Container; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; -import core.game.node.item.Item; import core.plugin.Initializable; -import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Represents the dialogue plugin used for making a dragon shield. * @author 'Vexia - * @version 1.0 + * @author Player Name + * @version 1.1 */ @Initializable public final class DragonShieldDialogue extends DialoguePlugin { - - /** - * Represents the item shield parts. - */ - private static final Item[] SHIELD_PARTS = new Item[] { new Item(2366), new Item(2368) }; - - /** - * Represents th edraconic visage item. - */ - private static final Item DRACONIC_VISAGE = new Item(11286); - - /** - * Represents the anti dragon fire shield. - */ - private static final Item ANTI_DRAGONSHIELD = new Item(1540); - - /** - * Represents the dragon fire shield item. - */ - private static final Item DRAGON_FIRESHIELD = new Item(11284); - - /** - * Represents the shield item. - */ - private static final Item SQ_SHIELD = new Item(1187); - - /** - * Represents the hammering animation. - */ - private static Animation ANIMATION = new Animation(898); - - /** - * Represents the shield type. - */ - private int type; - /** * Constructs a new {@code DragonShieldDialogue} {@code Object}. */ @@ -74,11 +42,26 @@ public final class DragonShieldDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - type = (int) args[0]; + if (!inInventory(player, Items.HAMMER_2347, 1) && !SkillcapePerks.isActive(SkillcapePerks.BAREFISTED_SMITHING, player)) { + interpreter.sendDialogue("You need a hammer to work the metal with."); + } + int type = (int) args[0]; if (type == 1) { - interpreter.sendDialogue("You set to work trying to fix the ancient shield. It's seen some", "heavy reward and needs some serious work doing to it."); + if (!(inInventory(player, Items.SHIELD_RIGHT_HALF_2368, 1) && inInventory(player, Items.SHIELD_LEFT_HALF_2366, 1))) { + interpreter.sendDialogue("You need the other half of the shield."); //todo authentic message + return false; + } + interpreter.sendDialogue("You set to work trying to fix the ancient shield. It's seen some", "heavy action and needs some serious work doing to it."); stage = 0; } else { + if (!inInventory(player, Items.ANTI_DRAGON_SHIELD_1540, 1)) { + interpreter.sendDialogue("You need an anti-dragon shield to attach the visage to."); //todo authentic message + return false; + } + if (!inInventory(player, Items.DRACONIC_VISAGE_11286, 1)) { + interpreter.sendDialogue("You don't have anything you could attach to the shield."); //todo authentic message + return false; + } interpreter.sendDialogue("You set to work, trying to attach the ancient draconic", "visage to your anti-dragonbreath shield. It's not easy to", "work with the ancient artifact and it takes all of your", "skills as a master smith."); stage = 10; } @@ -89,26 +72,26 @@ public final class DragonShieldDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - player.lock(5); - player.animate(ANIMATION); - interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have restored the dragon square shield to", "its former glory."); - if (player.getInventory().remove(SHIELD_PARTS)) { - player.getInventory().add(SQ_SHIELD); + lock(player, 5); + animate(player, 898, false); + if (removeItem(player, Items.SHIELD_RIGHT_HALF_2368, Container.INVENTORY) && removeItem(player, Items.SHIELD_LEFT_HALF_2366, Container.INVENTORY)) { + interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have restored the dragon square shield to", "its former glory."); + addItem(player, Items.DRAGON_SQ_SHIELD_1187, 1, Container.INVENTORY); + rewardXP(player, Skills.SMITHING, 75); } - player.getSkills().addExperience(Skills.SMITHING, 75, true); stage = 1; break; case 1: end(); break; case 10: - player.lock(5); - player.animate(ANIMATION); - interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have crafted the", "draconic visage and anti-dragonbreath shield into a", "dragonfire shield."); - if (player.getInventory().remove(DRACONIC_VISAGE, ANTI_DRAGONSHIELD)) { - player.getInventory().add(DRAGON_FIRESHIELD); + lock(player, 5); + animate(player, 898, false); + if (removeItem(player, Items.ANTI_DRAGON_SHIELD_1540, Container.INVENTORY) && removeItem(player, Items.DRACONIC_VISAGE_11286, Container.INVENTORY)) { + interpreter.sendDialogue("Even for an experienced armourer it is not an easy task, but", "eventually it is ready. You have crafted the", "draconic visage and anti-dragonbreath shield into a", "dragonfire shield."); + addItem(player, Items.DRAGONFIRE_SHIELD_11284, 1, Container.INVENTORY); + rewardXP(player, Skills.SMITHING, 2000); } - player.getSkills().addExperience(Skills.SMITHING, 2000); stage = 1; break; } diff --git a/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java index 1468549d2..be0190327 100644 --- a/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java +++ b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java @@ -19,6 +19,7 @@ import core.plugin.Plugin; import java.util.ArrayList; import java.util.List; +import content.data.Quests; /** * Represents the plugin used for the furnace. @@ -64,7 +65,7 @@ public final class FurnaceOptionPlugin extends OptionHandler { private static void show(final Player player) { player.getInterfaceManager().openChatbox(311); player.getPacketDispatch().sendItemZoomOnInterface(2349, 150, 311, 4); - if (player.getQuestRepository().isComplete("The Knight's Sword")) { + if (player.getQuestRepository().isComplete(Quests.THE_KNIGHTS_SWORD)) { player.getPacketDispatch().sendString("



Blurite", 311, 20); } player.getPacketDispatch().sendItemZoomOnInterface(Bar.BLURITE.getProduct().getId(), 150, 311, 5); @@ -77,26 +78,6 @@ public final class FurnaceOptionPlugin extends OptionHandler { player.getPacketDispatch().sendItemZoomOnInterface(2363, 150, 311, 12); } - /** - * Method used to handle the tutorial island interaction. - * @param player the player. - */ - private final void handleTutorialIsland(final Player player) { - if (player.getInventory().containItems(438, 436)) { - player.animate(ANIMATION); - GameWorld.getPulser().submit(new Pulse(2, player) { - @Override - public boolean pulse() { - player.getInventory().remove(ITEMS); - player.getInventory().add(Bar.BRONZE.getProduct()); - player.getSkills().addExperience(Skills.SMITHING, Bar.BRONZE.getExperience()); - return true; - } - - }); - } - } - /** * Represents the plugin used to handle the ore on the furance. * @author 'Vexia diff --git a/Server/src/main/content/global/skill/smithing/SmithingBuilder.java b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java index 3638be844..6159f161b 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingBuilder.java +++ b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java @@ -41,7 +41,41 @@ public final class SmithingBuilder { public void build(Player player) { player.getGameAttributes().removeAttribute("smith-type"); player.getGameAttributes().setAttribute("smith-type", type); - player.getPacketDispatch().sendInterfaceConfig(300, 267, false);// pickaxe + if (type.name().equals("BLURITE")) { + // interface 300 spawns with most things already there. Hide everything except what we want + int[] values = { + 17, // dagger + 25, // axe + 33, // mace + 41, // med helm + // show this 49, // bolts + 57, // sword + 65, // dart tip + 73, // nail + 105, // arrow tip + 113, // scimmy + // show this 121, // limbs + 129, // long sword + 137, // throwing knife + 145, // full helm + 153, // square shield + 177, // warhammer + 185, // battleaxe + 193, // chain body + 201, // kiteshield + 217, // 2h + 225, // plate sk + 233, // plate l + 241, // platebody + }; + for (int childId : values) { + player.getPacketDispatch().sendInterfaceConfig(300, childId, true); + + } + } + else { + player.getPacketDispatch().sendInterfaceConfig(300, 267, false);// pickaxe + } final Bars bars[] = Bars.getBars(type); for (int i = 0; i < bars.length; i++) { if (bars[i].getSmithingType() == SmithingType.TYPE_GRAPPLE_TIP) { @@ -50,17 +84,23 @@ public final class SmithingBuilder { if (bars[i].getSmithingType() == SmithingType.TYPE_DART_TIP) { player.getPacketDispatch().sendInterfaceConfig(300, 65, false); } - if (bars[i].getSmithingType() == SmithingType.TYPE_SPIT_IRON || bars[i].getSmithingType() == SmithingType.TYPE_WIRE || bars[i].getSmithingType() == SmithingType.TYPE_BULLSEYE) { + if (bars[i].getSmithingType() == SmithingType.TYPE_WIRE){ + player.getPacketDispatch().sendInterfaceConfig(300, 81, false); + } + if (bars[i].getSmithingType() == SmithingType.TYPE_SPIT_IRON){ player.getPacketDispatch().sendInterfaceConfig(300, 89, false); } + if ( bars[i].getSmithingType() == SmithingType.TYPE_BULLSEYE) { + player.getPacketDispatch().sendInterfaceConfig(300, 161, false); + } if (bars[i].getSmithingType() == SmithingType.TYPE_CLAWS) { player.getPacketDispatch().sendInterfaceConfig(300, 209, false); } - if (bars[i].getSmithingType() == SmithingType.TYPE_LANTERN || bars[i].getSmithingType() == SmithingType.TYPE_OIL_LANTERN) { + if (bars[i].getSmithingType() == SmithingType.TYPE_OIL_LANTERN) { player.getPacketDispatch().sendInterfaceConfig(300, 161, false); } if (bars[i].getSmithingType() == SmithingType.TYPE_STUDS) { - player.getPacketDispatch().sendInterfaceConfig(300, 169, false); + player.getPacketDispatch().sendInterfaceConfig(300, 97, false); } String color = ""; if (player.getSkills().getLevel(Skills.SMITHING) < bars[i].getLevel()) { @@ -79,7 +119,7 @@ public final class SmithingBuilder { } InterfaceContainer.generateItems(player, new Item[] { new Item(bars[i].getProduct(), bars[i].getSmithingType().getProductAmount()) }, new String[] { "" }, 300, bars[i].getSmithingType().getChild() - 1); } - player.getPacketDispatch().sendString(type.getBarName(), 300, 15); + player.getPacketDispatch().sendString(type.getBarName(), 300, 14); player.getInterfaceManager().open(new Component(300)); } diff --git a/Server/src/main/content/global/skill/smithing/SmithingPulse.java b/Server/src/main/content/global/skill/smithing/SmithingPulse.java index 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..94465077e 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingType.java +++ b/Server/src/main/content/global/skill/smithing/SmithingType.java @@ -52,7 +52,7 @@ public enum SmithingType { /** * Bullseye */ - TYPE_BULLSEYE(1, 90, 91, new int[] { 96, 95, 94, 93 }, 1), + TYPE_BULLSEYE(1, 162, 163, new int[] { 168, 167, 166, 165 }, 1), /** * Spit Iron @@ -61,7 +61,7 @@ public enum SmithingType { /** * */ - TYPE_WIRE(1, 90, 91, new int[] { 96, 95, 94, 93 }, 1), + TYPE_WIRE(1, 82, 83, new int[] { 88, 87, 86, 85 }, 1), /** * Arrow Tips @@ -98,11 +98,6 @@ public enum SmithingType { */ TYPE_SQUARE_SHIELD(2, 154, 155, new int[] { 160, 159, 158, 157 }, 1), - /** - * Latern - */ - TYPE_LANTERN(1, 162, 163, new int[] { 168, 167, 166, 165 }, 1), - /** * Oil Lantern */ @@ -111,12 +106,12 @@ public enum SmithingType { /** * Grapple Tips */ - TYPE_GRAPPLE_TIP(1, 170, 171, new int[] { 175, 176, 175, 174, 173 }, 1), + TYPE_GRAPPLE_TIP(1, 170, 171, new int[] { 176, 175, 174, 173 }, 1), /** * The studs type. */ - TYPE_STUDS(1, 170, 171, new int[] { 175, 176, 174, 173, 172 }, 1), + TYPE_STUDS(1, 98, 99, new int[] { 104, 103, 102, 101, 100 }, 1), /** * Warhammer diff --git a/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java b/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java index c1dd2e570..7951ab0ed 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()) { @@ -110,6 +114,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); } } } @@ -173,36 +178,30 @@ public class SmeltingPulse extends SkillPulse { } /** - * 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. + * Checks if the forging is a success. * * @param player the player. * @return {@code True} if success. */ 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); - sendMessage(player, "Your ring of forging uses up its last charge and disintegrates."); + 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."); + // stop the smelting queue + stop(); + } 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..b91634318 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningPouch.java +++ b/Server/src/main/content/global/skill/summoning/SummoningPouch.java @@ -34,7 +34,7 @@ public enum SummoningPouch { /** * Represents a granite crab pouch. */ - GRANITE_CRAB_POUCH(4, 12009, 16, 31.6, 6796, 0.2, 2, false, new Item(12158), new Item(12155), new Item(440), new Item(12183, 7)), + GRANITE_CRAB_POUCH(4, 12009, 16, 21.6, 6796, 0.2, 2, false, new Item(12158), new Item(12155), new Item(440), new Item(12183, 7)), /** * Represents a spirit mosquito pouch. @@ -334,27 +334,27 @@ public enum SummoningPouch { /** * 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, 128)), /** * 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, 140)), /** * 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, 203)), /** * 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, 211)), /** * Represents a fire titan pouch. diff --git a/Server/src/main/content/global/skill/summoning/SummoningScroll.java b/Server/src/main/content/global/skill/summoning/SummoningScroll.java index 80ffe286d..cd02a4419 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, 12804), + TITANS_CONSTITUTION_SCROLL3(59, 12824, 7.9, 79, 12806), + 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..c47862b62 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt +++ b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt @@ -1,5 +1,7 @@ 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 class SummoningTabListener : InterfaceListener { @@ -7,46 +9,54 @@ class SummoningTabListener : InterfaceListener { 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.") } } 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/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..a7ed47a6d --- /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, 7, 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/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..089db9352 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,20 @@ 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 static core.api.ContentAPIKt.sendMessage; + /** * Represents a familiar. * @author Emperor @@ -78,6 +82,8 @@ public abstract class Familiar extends NPC implements Plugin { * The amount of special points left. */ protected int specialPoints = 60; + + public static final String INFINITE_SPECIAL_MOVE_ATTRIBUTE = "infinite-special-move"; /** * The pouch id. @@ -116,7 +122,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 +166,23 @@ public abstract class Familiar extends NPC implements Plugin { this.specialCost = specialCost; this.combatFamiliar = NPCDefinition.forId(getOriginalId() + 1).getName().equals(getName()); this.attackStyle = attackStyle; + /* The initial points are drained on summon. Then, the remaining points are drained over an interval. + * To prevent the last point from being drained only very late, we artificially extend the interval by one. + * Example: a dreadfowl drains 1 point on summon, and then needs to drain 3 points over 400 ticks. Naively + * draining a point on ticks 133, 266, and 399 allows players to save a point at the expense of just one tick. + * Instead, we drain on ticks 100, 200, and 300. + * Example 2: a spirit tz-kih drains 3 points on summon, then needs to drain 19 more points over 1800 ticks. + * This means it needs to drain a point every 90 ticks, since the 0th tick remaining will not drain. + * Example 3: a vampire bat needs to drain 27 points over 3300 ticks. It hence drains a point every ~118 ticks. + * Example 4: an abyssal titan drains 10 points on summon, then needs to drain 83 more points over 3200 ticks. + * This means it needs to drain a point every ~34 ticks. + */ + if (pouchId == -1) { + this.pointsPerTick = 0.0; + } else { + int drain = pouch.getLevelRequired() - pouch.getSummonCost() + 1; + this.pointsPerTick = (double) drain / maximumTicks; + } } /** @@ -172,17 +218,22 @@ public abstract class Familiar extends NPC implements Plugin { transform(); } } - - @Override + + @Override public void init() { init(getSpawnLocation(), true); } @Override public void handleTickActions() { - if (ticks-- % 50 == 0) { - updateSpecialPoints(-15); + ticks--; + fracDrain += pointsPerTick; + if (fracDrain > 1.0 && ticks > 0) { + fracDrain -= 1.0; owner.getSkills().updateLevel(Skills.SUMMONING, -1, 0); + } + if (ticks % 50 == 0) { + updateSpecialPoints(-15); if (!getText().isEmpty()) { super.sendChat(getText()); } @@ -210,7 +261,10 @@ public abstract class Familiar extends NPC implements Plugin { if (victim == null) { victim = owner.getAttribute("combat-attacker"); } - if (combat.getVictim() != this && victim != null && !victim.isInvisible() && getProperties().isMultiZone() && owner.getProperties().isMultiZone() && isCombatFamiliar() && !isBurdenBeast() && !isPeacefulFamiliar()) { + if (combat.getVictim() != this && victim != null && !victim.isInvisible() + && getProperties().isMultiZone() && owner.getProperties().isMultiZone() + && victim.getProperties().isMultiZone() && isCombatFamiliar() + && !isBurdenBeast() && !isPeacefulFamiliar()) { // Are you serious my brotha? getProperties().getCombatPulse().attack(victim); } } @@ -223,49 +277,121 @@ public abstract class Familiar extends NPC implements Plugin { } handleFamiliarTick(); } - + @Override public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if (entity == owner) { - if(message) { - owner.getPacketDispatch().sendMessage("You can't just betray your own familiar like that!"); + if (isAttackableBy(entity, style, message)) return false; + return super.isAttackable(entity, style, message); + } + + @Override + public boolean continueAttack(Entity target, CombatStyle style, boolean message) { + if (getProperties().getCombatPulse().getVictim() == target) { + return canAttackTarget(target, style, message); + } + if (target == owner) { + if (message) { + sendMessage(owner, "You can't just betray your own familiar like that!"); } return false; } - if (entity instanceof Player) { - if (!owner.isAttackable(entity, style, message)) { - return false; - } - } - if (!getProperties().isMultiZone()) { - if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) { - 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) { - ((Player) entity).getPacketDispatch().sendMessage("This familiar is not in the a multicombat zone."); - } + return true; + } + + /** + * Checks if the familiar can attack the target (familiar as attacker). + * Called from continueAttack() when familiar initiates combat. + * + * @param target The target to attack. + * @param style The combat style. + * @param message Whether to send messages. + * + * @return true if the familiar can attack the target. + */ + private boolean canAttackTarget(Entity target, CombatStyle style, boolean message) { + if (target == owner) { + if (message) { + sendMessage(owner, "You can't just betray your own familiar like that!"); } return false; } - if (entity instanceof Player) { - if (!((Player) entity).getSkullManager().isWilderness()) { - if(message) { - ((Player) entity).getPacketDispatch().sendMessage("You have to be in the wilderness to attack a player's familiar."); + if (target instanceof Player) { + Player targetPlayer = (Player) target; + if (!targetPlayer.getSkullManager().isWilderness()) { + if (message) { + sendMessage(owner, "You have to be in the wilderness to attack a player's familiar."); } return false; } if (!owner.getSkullManager().isWilderness()) { - if(message) { - ((Player) entity).getPacketDispatch().sendMessage("This familiar's owner is not in the wilderness."); + if (message) { + sendMessage(owner, "This familiar's owner is not in the wilderness."); + } + return false; + } + if (!owner.isAttackable(target, style, false)) { + return false; + } + if (!owner.getProperties().isMultiZone()) { + if (message) { + sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar."); } return false; } } - return super.isAttackable(entity, style, message); + + return true; + } + + /** + * Checks if the entity can attack this familiar (familiar as target). + * Called from isAttackable() when someone attacks the familiar. + * + * @param attacker The entity trying to attack the familiar. + * @param style The combat style. + * @param message Whether to send messages. + * + * @return true if the attacker cannot attack the familiar (invalid target). + */ + private boolean isAttackableBy(Entity attacker, CombatStyle style, boolean message) { + if (attacker == owner) { + if (message) { + sendMessage(owner, "You can't just betray your own familiar like that!"); + } + return true; + } + if (attacker instanceof Player) { + Player attackingPlayer = (Player) attacker; + if (!attackingPlayer.getSkullManager().isWilderness()) { + if (message) { + sendMessage(attackingPlayer, "You have to be in the wilderness to attack a player's familiar."); + } + return true; + } + if (!owner.getSkullManager().isWilderness()) { + if (message) { + sendMessage(attackingPlayer, "This familiar's owner is not in the wilderness."); + } + return true; + } + if (!owner.isAttackable(attackingPlayer, style, false)) { + return true; + } + if (!attackingPlayer.getProperties().isMultiZone()) { + if (message) { + sendMessage(attackingPlayer, "You have to be in a multicombat zone to attack a player's familiar."); + } + return true; + } + if (!this.getProperties().isMultiZone()) { + if (message) { + sendMessage(attackingPlayer, "This familiar is not in a multicombat zone."); + } + return true; + } + } + + return false; } @Override @@ -336,45 +462,46 @@ 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); + if (!owner.getAttribute(INFINITE_SPECIAL_MOVE_ATTRIBUTE, false)) { + updateSpecialPoints(specialCost); + } owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); } + return true; } /** @@ -383,9 +510,10 @@ public abstract class Familiar extends NPC implements Plugin { public void visualizeSpecialMove() { owner.visualize(Animation.create(7660), Graphics.create(1316)); } - + /** * Sends a familiar hit. + * * @param target the target. * @param maxHit the max hit. * @param graphics the graphics. @@ -402,6 +530,13 @@ public abstract class Familiar extends NPC implements Plugin { hit = RandomFunction.randomize(maxHit); } state.setEstimatedHit(hit); + if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) { + state.neutralizeHits(); + state.setPoisonDamage(-1); + if (state.getStyle() == CombatStyle.MAGIC) { + state.setEstimatedHit(-1); + } + } target.getImpactHandler().handleImpact(owner, hit, CombatStyle.MELEE, state); if (graphics != null) { target.graphics(graphics); @@ -427,7 +562,7 @@ public abstract class Familiar extends NPC implements Plugin { public void sendFamiliarHit(final Entity target, final int maxHit) { sendFamiliarHit(target, maxHit, null); } - + /** * Checks if this familiar can attack the target (used mainly for special * moves). @@ -438,11 +573,17 @@ public abstract class Familiar extends NPC implements Plugin { } if (target.getLocation().getDistance(getLocation()) > 8) { if (message) { - owner.getPacketDispatch().sendMessage("That target is too far."); + sendMessage(owner, "That target is too far."); } return false; } - if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, true)) { + if (target instanceof Player && !owner.getProperties().isMultiZone()) { + if (message) { + sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar."); + } + return false; + } + if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, message)) { return false; } return isCombatFamiliar(); @@ -452,11 +593,13 @@ public abstract class Familiar extends NPC implements Plugin { public boolean canAttack(Entity target) { return canAttack(target, true); } - + /** * Checks if a familiar can perform a combat special attack. + * * @param target the target. * @param message show message. + * * @return {@code True} if so. */ public boolean canCombatSpecial(Entity target, boolean message) { @@ -469,6 +612,10 @@ public abstract class Familiar extends NPC implements Plugin { if (getAttribute("special-delay", 0) > GameWorld.getTicks()) { return false; } + // The special move probably should go through but with all harmful effects neutralized, but for now just block the damn thing + if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) { + return false; + } return true; } @@ -539,9 +686,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 +705,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 +728,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,13 +751,11 @@ 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(); - } + setVarp(owner, 448, -1); + setVarp(owner, 1176, 0); + setVarp(owner, 1175, 182986); + setVarp(owner, 1174, -1); + owner.getAppearance().sync(); owner.getInterfaceManager().setViewedTab(3); } @@ -621,14 +768,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..e65c36cf1 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java @@ -1,5 +1,6 @@ package content.global.skill.summoning.familiar; +import content.data.Quests; import content.global.skill.summoning.pet.Pet; import content.global.skill.summoning.pet.Pets; import core.cache.def.impl.ItemDefinition; @@ -16,27 +17,39 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +import static core.api.ContentAPIKt.*; + /** * Handles a player's familiar. * @author Emperor + * @author Player Name */ public final class FamiliarManager { + /** + * These varbits are packed into varp 1160. Legacy code added 243269632 directly to the raw varp, + * which toggled the Summoning orb visibility on every login. + * They are now written as individual varbits, but two of them remain unknown. + */ + private static final int VARBIT_SUMMONING_ORB_VISIBILITY = 4280; + private static final int VARBIT_SUMMONING_UNKNOWN1 = 4281; + private static final int VARBIT_SUMMONING_UNKNOWN2 = 4282; + /** * The familiars mapping. */ 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 +71,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,34 +79,82 @@ 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())); @@ -112,7 +168,11 @@ public final class FamiliarManager { if (hasFamiliar()) { familiar.init(); } - player.getFamiliarManager().setConfig(243269632); + boolean unlocked = player.getQuestRepository().isComplete(Quests.WOLF_WHISTLE); + + setVarbit(player, VARBIT_SUMMONING_ORB_VISIBILITY, unlocked ? 1 : 0); + setVarbit(player, VARBIT_SUMMONING_UNKNOWN1, 0); + setVarbit(player, VARBIT_SUMMONING_UNKNOWN2, 7); } /** @@ -122,14 +182,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."); @@ -154,31 +214,29 @@ public final class FamiliarManager { 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 (!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; } 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 (!renew) { + familiar = fam; + spawnFamiliar(); + } else { + familiar.refreshTimer(); } + player.getAppearance().sync(); } /** @@ -196,11 +254,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 +266,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 +274,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 +287,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); @@ -295,6 +356,9 @@ public final class FamiliarManager { for (int food : pets.getFood()) { if (food == foodId) { player.getInventory().remove(new Item(foodId)); + if (foodId == Items.BUCKET_OF_MILK_1927) { + player.getInventory().add(new Item(Items.BUCKET_1925)); + } player.getPacketDispatch().sendMessage("Your pet happily eats the " + ItemDefinition.forId(food).getName() + "."); player.animate(new Animation(827)); npc.getDetails().updateHunger(-15.0); @@ -313,8 +377,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 +424,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 +448,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()); } /** @@ -407,16 +470,6 @@ public final class FamiliarManager { return true; } - /** - * Sets a config value. - * @param value the value. - */ - public void setConfig(int value) { - int current = player.getConfigManager().get(1160); - int newVal = current + value; - player.getConfigManager().set(1160, newVal); - } - /** * Gets the familiar. * @return The familiar. @@ -446,7 +499,7 @@ public final class FamiliarManager { * @return The usingSummoning. */ public boolean isUsingSummoning() { - return hasPouch || hasFamiliar(); + return hasPouch || (hasFamiliar() && !hasPet()); } /** @@ -482,7 +535,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..c4672b643 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java @@ -21,7 +21,6 @@ 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; @@ -132,7 +131,7 @@ public class PyreLordNPC 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/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/SmokeDevilNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java index 545ce6345..49a79fa6a 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java @@ -3,6 +3,7 @@ package content.global.skill.summoning.familiar; import java.util.List; import core.cache.def.impl.NPCDefinition; +import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -36,7 +37,7 @@ public class SmokeDevilNPC extends Familiar { * @param id The id. */ public SmokeDevilNPC(Player owner, int id) { - super(owner, id, 4800, 12085, 6); + super(owner, id, 4800, 12085, 6, WeaponInterface.STYLE_CAST); } @Override diff --git a/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java deleted file mode 100644 index cd6372f62..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/SnakelingNPC.java +++ /dev/null @@ -1,114 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.Metamorphosis; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles the metamorphosis of the zulrah pet. - * @author Empathy - * - */ -@Initializable -public class SnakelingNPC extends Metamorphosis { - - /** - * The snakeling ids. - */ - public static final int[] SNAKELING_IDS = new int[] { 8626, 8627, 8628 }; - - /** - * - * Constructs a new {@code SnakelingNPC} object. - */ - public SnakelingNPC() { - super(SNAKELING_IDS); - } - - @Override - public DialoguePlugin getDialoguePlugin() { - return new PetSnakelingDialogue(); - } - - /** - * Handles the pet snakeling dialogue. - * @author Empathy - * - */ - public final class PetSnakelingDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code PetSnakelingDialogue} {@code Object}. - */ - public PetSnakelingDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code PetSnakelingDialogue} {@code Object}. - * - * @param player the player. - */ - public PetSnakelingDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PetSnakelingDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("Hey little snake!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc(FacialExpression.OLD_NORMAL, "Soon, Zulrah shall establish dominion over this plane."); - stage = 1; - break; - case 1: - player("Wanna play fetch?"); - stage = 2; - break; - case 2: - npc(FacialExpression.OLD_NORMAL, "Submit to the almighty Zulrah."); - stage = 3; - break; - case 3: - player("Walkies? Or slidies...?"); - stage = 4; - break; - case 4: - npc(FacialExpression.OLD_NORMAL, "Zulrah's wilderness as a God will soon be demonstrated."); - stage = 5; - break; - case 5: - player("I give up..."); - stage = 6; - break; - case 6: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return SNAKELING_IDS; - } - } - -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java index 83f1d21ae..18d4946b2 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java @@ -56,7 +56,7 @@ public class SpiritCobraNPC extends Familiar { * @author Vexia */ public enum Egg { - COCKATRICE(new Item(1944), new Item(12109)), SARATRICE(new Item(10533), new Item(12113)), ZAMATRICE(new Item(10532), new Item(12115)), GUTHATRICE(new Item(10531), new Item(12111)), CORACATRICE(new Item(11964), new Item(12119)), PENGATRICE(new Item(12483), new Item(12117)), VULATRICE(new Item(11695), new Item(12121)); + COCKATRICE(new Item(1944), new Item(12109)), SARATRICE(new Item(5077), new Item(12113)), ZAMATRICE(new Item(5076), new Item(12115)), GUTHATRICE(new Item(5078), new Item(12111)), CORACATRICE(new Item(11964), new Item(12119)), PENGATRICE(new Item(12483), new Item(12117)), VULATRICE(new Item(11965), new Item(12121)); /** * The egg item. diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.java deleted file mode 100644 index 98721c62f..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.global.skill.summoning.familiar; - -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.TeleportManager.TeleportType; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.game.world.map.zone.impl.WildernessZone; - -/** - * Represents the spirit graahk's dialogue - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class SpiritGraahkDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GertrudesCatDialogue} {@code Object}. - */ - public SpiritGraahkDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GertrudesCatDialogue} {@code Object}. - * @param player the player. - */ - public SpiritGraahkDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SpiritGraahkDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (!(npc instanceof Familiar)) { - return false; - } - final Familiar fam = (Familiar) npc; - if (fam.getOwner() != player) { - player.getPacketDispatch().sendMessage("This is not your familiar."); - return true; - } else { - interpreter.sendOptions("Select an Option", "Chat", "Teleport"); - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (buttonId) { - case 1: - player.sendMessage("The Graahk does not feel like talking now."); - end(); - break; - case 2: - if (!WildernessZone.checkTeleport(player, 20)) { - player.sendMessage("You cannot teleport with the Graahk above level 20 wilderness."); - end(); - } else { - player.getTeleporter().send(new Location(2786, 3002), TeleportType.NORMAL); - end(); - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 7353, 7364 }; - } -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.kt b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.kt new file mode 100644 index 000000000..4a0c23c9e --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkDialogue.kt @@ -0,0 +1,115 @@ +package content.global.skill.summoning.familiar + +import core.api.openDialogue +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +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.TeleportManager.TeleportType +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.map.zone.impl.WildernessZone +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +/** + * Represents the spirit graahk's dialogue + * @author Splinter & Bishop + * @version 2.0 + */ +@Initializable +class SpiritGraahkDialogue : DialoguePlugin { + constructor() + constructor(player: Player?) : super(player) + + override fun newInstance(player: Player?): DialoguePlugin { + return SpiritGraahkDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC? + if (npc !is Familiar) { + return false + } + val fam = npc as Familiar + if (fam.getOwner() !== player) { + player.packetDispatch.sendMessage("This is not your familiar.") + return true + } else { + interpreter.sendOptions("Select an Option", "Chat", "Teleport") + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (buttonId) { + 1 -> if (player.skills.getStaticLevel(Skills.SUMMONING) >= 67) { + openDialogue(player, SpiritGraahkDialogueFile(), npc) + } else { + player.sendMessage("The Graahk does not feel like talking now.") //This message is likely inauthentic, but I cannot source the correct one so I'm keeping the default here -Bishop + end() + } + + 2 -> if (!WildernessZone.checkTeleport(player, 20)) { + player.sendMessage("You cannot teleport with the Graahk above level 20 wilderness.") + end() + } else { + player.teleporter.send(Location(2786, 3002), TeleportType.NORMAL) + end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SPIRIT_GRAAHK_7363, NPCs.SPIRIT_GRAAHK_7364) + } +} + +class SpiritGraahkDialogueFile : DialogueLabeller() { + override fun addConversation() { + exec { player, npc -> + val spiritGraahkRandomConversation = RandomFunction.random(4) + when (spiritGraahkRandomConversation) { + 0 -> loadLabel(player, "spiky spikes") + 1 -> loadLabel(player, "pet the spikes") + 2 -> loadLabel(player, "therapeutic spikes") + 3 -> loadLabel(player, "inspect the spikes") + } + } + + label("spiky spikes") + player(ChatAnim.NEUTRAL, "Your spikes are looking particularly spiky today.") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Graaaaahk raaaawr?", "(Really, you think so?)") + player(ChatAnim.HAPPY, "Yes. Most pointy, indeed.") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Raaaawr...", "(That's really kind of you to say. I was going to spike", "you but I won't now...)") + player(ChatAnim.HAPPY, "Thanks?") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "...Grrrrr ark.", "(...I'll do it later instead.)") + player(ChatAnim.HAPPY, "*Sigh!*") + + label("pet the spikes") + npc(ChatAnim.GHRAAK_SHAKE_MILD, "Graahk grrrrowl?", "(My spikes hurt, could you pet them for me?)") + player(ChatAnim.HAPPY, "Aww, of course I can I'll just... Oww! I think you drew", "blood that time.") + + label("therapeutic spikes") + npc(ChatAnim.GHRAAK_SHAKE_MILD, "Graahk!", "(Hi!)") //? + player(ChatAnim.SUSPICIOUS, "Hello. Are you going to spike me again?") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Grar rawr howl!", "(No, I got a present to apologise for last time.)") + player(ChatAnim.HAPPY, "That's really sweet, thank you.") + npc(ChatAnim.GRAAHK_NOD, "Howl graaahk rawr.", "(Here you go, it's a special cushion to make you", "comfortable.)") + player(ChatAnim.ANGRY, "It's made of spikes!") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Howl graaahk rawr.", "(Yes, but they're therapeutic spikes.)") + player(ChatAnim.ANGRY, "...") + + label("inspect the spikes") + player(ChatAnim.HAPPY, "How's your day going?") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Graaahk. Grak grak!", "(It's great! Actually I've got something to show you!)") + player(ChatAnim.HALF_ASKING, "Oh? What's that?") + npc(ChatAnim.GRAAHK_NOD, "Grrrrrk hiss graaaaa!", "(You'll need to get closer!)") + player(ChatAnim.HALF_ASKING, "I can't see anything...") + npc(ChatAnim.GHRAAK_SHAKE_VIGOROUS, "Grah grr aaaaahk grahk.", "(It's really small - even closer.)") + player(ChatAnim.ANGRY, "Oww! I'm going to have your spikes trimmed!") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.java deleted file mode 100644 index dd6b48975..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.java +++ /dev/null @@ -1,52 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.game.node.entity.skill.SkillBonus; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the Spirit Graahk familiar. - * @author Aero - */ -@Initializable -public class SpiritGraahkNPC extends Familiar { - - /** - * Constructs a new {@code SpiritGraahkNPC} {@code Object}. - */ - public SpiritGraahkNPC() { - this(null, 7363); - } - - /** - * Constructs a new {@code SpiritGraahkNPC} {@code Object}. - * @param owner The owner. - * @param id The id. - */ - public SpiritGraahkNPC(Player owner, int id) { - super(owner, id, 4900, 12810, 3, WeaponInterface.STYLE_AGGRESSIVE); - boosts.add(new SkillBonus(Skills.HUNTER, 5)); - } - - @Override - public Familiar construct(Player owner, int id) { - return new SpiritGraahkNPC(owner, id); - } - - @Override - protected boolean specialMove(FamiliarSpecial special) { - if (!super.isOwnerAttackable()) { - return false; - } - call(); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 7363, 7364 }; - } - -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.kt b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.kt new file mode 100644 index 000000000..af84870d7 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkNPC.kt @@ -0,0 +1,51 @@ +package content.global.skill.summoning.familiar + +import core.game.node.entity.Entity +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.player.Player +import core.game.node.entity.skill.SkillBonus +import core.game.node.entity.skill.Skills +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 + +/** + * Represents the Spirit Graahk familiar. + * @author Aero & Bishop + */ +@Initializable +class SpiritGraahkNPC @JvmOverloads constructor(owner: Player? = null, id: Int = NPCs.SPIRIT_GRAAHK_7363) : + Familiar(owner, id, 4900, Items.SPIRIT_GRAAHK_POUCH_12810, 3, WeaponInterface.STYLE_AGGRESSIVE) { + + init { + boosts.add(SkillBonus(Skills.HUNTER, 5.0)) + } + + override fun construct(owner: Player?, id: Int): Familiar { + return SpiritGraahkNPC(owner, id) + } + + override fun specialMove(special: FamiliarSpecial): Boolean { + val target: Entity = special.target ?: return false + if (!canCombatSpecial(target)) { + return false + } + properties.combatPulse.attack(target) + return true + } + + override fun visualizeSpecialMove() { + owner.visualize(Animation.create(7660), Graphics.create(1316)) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SPIRIT_GRAAHK_7363, NPCs.SPIRIT_GRAAHK_7364) + } + + override fun getText(): String { + return if (RandomFunction.random(2) == 0) "Howl!" else "Rowr!" + } +} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.java deleted file mode 100644 index 44c4678a9..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.java +++ /dev/null @@ -1,29 +0,0 @@ -package content.global.skill.summoning.familiar; - -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; - -/** - * Represents the plugin used to handle the spirit graahk familiar - * @author Splinter - */ -@Initializable -public final class SpiritGraahkOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(7363).getHandlers().put("option:interact", this); - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - player.getDialogueInterpreter().open(7353, node.asNpc()); - return true; - } - -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.kt b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.kt new file mode 100644 index 000000000..d3b3b1e97 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritGraahkOptionPlugin.kt @@ -0,0 +1,27 @@ +package content.global.skill.summoning.familiar + +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 +import org.rs09.consts.NPCs; + +/** + * Represents the plugin used to handle the spirit graahk familiar + * @author Splinter + */ +@Initializable +class SpiritGraahkOptionPlugin : OptionHandler() { + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin { + NPCDefinition.forId(NPCs.SPIRIT_GRAAHK_7363).getHandlers()["option:interact"] = this + return this + } + + override fun handle(player: Player, node: Node, option: String?): Boolean { + player.dialogueInterpreter.open(NPCs.SPIRIT_GRAAHK_7364, node.asNpc()) + return true + } +} 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/pet/IncubatorHandler.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt new file mode 100644 index 000000000..4b9969574 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt @@ -0,0 +1,74 @@ +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() + + if (addItem(player, product.id)) { + sendMessage(player, "You take your $name out of the incubator.") + } + return true + } +} diff --git a/Server/src/main/content/global/skill/summoning/pet/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..24a387d18 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,12 +60,14 @@ 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; break; - case 2:// chase-vermine + case 2:// chase-vermin end(); player.sendChat("Go on puss...kill that rat!"); boolean cant = true; @@ -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..a115040d1 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,18 @@ 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 static core.api.ContentAPIKt.*; /** * Represents a pet. * @author Emperor * @author 'Vexia + * @author Player Name */ public final class Pet extends Familiar { @@ -24,13 +29,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 +58,53 @@ 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 (getPet().getFood().length > 0 && !pet.isGrownCat(itemId)) { + if(!SkillcapePerks.isActive(SkillcapePerks.PET_MASTERY, owner)) { + double amount = itemId == pet.getBabyItemId() ? 0.025 : 0.018; + if (owner.getAttribute("petrate",1) == 0) { + amount = 0 ; + } + else if (owner.getAttribute("petrate",1) == 2) { + 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 (owner.getAttribute("petrate",1) == 0) { + growthrate = 0; + } + else if (owner.getAttribute("petrate",1) == 2) { + growthrate *= 100; + } + petDetails.updateGrowth(growthrate); if (growth == 100.0) { growNextStage(); } @@ -87,34 +116,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 +155,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 +179,36 @@ public final class Pet extends Familiar { return pet; } + /** + * Gets the hunger level. + */ + public double getHunger() { + return details.getHunger(); + } + + /** + * Gets the growth level. + */ + public double getGrowth() { + return details.getGrowth(); + } + + /** + * Gets the hunger warning level. + */ + public int getHasWarned() { + return hasWarned; + } + + /** + * Sets the hunger warning level. + */ + public void setHasWarned(int value) { + this.hasWarned = value; + } + @Override public int[] getIds() { return new int[] { 761, 762, 763, 764, 765, 766, 3505, 3598, 6969, 7259, 7260, 6964, 7249, 7251, 6960, 7241, 7243, 6962, 7245, 7247, 6966, 7253, 7255, 6958, 7237, 7239, 6915, 7277, 7278, 7279, 7280, 7018, 7019, 7020, 6908, 7313, 7316, 6947, 7293, 7295, 7297, 7299, 6911, 7261, 7263, 7265, 7267, 7269, 6919, 7301, 7303, 7305, 7307, 6949, 6952, 6955, 6913, 7271, 7273, 6945, 7319, 7321, 7323, 7325, 7327, 6922, 6942, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 6900, 6902, 6904, 6906, 768, 769, 770, 771, 772, 773, 3504, 6968, 7257, 7258, 6965, 7250, 7252, 6961, 7242, 7244, 6963, 7246, 7248, 6967, 7254, 7256, 6859, 7238, 7240, 6916, 7281, 7282, 7283, 7284, 7015, 7016, 7017, 6909, 7314, 7317, 6948, 7294, 7296, 7298, 7300, 6912, 7262, 7264, 7266, 7268, 7270, 6920, 7302, 7304, 7306, 7308, 6950, 6953, 6956, 6914, 7272, 7274, 6946, 7320, 7322, 7324, 7326, 7328, 6923, 6943, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 6901, 6903, 6905, 6907, 774, 775, 776, 777, 778, 779, 3503, 6951, 6954, 6957 }; } - } diff --git a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java index 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..3258099fc 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pets.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pets.java @@ -1,36 +1,30 @@ 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. */ CLOCKWORK_CAT(7771, 7772, -1, 3598, -1, -1, 0.0, 0), - /** - * The firemaker's curse pets. - */ - SEARING_FLAME(22994, -1, -1, 14769, -1, -1, 0.0, 0), GLOWING_EMBER(22993, -1, -1, 14768, -1, -1, 0.0, 0), TWISTED_FIRESTARTER(22995, -1, -1, 14770, -1, -1, 0.0, 0), WARMING_FLAME(22992, -1, -1, 14767, -1, -1, 0.0, 0), - - /** - * Troll baby pet. - */ - TROLL_BABY(23030, 23030, -1, 14846, -1, -1, 0.0, 0), - /** * A bulldog pet. */ @@ -59,37 +53,7 @@ public enum Pets { /** * A terrier pet. */ - TERRIER(12512, 12513, -1, 6958, 6859, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_1(12700, 12701, -1, 7237, 7238, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_2(12702, 12703, -1, 7239, 7240, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A creeping hand pet. - */ - //CREEPING_HAND(14652, -1, -1, 8619, -1, -1, 0.0033333333333333, 4, 1059), - - /** - * Minitrice pet. - */ - //MINITRICE(14653, -1, -1, 8620, -1, -1, 0.0033333333333333, 4, 225), - - /** - * Baby basilisk pet. - */ - //BABY_BASILISK(14654, -1, -1, 8621, -1, -1, 0.0033333333333333, 4, 221), - - /** - * Baby kurask pet. - */ - //BABY_KURASK(14655, -1, -1, 8622, -1, -1, 0.0033333333333333, 4, 526), - - /** - * Abyssal minion pet. - */ - //ABYSSAL_MINION(14651, -1, -1, 8624, -1, -1, 0.0033333333333333, 4, 592), - - /** - * Rune guardian pets. - */ - RUNE_GUARDIAN(15626, -1, -1, 9656, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_1(15627, -1, -1, 9657, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_2(15628, -1, -1, 9658, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_3(15629, -1, -1, 9659, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_4(15630, -1, -1, 9660, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_5(15631, -1, -1, 9661, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_6(15632, -1, -1, 9662, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_7(15633, -1, -1, 9663, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_8(15634, -1, -1, 9664, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_9(15635, -1, -1, 9665, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_10(15636, -1, -1, 9666, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_11(15637, -1, -1, 9667, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_12(15638, -1, -1, 9668, -1, -1, 0.0033333333333333, 4), RUNE_GUARDIAN_13(15639, -1, -1, 9669, -1, -1, 0.0033333333333333, 4), + TERRIER(12512, 12513, -1, 6958, 6959, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_1(12700, 12701, -1, 7237, 7238, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), TERRIER_2(12702, 12703, -1, 7239, 7240, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), /** * Gecko pet. @@ -111,11 +75,6 @@ public enum Pets { */ PENGUIN(12481, 12482, -1, 6908, 6909, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), PENGUIN_1(12763, 12762, -1, 7313, 7314, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), PENGUIN_2(12765, 12764, -1, 7316, 7317, -1, 0.0046296296296296, 30, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), - /** - * A tooth creature pet. - */ - TOOTH_CREATURE(18671, 18669, -1, 11411, 11413, -1, 0.075757575757576, 37, 1927, 1977), - /** * A giant crab pet. */ @@ -151,11 +110,6 @@ public enum Pets { */ RACCOON(12486, 12487, -1, 6913, 6914, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), RACCOON_1(12734, 12735, -1, 7271, 7272, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), RACCOON_2(12736, 12737, -1, 7273, 7274, -1, 0.0029444444444444, 80, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 2132, 2134, 2136, 2138, 10816, 9986, 9978), - /** - * A sneaker peeper pet. - */ - SNEAKER_PEEPER(19894, 19895, -1, 13089, 13090, -1, 0.05, 80, 221), - /** * A vulture pet. */ @@ -176,61 +130,6 @@ public enum Pets { */ BABY_DRAGON(12469, 12470, -1, 6900, 6901, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_1(12471, 12472, -1, 6902, 6903, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_2(12473, 12474, -1, 6904, 6905, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), BABY_DRAGON_3(12475, 12476, -1, 6906, 6907, -1, 0.0052, 99, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270); - -// /** -// * Emperor's pets (that's right, MY pet"S"). -// */ -// GIANT_WOLPERTINGER(8888, -1, -1, 6990, -1, -1, 0.0, 99), DRAKAN(8889, -1, -1, 4794, -1, -1, 0.0, 99), DILL(8890, -1, -1, 7770, -1, -1, 0.0, 99), - -// /** -// * Vexias pet. -// */ -// IMP(9952, -1, -1, 1531, -1, -1, 0.0, 99), BIG_GUY(9951, -1, -1, 3101, -1, -1, 0.0, 99), LITTLE_GUY(8887, -1, -1, 5805, -1, -1, 0.0, 99), -// -// /** -// * Godwars boss pets. -// */ -// KRIL_JR(14648, -1, -1, 8591, -1, -1, 0.0, 1), KREE_JR(14645, -1, -1, 8592, -1, -1, 0.0, 1), ZILYANA_JR(14647, -1, -1, 8593, -1, -1, 0.0, 1), GRAARDOOR_JR(14646, -1, -1, 8594, -1, -1, 0.0, 1), -// -// /** -// * Classic boss pets. -// */ -// CHAOS_ELE_JR(14638, -1, -1, 8595, -1, -1, 0.0, 1), PRINCE_BLACK_DRAGON(14649, -1, -1, 8596, -1, -1, 0.0, 1), BABY_MOLE(14642, -1, -1, 8601, -1, -1, 0.0, 1), KQ_FORM_1(14643, -1, -1, 8602, -1, -1, 0.0, 1), KQ_FORM_2(14650, -1, -1, 8603, -1, -1, 0.0, 1), DARK_CORE(14653, -1, -1, 8630, -1, -1, 0.0, 1), -// -// /** -// * The boss pets for the Dagannoths -// */ -// DAGANNOTH_SUPREME(14639, -1, -1, 8605, -1, -1, 0.0, 1), DAGANNOTH_PRIME(14640, -1, -1, 8606, -1, -1, 0.0, 1), DAGANNOTH_REX(14641, -1, -1, 8607, -1, -1, 0.0, 1), -// /** -// * The new OSRS bosses. -// */ -// CALLISTO_CUB(14658, -1, -1, 8597, -1, -1, 0.0, 1), -// SCORPIA_JR(14661, -1, -1, 8598, -1, -1, 0.0, 1), -// VENENATIS_JR(14657, -1, -1, 8654, -1, -1, 0.0, 1), -// VETION_JR(14659, -1, -1, 8600, -1, -1, 0.0, 1), -// VETION_JR_2(14660, -1, -1, 8654, -1, -1, 0.0, 1), -// RELEASE_THE_KRAKEN(14651, -1, -1, 8608, -1, -1, 0.0, 1), -// SMOKE_DEVIL(14644, -1, -1, 8609, -1, -1, 0.0, 1), -// SNAKELING_YELLOW(14654, -1, -1, 8626, -1, -1, 0.0, 1), -// SNAKELING_ORANGE(14655, -1, -1, 8627, -1, -1, 0.0, 1), -// SNAKELING_PURPLE(14656, -1, -1, 8628, -1, -1, 0.0, 1), -// /** -// * The boss pet representing the likeness of the Penance Queen -// */ -// DRAMA_QUEEN(14652, -1, -1, 8604, -1, -1, 0.0, 1), -// /** -// * The skilling pets -// */ -// BEAVER(14821, -1, -1, 8635, -1, -1, 0.0, 1), -// ROCK_GOLEM(14822, -1, -1, 8637, -1, -1, 0.0, 1), -// BABY_RED_CHINCHOMPA(14823, -1, -1, 8643, -1, -1, 0.0, 1), -// BABY_GREY_CHINCHOMPA(14824, -1, -1, 8644, -1, -1, 0.0, 1), -// BABY_BLACK_CHINCHOMPA(14825, -1, -1, 8657, -1, -1, 0.0, 1), -// BABY_GOLD_CHINCHOMPA(14826, -1, -1, 8658, -1, -1, 0.0, 1), -// HERON(14827, -1, -1, 8647, -1, -1, 0.0, 1), -// TZREK_JAD(14828, -1, -1, 8650, -1, -1, 0.0, 1); - - /** * The baby pets mapping. */ @@ -446,35 +345,75 @@ 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; + } + } + + /** + * Checks if this pet is a grown cat + * @return a boolean, true if the pet is a grown cat + */ + public boolean isGrownCat(int id) { + switch (this) { + case CAT: + case CAT_1: + case CAT_2: + case CAT_3: + case CAT_4: + case CAT_5: + case CAT_6: + case HELLCAT: + return id == grownItemId || id == overgrownItemId; + default: + return false; + } + } +} diff --git a/Server/src/main/content/global/skill/thieving/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..4580f2518 100644 --- a/Server/src/main/content/global/skill/thieving/Pickpockets.kt +++ b/Server/src/main/content/global/skill/thieving/Pickpockets.kt @@ -10,7 +10,7 @@ import core.api.utils.WeightedItem import java.util.stream.IntStream enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Double, val high: Double, val experience: Double, val stunDamageMin: Int, val stunDamageMax: Int, val stunTime: Int, val table: WeightBasedTable) { - MAN(intArrayOf(1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 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( @@ -135,7 +135,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..ef687a850 100644 --- a/Server/src/main/content/global/skill/thieving/ThievingListeners.kt +++ b/Server/src/main/content/global/skill/thieving/ThievingListeners.kt @@ -1,29 +1,55 @@ 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 content.global.activity.ttrail.TreasureTrailManager 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.") @@ -40,58 +66,32 @@ class ThievingListeners : InteractionListener { 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 - } - player.animator.animate(PICKPOCKET_ANIM) - val chance = RandomFunction.randomDouble(1.0,100.0) - val failThreshold = pickpocketData.getSuccessChance(player) + successMod - - if(chance > failThreshold){ + val effectiveTable = if (TreasureTrailManager.getInstance(player).hasClue()) { + pickpocketData.table.excluding(*WeightBasedTable.CLUE_SCROLL_SLOTS) + } else { + pickpocketData.table + } + val lootTable = pickpocketRoll(player, pickpocketData.low, pickpocketData.high, effectiveTable) + 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) player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL) node.asNpc().face(null) } else { - player.audioManager.send(SUCCESS) + playAudio(player, Sounds.PICK_2581) player.lock(2) - pickpocketData.table.roll().forEach { player.inventory.add(it) } + lootTable.forEach { player.inventory.add(it) } player.skills.addExperience(Skills.THIEVING,pickpocketData.experience) } 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..4058fc375 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 (hasRequirement(player, Quests.CABIN_FEVER)) { + cost -= Math.round((cost / 2.)); + } + if (player.getEquipment().containsItem(RING_OF_CHAROS)) { cost -= Math.round((cost / 2.)); } return cost; @@ -96,12 +100,10 @@ public final class ShipCharter { * @return the hidden childs. */ public static int[] getHiddenComponents(final Player player, Destination base) { - final Destination[] restrictions = new Destination[] { /** - * - * Destination.MOS_LE_HARMLESS, - */ - Destination.OO_GLOG, Destination.SHIPYARD, /* Destination.PORT_TYRAS, */ - Destination.CRANDOR }; + final Destination[] restrictions = new Destination[] { /* Destination.MOS_LE_HARMLESS, */ + Destination.OO_GLOG, Destination.SHIPYARD, /* Destination.PORT_TYRAS, */ + Destination.CRANDOR + }; List childs = new ArrayList<>(20); for (Destination destination : restrictions) { childs.add(destination.getXChild()); @@ -137,15 +139,20 @@ public final class ShipCharter { * @author 'Vexia */ public enum Destination { - CATHERBY(Location.create(2792, 3417, 1), 25, new int[] { 480, 0, 480, 625, 1600, 3250, 1000, 1600, 3200, 3400 }, Location.create(2797, 3414, 0), 3, 14), + CATHERBY(Location.create(2792, 3417, 1), 25, new int[] { 480, 0, 480, 1250, 1600, 3250, 1000, 1600, 3200, 3400 }, Location.create(2797, 3414, 0), 3, 14), PORT_PHASMATYS(Location.create(3705, 3503, 1), 24, new int[] { 3650, 3250, 1850, 0, 0, 0, 2050, 1850, 3200, 1100 }, Location.create(3702, 3502, 0), 2, 13) { @Override public boolean checkTravel(Player player) { - return requireQuest(player, "Priest in Peril", "to go there"); + return requireQuest(player, Quests.PRIEST_IN_PERIL, "to go there."); } }, - CRANDOR(new Location(2792, 3417, 1), 32, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21), - BRIMHAVEN(Location.create(2763, 3238, 1), 28, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, Location.create(2760, 3238, 0), 6, 17){ + CRANDOR(Location.create(2792, 3417, 1), 32, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21) { + @Override + public boolean checkTravel(Player player) { + return requireQuest(player, Quests.DRAGON_SLAYER, "to go there."); + } + }, + BRIMHAVEN(Location.create(2763, 3238, 1), 28, new int[] { 0, 480, 480, 1950, 400, 3650, 1600, 400, 3200, 3800 }, Location.create(2760, 3238, 0), 6, 17){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -153,7 +160,7 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_SARIM(Location.create(3038, 3189, 1), 30, new int[] { 1600, 1000, 0, 325, 1280, 650, 1280, 400, 3200, 1400 }, Location.create(3039, 3193, 0), 8, 19){ + PORT_SARIM(Location.create(3038, 3189, 1), 30, new int[] { 1600, 1000, 0, 650, 1280, 650, 1280, 400, 3200, 1400 }, Location.create(3039, 3193, 0), 8, 19){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -161,8 +168,14 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_TYRAS(Location.create(2142, 3122, 0), 23, new int[] { 3200, 3200, 3200, 1600, 3200, 3200, 3200, 3200, 0, 3200 }, Location.create(2143, 3122, 0), 1, 12), - KARAMJA(Location.create(2957, 3158, 1), 27, new int[] { 200, 480, 0, 225, 400, 1850, 0, 200, 3200, 2000 }, Location.create(2954, 3156, 0), 5, 16) { + PORT_TYRAS(Location.create(2142, 3122, 0), 23, new int[] { 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 0, 3200 }, Location.create(2143, 3122, 0), 1, 12) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.REGICIDE); + } + + }, + KARAMJA(Location.create(2957, 3158, 1), 27, new int[] { 200, 480, 0, 450, 400, 1850, 0, 200, 3200, 2000 }, Location.create(2954, 3156, 0), 5, 16) { @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -170,7 +183,7 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - PORT_KHAZARD(Location.create(2674, 3141, 1), 29, new int[] { 1600, 1000, 0, 325, 180, 650, 1280, 400, 3200, 1400 }, Location.create(2674, 3144, 0), 7, 18){ + PORT_KHAZARD(Location.create(2674, 3141, 1), 29, new int[] { 1600, 1000, 0, 2050, 180, 650, 1280, 400, 3200, 1400 }, Location.create(2674, 3144, 0), 7, 18){ @Override public int getCost(Player player, Destination destination) { boolean hasGloves = DiaryType.KARAMJA.hasRewardEquipment(player); @@ -178,9 +191,19 @@ public final class ShipCharter { return super.getCost(player, destination); } }, - SHIPYARD(Location.create(3001, 3032, 0), 26, new int[] { 400, 1600, 200, 225, 720, 1850, 400, 0, 3200, 900 }, Location.create(3001, 3032, 0), 4, 15), - OO_GLOG(Location.create(2623, 2857, 0), 33, new int[] { 300, 3400, 2000, 550, 5000, 2800, 1400, 900, 3200, 0}, Location.create(2622, 2857, 0), 11, 22), - MOS_LE_HARMLESS(Location.create(3671, 2931, 0), 31, new int[] { 725, 625, 1025, 0, 1025, 0, 325, 275, 1600, 500 }, Location.create(3671, 2933, 0), 9, 20); + SHIPYARD(Location.create(3001, 3032, 0), 26, new int[] { 400, 1600, 200, 450, 720, 1850, 400, 0, 3200, 900 }, Location.create(3001, 3032, 0), 4, 15) { + @Override + public boolean checkTravel(Player player) { + return requireQuest(player, Quests.THE_GRAND_TREE, "to go there."); + } + }, + OO_GLOG(Location.create(2623, 2857, 0), 33, new int[] { 300, 3400, 2000, 1100, 5000, 2800, 1400, 900, 3200, 0}, Location.create(2622, 2857, 0), 11, 22), + MOS_LE_HARMLESS(Location.create(3671, 2931, 0), 31, new int[] { 1550, 1250, 2050, 0, 2050, 0, 650, 550, 3200, 1000 }, Location.create(3671, 2933, 0), 9, 20) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.CABIN_FEVER); + } + }; /** * Constructs a new {@code ShipCharter} {@code Object}. @@ -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..2f6e9a7ed 100644 --- a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt +++ b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt @@ -1,9 +1,5 @@ package content.global.travel.trees -import core.api.isQuestComplete -import core.api.openDialogue -import core.api.sendDialogue -import core.api.teleport import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -12,15 +8,16 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import content.region.kandarin.quest.tree.TreeGnomeVillage import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld.Pulser import core.tools.END_DIALOGUE +import content.data.Quests +import core.api.* class GnomeSpiritTreeListener: InteractionListener { - val spiritTrees = intArrayOf(1317,1293,1294) + val spiritTrees = intArrayOf(1317,1293,1294,8355) override fun defineListeners() { on(spiritTrees, IntType.SCENERY, "talk-to"){ player, _ -> @@ -39,14 +36,18 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { Location(2542, 3170, 0), Location(2461, 3444, 0), Location(2556, 3259, 0), - Location(3184, 3508, 0) + Location(3184, 3508, 0), + Location(3060, 3256, 0), //Port Sarim + Location(2613, 3856, 0), //Etceteria + Location(2800, 3203, 0) //Brimhaven + ) private val ANIMATIONS = arrayOf(Animation(7082), Animation(7084)) private val GRAPHICS = arrayOf(Graphics(1228), Graphics(1229)) fun hasQuestCompleted(player: Player): Boolean { - if (!isQuestComplete(player, TreeGnomeVillage.questName)) { + if (!isQuestComplete(player, Quests.TREE_GNOME_VILLAGE)) { sendDialogue(player, "The tree doesn't feel like talking.") stage = END_DIALOGUE return false @@ -86,6 +87,15 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { stage = END_DIALOGUE return } + + var plantedSpiritTreeLocation = when { + getVarbit(player!!, 720) == 20 -> "Port Sarim" + getVarbit(player!!, 722) == 20 -> "Etceteria" + getVarbit(player!!, 724) == 20 -> "Brimhaven" + else -> null + //There should never be a case where more than one spirit tree is planted. + } + if(plantedSpiritTreeLocation == null) { when (stage) { 0 -> interpreter!!.sendOptions( "Where would you like to go?", @@ -100,6 +110,29 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { 3 -> sendTeleport(player!!, LOCATIONS[2]) 4 -> sendTeleport(player!!, LOCATIONS[3]) } + } + } + else when (stage) { + 0 -> interpreter!!.sendOptions( + "Where would you like to go?", + "Tree Gnome Village", + "Tree Gnome Stronghold", + "Battlefield of Khazard", + "Grand Exchange", + plantedSpiritTreeLocation + ).also { stage++ } + 1 -> when (buttonID) { + 1 -> sendTeleport(player!!, LOCATIONS[0]) + 2 -> sendTeleport(player!!, LOCATIONS[1]) + 3 -> sendTeleport(player!!, LOCATIONS[2]) + 4 -> sendTeleport(player!!, LOCATIONS[3]) + 5 -> when (plantedSpiritTreeLocation) { + "Port Sarim" -> sendTeleport(player!!,LOCATIONS[4]) + "Etceteria" -> sendTeleport(player!!,LOCATIONS[5]) + "Brimhaven" -> sendTeleport(player!!,LOCATIONS[6]) + else -> stage = END_DIALOGUE + } + } } } } diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt index 54aeb81ba..901bec497 100644 --- a/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt +++ b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt @@ -1,66 +1,67 @@ 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 + * @author Ceikry, Player Name */ -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("near the River Salve", 43, 5146, Location.create(3396, 3464, 0), 216.2, 8065), + RAG_AND_BONE("near the Rag and Bone Man", 43, 5147, Location.create(3343, 3510, 0), 235.8, 8066), + JOLLY_BOAR("north of the Jolly Boar", 48, 5148, Location.create(3278, 3525, 0), 193.8, 8067), + NORTH_VARROCK_CASTLE("north of Varrock Palace", 53, 5149, Location.create(3236, 3527, 0), 178.5, 8068), + GRAND_EXCHANGE("north of the Grand Exchange", 59, 5150, Location.create(3170, 3536, 0), 194.3, 8069), + EDGEVILLE("near Edgeville", 62, 5151, Location.create(3087, 3516, 0), 86.7, 8070), + MONASTERY("near the Black Knights' Fortress", 68, 5152, Location.create(3034, 3518, 0), 224.4, 8071), + GOBLIN_VILLAGE("near Goblin Village", 72, 5153, Location.create(2968, 3516, 0), 194.8, 8072), + BURTHORPE("near Burthorpe", 76, 5154, Location.create(2940, 3565, 0), 195.3, 8073), + DEATH_PLATEAU("east of Death Plateau", 79, 5155, Location.create(2944, 3622, 0), 249.9, 8074), + TROLLHEIM("east of Trollheim", 83, 5156, Location.create(2939, 3680, 0), 201.0, 8075), + GWD("east of the God Wars dungeon temple", 87, 5157, Location.create(2937, 3773, 0), 255.0, 8076), + TEMPLE("north of the small temple in the Wilderness", 89, 5158, Location.create(2946, 3836, 0), 198.9), + PLATEAU("on the Frozen Waste 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, true) } fun diminish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,3).send(player) + setVarbit(player, varbit, 3, true) } fun extinguish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,0).send(player) + setVarbit(player, varbit, 0, true) } fun lightGnomish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,4).send(player) + setVarbit(player, varbit, 4, true) } 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 +71,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..1522add6c 100644 --- a/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt +++ b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt @@ -4,12 +4,23 @@ 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.task.Pulse 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 +import content.data.Quests +import core.api.delayScript +import core.api.getAttribute +import core.api.getOrStartTimer +import core.api.getQuestStage +import core.api.lock +import core.api.queueScript +import core.api.removeAttribute +import core.api.sendMessage +import core.api.setAttribute +import core.api.setQuestStage +import core.api.stopExecuting +import core.game.interaction.QueueStrength 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) @@ -17,32 +28,32 @@ private val LIGHT_ANIM = Animation(7307) /** * Handles interactions for beacons - * @author Ceikry + * @author Ceikry, Player Name */ 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 +61,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){ @@ -83,113 +96,80 @@ class AFUBeaconListeners : InteractionListener { val logs = getLogs(player,20) if (logs.id != 0 && player.inventory.remove(logs)) { - player.lock() - - var session: AFUSession? = null - if(questComplete){ - session = player.getAttribute("afu-session", null) - if(session == null) { - session = AFUSession(player) - session.init() + lock(player, FILL_ANIM.duration) + queueScript(player, 0, QueueStrength.SOFT) { stage -> + if (stage == 1) { + player.animator.animate(FILL_ANIM) + return@queueScript delayScript(player, FILL_ANIM.duration) } + setAttribute(player, "/save:beacon:${beacon.ordinal}:logsId", logs.id) + beacon.fillWithLogs(player) + return@queueScript stopExecuting(player) } - - GameWorld.Pulser.submit(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 0 -> player.animator.animate(FILL_ANIM) - 1 -> { - beacon.fillWithLogs(player) - if(questComplete){ - session?.setLogs(beacon.ordinal,logs) - } - player.varpManager.flagSave(beacon.varpId); - } - 2 -> player.unlock().also {player.animator.animate(Animation.RESET); return true } - } - return false - } - }) } else { player.dialogueInterpreter.sendDialogue("You need some logs to do this.") } } - fun lightBeacon(player: Player, beacon: content.minigame.allfiredup.AFUBeacon, questComplete: Boolean){ - var session: AFUSession? = null - if(questComplete){ - session = player.getAttribute("afu-session",null) - if(session == null) return - } - + fun lightBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean) { if(player.inventory.contains(Items.TINDERBOX_590,1)){ - player.lock() - GameWorld.Pulser.submit(object: Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player.animator.animate(LIGHT_ANIM) - 1 -> { - beacon.light(player) - if(questComplete){ - session?.startTimer(beacon.ordinal) - if(session?.getLitBeacons() == 6 && !player.hasFireRing()){ - player.sendMessage("Congratulations on lighting 6 beacons at once! King Roald has something for you.") - player.setAttribute("/save:afu-mini:ring",true) - } - if(session?.getLitBeacons() == 10 && !player.hasFlameGloves()){ - player.sendMessage("Congratulations on lighting 10 beacons at once! King Roald has something for you.") - player.setAttribute("/save:afu-mini:gloves",true) - } - if(session?.getLitBeacons() == 14 && !player.hasInfernoAdze()){ - player.sendMessage("Congratulations on lighting all 14 beacons! King Roald has something special for you.") - player.setAttribute("/save:afu-mini:adze",true) - } - var experience = beacon.experience - 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) - } - } - 2 -> player.unlock().also { return true } - } - return false + lock(player, LIGHT_ANIM.duration) + queueScript(player, 0, QueueStrength.SOFT) { stage -> + if (stage == 1) { + player.animator.animate(LIGHT_ANIM) + return@queueScript delayScript(player, LIGHT_ANIM.duration) } - }) + val logsId = getAttribute(player, "/save:beacon:${beacon.ordinal}:logsId", Items.LOGS_1511) + removeAttribute(player, "/save:beacon:${beacon.ordinal}:logsId") + beacon.light(player) + if (!questComplete) { + setQuestStage(player, Quests.ALL_FIRED_UP, getQuestStage(player, Quests.ALL_FIRED_UP) + 10) + return@queueScript stopExecuting(player) + } + val timer = getOrStartTimer(player) + timer.addTime(beacon.ordinal, logsId, 20) + val lit = timer.getLitBeacons() + if (lit == 6 && !player.hasFireRing()) { + sendMessage(player, "Congratulations on lighting 6 beacons at once! King Roald has something for you.") + setAttribute(player, "/save:afu-mini:ring", true) + } + if (lit == 10 && !player.hasFlameGloves()) { + sendMessage(player, "Congratulations on lighting 10 beacons at once! King Roald has something for you.") + setAttribute(player, "/save:afu-mini:gloves", true) + } + if (lit == 14 && !player.hasInfernoAdze()) { + sendMessage(player, "Congratulations on lighting all 14 beacons! King Roald has something special for you.") + setAttribute(player, "/save:afu-mini:adze", true) + } + val xp = beacon.experience + timer.getBonusExperience() + player.skills.addExperience(Skills.FIREMAKING, xp) + return@queueScript stopExecuting(player) + } } else { player.dialogueInterpreter.sendDialogue("You need a tinderbox to light this.") } } - fun restoreBeacon(player: Player, beacon: content.minigame.allfiredup.AFUBeacon, questComplete: Boolean){ - var session: AFUSession? = null - if(questComplete){ - session = player.getAttribute("afu-session",null) - if(session == null) return - } - + fun restoreBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ val logs = getLogs(player, 5) if (logs.id != 0 && player.inventory.remove(logs)) { - player.lock() - GameWorld.Pulser.submit(object: Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player.animator.animate(FILL_ANIM) - 1 -> beacon.light(player).also { - if(questComplete){ - session?.refreshTimer(beacon,logs.id) - } else { - player.questRepository.getQuest("All Fired Up").setStage(player, 80) - } - } - 2 -> player.unlock().also { return true } - } - return false + lock(player, FILL_ANIM.duration) + queueScript(player, 0, QueueStrength.SOFT) { stage -> + if (stage == 1) { + player.animator.animate(LIGHT_ANIM) + return@queueScript delayScript(player, LIGHT_ANIM.duration) } - }) + val backupLogsId = getAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", Items.LOGS_1511) + removeAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId") + beacon.light(player) + if (!questComplete) { + setQuestStage(player, Quests.ALL_FIRED_UP, 80) + return@queueScript stopExecuting(player) + } + val timer = getOrStartTimer(player) + timer.addTime(beacon.ordinal, backupLogsId, 5) + return@queueScript stopExecuting(player) + } } else { player.dialogueInterpreter.sendDialogue("You need some logs to do this.") } @@ -213,4 +193,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..22e87dfdc 100644 --- a/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt +++ b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt @@ -1,5 +1,6 @@ package content.minigame.allfiredup +import content.data.Quests import core.game.node.entity.impl.ForceMovement import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills @@ -11,7 +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 java.util.* +import core.api.* /** * Handles repairing and climbing of the 3 beacon shortcuts needed to access them @@ -25,26 +26,74 @@ class AFURepairClimbHandler : InteractionListener { override fun defineListeners() { on(repairIDs, IntType.SCENERY, "repair"){ player, _ -> - var rco: RepairClimbObject = RepairClimbObject.GWD - for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent - repair(player,rco) - return@on true + if (hasRequirement(player, Quests.ALL_FIRED_UP)){ + val rco: RepairClimbObject? = getClimbingObject(player) + repair(player,rco!!) + return@on true + } + return@on false } on(climbIDs, IntType.SCENERY, "climb"){ player, node -> - var rco: RepairClimbObject = RepairClimbObject.GWD - for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent - climb(player,rco,node.location) + val rco: RepairClimbObject? = getClimbingObject(player) + climb(player,rco!!,node.location) return@on true } } + private fun getClimbingObject(player: Player): RepairClimbObject?{ + for(ent in RepairClimbObject.values()) + if(ent.destinationDown?.withinDistance(player.location,2) == true || + ent.destinationUp?.withinDistance(player.location,2) == true){ + return ent + } + return RepairClimbObject.GWD //the only one that does not have down/up destinations + } + private fun repair(player: Player,rco: RepairClimbObject){ + if (rco == RepairClimbObject.TEMPLE){ + // You can do this 2 different ways + val hasSmithingLevel = getStatLevel(player, Skills.SMITHING) >= 70 + val hasConstructionLevel = getStatLevel(player, Skills.CONSTRUCTION) >= 59 + + if (!hasConstructionLevel && !hasSmithingLevel){ + sendDialogue(player, "You need level 70 smithing or 59 construction for this.") + return + } + + val hasHammer = inInventory(player, Items.HAMMER_2347) + val hasSmithingItems = hasHammer && inInventory(player, Items.IRON_BAR_2351, 2) + val hasConstructionItems = hasHammer && inInventory(player, Items.PLANK_960, 2) + + if (hasSmithingLevel && hasSmithingItems){ + if (removeItem(player,Item(Items.IRON_BAR_2351, 2))) { + setVarbit(player, rco.varbit, 1, true) + return + } + } + // Only check this if the smithing repair didn't work + if (hasConstructionLevel && hasConstructionItems){ + val nails = NailType.get(player, 4) + if (nails != null){ + if (removeItem(player, Item(Items.PLANK_960, 2)) && removeItem(player, Item(nails.itemId, 4))) { + setVarbit(player, rco.varbit, 1, true) + return + } + } + } + + var msg = "You need " + msg += if (hasSmithingLevel) "a hammer and 2 iron bars" else "" + msg += if (hasSmithingLevel && hasConstructionLevel) " or " else "" + msg += if (hasConstructionLevel) "a hammer, 2 planks and 4 nails for this." else " for this." + sendDialogue(player, msg) + return + } val skill = rco.levelRequirement?.first ?: 0 val level = rco.levelRequirement?.second ?: 0 - if(player.skills.getLevel(skill) < level){ - player.dialogueInterpreter.sendDialogue("You need level $level ${Skills.SKILL_NAME[skill]} for this.") + if(getStatLevel(player, skill) < level){ + sendDialogue(player, "You need level $level ${Skills.SKILL_NAME[skill]} for this.") return } @@ -52,60 +101,52 @@ class AFURepairClimbHandler : InteractionListener { val requiredItems = when(rco){ RepairClimbObject.DEATH_PLATEAU -> { - arrayOf(Item(Items.PLANK_960,2)) + Item(Items.PLANK_960,2) } RepairClimbObject.BURTHORPE -> { - arrayOf(Item(Items.IRON_BAR_2351,2)) + Item(Items.IRON_BAR_2351,2) } RepairClimbObject.GWD -> { requiresNeedle = true - arrayOf(Item(Items.JUTE_FIBRE_5931,3)) - } - - RepairClimbObject.TEMPLE -> { - arrayOf(Item(Items.IRON_BAR_2351,2)) + Item(Items.JUTE_FIBRE_5931,3) } + else -> return } if(requiresNeedle){ - if(player.inventory.containsItem(Item(Items.NEEDLE_1733)) && player.inventory.containItems(*requiredItems.map { it.id }.toIntArray())) { - player.inventory.remove(*requiredItems) - if (Random().nextBoolean()) player.inventory.remove(Item(Items.NEEDLE_1733)) - } else { - player.dialogueInterpreter.sendDialogue("You need a needle and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") + if (!inInventory(player, Items.NEEDLE_1733) || !removeItem(player, requiredItems)) { + sendDialogue(player, "You need a needle and ${requiredItems.amount} ${requiredItems.name.lowercase()}s for this.") return } } else { - if(player.inventory.containsItem(Item(Items.HAMMER_2347)) && player.inventory.containItems(*requiredItems.map { it.id }.toIntArray())) { + if(inInventory(player, Items.HAMMER_2347) && inInventory(player, requiredItems.id, requiredItems.amount)) { val nails = NailType.get(player,4) if(nails == null && rco == RepairClimbObject.DEATH_PLATEAU){ - player.dialogueInterpreter.sendDialogue("You need 4 nails for this.") + sendDialogue(player, "You need 4 nails for this.") return } else if (rco == RepairClimbObject.DEATH_PLATEAU){ - player.inventory.remove(Item(nails.itemId,4)) + removeItem(player, Item(nails.itemId,4)) } - player.inventory.remove(*requiredItems) + removeItem(player, requiredItems) } else { - player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") + sendDialogue(player, "You need a hammer and ${requiredItems.amount} ${requiredItems.name.lowercase()}s for this.") return } } - - 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 +165,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 deleted file mode 100644 index ef4f837df..000000000 --- a/Server/src/main/content/minigame/allfiredup/AFUSession.kt +++ /dev/null @@ -1,121 +0,0 @@ -package content.minigame.allfiredup - -import core.api.LogoutListener -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 org.rs09.consts.Items -import core.tools.colorize - -/** - * Handles keeping track of lit beacons and their burn time remaining - * @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 logInventories = Array(14){Item(0,0)} - private val beaconWatched = Array(14){false} - private var isActive = false - - fun init() { - isActive = true - GameWorld.Pulser.submit(object: Pulse(){ - override fun pulse(): Boolean { - player!!.setAttribute("afu-pulse",this) - beaconTimers.forEach {timer -> - timer.ticks-- - if(timer.ticks == 300) timer.beacon.diminish(player).also { - if(beaconWatched[timer.beacon.ordinal]){ - beaconWatched[timer.beacon.ordinal] = false - timer.ticks += (getTicks(logInventories[timer.beacon.ordinal].id) * 5) - timer.beacon.light(player) - player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_"," ")} watcher has used your backup logs.")) - } else { - player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_", " ")} beacon is dying!")) - } - } - if(timer.ticks == 0) timer.beacon.extinguish(player).also { player.sendMessage(colorize("%RThe ${timer.beacon.name.toLowerCase().replace("_"," ")} beacon has gone out!")) } - } - return !isActive - } - }) - player!!.setAttribute("afu-session",this) - } - - fun getLitBeacons(): Int{ - return beaconTimers.count { it.ticks > 0 } - } - - fun end(){ - isActive = false - } - - fun setLogs(beaconIndex: Int, logs: Item){ - logInventories[beaconIndex] = logs - } - - fun startTimer(beaconIndex: Int){ - val ticks = getTicks(logInventories[beaconIndex].id) * 20 - logInventories[beaconIndex] = Item(0,0) - beaconTimers[beaconIndex].ticks = ticks - } - - fun refreshTimer(beacon: content.minigame.allfiredup.AFUBeacon, logID: Int){ - val ticks = getTicks(logID) * 5 - beaconTimers.forEach { - if(it.beacon.ordinal == beacon.ordinal) it.ticks += ticks - } - } - - fun setWatcher(index: Int, logs: Item){ - beaconWatched[index] = true - logInventories[index] = logs - } - - fun isWatched(index: Int): Boolean{ - return beaconWatched[index] - } - - fun getTicks(logID: Int): Int{ - val ticks = when(logID){ - Items.LOGS_1511 -> 65 - Items.OAK_LOGS_1521 -> 68 - Items.WILLOW_LOGS_1519 -> 73 - Items.MAPLE_LOGS_1517 -> 79 - Items.YEW_LOGS_1515 -> 83 - Items.MAGIC_LOGS_1513 -> 90 - else -> 0 - } - return ticks - } - - fun getBonusExperience(): Double{ - return when(getLitBeacons()){ - 1 -> 608.4 - 2 -> 1622.4 - 3 -> 1987.4 - 4 -> 2149.6 - 5 -> 2149.6 - 6 -> 2514.6 - 7 -> 2555.2 - 8 -> 2758.0 - 9 -> 2839.1 - 10 -> 3041.9 - 11 -> 3123.0 - 12 -> 3244.7 - 13 -> 3366.4 - 14 -> 4867.4 - else -> 0.0 - } - } - - override fun logout(player: Player) { - content.minigame.allfiredup.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) -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/allfiredup/AFUTimer.kt b/Server/src/main/content/minigame/allfiredup/AFUTimer.kt new file mode 100644 index 000000000..b7ee735b4 --- /dev/null +++ b/Server/src/main/content/minigame/allfiredup/AFUTimer.kt @@ -0,0 +1,104 @@ +package content.minigame.allfiredup + +import core.api.getAttribute +import core.api.log +import core.api.removeAttribute +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.tools.Log +import core.tools.colorize +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Items + +class AFUTimer : PersistTimer(1, "AFU timer", isSoft = true) { + private val burnTicks = Array(14) { 0 } + + fun addTime(index: Int, logsId: Int, numLogs: Int) { + val ticksPerLog = when (logsId) { + Items.LOGS_1511 -> 65 + Items.OAK_LOGS_1521 -> 68 + Items.WILLOW_LOGS_1519 -> 73 + Items.MAPLE_LOGS_1517 -> 79 + Items.YEW_LOGS_1515 -> 83 + Items.MAGIC_LOGS_1513 -> 90 + else -> 0 + } + burnTicks[index] += numLogs * ticksPerLog + } + + fun getBonusExperience(): Double { + return when (getLitBeacons()) { + 1 -> 608.4 + 2 -> 1622.4 + 3 -> 1987.4 + 4 -> 2149.6 + 5 -> 2149.6 + 6 -> 2514.6 + 7 -> 2555.2 + 8 -> 2758.0 + 9 -> 2839.1 + 10 -> 3041.9 + 11 -> 3123.0 + 12 -> 3244.7 + 13 -> 3366.4 + 14 -> 4867.4 + else -> 0.0 + } + } + + fun getLitBeacons(): Int { + return burnTicks.count { it > 0 } + } + + override fun save(root: JSONObject, entity: Entity) { + val beaconArray = JSONArray() + for (i in 0..13) { + beaconArray.add(burnTicks[i]) + } + root["beacons"] = beaconArray + } + + override fun parse(root: JSONObject, entity: Entity) { + val beacons = root["beacons"] as JSONArray + for (i in 0..13) { + burnTicks[i] = beacons[i].toString().toInt() + } + } + + override fun run(entity: Entity) : Boolean { + if (entity !is Player) { + log(this::class.java, Log.ERR, "How did a non-Player light an AFU beacon?") + return false + } + val player = entity + var keepRunning = false + for (i in 0..13) { + val beacon = AFUBeacon.values()[i] + if (burnTicks[i] > 0) { + burnTicks[i]-- + if (burnTicks[i] > 0) { + keepRunning = true + } + if (burnTicks[i] == 300) { + val backupLogsId = getAttribute(player, "/save:beacon:$i:backupLogsId", 0) + if (backupLogsId != 0) { + addTime(i, backupLogsId, 5) + removeAttribute(player, "/save:beacon:$i:backupLogsId") + sendMessage(player, colorize("%RThe beacon watcher ${beacon.title} has used your backup logs.")) + } else { + beacon.diminish(player) + sendMessage(player, colorize("%RThe beacon ${beacon.title} is dying!")) + } + } else if (burnTicks[i] == 0) { + beacon.extinguish(player) + removeAttribute(player, "/save:beacon:$i:logsId") + sendMessage(player, colorize("%RThe beacon ${beacon.title} has gone out!")) + } + } + } + return keepRunning + } +} \ 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..d2cf48053 100644 --- a/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt +++ b/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt @@ -1,48 +1,49 @@ package content.minigame.allfiredup -import core.game.dialogue.DialoguePlugin +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 core.api.getAttribute +import core.api.setAttribute 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 && getAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", 0) == 0) { options("Can you watch this beacon for me?","Nevermind.").also { stage = 10 } } else { npc("Carry on, adventurer.").also { stage = 1000 } @@ -60,7 +61,7 @@ class BeaconTenderDialogue(player: Player? = null) : core.game.dialogue.Dialogue 13 -> npc("Great, hand them over.").also { stage++ } 14 -> player("Here you go!").also { player.inventory.remove(logs) - session?.setWatcher(index,logs) + setAttribute(player, "/save:beacon:${beacon.ordinal}:backupLogsId", logs.id) stage = 1000 } @@ -88,4 +89,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/BarbAssaultArea.kt b/Server/src/main/content/minigame/barbassault/BarbAssaultArea.kt new file mode 100644 index 000000000..7c95758ba --- /dev/null +++ b/Server/src/main/content/minigame/barbassault/BarbAssaultArea.kt @@ -0,0 +1,18 @@ +package content.minigame.barbassault + +import core.api.MapArea +import core.api.getRegionBorders +import core.game.node.entity.Entity +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneType + +class BarbAssaultArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(7509)) + } + + override fun areaEnter(entity: Entity) { + zone.zoneType = ZoneType.BARBARIAN_ASSAULT.id + super.areaEnter(entity) + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt index f1d91e854..d4b31bc8e 100644 --- a/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt +++ b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt @@ -6,22 +6,26 @@ 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,28 +35,32 @@ 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++ } - 11 -> options("Here you go!","Nevermind.").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "Alright, then, that'll be %,d gold please.".format(TORSO_PRICE)).also { stage++ } + 11 -> options("Here you go!","Never mind.").also { stage++ } 12 -> when(buttonId){ - 1 -> if(inInventory(player, 995, 7500000)) - 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)) { - addItem(player, Items.FIGHTER_TORSO_10551, 1) + if (hasSpaceFor(player, Item(Items.FIGHTER_TORSO_10551)) || amountInInventory(player, Items.COINS_995) == TORSO_PRICE) { + npcl(FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") + if (removeItem(player, Item(Items.COINS_995, TORSO_PRICE), Container.INVENTORY)) { + addItem(player, Items.FIGHTER_TORSO_10551, 1) + } + } else { + npcl(FacialExpression.FRIENDLY, "Sorry, you don't have space for it! Give my regards to Player Name - he made me check this before I take your cash.") } stage = END_DIALOGUE } diff --git a/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java index b5089c978..48b95f68f 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,7 @@ 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) { + if (getVarp(player, 452) == 0) { shuffleCatacombs(player); } sendConfiguration(player); @@ -212,7 +213,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 +259,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 +293,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 +335,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..ccb57c985 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) + BossKillCounter.addToBarrowsChestCount(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..5bbdedef1 100644 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt @@ -1,360 +1,260 @@ 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-- - } - if (stoveCoke > 0) { - if (stoveTemp < 100) { - stoveTemp += 1 - } - } 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) + 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) } } - /**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 areaEnter(entity: Entity) { + if (entity is Player) { + playersInArea.add(entity) + val state = getPlayerState(entity) + for (ore in state.oresOnBelt) + ore.createNpc() } } - /**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 - } - } - } - } - } - 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) - } - else if(playerOre.isEmpty()) { - player.varpManager.get(543).setVarbit(12,0).send(player) - } - if(playerOre.isEmpty() && player.getAttribute("OreInPot",false)){ - player.removeAttribute("OreInPot") + 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 } } } - /**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 + 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 } - /**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 - } + 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 } - } - 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) + rewardXP(pumper!!, Skills.STRENGTH, 4.0) } } - /**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) + 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 (!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) + if (oresPedaled) { + rewardXP(pedaler!!, Skills.AGILITY, 2.0) + pedaler!!.settings.runEnergy -= 2 } } - /**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???*/ + 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) + } - 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) - } - } + 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 } - 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) - } - } + + 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))) + addOreToBelt(p, oreId, maxAmt) } } + + 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 (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) + } + } + + 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) + } } -} +} \ 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..deaaae199 100644 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt @@ -1,382 +1,314 @@ 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.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) + } } 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") } 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 deleted file mode 100644 index 73d3c1369..000000000 --- a/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt +++ /dev/null @@ -1,120 +0,0 @@ -package content.minigame.bountyhunter - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Provides dialogue tree for Maximillian Sackville, - * the Bounty Hounter roving banker. - * - * @author vddCore - */ -@Initializable -class MaximillianSackvilleDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> when { - hasIronmanRestriction(player, IronmanMode.ULTIMATE) -> { - npcl( - core.game.dialogue.FacialExpression.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, - "Good day, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - } - - 1 -> npcl( - core.game.dialogue.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?" - ).also { stage++ } - - 3 -> npcl( - core.game.dialogue.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), - IfTopic( - core.game.dialogue.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) - ) - - 5 -> npcl( - core.game.dialogue.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, - "This allows us to provide our services to customers regardless of their current " + - "whereabouts. Our desire to serve is stronger than our fear of the Wilderness." - ).also { stage = END_DIALOGUE } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - toggleBankAccount(player) - - npcl( - core.game.dialogue.FacialExpression.NEUTRAL, - "Naturally. You can now access your ${getBankAccountName(player)} bank account." - ).also { stage = END_DIALOGUE } - } - - 12 -> { - openBankPinSettings(player) - end() - } - - 13 -> { - openGrandExchangeCollectionBox(player) - end() - } - } - - return true - } - - override fun getIds() = intArrayOf(NPCs.BANKER_6538) -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt b/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt new file mode 100644 index 000000000..60050f9c7 --- /dev/null +++ b/Server/src/main/content/minigame/bountyhunter/UnimplementedCraterArea.kt @@ -0,0 +1,75 @@ +package content.minigame.bountyhunter.handlers + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile +import core.game.world.GameWorld + +class UnimplementedCraterArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(3200, 5632, 3391, 5823) + ) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player && ( + defineAreaBorders()[0].insideBorder(entity) + )) { + kickThemOut(entity) + } + } + + private fun kickThemOut(entity: Player) { + val watchdog = NPC(NPCs.BANKER_6538) + watchdog.isNeverWalks = true + watchdog.isWalks = false + watchdog.location = entity.location + watchdog.init() + entity.lock() + + runTask(watchdog, 1) { + watchdog.moveStep() + watchdog.face(entity) + openDialogue(entity, UnimplementedCraterDialogue(), watchdog) + GameWorld.Pulser.submit(object : Pulse() { + override fun pulse(): Boolean { + if (getAttribute(entity, "teleporting-away", false)) + return true + if (!entity.isActive) + poofClear(watchdog) + if (entity.dialogueInterpreter.dialogue == null || entity.dialogueInterpreter.dialogue.file == null) + openDialogue(entity, UnimplementedCraterDialogue(), watchdog) + return !watchdog.isActive || !entity.isActive + } + }) + } + } + + class UnimplementedCraterDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.WORRIED, "This is unimplemented content, and you are now stuck. Don't worry, I'll get you out of here!").also { stage++ } + 1 -> { + end() + visualize(npc!!, 1818, 343) + sendGraphics(342, player!!.location) + setAttribute(player!!, "teleporting-away", true) + runTask(player!!, 3) { + poofClear(npc!!) + teleport(player!!, Location.create(3179, 3685, 0)) + unlock(player!!) + removeAttribute(player!!, "teleporting-away") + } + } + } + } + } +} + diff --git a/Server/src/main/content/minigame/castlewars/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/CastlewarsBook.kt b/Server/src/main/content/minigame/castlewars/CastlewarsBook.kt new file mode 100644 index 000000000..8395ece6a --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/CastlewarsBook.kt @@ -0,0 +1,146 @@ +package content.minigame.castlewars + +import content.data.Quests +import content.global.handlers.iface.BookInterface +import content.global.handlers.iface.BookLine +import content.global.handlers.iface.Page +import content.global.handlers.iface.PageSet +import core.api.getAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_MODEL_NUMBER +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_PITCH +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_YAW +import core.game.system.command.sets.ModelViewerCommandSet.Companion.ATTRIBUTE_ZOOM +import core.game.system.command.sets.ModelViewerCommandSet.Companion.DEF_BOOK +import core.game.world.GameWorld +import org.rs09.consts.Items + +class CastlewarsBook : InteractionListener { + + companion object { + private val TITLE = "Castle Wars Manual" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Objective:", 38), + BookLine("The aim is to get into your", 39), + BookLine("opponents castle and take", 40), + BookLine("their team standard. Then", 41), + BookLine("bring that back and capture", 42), + BookLine("it on your teams standard. ", 43), + ), + Page( + BookLine("Toolkit:", 58), + BookLine("This useful item allows you", 59), + BookLine("to repair broken doors and", 60), + BookLine("catapults. Simply use it on", 61), + BookLine("the item to be repaired, or", 62), + BookLine("have one in your inventory", 63), + BookLine("when you select the option,", 64), + BookLine("and you'll rebuild it!", 65) + ) + ), + PageSet( + Page( + BookLine("Bandages:", 43), + BookLine("These can be used to heal", 44), + BookLine("some health and restore some", 45), + BookLine("of your running energy.", 46), + BookLine("You can also use them to", 47), + BookLine("heal fellow players.", 48), + ), + Page( + BookLine("Explosive Potion:", 58), + BookLine("A simple but effective item,", 59), + BookLine("use it to blow up your", 60), + BookLine("opponents catapult and", 61), + BookLine("barricades! It can also be", 62), + BookLine("used to clear the tunnels", 63), + BookLine("under the arena for some,", 64), + BookLine("sneak attacks into your", 65), + BookLine("opponents castle!", 66) + ) + ), + PageSet( + Page( + BookLine("Barricade:", 43), + BookLine("Use these constructs to block", 44), + BookLine("your opponents movement", 45), + BookLine("and prevent them accessing", 46), + BookLine("your castle. Each team can", 47), + BookLine("only have 10 built at any one", 48), + BookLine("time.", 49), + ), + Page( + BookLine("Bucket:", 58), + BookLine("Fill a bucket with water and", 59), + BookLine("you can use it to put out a", 60), + BookLine("burning catapult or barricade,", 61), + BookLine("but be quick or it'll be", 62), + BookLine("destroyed.", 63), + ) + ), + PageSet( + Page( + BookLine("Tinderbox:", 43), + BookLine("Logs aren't all that's", 44), + BookLine("flammable, use a tinderbox to", 45), + BookLine("set light to your opponents", 46), + BookLine("You can also use them to", 47), + BookLine("catapult and barricades.", 48), + ), + Page( + BookLine("Pickaxe:", 58), + BookLine("Use a pickaxe to mine your", 59), + BookLine("way through the tunnels", 60), + BookLine("under the arena for a sneak", 61), + BookLine("attack into your opponents", 62), + BookLine("castle. Don't forget to", 63), + BookLine("collapse the tunnels into your", 64), + BookLine(" own castle though! ", 65), + ) + ), + PageSet( + Page( + BookLine("Catapult:", 43), + BookLine("Use this war machine to", 44), + BookLine("launch rocks at your", 45), + BookLine("opponents. Just give it rough", 46), + BookLine("coordinates and let the rock", 47), + BookLine("fly, just be careful not to hit", 48), + BookLine("your team with it!", 49), + ), + Page( + BookLine("Rock:", 58), + BookLine("Used as ammo for the", 59), + BookLine("catapult, and not much else.", 60), + BookLine("Brings new meaning to the", 61), + BookLine("phrase 'flies like a rock'.", 62), + ) + ), + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_2_27, TITLE, CONTENTS) + BookInterface.clearModelsOnPage(player, BookInterface.FANCY_BOOK_2_27); + BookInterface.setItemOnPage(player, 0, Items.TOOLKIT_4051, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 1, Items.BANDAGES_4049, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 1, Items.EXPLOSIVE_POTION_4045, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 2, Items.BARRICADE_4053, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 2, Items.BUCKET_OF_WATER_1929, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setItemOnPage(player, 3, Items.TINDERBOX_590, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[2], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[2], 768, 192, 1792) + BookInterface.setItemOnPage(player, 3, Items.BRONZE_PICKAXE_1265, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + BookInterface.setModelOnPage(player, 4, 38202, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[4], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[4], 4048, 192, 768) + BookInterface.setItemOnPage(player, 4, Items.ROCK_4043, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[17], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[17], 768, 192, 1792) + return true + } + } + + override fun defineListeners() { + on(Items.CASTLEWARS_MANUAL_4055, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/castlewars/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..fb44ea7d9 --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt @@ -0,0 +1,100 @@ +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.game.world.map.zone.ZoneType +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) + zone.zoneType = ZoneType.CASTLE_WARS.id + 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..53f05bc13 100644 --- a/Server/src/main/content/minigame/mta/EnchantSpell.kt +++ b/Server/src/main/content/minigame/mta/EnchantSpell.kt @@ -1,6 +1,8 @@ package content.minigame.mta import content.minigame.mta.impl.EnchantingZone.Shapes +import core.ServerConstants +import core.api.replaceSlot import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.combat.spell.SpellType @@ -9,6 +11,8 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.combat.spell.MagicSpell import core.game.node.entity.combat.spell.Runes +import core.game.node.entity.player.info.LogType +import core.game.node.entity.player.info.PlayerMonitor import core.game.node.item.Item import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics @@ -47,7 +51,7 @@ class EnchantSpell : MagicSpell { return false } entity.interfaceManager.setViewedTab(6) - val enchanted = jewellery?.getOrDefault(target.id,null) + var enchanted = jewellery?.getOrDefault(target.id,null) if (enchanted == null) { entity.packetDispatch.sendMessage("You can't use this spell on this item.") @@ -56,12 +60,15 @@ class EnchantSpell : MagicSpell { if (!meetsRequirements(entity, true, true)) { return false } - - if (entity.inventory.remove(target)) { - visualize(entity, target) - entity.inventory.add(enchanted) + if (enchanted.id == Items.RING_OF_WEALTH_2572 && ServerConstants.RING_OF_WEALTH_TELEPORT) { + enchanted = Item(Items.RING_OF_WEALTH_14638) } + visualize(entity, target) + val ret = replaceSlot(entity, target.slot, enchanted) + if (ret != target) { + PlayerMonitor.log(entity, LogType.DUPE_ALERT, "Unknown slot-replacement problem when enchanting jewellery (adding $enchanted replaced $ret rather than $target)") + } //MTA-Specific Code if (entity.zoneMonitor.isInZone("Enchantment Chamber")) { @@ -86,7 +93,7 @@ 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) } } return true @@ -184,10 +191,10 @@ class EnchantSpell : MagicSpell { SpellBook.MODERN.register(51, EnchantSpell(68, 78.0, mapOf( //Begin Jewelry Enchantment - Items.DRAGONSTONE_RING_1645 to Item(14646), - Items.DRAGON_NECKLACE_1664 to Item(Items.SKILLS_NECKLACE4_11105), - Items.DRAGONSTONE_AMMY_1702 to Item(Items.AMULET_OF_GLORY4_1712), - Items.DRAGON_BRACELET_11115 to Item(Items.COMBAT_BRACELET4_11118), + Items.DRAGONSTONE_RING_1645 to Item(Items.RING_OF_WEALTH_2572), + Items.DRAGON_NECKLACE_1664 to Item(Items.SKILLS_NECKLACE_11113), + Items.DRAGONSTONE_AMMY_1702 to Item(Items.AMULET_OF_GLORY_1704), + Items.DRAGON_BRACELET_11115 to Item(Items.COMBAT_BRACELET_11126), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), diff --git a/Server/src/main/content/minigame/mta/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/TelekineticGrabSpell.java b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java index 7e1ac3530..72728d1e6 100644 --- a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java +++ b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java @@ -4,6 +4,7 @@ import content.minigame.mta.impl.TelekineticZone; import core.game.interaction.SpecialGroundItems; import core.game.node.Node; import core.game.node.entity.Entity; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.spell.SpellType; import core.game.node.entity.combat.spell.SpellBlocks; import core.game.node.entity.impl.Projectile; @@ -22,6 +23,9 @@ 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 static core.api.ContentAPIKt.*; /** * Represents the telekenitic grab spell. @@ -96,6 +100,8 @@ 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; @@ -141,7 +147,7 @@ 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())); } else { @@ -171,8 +177,12 @@ public final class TelekineticGrabSpell extends MagicSpell { } if (entity instanceof Player) { final Player player = (Player) entity; - if (!player.getInventory().hasSpaceFor(((Item) item))) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); + if (!CombatSwingHandler.isProjectileClipped(player, item, false)) { + sendMessage(player, "I can't reach that."); //TODO authentic message? + return false; + } + if (!hasSpaceFor(player, item)) { + sendMessage(player, "You don't have enough inventory space."); return false; } if (!PickupHandler.canTake(player, item, 1)) { diff --git a/Server/src/main/content/minigame/mta/impl/TelekineticZone.java b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java index 1ff0f9b7e..a810548d6 100644 --- a/Server/src/main/content/minigame/mta/impl/TelekineticZone.java +++ b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java @@ -32,7 +32,7 @@ import content.minigame.mta.MTAZone; /** * Handles the telekinetic zone. - * @author Vexia + * @author Vexia, Player Name */ public class TelekineticZone extends MTAZone { @@ -302,7 +302,7 @@ public class TelekineticZone extends MTAZone { } Location next = statue.getLocation().transform(dir, 1); Path path = Pathfinder.find(statue.getLocation(), next); - boolean end = !path.isSuccessful() || path.isMoveNear() || path.getPoints().size() != 1; + boolean end = !path.isSuccessful() || path.isMoveNear() || path.getPoints().size() > 2; if (end) { return true; } diff --git a/Server/src/main/content/minigame/pestcontrol/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..f1fb680a0 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. @@ -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..9140bfda2 100644 --- a/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java @@ -15,6 +15,8 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.getStatLevel; + /** * Represents the pest control reward interface. * @author 'Vexia @@ -185,20 +187,36 @@ public final class PCRewardInterface extends ComponentPlugin { } /** - * Method used to calculate the experience the player can recieve in this - * skill. + * Method used to calculate the experience the player can receive in this skill. * @param player the player. * @return the experience as an integer. */ - public static double calculateExperience(final Player player, final int skillId) { - int level = player.getSkills().getStaticLevel(skillId); - double divideBy = 30;//17.5-33 ideal range - if (skillId == Skills.PRAYER) { - divideBy = 67;// 34-75 ideal range - } else if (skillId == Skills.MAGIC || skillId == Skills.RANGE) { - divideBy = 29;//19.1-31 ideal range + public static int calculateExperience(final Player player, final int skillId, final int points) { + int level = getStatLevel(player, skillId); + int N = 0; + switch (skillId) { + case Skills.PRAYER: + N = 18; + break; + case Skills.MAGIC: + case Skills.RANGE: + N = 32; + break; + case Skills.ATTACK: + case Skills.STRENGTH: + case Skills.DEFENCE: + case Skills.HITPOINTS: + N = 35; + break; } - return (int) ((level * level) / divideBy) * (player.getSkills().experienceMutiplier / 2); + int xpPerPoint = (int) ((double) (level * level) / 600) * N; + double bonus = 1.0; + if (points >= 100) { + bonus = 1.1; + } else if (points >= 10) { + bonus = 1.01; + } + return (int) (points * xpPerPoint * bonus); } /** @@ -207,7 +225,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @param skillId the skillId. * @return the string to send. */ - public static final String getSkillCondition(final Player player, final int skillId) { + public static String getSkillCondition(final Player player, final int skillId) { if (player.getSkills().getStaticLevel(skillId) < 25) { return RED + "Must reach level 25 first."; } @@ -221,7 +239,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @return the string. */ public static String getSkillXp(final Player player, int skillId) { - return Skills.SKILL_NAME[skillId] + " - " + (int) calculateExperience(player, skillId) + " xp"; + return Skills.SKILL_NAME[skillId] + " - " + calculateExperience(player, skillId, 1) + " xp"; } /** @@ -229,7 +247,7 @@ public final class PCRewardInterface extends ComponentPlugin { * @param skill the skill index. * @return the skill child id. */ - public static final int getSkillChild(final int skill) { + public static int getSkillChild(final int skill) { return SKILL_HEADER[skill]; } @@ -264,7 +282,7 @@ public final class PCRewardInterface extends ComponentPlugin { * Method used to confirm the reward. * @param player the player. */ - public final void confirm(final Player player) { + public void confirm(final Player player) { if (!hasReward(player)) { player.getPacketDispatch().sendMessage("Please choose a reward."); return; @@ -281,9 +299,9 @@ public final class PCRewardInterface extends ComponentPlugin { if (player.getSavedData().getActivityData().getPestPoints() >= points) { player.getSavedData().getActivityData().decreasePestPoints(points); if (reward.isSkillReward()) { - final double experience = ((int) calculateExperience(player, reward.getSkill()) * points); + int experience = calculateExperience(player, reward.getSkill(), points); player.getSkills().addExperience(reward.getSkill(), experience); - message = "The Void Knight has granted you " + (int) (experience * player.getSkills().experienceMutiplier) + " " + reward.getName() + "."; + message = "The Void Knight has granted you " + experience + " " + reward.getName() + "."; } else { if (!reward.checkItemRequirement(player, option)) { return; diff --git a/Server/src/main/content/minigame/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..5ebad5751 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.") @@ -819,7 +820,7 @@ class GardenObjectsPlugin : InteractionListener { stage = 10 } 2 -> { - interpreter.sendDialogues(player, null, "What is th second thing you need?") + interpreter.sendDialogues(player, null, "What is the second thing you need?") stage = 20 } 3 -> { 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/troublebrewing/TroubleBrewingArea.kt b/Server/src/main/content/minigame/troublebrewing/TroubleBrewingArea.kt new file mode 100644 index 000000000..55a299b4e --- /dev/null +++ b/Server/src/main/content/minigame/troublebrewing/TroubleBrewingArea.kt @@ -0,0 +1,18 @@ +package content.minigame.troublebrewing + +import core.api.MapArea +import core.api.getRegionBorders +import core.game.node.entity.Entity +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneType + +class TroubleBrewingArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(15150)) + } + + override fun areaEnter(entity: Entity) { + zone.zoneType = ZoneType.TROUBLE_BREWING.id + super.areaEnter(entity) + } +} diff --git a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt index 8229c1530..04fe7686a 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,15 +519,14 @@ 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 } - npc.animate(Animation(437)) - npc.faceTemporary(player, 1) + // https://youtu.be/61jVjmXf8tU?t=45 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 +555,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 +572,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..e59b3d907 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.dialogue +import core.ServerConstants import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -18,17 +19,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)) { + 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 +41,28 @@ 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) + IfTopic( + FacialExpression.ASKING, + "Yes, but can you open a secondary bank account for me?", + 7, + ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) + ), + Topic(FacialExpression.ASKING, "Yes, but can you show me my PIN settings?", 5), + Topic(FacialExpression.ASKING, "Yes, but can you show me my collection box?", 6), + Topic(FacialExpression.ANNOYED, "Yes, thanks. And I'll keep hold of it too.", END_DIALOGUE) ) 3 -> { @@ -66,9 +73,9 @@ 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 } + ).also { stage = 2 } } 5 -> { @@ -80,10 +87,24 @@ class EmeraldBenedictDialogue(player: Player? = null) : core.game.dialogue.Dialo openGrandExchangeCollectionBox(player) end() } + + 7 -> { + npcl( + FacialExpression.SUSPICIOUS, + "Sure, just give me five million in gold and I'll take care of it." + ).also { stage++ } + } + + 8 -> { + playerl( + FacialExpression.SUSPICIOUS, + "On second thought, I think I'll ask somebody more reputable..." + ).also { stage = END_DIALOGUE } + } } return true } override fun getIds(): IntArray = intArrayOf(NPCs.EMERALD_BENEDICT_2271) -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt 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 deleted file mode 100644 index 188d96933..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt +++ /dev/null @@ -1,106 +0,0 @@ -package content.region.asgarnia.burthorpe.dialogue - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Provides a dialogue tree for Jade inside Warriors' Guild. - * - * @author vddCore - */ -@Initializable -class JadeDialogue(player: Player? = null) : core.game.dialogue.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, - "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, - "Greetings warrior, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - - 1 -> npcl( - core.game.dialogue.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), - IfTopic( - core.game.dialogue.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) - ) - - 3 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Oh, ever since the Guild opened. I like it here." - ).also { stage++ } - - 4 -> playerl( - core.game.dialogue.FacialExpression.ASKING, - "Why's that?" - ).also { stage++ } - - 5 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Well... What with all these warriors around, there's not much chance of my bank being robbed, is there?" - ).also { stage = 2 } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - openBankPinSettings(player) - end() - } - - 12 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 13 -> { - toggleBankAccount(player) - npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Of course! Your ${getBankAccountName(player)} account is now active!" - ).also { stage = END_DIALOGUE } - } - } - - return true - } - - override fun getIds(): IntArray = intArrayOf(NPCs.JADE_4296) -} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/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..60e6c86fd 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java @@ -1,5 +1,6 @@ package content.region.asgarnia.burthorpe.handlers; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import content.data.EnchantedJewellery; import core.game.global.action.DoorActionHandler; @@ -17,9 +18,16 @@ import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.hasRequirement; +import static core.api.ContentAPIKt.sendMessage; + +import content.data.Quests; +import org.rs09.consts.Items; + /** * Represents the hero guild. * @author Vexia + * @author Player Name */ @Initializable public final class HeroGuildPlugin extends OptionHandler { @@ -40,7 +48,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; } @@ -52,6 +61,7 @@ public final class HeroGuildPlugin extends OptionHandler { /** * Handles the recharging of dragonstone jewellery. * @author Vexia + * @author Player Name */ public static final class JewelleryRechargePlugin extends UseWithHandler { @@ -61,8 +71,7 @@ public final class HeroGuildPlugin extends OptionHandler { private static final int[] IDS = new int[] { 1710, 1708, 1706, 1704, 11107, 11109, 11111, 11113, 11120, 11122, 11124, 11126, 10354, 10356, 10358, 10360, 10362, 14644,14642,14640,14638, 2572 }; /** - * Constructs a new {@Code JewelleryRechargePlugin} {@Code - * Object} + * Constructs a new JewelleryRechargePlugin object */ public JewelleryRechargePlugin() { super(IDS); @@ -79,11 +88,24 @@ 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; //hasRequirement shows the message + } final EnchantedJewellery jewellery; assert event.getUsedItem() != null; jewellery = EnchantedJewellery.Companion.getIdMap().get(event.getUsedItem().getId()); - if (jewellery == null && event.getUsedItem().getId() != 2572) { - return true; + if (jewellery == null) { + return false; //nothing interesting happens + } + if (jewellery == EnchantedJewellery.RING_OF_WEALTH) { + if (!ServerConstants.RING_OF_WEALTH_TELEPORT) { + return false; + } + } + if (jewellery == EnchantedJewellery.COMBAT_BRACELET || jewellery == EnchantedJewellery.SKILLS_NECKLACE) { + if (!hasRequirement(player, Quests.LEGENDS_QUEST)) { + return true; + } } boolean fam = event.getUsedWith() instanceof NPC; if (fam && jewellery != EnchantedJewellery.AMULET_OF_GLORY & jewellery != EnchantedJewellery.AMULET_OF_GLORY_T) { @@ -102,9 +124,9 @@ public final class HeroGuildPlugin extends OptionHandler { player.getInventory().replace(rechargedItem, event.getUsedItem().getSlot()); String name = jewellery.getJewelleryName(rechargedItem); if (!fam) { - player.sendMessage("You dip the " + name + " in the fountain..."); + sendMessage(player, "You dip the " + name.toLowerCase() + " in the fountain..."); } else { - player.sendMessage("Your titan recharges the glory."); + sendMessage(player, "Your titan recharges the " + name.toLowerCase() + "."); } return true; } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/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..fd59d4697 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt @@ -0,0 +1,102 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import content.data.Quests +import content.global.handlers.iface.ScrollInterface +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.Components +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 + ) + val combinationScroll = arrayOf( + "Red is North of Blue. Yellow is South of Purple.", + "Green is North of Purple. Blue is West of", + "Yellow. Purple is East of Red." + ) + } + 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, _ -> + // https://youtu.be/C8n8Yi-J8wU?t=466 + openInterface(player, 222) + setInterfaceText(player, combinationScroll.joinToString("
"), 222, 5) + + 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..29978f609 --- /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.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.node.item.Item +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 + InteractionListeners.run(Items.COMBINATION_3102, IntType.ITEM, "read", player!!, Item(Items.COMBINATION_3102)) + } + } + } + } + } +} \ 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..b11f3845c 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: @@ -154,7 +155,7 @@ public final class ThuroDialogue extends DialoguePlugin { stage = 14; return true; } - player.getInventory().add(player.getSkills().getMasteredSkills() >= 1 ? ITEMS[1] : ITEMS[0]); + player.getInventory().add(player.getSkills().getMasteredSkills() > 1 ? ITEMS[1] : ITEMS[0]); player.getInventory().add(ITEMS[2]); interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "There you go! You're truley a master of Smithing."); stage = 16; @@ -487,7 +488,7 @@ public final class ThuroDialogue extends DialoguePlugin { stage = 14; return true; } - player.getInventory().add(player.getSkills().getMasteredSkills() >= 1 ? ITEMS[1] : ITEMS[0]); + player.getInventory().add(player.getSkills().getMasteredSkills() > 1 ? ITEMS[1] : ITEMS[0]); player.getInventory().add(ITEMS[2]); interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "There you go! You're truley a master of Smithing."); stage = 16; 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..b26643bba 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt @@ -3,12 +3,12 @@ package content.region.asgarnia.falador.dialogue import content.data.tables.BirdNest import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items -import core.game.diary.DiaryLevel +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.link.diary.* /** * Represents the Wyson the gardener dialogue. @@ -16,46 +16,53 @@ import core.game.diary.DiaryLevel * @version 1.0 */ @Initializable -class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { - /** - * If its a bird nest reward. - */ - private var birdNest = false - - /** - * Constructs a new `WysonTheGardenerDialogue` `Object`. - */ - constructor() { - /** - * empty. - */ - } +class WysonTheGardenerDialogue(player: Player? = null) : DialoguePlugin(player) { /** * Constructs a new `WysonTheGardenerDialogue` `Object`. + * Mole part dialogue source: https://www.youtube.com/watch?v=Dw-P9T7EhZk and https://www.youtube.com/watch?v=krZiIRupKbs * @param player the player. */ - constructor(player: Player?) : super(player) {} + //constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return WysonTheGardenerDialogue(player) } + /** + * Choose greeting. Either you have mole parts or just the normal greeting. + */ override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - birdNest = player.inventory.containsItem(MOLE_CLAW) || player.inventory.containsItem(MOLE_SKIN) - if (birdNest) { - npc("If I'm not mistaken, you've got some mole bits there!", "I'll trade 'em for bird nest if ye likes.") + if (inInventory(player, Items.MOLE_CLAW_7416, 1) && inInventory(player, Items.MOLE_SKIN_7418, 1)) { + npc("If I'm not mistaken, you've got some claws and skin", " from a big mole there! I'll trade 'em for bird nests if ye", "likes. Or was ye wantin' some woad leaves instead?") + stage = 102 + return true + } + if (inInventory(player, Items.MOLE_SKIN_7418, 1)) { + npc("If I'm not mistaken, you've got some skin from a big", "mole there! I'll trade it for bird nests if ye likes. Or", "was ye wantin' some woad leaves instead?") stage = 100 return true } + if (inInventory(player, Items.MOLE_CLAW_7416, 1)) { + npc("If I'm not mistaken, you've got some claws from a big", "mole there! I'll trade it for bird nests if ye likes. Or", "was ye wantin' some woad leaves instead?") + stage = 101 + return true + } npc("I'm the head gardener around here.", "If you're looking for woad leaves, or if you need help", "with owt, I'm yer man.") stage = 0 return true } + /** + * Dialogue. + */ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { + + /** + * Dialogue options: woad leaves. + */ 0 -> { options("Yes please, I need woad leaves.", "Sorry, but I'm not interested.") stage = 1 @@ -105,15 +112,15 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { npc("Mmmm... ok, that sounds fair.") stage = 131 } - 131 -> if (player.inventory.contains(995, 15)) { - player.inventory.remove(COINS[0]) - player.inventory.add(WOAD_LEAF) + 131 -> if (removeItem(player,Item(Items.COINS_995, 15) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 1) + player("Thanks.") - player.packetDispatch.sendMessage("You buy a woad leaf from Wyson.") + sendMessage(player, "You buy a woad leaf from Wyson.") stage = 132 } else { end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") + sendMessage(player, "You need 15 gold coins to buy a woad leaf.") } 132 -> { npc("I'll be around if you have any more gardening needs.") @@ -121,35 +128,70 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { } 133 -> end() 140 -> { - npc("Thanks for being generous", "here's an extra woad leave.") + npc("Thanks for being generous", "here's an extra woad leaf.") stage = 141 } - 141 -> if (player.inventory.contains(995, 20)) { - player.inventory.remove(COINS[1]) - var i = 0 - while (i < 2) { - player.inventory.add(WOAD_LEAF, player) - i++ - } + 141 -> if (removeItem(player,Item(Items.COINS_995, 20) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 2) player("Thanks.") - player.packetDispatch.sendMessage("You buy two woad leaves from Wyson.") + sendMessage(player, "You buy two woad leaves from Wyson.") stage = 132 } else { end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") + sendMessage(player, "You need 20 gold coins to buy a woad leaf.") } 200 -> { npc("Fair enough.") stage = 201 } 201 -> end() + + /** + * Dialogue options: mole parts. + */ 100 -> { - options("Yes, I will trade the mole claws.", "Okay, I will trade the mole skin.", "I'd like to trade both.", "No, thanks.") + options("Ok, I will trade the mole skin.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") stage = 900 } + 101 -> { + options("Yeah, I will trade the mole claws.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") + stage = 901 + } + 102 -> { + options("Yeah, I will trade the mole claws.", "Okay, I will trade the mole skin.", "Alright, I'll trade the claws and skin.", "Yes please, I need woad leaves.", "Sorry, but I'm not interested.") + stage = 902 + } 900 -> when (buttonId) { 1 -> { - player("Yes, I will trade the mole claws.") + player("Ok, I will trade the mole skin.") + stage = 920 + } + 2 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 3 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 901 -> when (buttonId) { + 1 -> { + player("Yeah, I will trade the mole claws.") + stage = 910 + } + 2 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 3 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 902 -> when (buttonId) { + 1 -> { + player("Yeah, I will trade the mole claws.") stage = 910 } 2 -> { @@ -157,37 +199,48 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { stage = 920 } 3 -> { - player("I'd like to trade both.") + player("Alright, I'll trade the claws and skin.") stage = 930 } 4 -> { - player("No, thanks.") - stage = 999 + player("Yes please, I need woad leaves.") + stage = 10 + } + 5 -> { + player("Sorry, but I'm not interested.") + stage = 200 } } 910 -> { - if (!player.inventory.containsItem(MOLE_CLAW)) { + if (!inInventory(player, Items.MOLE_CLAW_7416, 1)) { player("Sorry, I don't have any mole claws.") stage = 999 + } else { + addClawRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - end() - addRewards() } 920 -> { - if (!player.inventory.containsItem(MOLE_SKIN)) { + if (!inInventory(player, Items.MOLE_SKIN_7418, 1)) { player("Sorry, I don't have any mole skins.") stage = 999 + } else { + addSkinRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - end() - addRewards() } 930 -> { - if (!player.inventory.containsItem(MOLE_CLAW) && !player.inventory.containsItem(MOLE_SKIN)) { + if (!inInventory(player, Items.MOLE_CLAW_7416, 1) || !inInventory(player, Items.MOLE_SKIN_7418, 1)) { player("Sorry, I don't have any.") stage = 999 + } else { + addClawRewards() + addSkinRewards() + npc("Pleasure doing business with ya.") + stage = 999 } - addRewards() - end() } 999 -> end() } @@ -196,26 +249,31 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { /** * Adds nests. - * @param nestAmount the amount. */ - private fun addRewards() { - val moleClaws = player.inventory.getAmount(Items.MOLE_CLAW_7416) - val moleSkin = player.inventory.getAmount(Items.MOLE_SKIN_7418) - val nestAmount = moleClaws + moleSkin - - //Remove claws and skins - player.inventory.remove(Item(Items.MOLE_CLAW_7416,moleClaws)) - player.inventory.remove(Item(Items.MOLE_SKIN_7418, moleSkin)) - - //Add white lily seeds if the player has the hard diary done - if(moleSkin > 0 && player.achievementDiaryManager.getDiary(DiaryType.FALADOR).checkComplete(DiaryLevel.HARD)) { - player.inventory.add(Item(14589, moleSkin), player) + private fun addClawRewards() { + // count the number of claws + val nestAmount = amountInInventory(player, Items.MOLE_CLAW_7416) + // remove the counted number of skins + if(removeItem(player, Item(Items.MOLE_CLAW_7416, nestAmount), Container.INVENTORY)){ + // add the counted number of nests. one by one so they each have random contents + for (i in 0 until nestAmount) { + addItemOrDrop(player, BirdNest.getRandomNest(true).nest.id, 1) + } } + } - //Add nests - for (i in 0 until nestAmount) { - if(!player.inventory.add(Item(BirdNest.getRandomNest(true).nest.id, 1), player)){ - GroundItemManager.create(Item(BirdNest.getRandomNest(true).nest.id,1),player.location,player) + private fun addSkinRewards() { + // count the number of skins + val nestAmount = amountInInventory(player, Items.MOLE_SKIN_7418) + // remove the counted number of skins + if(removeItem(player, Item(Items.MOLE_SKIN_7418, nestAmount), Container.INVENTORY)) { + // add the counted number of nests. one by one so they each have random contents + // if Falador Hard diary is complete, add a white lilly seed + for (i in 0 until nestAmount) { + addItemOrDrop(player, BirdNest.getRandomNest(true).nest.id, 1) + if (player.achievementDiaryManager.getDiary(DiaryType.FALADOR).isComplete(2)) { + addItemOrDrop(player, Items.WHITE_LILY_SEED_14589, 1) + } } } } @@ -224,25 +282,4 @@ class WysonTheGardenerDialogue : core.game.dialogue.DialoguePlugin { return intArrayOf(36) } - companion object { - /** - * Represents the coins item that can be used. - */ - private val COINS = arrayOf(Item(995, 15), Item(995, 20)) - - /** - * Represents the woad leaf item. - */ - private val WOAD_LEAF = Item(1793, 1) - - /** - * The mole claw item. - */ - private val MOLE_CLAW = Item(7416) - - /** - * The mole skin. - */ - private val MOLE_SKIN = Item(7418) - } } \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt index bdf287db7..aac922c9a 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(2925, 3213, 2939, 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..20edbb3ac 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; } @@ -295,7 +299,7 @@ public final class GiantMoleNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { Player player = killer.asPlayer(); - BossKillCounter.addtoKillcount(player, this.getId()); + BossKillCounter.addToBossKillCount(player, this.getId()); } } diff --git a/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java b/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java index 39678d7f9..1409c3aca 100644 --- a/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java @@ -30,12 +30,17 @@ public final class MiningGuildPlugin extends OptionHandler { SceneryDefinition.forId(2113).getHandlers().put("option:climb-down", this); SceneryDefinition.forId(30941).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(2112).getHandlers().put("option:open", this); + SceneryDefinition.forId(35783).getHandlers().put("option:climb-down", this); return this; } @Override public boolean handle(Player player, Node node, String option) { if (option.equals("climb-down")) { + if (player.getLocation().withinDistance(Location.create(3021, 3343, 1),2)) { + ClimbActionHandler.climb(player, new Animation(0), Location.create(3017, 3343, 0)); + return true; + } if (player.getLocation().withinDistance(Location.create(3019, 3339, 0), 4)) { if (getDynLevel(player, Skills.MINING) < 60) { player.getDialogueInterpreter().open(382, NPC.create(382, Location.create(0, 0, 0)), 1); 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..ad3d9c910 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the demon slayer quest. @@ -15,12 +16,6 @@ import core.plugin.ClassScanner; */ @Initializable public class GoblinDiplomacy extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "Goblin Diplomacy"; - /** * Represents the orange goblin mail. */ @@ -45,7 +40,7 @@ public class GoblinDiplomacy extends Quest { * Constructs a new {@Code GoblinDiplomacy} {@Code Object} */ public GoblinDiplomacy() { - super("Goblin Diplomacy", 20, 19, 5); + super(Quests.GOBLIN_DIPLOMACY, 20, 19, 5); } @Override @@ -87,7 +82,7 @@ public class GoblinDiplomacy extends Quest { line(player, BLUE + "I have some " + RED + "Blue Goblin Armour. " + BLUE + "I should show it to the", 12+ 7); line(player, BLUE + "generals.", 13+ 7); } else { - line(player, BLUE + "I should bring the goblins s+ 7+ 7);e " + RED + "Blue Goblin Armour", 12+ 7); + line(player, BLUE + "I should bring the goblins some " + RED + "Blue Goblin Armour", 12+ 7); line(player, BLUE + "Maybe the generals will know where to get some.", 13+ 7); } break; diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/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..6d31ea26b --- /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 core.tools.colorize +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, _ -> + // https://runescape.wiki/w/Mudskipper_Point?oldid=969991 + sendDialogue(player, colorize("%RBEWARE OF THE MUDSKIPPERS!")) + 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..399daecc7 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); @@ -144,7 +148,11 @@ public final class PikkupstixDialogue extends DialoguePlugin { } break; case 100: - npc("Welcome to my humble abode. How can I help", "you?"); + if (player.getSkills().getStaticLevel(Skills.SUMMONING) == 99) { + npc(FacialExpression.AMAZED, "Why, I've never seen such raw Summoning power", "before! If anyone has the right to own a Skillcape of", "Summoning, then it is you!"); + } else { + npc("Welcome to my humble abode. How can I help", "you?"); + } break; } return true; @@ -627,7 +635,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { switch (stage) { case 0: if (player.getSkills().getStaticLevel(Skills.SUMMONING) == 99) { - options("So, what's Summoning all about, then?", "Can I buy some Summoning supplies?", "Can I buy a Summoning skillcape?"); + options("Yes, please sell me a Skillcape of Summoning.", "So, what's Summoning all about then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); stage = 600; } else { options("So, what's Summoning all about, then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); @@ -696,7 +704,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: @@ -752,35 +760,40 @@ public final class PikkupstixDialogue extends DialoguePlugin { case 600: switch (buttonId) { case 1: + player(FacialExpression.ASKING, "May I buy a Skillcape of Summoning, please?"); + stage = 599; + break; + case 2: player("So, what's summoning all about, then?"); stage = 10; break; - case 2: + case 3: player("Can I buy some summoning supplies, please?"); stage = 34; break; - case 3: - player("Can I buy a Skillcape of Summoning?"); - stage = 599; + case 4: + player("Please tell me about skillcapes."); + stage = 400; break; } break; case 599: - npc("Why yes you can! I must warn you that they cost", "a total of 99000 coins. Do you wish to still", "buy a skillcape of Summoning?"); + npc(FacialExpression.NEUTRAL, "That is a pretty tall order, even for someone with your", "skills, " + player.getUsername() + ". You'll have to pay a fee of 99000 coins", "to get the cape."); stage = 601; break; case 601: - options("Yes.", "No."); + options("99000 coins? I think not!", "I'll take one!"); stage = 602; break; case 602: switch (buttonId) { case 1: - player("Yes, please."); - stage = 603; + player(FacialExpression.AMAZED, "99000 coins? I think not!"); + stage = 605; break; case 2: - end(); + player(FacialExpression.HAPPY, "I'll take one!"); + stage = 603; break; } break; @@ -800,13 +813,17 @@ public final class PikkupstixDialogue extends DialoguePlugin { return true; } if (player.getInventory().add(ITEMS[player.getSkills().getMasteredSkills() > 1 ? 1 : 0], ITEMS[2])) { - player("There you go, enjoy!"); + npc(FacialExpression.HAPPY, "Excellent! Wear that cape with pride my friend."); stage = 604; } break; case 604: end(); break; + case 605: + npc(FacialExpression.NEUTRAL, "Well, keep that in mind the next time you ask. These", "things are not cheap to make you know!"); + stage = 604; + break; } break; } 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..90063d64c 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 } + 14 -> sendItemDialogue(player, Items.DUSTY_KEY_1590, "Velrak reaches somewhere mysterious and passes you a key.").also { addItemOrDrop(player, Items.DUSTY_KEY_1590, 1); stage = END_DIALOGUE } - 15 -> npcl(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..29160561d 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. @@ -18,42 +23,137 @@ public class WolfWhistle extends Quest { */ public static final Item WOLF_BONES = new Item(2859, 2); + /** + * Gates the visibility of the Summoning orb next to the minimap behind quest completion. + */ + private static final int VARBIT_SUMMONING_ORB_VISIBILITY = 4280; + /** * 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); } } @@ -69,13 +169,14 @@ public class WolfWhistle extends Quest { player.getSkills().addExperience(Skills.SUMMONING, 276); player.getInventory().add(new Item(12158, 275), player); player.removeAttribute("searched-body"); + setVarbit(player, VARBIT_SUMMONING_ORB_VISIBILITY, 1); player.getQuestRepository().syncronizeTab(player); player.getInterfaceManager().openInfoBars(); } @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..0bf3fffce --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchesDiaryBook.kt @@ -0,0 +1,139 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..d6a2c1fc6 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; } @@ -204,7 +202,7 @@ public final class TrollheimPlugin extends OptionHandler { switch (id) { case 32738: if (loc.equals(new Location(2892, 10072, 0))) { - player.getProperties().setTeleportLocation(LOCATIONS[0]); + player.getProperties().setTeleportLocation(LOCATIONS[9]); return true; } player.teleport(LOCATIONS[1]); @@ -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..2e18d36e2 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 @@ -6,14 +6,15 @@ import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.equipment.ArmourSet; -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.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.applyPoison; + /** * Handles K'ril Tsutsaroth's combat. * @author Emperor @@ -61,21 +62,23 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler { if (RandomFunction.randomize(10) < 4) { ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); style = CombatStyle.MAGIC; - } else if (RandomFunction.randomize(10) == 0) { - if (special = (victim instanceof Player)) { - ((Player) victim).getPacketDispatch().sendMessage("K'ril Tsutsaroth slams through your protection prayer, leaving you feeling drained."); - } + } else if (victim instanceof Player && RandomFunction.randomize(10) == 0) { + Player player = (Player) victim; + special = player.hasProtectionPrayer(CombatStyle.MELEE); + if (special) { + player.getPacketDispatch().sendMessage("K'ril Tsutsaroth slams through your protection prayer, leaving you feeling drained."); entity.sendChat("YARRRRRRR!"); + } } if (style.getSwingHandler().isAccurateImpact(entity, victim)) { int max = style.getSwingHandler().calculateHit(entity, victim, special ? 1.08 : 1.0); hit = RandomFunction.random(max); state.setMaximumHit(max); if (style == CombatStyle.MELEE) { - victim.getStateManager().register(EntityState.POISONED, false, 168, entity); + applyPoison(victim, entity, 80); } if (special) { - ((Player) victim).getSkills().decrementPrayerPoints((double) hit / 2); + victim.getSkills().decrementPrayerPoints((double) hit / 2); } } state.setEstimatedHit(hit); @@ -152,4 +155,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/GodwarsBossNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java index 4d62e0f41..ad04fa730 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java @@ -231,7 +231,7 @@ public final class GodwarsBossNPC extends AbstractNPC { public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); if (getId() == 6222 || getId() == 6260 || getId() == 6247 || getId() == 6203) { - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); } if (minions == null) { return; 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..1c37aebab 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 @@ -63,16 +64,16 @@ public class PrinceAliRescue extends Quest { break; case 10: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + line(player, "Palace. He told me I should speak to Osman the spymaster.", 5+ 7); line(player, BLUE + "I should go and speak to " + RED + "Osman " + BLUE + "for details on the quest.", 6+ 7); break; case 20: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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); //authentic typo on the 'draynor village' capitialization + 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); @@ -84,11 +85,11 @@ public class PrinceAliRescue extends Quest { break; case 30: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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); @@ -96,15 +97,15 @@ public class PrinceAliRescue extends Quest { line(player, hasItem(player, ROPE) ? "I have some rope with me." : BLUE + "Get some " + RED + "rope " + BLUE + "to tie up the Princes' " + RED + "kidnapper.", 14+ 7); line(player, hasItem(player, PASTE) ? "I have some skin paste suitable for disguise with me." : BLUE + "Get something to " + RED + "colour " + BLUE + "the " + RED + "Princes' skin " + BLUE + "as a " + RED + "disguise.", 15+ 7); line(player, hasItem(player, SKIRT) ? "I have a skirt suitable for a disguise with me." : BLUE + "Get a " + RED + "skirt " + BLUE + "similar to his " + RED + "kidnapper " + BLUE + "as " + RED + "disguise.", 16+ 7); - line(player, hasItem(player, YELLOW_WIG) ? "I have a wig suitable for disguise with me." : BLUE + "Get a " + RED + "Wig " + BLUE + "to " + RED + "help disguise" + BLUE + "the " + RED + "prince.", 17+ 7); + line(player, hasItem(player, YELLOW_WIG) ? "I have a wig suitable for disguise with me." : BLUE + "Get a " + RED + "Wig " + BLUE + "to " + RED + "help disguise " + BLUE + "the " + RED + "prince.", 17+ 7); break; case 40: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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); if (player.getAttribute("guard-drunk", false)) { line(player, "Prince Ali has been kidnapped but luckily the spy Leela has", 7+ 7); - line(player, "found he is being held near Draynor village. I will need to", 8+ 7); + line(player, "found he is being held near Draynor Village. I will need to", 8+ 7); line(player, "disguise the Prince and tie up his captor to free him from", 9+ 7); line(player, "their clutches.", 10+ 7); line(player, "I also had to prevent the Guard from seeing that I was up", 10+ 7); @@ -115,32 +116,32 @@ public class PrinceAliRescue extends Quest { } else { line(player, BLUE + "Do something to prevent " + RED + "Joe the Guard " + BLUE + "seeing the", 7+ 7); line(player, BLUE + "escape.", 8+ 7); - line(player, BLUE + "Use the " + RED + "Skin potion" + BLUE + ", " + RED + "Pink Skirt" + BLUE + "," + RED + "Rope" + BLUE + "," + RED + "Blonde Wig " + BLUE + "and " + RED + "Cell", 9+ 7); + line(player, BLUE + "Use the " + RED + "Skin Potion" + BLUE + ", " + RED + "Pink Skirt" + BLUE + ", " + RED + "Rope" + BLUE + ", " + RED + "Blonde Wig " + BLUE + "and " + RED + "Cell", 9+ 7); line(player, RED + "Key" + BLUE + " to free " + RED + "Prince Ali " + BLUE + "from his cell somehow.", 10+ 7); } break; case 50: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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, "Prince Ali has been kidnapped but luckily the spy Leela has", 7+ 7); - line(player, "found he is being held near Draynor village. I will need to", 8+ 7); + line(player, "found he is being held near Draynor Village. I will need to", 8+ 7); line(player, "disguise the Prince and tie up his captor to free him from", 9+ 7); line(player, "their clutches.", 10+ 7); line(player, "I also had to prevent the Guard from seeing that I was up", 10+ 7); line(player, "to, by getting him drunk.", 11+ 7); line(player, "With the guard disposed of, I used my rope to tie up Lady", 11+ 7); line(player, "Keli in a cupboard, so I could disguise the Prince.", 12+ 7); - line(player, BLUE + "I need to " + RED + "Unlock the cell door " + BLUE + "and then give the Prince the", 13+ 7); - line(player, RED + "Pink Skirt" + BLUE + ", the " + RED + "Skin paste " + BLUE + "and the " + RED + "Blonde Swig " + BLUE + "so that the", 14+ 7); + line(player, BLUE + "I need to " + RED + "unlock the cell door " + BLUE + "and then give the Prince the", 13+ 7); + line(player, RED + "Pink Skirt" + BLUE + ", the " + RED + "Skin Paste " + BLUE + "and the " + RED + "Blonde Wig " + BLUE + "so that they", 14+ 7); line(player, BLUE + "can safely " + RED + "escape " + BLUE + "disguised as " + RED + "Lady Keli.", 15+ 7); break; case 60: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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, "Prince Ali has been kidnapped but luckily the spy Leela has", 7+ 7); - line(player, "found he is being held near Draynor village. I will need to", 8+ 7); + line(player, "found he is being held near Draynor Village. I will need to", 8+ 7); line(player, "disguise the Prince and tie up his captor to free him from", 9+ 7); line(player, "their clutches.", 10+ 7); line(player, "I also had to prevent the Guard from seeing that I was up", 10+ 7); @@ -154,10 +155,10 @@ public class PrinceAliRescue extends Quest { break; case 100: line(player, "I started this quest by speaking to Hassan in Al-Kharid", 4+ 7); - line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); + 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, "Prince Ali has been kidnapped but luckily the spy Leela has", 7+ 7); - line(player, "found he is being held near Draynor village. I will need to", 8+ 7); + line(player, "found he is being held near Draynor Village. I will need to", 8+ 7); line(player, "disguise the Prince and tie up his captor to free him from", 9+ 7); line(player, "their clutches.", 10+ 7); line(player, "I also had to prevent the Guard from seeing that I was up", 10+ 7); @@ -169,7 +170,7 @@ public class PrinceAliRescue extends Quest { line(player, "freedom with Leela after unlocking his cell door.", 15+ 7); line(player, "Hassan the chancellor rewarded me for all of my help.", 16+ 7); line(player, "I am now a friend of Al-Kharid and may pass through the", 17+ 7); - line(player, "gate leading between Lumbridge and Al-Kharid for free", 18+ 7); + line(player, "gate leading between Lumbridge and Al-Kharid for free.", 18+ 7); line(player, "QUEST COMPLETE!", 19+ 7); break; } 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..29a7809d1 --- /dev/null +++ b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt @@ -0,0 +1,64 @@ +package content.region.desert.bandits.handlers + +import core.api.God +import core.api.hasGodItem +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BanditNPC(id: Int = NPCs.BANDIT_1926, location: Location? = null) : AbstractNPC(id, location) { + private val supportRange: Int = 3 + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return BanditNPC(id, location) + } + + override fun tick() { + if (!inCombat()) { + val players = RegionManager.getLocalPlayers(this, 5) + for (player in players) { + if (player.inCombat()) continue + if (hasGodItem(player, God.SARADOMIN)) { + sendChat("Time to die, Saradominist filth!") + attack(player) + break + } else if (hasGodItem(player, God.ZAMORAK) || inEquipment(player, Items.DAGONHAI_HAT_14499) || inEquipment(player, Items.DAGONHAI_ROBE_TOP_14497) || inEquipment(player, Items.DAGONHAI_ROBE_BOTTOM_14501)) { + sendChat("Prepare to suffer, Zamorakian scum!") + attack(player) + break + } + } + } + super.tick() + } + + //Clear target on death + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + + override fun onImpact(entity: Entity?, state: BattleState?) { + if (entity is Player) { + RegionManager.getLocalNpcs(entity, supportRange).forEach { + if (it.id == NPCs.BANDIT_1926 && !it.properties.combatPulse.isAttacking && it != this) { + it.sendChat("You picked the wrong place to start trouble!") + it.attack(entity) + } + } + } + super.onImpact(entity, state) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BANDIT_1926) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/dialogue/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/ShantayDialogue.java b/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java index be8dde907..fcbf3077f 100644 --- a/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java +++ b/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java @@ -47,11 +47,11 @@ public final class ShantayDialogue extends DialoguePlugin { } if (args.length == 2) { player.getPacketDispatch().sendMessage("Shantay saunters over to talk with you."); - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "If you want to be let out, you have to pay a fine of", "five gold. Do you want to pay now?"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "If you want to be let out, you have to pay a fine of", "five gold. Do you want to pay now?"); stage = 703; return true; } - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "Hello effendi, I am Shantay."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Hello effendi, I am Shantay."); stage = 0; return true; } @@ -60,11 +60,11 @@ public final class ShantayDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "I see you're new. Please read the billboard poster", "before going into the desert. It'll give yer details on the", "dangers you can face."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "I see you're new. Please read the billboard poster", "before going into the desert. It'll give yer details on the", "dangers you can face."); stage = 1; break; case 1: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "There is a heartbroken mother just past the gates and", "in the desert. Her name is Irena and she mourns her", "lost daughter. Such a shame."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "There is a heartbroken mother just past the gates and", "in the desert. Her name is Irena and she mourns her", "lost daughter. Such a shame."); stage = 2; break; case 2: @@ -74,19 +74,19 @@ public final class ShantayDialogue extends DialoguePlugin { case 3: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What is this place?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I see what you have to sell please?"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Can I see what you have to sell please?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I must be going."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I must be going."); stage = 30; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to buy a Shantay pass for 5 gold coins."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I want to buy a Shantay pass for 5 gold coins."); stage = 40; break; @@ -101,7 +101,7 @@ public final class ShantayDialogue extends DialoguePlugin { player.getInventory().add(new Item(1854)); interpreter.sendItemMessage(1854, "You purchase a Shantay Pass."); } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough money."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); } stage = 41; break; @@ -109,18 +109,18 @@ public final class ShantayDialogue extends DialoguePlugin { end(); break; case 30: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "So long..."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "So long..."); stage = 31; break; case 31: end(); break; case 10: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "This is the pass of Shantay. I guard this area with my", "men. I am responsible for keeping this pass open and", "repaired."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "This is the pass of Shantay. I guard this area with my", "men. I am responsible for keeping this pass open and", "repaired."); stage = 11; break; case 11: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "My men and I prevent outlaws from getting out of the", "desert. And we stop the inexeperienced from a dry death", "in the sands. Which would you say you were?"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "My men and I prevent outlaws from getting out of the", "desert. And we stop the inexeperienced from a dry death", "in the sands. Which would you say you were?"); stage = 12; break; case 12: @@ -130,21 +130,21 @@ public final class ShantayDialogue extends DialoguePlugin { case 13: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I am definitely an outlaw, prepare to die."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I am definitely an outlaw, prepare to die."); stage = 700; break; case 2: - interpreter.sendDialogues(player, null, "I am a little inexperienced."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I am a little inexperienced."); stage = 710; break; case 3: - interpreter.sendDialogues(player, null, "Er, neither, I'm an adventurer."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Er, neither, I'm an adventurer."); stage = 720; break; } break; case 20: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "Absolutely Effendi!"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Absolutely Effendi!"); stage = 21; break; case 21: @@ -152,11 +152,11 @@ public final class ShantayDialogue extends DialoguePlugin { npc.openShop(player); break; case 700: - interpreter.sendDialogues(836, null, "Ha, very funny....."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Ha, very funny....."); stage = 701; break; case 701: - interpreter.sendDialogues(836, null, "Guards arrest him!"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Guards arrest him!"); stage = 702; break; case 702: @@ -168,7 +168,7 @@ public final class ShantayDialogue extends DialoguePlugin { public boolean pulse() { player.setAttribute("/save:shantay-jail", true); player.getProperties().setTeleportLocation(Location.create(3298, 3123, 0)); - interpreter.sendDialogues(836, null, "You'll have to stay in there until you pay the fine of", "five gold pieces. Do you want to pay now?"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "You'll have to stay in there until you pay the fine of", "five gold pieces. Do you want to pay now?"); stage = 703; return true; } @@ -181,11 +181,11 @@ public final class ShantayDialogue extends DialoguePlugin { case 704: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Yes, okay."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Yes, okay."); stage = 705; break; case 2: - interpreter.sendDialogues(player, null, "No thanks, you're not having my money."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks, you're not having my money."); stage = 800; break; } @@ -194,20 +194,20 @@ public final class ShantayDialogue extends DialoguePlugin { if (player.getInventory().remove(new Item(995, 5))) { player.getPacketDispatch().sendMessage("You hand over the five gold pieces to Shantay."); player.getPacketDispatch().sendMessage("Shantay unlocks the door to the cell."); - interpreter.sendDialogues(836, null, "Great, Effendi, now please try to keep the peace."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Great, Effendi, now please try to keep the peace."); player.removeAttribute("shantay-jail"); stage = 822; } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough money."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); stage = 825; } break; case 800: - interpreter.sendDialogues(836, null, "You have a choice. You can either pay five gold pieces", "or... You can be transported to a maximum security", "prision in Port Sarim"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "You have a choice. You can either pay five gold pieces", "or... You can be transported to a maximum security", "prison in Port Sarim"); stage = 801; break; case 801: - interpreter.sendDialogues(836, null, "Will you pay the five gold pieces?"); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Will you pay the five gold pieces?"); stage = 802; break; case 802: @@ -217,17 +217,17 @@ public final class ShantayDialogue extends DialoguePlugin { case 803: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Yes, okay."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Yes, okay."); stage = 820; break; case 2: - interpreter.sendDialogues(player, null, "No, do your worst!"); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, do your worst!"); stage = 804; break; } break; case 804: - interpreter.sendDialogues(836, null, "You are to be transported to a maximum security", "prision in Port Sarim. I hope you've learn an important", "lesson from this."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "You are to be transported to a maximum security", "prison in Port Sarim. I hope you've learnt an important", "lesson from this."); stage = 805; break; case 805: @@ -237,17 +237,17 @@ public final class ShantayDialogue extends DialoguePlugin { end(); break; case 820: - interpreter.sendDialogues(836, null, "Good, I see that you have come to your senses."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Good, I see that you have come to your senses."); stage = 821; break; case 821: if (player.getInventory().remove(new Item(995, 5))) { player.getPacketDispatch().sendMessage("You hand over the five gold pieces to Shantay."); player.getPacketDispatch().sendMessage("Shantay unlocks the door to the cell."); - interpreter.sendDialogues(836, null, "Great, Effendi, now please try to keep the peace."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Great, Effendi, now please try to keep the peace."); stage = 822; } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough money."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); stage = 825; } break; @@ -258,26 +258,26 @@ public final class ShantayDialogue extends DialoguePlugin { end(); break; case 710: - interpreter.sendDialogues(836, null, "Can I recommend that you purchase a full waterskin", "and a knife! These items will no doubt save your life. A", "waterskin will keep water from evaporating in the desert."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Can I recommend that you purchase a full waterskin", "and a knife! These items will no doubt save your life. A", "waterskin will keep water from evaporating in the desert."); stage = 711; break; case 711: - interpreter.sendDialogues(836, null, "And a keen woodsman with a knife can extract juice", "from a cactus. Before you go into the desert, it's", "advisable to wear desert clothes. It's very hot in the", "desert and you'll surely cook if you wear amour."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "And a keen woodsman with a knife can extract juice", "from a cactus. Before you go into the desert, it's", "advisable to wear desert clothes. It's very hot in the", "desert and you'll surely cook if you wear amour."); stage = 712; break; case 712: end(); break; case 720: - interpreter.sendDialogues(836, null, "Great, I have just the thing for you the desert adventurer.", "I sell desert clothes which will keep you cool in the heat", "of the desert. I also sell waterskins so that you won't", "die in the desert."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Great, I have just the thing for you the desert adventurer.", "I sell desert clothes which will keep you cool in the heat", "of the desert. I also sell waterskins so that you won't", "die in the desert."); stage = 721; break; case 721: - interpreter.sendDialogues(836, null, "A waterskin and a knife help you survive from the juice", "of a cactus. Use the chest to store your items, we'll take", "them to the bank. It's hot in the desert, you'll bake in", "all that armour."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "A waterskin and a knife help you survive from the juice", "of a cactus. Use the chest to store your items, we'll take", "them to the bank. It's hot in the desert, you'll bake in", "all that armour."); stage = 722; break; case 722: - interpreter.sendDialogues(836, null, "To keep the pass open we ask for 5 gold pieces. And", "we give you a Shantay Pass, just ask to see what I sell", "to buy one."); + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "To keep the pass open we ask for 5 gold pieces. And", "we give you a Shantay Pass, just ask to see what I sell", "to buy one."); stage = 723; break; case 723: 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..3110c7233 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. @@ -35,7 +39,7 @@ public final class DesertZone extends MapZone implements Plugin { /** * Represents data of water vessils to dry up. */ - private static final int[][] VESSILS = new int[][] { { 1937, 1935 }, { 1929, 1925 }, { 1921, 1923 }, { 227, 229 } }; + private static final int[][] VESSILS = new int[][] { { 1937, 1935 }, { 1929, 1925 }, { 1921, 1923 }, { 227, 229 }, { 4458, 1980} }; /** * Represents the animation of drinking water. @@ -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..39dbc9399 100644 --- a/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java +++ b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java @@ -95,12 +95,12 @@ public final class KalphiteQueenNPC extends AbstractNPC { public void finalizeDeath(Entity killer) { if (getId() == 1160) { removeAttribute("disable:drop"); - reTransform(); super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, 1160); + reTransform(); + BossKillCounter.addToBossKillCount((Player) killer, 1160); return; } - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); setAttribute("disable:drop", true); super.finalizeDeath(killer); super.setRespawnTick(-1); @@ -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..6ac1468ad --- /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 @g[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..dae618e1d --- /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..1b996dafc --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt @@ -0,0 +1,81 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class DamisBehavior : NPCBehavior(NPCs.DAMIS_1974, NPCs.DAMIS_1975) { + private var disappearing = false + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Damis has vanished once more into the shadows...") + removeAttribute(player, DesertTreasure.attributeDamisInstance) + } + poofClear(self) + } + return true + } + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints && self.id == NPCs.DAMIS_1974) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + + transformNpc(self, NPCs.DAMIS_1975, 500) + self.skills.lifepoints = self.skills.maximumLifepoints + sendChat(self, "Armour... is for restraint, not... protection...") + queueScript(self, 2, QueueStrength.NORMAL) { stage: Int -> + sendChat(self, "Now I show... you... my true power!") + self.properties.attackSpeed = 3 + return@queueScript stopExecuting(self) + } + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + // In second form, drain prayer by 5. + if (self.id == NPCs.DAMIS_1975) { + victim.skills.decrementPrayerPoints(5.0) + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (self.id == NPCs.DAMIS_1975) { + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeShadowStage) == 3) { + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), self.location, killer) + DesertTreasure.setSubStage(killer, DesertTreasure.attributeShadowStage, 100) + removeAttribute(killer, DesertTreasure.attributeFareedInstance) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt index f42498ac6..f23f6e024 100644 --- a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt @@ -1,103 +1,544 @@ -//package rs09.game.content.quest.members.deserttreasure +package content.region.desert.quest.deserttreasure -//import core.game.node.entity.player.Player -//import core.game.node.entity.player.link.quest.Quest -//import core.game.node.entity.skill.Skills -//import core.plugin.Initializable +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items /** - * @author qmqz + * Desert Treasure Quest + * + * https://www.youtube.com/watch?v=INaSdOAHdT8: Jesus fucking devil best shit on planet fuckall 31:57 + * https://www.youtube.com/watch?v=LAoOISdsX4w: This guy did the best initial quest journal and then ignored the rest. + * https://www.youtube.com/watch?v=3cyU36qNBpI: one impt quest log part in the front. + * https://www.youtube.com/watch?v=pY-Czja2lE4: Good shit part 2 6:24 + * https://www.youtube.com/watch?v=NB1-m_hoTPk: Blur ass quest log. + * https://www.youtube.com/watch?v=bcy54b6NnJs: Blur ass quest log. + * https://www.youtube.com/watch?v=J_dfMXFz3WQ: Blur ass quest log. + * https://www.youtube.com/watch?v=7n0OcHUNFHc: Blur ass quest log 1:07. + * https://www.youtube.com/@Gunz4Range/videos + * https://www.youtube.com/watch?v=baQ6oJOk7Gc + * https://www.youtube.com/watch?v=u0C2zW6mrcc: Blur ass ending quest log 3:23 + * https://www.youtube.com/watch?v=SUzM0WKY6jk: 1:03 + * https://www.youtube.com/watch?v=3AA5fkkBW-I: 2:05 + * + * https://www.youtube.com/watch?v=ofRV2-h3Dug: 17:21 27:45 + * + * if (VARPBIT[358] == 15) return 2; if (VARPBIT[358] == 0) return 0; return 1; }; if (arg0 == 13) */ +@Initializable +class DesertTreasure : Quest(Quests.DESERT_TREASURE,45, 44, 3, 440, 358, 0, 1, 15){ -//@Initializable -//class DesertTreasure : Quest("Desert Treasure",15, 44, 3, 440, 0, 1, 15){ + companion object { + val questName = Quests.DESERT_TREASURE + /** This is an important varbit as it is literally controlling the Quest varbit (440 Varp 0-14 bits -> 358 Varbit )*/ + const val varbitDesertTreasure = 358 // 10-15-Eblis 1924 shows up other values Eblis disappears. This is tied to the quest. - //override fun drawJournal(player: Player?, stage: Int) { - // super.drawJournal(player, stage) - // var line = 12 + // Desert Treasure Start + const val attributeBoughtBeer = "/save:quest:deserttreasure-boughtbeer" + const val attributeCountMagicLogs = "/save:quest:deserttreasure-countmagiclogs" + const val attributeCountSteelBars = "/save:quest:deserttreasure-countsteelbars" + const val attributeCountMoltenGlass = "/save:quest:deserttreasure-countmoltenglass" + const val attributeCountBones = "/save:quest:deserttreasure-countbones" + const val attributeCountAshes = "/save:quest:deserttreasure-countashes" + const val attributeCountCharcoal = "/save:quest:deserttreasure-countcharocal" + const val attributeCountBloodRune = "/save:quest:deserttreasure-countbloodrune" + const val varbitMirrors = 392 // Set to 1 to show mirrors, they are cactuses before desert treasure. - //val stage = player?.questRepository?.getStage("Desert Treasure")!! + // Blood Diamond + const val attributeBloodStage = "/save:quest:deserttreasure-bloodstage" + const val attributeDessousInstance = "quest:deserttreasure-dessousinstance" - //when (stage) { - // 0 -> { - // line(player,"I can start this quest by speaking to !!The Archaeologist??", line++) - // line(player,"who is exploring the !!Bedabin Camp?? South West of the", line++) - // line(player,"!!Shantay Pass.??", line++) - // line(player,"To complete this quest I will need:", line++) - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 10 Slayer", line++) - // } else { - // line(player,"---Level 10 Slayer/--", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 50 Firemaking", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 50 Magic", line++) - // } - // if (player.skills.getStaticLevel(Skills.SLAYER) < 10) { - // line(player,"Level 53 Thieving", line++) - // } - // line(player,"I must have completed the following quests:", line++) + // Smoke Diamond + const val attributeSmokeStage = "/save:quest:deserttreasure-smokestage" + const val attributeUnlockedGate = "/save:quest:deserttreasure-unlockedgate" + const val attributeFareedInstance = "quest:deserttreasure-fareedinstance" + const val varbitStandingTorchNorthEast = 360 // North East standing torch 6406 + const val varbitStandingTorchSouthEast = 361 // South East standing torch 6408 + const val varbitStandingTorchSouthWest = 362 // South West standing torch 6410 + const val varbitStandingTorchNorthWest = 363 // North West standing torch 6412 - // if (player.questRepository.isComplete("The Digsite Quest")) { - // line(player,"---!!The digsite Quest??/--", line++) - // } else { - // line(player,"!!The digsite Quest??", line++) - // } + // Ice Diamond + const val attributeIceStage = "/save:quest:deserttreasure-icestage" + const val attributeTrollKillCount = "/save:quest:deserttreasure-iciclecount" + const val attributeKamilInstance = "quest:deserttreasure-kamilinstance" + const val varbitFrozenFather = 380 // 0-frozen 1-defrosted | Base: 1943 Iced: 1944 Broke: 1948 Reunion: 1947 + const val varbitFrozenMother = 381 // 0-frozen 1-defrosted | Base: 1945 Iced: 1946 Broke: 1950 Reunion: 1949 + const val varbitChildReunite = 382 // 0-frozen 4-reunited 5-reunited 6-varbitfails/ends (This varbit seems to manage ice stages...) + // 0 - start, 1 - feed the crying child, 2 - fought kamil, 3 - unfreeze dad/mum, 4 - reunite, 5 - all chat complete. + const val varbitCaveEntrance = 378 // 6446 0 - 6 6441 - // if (player.questRepository.isComplete("The Tourist Trap")) { - // line(player,"---!!The Tourist Trap??/--", line++) - // } else { - // line(player,"!!The Tourist Trap??", line++) - // } + // Shadow Diamond + const val attributeShadowStage = "/save:quest:deserttreasure-shadowstage" + const val attributeDamisWarning = "quest:deserttreasure-damiswarning" + const val attributeDamisInstance = "quest:deserttreasure-damisinstance" + const val varbitRingOfVisibility = 393 // Set to 1 to show ladder and other cool stuff when wearing ring of visibility. - // if (player.questRepository.isComplete("The Temple of Ikov")) { - // line(player,"---!!The Temple of Ikov??/--", line++) - // } else { - // line(player,"!!The Temple of Ikov??", line++) - // } + // Desert Treasure End + const val attributeBloodDiamondInserted = "/save:quest:deserttreasure-blooddiamondinserted" + const val attributeSmokeDiamondInserted = "/save:quest:deserttreasure-smokediamondinserted" + const val attributeIceDiamondInserted = "/save:quest:deserttreasure-icediamondinserted" + const val attributeShadowDiamondInserted = "/save:quest:deserttreasure-shadowdiamondinserted" + const val varbitBloodObelisk = 390 + const val varbitSmokeObelisk = 387 + const val varbitIceObelisk = 389 + const val varbitShadowObelisk = 388 - // if (player.questRepository.isComplete("Priest In Peril")) { - // line(player,"---!!Priest In Peril??/--", line++) - // } else { - // line(player,"!!Priest In Peril??", line++) - // } + fun completedAllSubstages(player: Player): Boolean { + return getSubStage(player, attributeBloodStage) == 100 && + getSubStage(player, attributeSmokeStage) == 100 && + getSubStage(player, attributeIceStage) == 100 && + getSubStage(player, attributeShadowStage) == 100 + } - // if (player.questRepository.isComplete("Waterfall Quest")) { - // line(player,"---!!Waterfall Quest??/--", line++) - // } else { - // line(player,"!!Waterfall Quest??", line++) - // } + fun getSubStage(player: Player, attributeName: String): Int { + return getAttribute(player, attributeName, 0) + } - // if (player.questRepository.isComplete("Troll Stronghold")) { - // line(player,"---!!Troll Stronghold??/--", line++) - // } else { - // line(player,"!!Troll Stronghold??", line++) - // } + fun setSubStage(player: Player, attributeName: String, value: Int) { + return setAttribute(player, attributeName, value) + } + + fun hasRequirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.SLAYER, 10), + hasLevelStat(player, Skills.FIREMAKING, 50), + hasLevelStat(player, Skills.MAGIC, 50), + hasLevelStat(player, Skills.THIEVING, 53), + isQuestComplete(player, Quests.THE_DIG_SITE), + isQuestComplete(player, Quests.THE_TOURIST_TRAP), + isQuestComplete(player, Quests.TEMPLE_OF_IKOV), + isQuestComplete(player, Quests.PRIEST_IN_PERIL), + isQuestComplete(player, Quests.WATERFALL_QUEST), + isQuestComplete(player, Quests.TROLL_STRONGHOLD), + ).all { it } + } + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if(!started){ + line(player,"I can start this quest by speaking to !!The Archaeologist??", line++) + line(player,"who is exploring the !!Bedabin Camp?? South West of the", line++) + line(player,"!!Shantay Pass??.", line++) + line(player,"To complete this quest I will need:", line++) + line(player, "Level 10 Slayer", line++, hasLevelStat(player, Skills.SLAYER, 10)) + line(player, "Level 50 Firemaking", line++, hasLevelStat(player, Skills.FIREMAKING, 50)) + line(player, "Level 50 Magic", line++, hasLevelStat(player, Skills.MAGIC, 50)) + line(player, "Level 53 Thieving", line++, hasLevelStat(player, Skills.THIEVING, 53)) + line(player,"I must have completed the following quests:", line++) // After - I have completed all of the required quests: + line(player, "The Digsite Quest", line++, isQuestComplete(player, Quests.THE_DIG_SITE)) + line(player, "The Tourist Trap", line++, isQuestComplete(player, Quests.THE_TOURIST_TRAP)) + line(player, "The Temple of Ikov", line++, isQuestComplete(player, Quests.TEMPLE_OF_IKOV)) + line(player, "Priest In Peril", line++, isQuestComplete(player, Quests.PRIEST_IN_PERIL)) + line(player, "Waterfall Quest", line++, isQuestComplete(player, Quests.WATERFALL_QUEST)) + line(player, "Troll Stronghold", line++, isQuestComplete(player, Quests.TROLL_STRONGHOLD)) + } else { + + if (stage >= 2) { + line(player, "I took some etchings of a stone tablet discovered by the", line++, true) + line(player, "archaeologist in the desert to Terry Balando at the", line++, true) + line(player, "Varrock digsite.", line++, true) + } else if (stage >= 1) { + line(player, "The !!archaeologist?? has given me some !!etchings from a??", line++, false) + line(player, "!!stone tablet?? that he discovered in the desert somewhere,", line++, false) + line(player, "and asked me to take it to an archaeological expert called", line++, false) + line(player, "!!Terry Balando?? at the Varrock !!digsite??.", line++, false) + } + + if (stage >= 4) { + line(player, "He made a rough translation, which I returned to the", line++, true) + line(player, "archaeologist.", line++, true) + } else if (stage == 3) { + line(player, "Terry Balando made some quick !!translation notes?? of the", line++, false) + line(player, "tablet and asked me to return them to the !!archaeologist?? in", line++, false) + line(player, "the !!Bedabin Camp??.", line++, false) + } else if (stage == 2) { + line(player, "I should get the !!translation notes?? from !!Terry Balando??", line++, false) + } + + if(stage >= 5) { + line(player, "The archaeologist I met in the desert seemed to think that", line++, true) + line(player, "there was some hidden treasure in the desert somewhere,", line++, true) + line(player, "I agreed to help him find it in return for a fifty percent", line++, true) + line(player, "share of whatever we found.", line++, true) + } else if (stage == 4) { + line(player, "I should find out what the !!archaeologist?? has to say about", line++, false) + line(player, "the !!translation notes??.", line++, false) + } + + if (stage >= 6) { + line(player, "I headed South and found a Bandit Camp.", line++, true) + } else if (stage >= 5) { + line(player, "I should head South to the !!Bandit Camp?? and try to find out", line++, false) + line(player, "more about this !!treasure??.", line++, false) + } + + if (stage >= 7) { + line(player, "I asked around the Bandit Camp and discovered someone", line++, true) + line(player, "who was willing to help me find the diamonds, by using a", line++, true) + line(player, "magic spell and some scrying glasses.", line++, true) + } else if (stage >= 6) { + line(player, "After some searching, the barman let slip some", line++, true) + line(player, "information about the 'Four Diamonds of Azzanadra'.", line++, true) + line(player, "I should ask around the !!Bandit Camp?? and see if anyone", line++, false) + line(player, "else has any information about the !!Four Diamonds of??", line++, false) + line(player, "!!Azzanadra??", line++, false) + } + + if (stage >= 10 || (stage >= 9 && completedAllSubstages(player))) { + // This disappears after you find all the diamonds. + } else if (stage >= 8) { + line(player, "I brought Eblis the ingredients so that he could cast the", line++, true) + line(player, "spell to see the places touched by the magic of the", line++, true) + line(player, "diamonds.", line++, true) + } else if (stage >= 7) { + // Crosses out gradually as you SUBMIT them. + line(player, "To make the scrying glasses I need to bring the following", line++, false) + // Crosses out with "I have brought him all xxx(6 Steel bars)" + line(player, "items to Eblis:", line++, false) + line(player, "!!12 Magic logs??", line++, false) + line(player, "!!6 Steel bars??", line++, false) + line(player, "!!6 Molten glass??", line++, false) + line(player, "To cast the spell I will need to bring Eblis:", line++, false) + // Crosses out with "I have brought him xxx(some bones)" + line(player, "!!Some bones??", line++, false) + line(player, "!!Some ash??", line++, false) + line(player, "!!Some charcoal??", line++, false) + line(player, "!!A blood rune??", line++, false) + } + + if (stage >= 10 || (stage >= 9 && completedAllSubstages(player))) { + // This disappears after you find all the diamonds. + } else if (stage >= 9 && !completedAllSubstages(player)) { + // This was supposed to disappear... youtu.be/J4cQMY66MI4 is old but disagrees + line(player, "I headed East into the desert and used the scrying", line++, true) + line(player, "glasses set up for me there by Eblis to try and find the", line++, true) + line(player, "Four Diamonds of Azzanadra.", line++, true) + line++ + } else if (stage >= 8) { + line(player, "I should head !!East into the desert?? and meet Eblis where he", line++, false) + line(player, "has set up the scrying glasses, and use them to try and", line++, false) + line(player, "track down the !!Four Diamonds of Azzanadra??.", line++, false) + line++ + } - // line(player,"", line++) - //} - // } - //} + // The huge one with sub quests. + if (stage >= 9 && completedAllSubstages(player)) { + line(player, "I found all four diamonds using this spell.", line++, true) + } else if (stage == 9) { + line++ + // DIAMOND OF BLOOD (DESSOUS) + // Dessous returns to his grave, bored of toying with you. // https://youtu.be/7CvfS7pypso + if (getSubStage(player, attributeBloodStage) == 100) { + line(player, "I defeated a vampire named Dessous to claim the Diamond", line++, true) + line(player, "of Blood.", line++, true) + } else if (getSubStage(player, attributeBloodStage) >= 1) { + // ZSOCwWEwimM 4:25 helped the first line. + line(player, "I discovered that the location of the Diamond of Blood was", line++, true) + line(player, "somewhere in Morytania, and in the possession of a", line++, true) + line(player, "vampire warrior named Dessous.", line++, true) - // override fun finish(player: Player) { - // var ln = 10 - // super.finish(player) - // player.packetDispatch.sendString("You have completed Desert Treasure!", 277, 4) - // player.packetDispatch.sendItemZoomOnInterface(1891, 240, 277, 5) -// - // drawReward(player,"3 Quest Points", ln++) - // drawReward(player,"20,000 Magic XP", ln++) - // drawReward(player,"Ability to use", ln++) - // drawReward(player,"Ancient Magicks", ln) + if (getSubStage(player, attributeBloodStage) >= 3) { + line(player, "I don't fully trust Malek, but he has agreed to help me kill", line++, true) + line(player, "Dessous.", line++, true) - // player.skills.addExperience(Skills.MAGIC, 20000.0) + line(player, "I made a special blessed pot, filled with blood, garlic and", line++, true) + line(player, "spices, which I used to lure Dessous from his tomb.", line++, true) + line(player, "I managed to defeat the vampire warrior Dessous, but", line++, true) + line(player, "there was no sign of the Diamond of Blood anywhere.", line++, true) + line(player, "I should find out what game !!Malek?? has been playing with", line++, false) + line(player, "me, and where I can actually find the !!Diamond of Blood??.", line++, false) + } else if (getSubStage(player, attributeBloodStage) >= 2) { + line(player, "I don't fully trust Malek, but he has agreed to help me kill", line++, true) + line(player, "Dessous.", line++, true) - // } + // None of these lines cross out when you do it. So it remains like this. + line(player, "Apparently I can find an old !!assistant of Count Draynor?? in", line++, false) + line(player, "the !!sewers of Draynor Village??, who will be able to help me", line++, false) + line(player, "make a !!sacrificial pot??", line++, false) + line(player, "I then need to take that !!sacrificial pot?? to !!Entrana?? and get", line++, false) + line(player, "it blessed by the !!head priest??", line++, false) + line(player, "When I have done that, I should return to !!Malak??, and he will", line++, false) + line(player, "provide me with some !!fresh blood??", line++, false) + line(player, "I then need to add !!garlic?? and !!spices?? to the pot in order to", line++, false) + line(player, "lure Dessous from his tomb.", line++, false) + line(player, "When I have done all of this, I must !!kill Dessous!??", line++, false) + } else if (getSubStage(player, attributeBloodStage) >= 1) { + line(player, "I should speak to !!Malek?? again and find out how exactly I", line++, false) + line(player, "can kill !!Dessous??.", line++, false) // This doesn't stay + } + } else if (getSubStage(player, attributeBloodStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Blood??.", line++, false) + } - // override fun newInstance(`object`: Any?): Quest { - // return this - // } -//} \ No newline at end of file + line++ + // DIAMOND OF SMOKE (FAREED) + // Fareed has lost interest in you, and returned to his flames. + if (getSubStage(player, attributeSmokeStage) == 100) { + line(player, "I defeated a fire warrior, and now have the Diamond of", line++, true) + line(player, "Smoke.", line++, true) + } else if (getSubStage(player, attributeSmokeStage) >= 1) { + line(player, "I entered a smokey well and lit up the path. I found a", line++, true) // Derived. + line(player, "key in a chest.", line++, true) // Derived. + line(player, "I should find out what the !!key?? unlocks.", line++, false) // Derived. + } else if (getSubStage(player, attributeSmokeStage) == 0) { + // This doesn't change to stage 1 even when you are lighting the fires... + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Smoke??.", line++, false) + } + + line++ + // DIAMOND OF ICE (KAMIL) + // Kamil vanishes on an icy wind... + if (getSubStage(player, attributeIceStage) == 100) { + line(player, "I defeated a warrior named Kamil, and now have the", line++, true) + line(player, "Diamond of Ice.", line++, true) + } else if (getSubStage(player, attributeIceStage) >= 1) { + // https://www.youtube.com/watch?v=F5F6Ds-T1P8 28:52 + line(player, "I met a crying ice troll child to the North of Trollheim.", line++, true) + if (getSubStage(player, attributeIceStage) >= 3) { + line(player, "I managed to cheer him up slightly with a sweet treat.", line++, true) + line(player, "After speaking with him, I discovered that his parents had", line++, true) + line(player, "been hurt by a 'bad man' who had the Diamond of Ice, and I", line++, true) + line(player, "agreed to help him rescue them.", line++, true) + line(player, "While heading through the icy area, I was attacked by an", line++, true) + line(player, "ice warrior named Kamil, and managed to defeat him.", line++, true) + line(player, "Was this the 'bad man' the troll child has spoken of?", line++, true) + line(player, "I should head further into the icy area to try and find", line++, false) + line(player, "them.", line++, false) + } else if (getSubStage(player, attributeIceStage) >= 2) { + line(player, "I managed to cheer him up slightly with a sweet treat.", line++, true) + line(player, "After speaking with him, I discovered that his parents had", line++, true) + line(player, "been hurt by a 'bad man' who had the Diamond of Ice, and I", line++, true) + line(player, "agreed to help him rescue them.", line++, true) + line(player, "I should head further into the icy area to try and find", line++, false) + line(player, "them.", line++, false) + } else if (getSubStage(player, attributeIceStage) >= 1) { + line(player, "I should cheer him up with something sweet.", line++, false) // Derived + } + } else if (getSubStage(player, attributeIceStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Ice??.", line++, false) + } + + line++ + // DIAMOND OF SHADOW (DAMIS) + // Damis has vanished once more into the shadows... + if (getSubStage(player, attributeShadowStage) == 100) { + line(player, "I defeated a warrior named Damis, and now have the", line++, true) + line(player, "Diamond of Shadow.", line++, true) + } else if (getSubStage(player, attributeShadowStage) >= 1) { + line(player, "A travelling merchant named Rasolo had some information", line++, true) + line(player, "about the Diamond of Shadow.", line++, true) + line(player, "Apparently it was owned by an invisible warrior, who I", line++, true) + line(player, "needed a special ring to see.", line++, true) + line(player, "Rasolo owned such a ring, but would only trade it in return", line++, true) + line(player, "for a gilded cross stolen from him by a bandit named.", line++, true) + line(player, "Laheeb.", line++, true) + if (getSubStage(player, attributeShadowStage) >= 3) { + line(player, "I found Laheeb's treasure chest, and managed to bypass", line++, true) + line(player, "the traps on it to take the gilded cross, which I returned to", line++, true) + line(player, "Rasolo.", line++, true) + line(player, "In return, he gave me the Ring of Visibility.", line++, true) + line(player, "I should put the !!Ring of Visibility?? on and try and find the", line++, false) + line(player, "hidden home of !!Damis?? - Rasolo suggested it was very", line++, false) + line(player, "close by to where he is...", line++, false) + } else if (getSubStage(player, attributeShadowStage) >= 2) { + line(player, "I found Laheeb's treasure chest, and managed to bypass", line++, true) + line(player, "the traps on it to take the gilded cross.", line++, true) + line(player, "I need to return the !!gilded cross?? to !!Rasolo??.", line++, false) // Derived + } else if (getSubStage(player, attributeShadowStage) >= 1) { + line(player, "I need to find !!Laheeb's loot?? and retrieve the stolen !!gilded??", line++, false) + line(player, "!!cross??.", line++, false) + } + } else if (getSubStage(player, attributeShadowStage) == 0) { + line(player, "I can use the !!scrying glasses?? to help find the", line++, false) + line(player, "!!Diamond of Shadow??.", line++, false) + } + } + + // If you hold a diamond too long a stranger will try to kill you. + // https://oldschool.runescape.wiki/w/Transcript:Stranger + // After defeating the stranger + // player(THINKING, "I wonder what that was all about?") + + // After all 4 diamonds + // Player dialogue("I should make sure I have all four diamonds with me", "before speaking to Eblis again.") + + if (stage >= 10) { + // This disappears after you place all the diamonds. + } else if (stage >= 9 && completedAllSubstages(player)) { + line(player, "Now that I have recovered all of the !!Diamonds of??", line++, false) + line(player, "!!Azzanadra?? I should take them all to !!Eblis?? and find out what.", line++, false) + line(player, "is so special about them.", line++, false) + // This is the current message even AFTER you speak to Eblis... baQ6oJOk7Gc + } + + if (stage >= 11) { + // This disappears at the end of this quest. + } else if (stage >= 10) { + line(player, "I should explore the !!pyramid?? and see what !!treasure?? awaits", line++, false) + line(player, "me!", line++, false) + } + + if (stage >= 100) { + line(player, "At the heart of the pyramid I found a strange being, who", line++, true) + line(player, "gave me some powerful new magic spells.", line++, true) + line(player, "I can switch between my old spells and my new spells any", line++, true) + line(player, "time by using the altar there, and can avoid the traps by", line++, true) + line(player, "using the secret passage.", line++, true) + line++ + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun reset(player: Player) { + setVarbit(player, varbitChildReunite, 0, true) + + removeAttribute(player, attributeBoughtBeer) + removeAttribute(player, attributeCountMagicLogs) + removeAttribute(player, attributeCountSteelBars) + removeAttribute(player, attributeCountMoltenGlass) + removeAttribute(player, attributeCountBones) + removeAttribute(player, attributeCountAshes) + removeAttribute(player, attributeCountCharcoal) + removeAttribute(player, attributeCountBloodRune) + setVarbit(player, varbitMirrors, 0, true) + + removeAttribute(player, attributeBloodStage) + removeAttribute(player, attributeDessousInstance) + + removeAttribute(player, attributeSmokeStage) + removeAttribute(player, attributeFareedInstance) + removeAttribute(player, attributeUnlockedGate) + setVarbit(player, varbitStandingTorchNorthEast, 0, true) + setVarbit(player, varbitStandingTorchSouthEast, 0, true) + setVarbit(player, varbitStandingTorchSouthWest, 0, true) + setVarbit(player, varbitStandingTorchNorthWest, 0, true) + + removeAttribute(player, attributeIceStage) + removeAttribute(player, attributeKamilInstance) + removeAttribute(player, attributeTrollKillCount) + setVarbit(player, varbitFrozenFather, 0, true) // 0-frozen 1-defrosted + setVarbit(player, varbitFrozenMother, 0, true) // 0-frozen 1-defrosted + setVarbit(player, varbitChildReunite, 0, true) // 0-frozen 4-reunited 5-reunited 6-varbitfails/ends (This varbit seems to manage ice stages...) + setVarbit(player, varbitCaveEntrance, 0, true) // 6446 0 - 6 6441 + + removeAttribute(player, attributeShadowStage) + removeAttribute(player, attributeDamisInstance) + removeAttribute(player, attributeDamisWarning) + setVarbit(player, varbitRingOfVisibility, 0, true) // Set to 1 to show ladder when wearing ring of visibility. + + removeAttribute(player, attributeBloodDiamondInserted) + removeAttribute(player, attributeSmokeDiamondInserted) + removeAttribute(player, attributeIceDiamondInserted) + removeAttribute(player, attributeShadowDiamondInserted) + setVarbit(player, varbitBloodObelisk, 0, true) + setVarbit(player, varbitSmokeObelisk, 0, true) + setVarbit(player, varbitIceObelisk, 0, true) + setVarbit(player, varbitShadowObelisk, 0, true) + } + + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Desert Treasure Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.ANCIENT_STAFF_4675, 240, 277, 5) + + drawReward(player,"3 Quest Points", ln++) + drawReward(player,"20,000 Magic XP", ln++) + drawReward(player,"Ancient Magicks", ln) + + player.skills.addExperience(Skills.MAGIC, 20000.0) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + override fun updateVarps(player: Player) { + + // Ice: Cave Entrance Varbit + setVarbit(player, varbitCaveEntrance, getAttribute(player,attributeTrollKillCount, 0)) + + // Shadow: Ring of Visibility Ladder Varbit + if (inEquipment(player, Items.RING_OF_VISIBILITY_4657)) { + setVarbit(player, varbitRingOfVisibility, 1) + } else { + setVarbit(player, varbitRingOfVisibility, 0) + } + + // Obelisks Varbits + if (getAttribute(player, attributeBloodDiamondInserted, 0) == 1) { + setVarbit(player, varbitBloodObelisk, 1) + } else { + setVarbit(player, varbitBloodObelisk, 0) + } + + if (getAttribute(player, attributeSmokeDiamondInserted, 0) == 1) { + setVarbit(player, varbitSmokeObelisk, 1) + } else { + setVarbit(player, varbitSmokeObelisk, 0) + } + + if (getAttribute(player, attributeIceDiamondInserted, 0) == 1) { + setVarbit(player, varbitIceObelisk, 1) + } else { + setVarbit(player, varbitIceObelisk, 0) + } + + if (getAttribute(player, attributeShadowDiamondInserted, 0) == 1) { + setVarbit(player, varbitShadowObelisk, 1) + } else { + setVarbit(player, varbitShadowObelisk, 0) + } + + // Special Varbit for Ice Stage + if (getAttribute(player, attributeIceStage, 0) > 5) { + setVarbit(player, varbitChildReunite, 5) + } else { + setVarbit(player, varbitChildReunite, 0) + } + + // Stage Varbits + if(getQuestStage(player, questName) == 0) { + setVarbit(player, varbitDesertTreasure, 0, true) + setVarbit(player, varbitMirrors, 0, true) + } + if(getQuestStage(player, questName) in 1..7) { + setVarbit(player, varbitDesertTreasure, 1, true) + setVarbit(player, varbitMirrors, 0, true) + } + if(getQuestStage(player, questName) in 8..9) { + setVarbit(player, varbitDesertTreasure, 10, true) + setVarbit(player, varbitMirrors, 1, true) + } + if(getQuestStage(player, questName) == 10) { + setVarbit(player, varbitDesertTreasure, 13, true) + setVarbit(player, varbitMirrors, 1, true) + } + if(getQuestStage(player, questName) >= 100) { + setVarbit(player, varbitDesertTreasure, 15, true) + setVarbit(player, varbitMirrors, 1, true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt new file mode 100644 index 000000000..3115efd3f --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasureListeners.kt @@ -0,0 +1,464 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.activity.Cutscene +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class DesertTreasureListeners : InteractionListener { + + companion object { + fun allDiamondsInserted(player: Player): Boolean{ + return getAttribute(player, DesertTreasure.attributeBloodDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeSmokeDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeIceDiamondInserted, 0) == 1 && + getAttribute(player, DesertTreasure.attributeShadowDiamondInserted, 0) == 1 + } + } + var temp = 6517 + + override fun defineListeners() { + + // THE MIRRORS + on(Scenery.MYSTICAL_MIRROR_6423, SCENERY, "look-into") { player, node -> + SouthMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6425, SCENERY, "look-into") { player, node -> + SouthWestMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6427, SCENERY, "look-into") { player, node -> + NorthWestMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6429, SCENERY, "look-into") { player, node -> + NorthMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6431, SCENERY, "look-into") { player, node -> + NorthEastMirrorLookCutscene(player).start() + return@on true + } + on(Scenery.MYSTICAL_MIRROR_6433, SCENERY, "look-into") { player, node -> + SouthEastMirrorLookCutscene(player).start() + return@on true + } + + + // THE OBELISKS + onUseWith(IntType.SCENERY, intArrayOf(Items.ICE_DIAMOND_4671, Items.SMOKE_DIAMOND_4672, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6483) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.BLOOD_DIAMOND_4670, Scenery.OBELISK_6483) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitBloodObelisk, 1) + setAttribute(player, DesertTreasure.attributeBloodDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.ICE_DIAMOND_4671, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6486) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.SMOKE_DIAMOND_4672, Scenery.OBELISK_6486) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitSmokeObelisk, 1) + setAttribute(player, DesertTreasure.attributeSmokeDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.SMOKE_DIAMOND_4672, Items.SHADOW_DIAMOND_4673), Scenery.OBELISK_6489) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.ICE_DIAMOND_4671, Scenery.OBELISK_6489) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitIceObelisk, 1) + setAttribute(player, DesertTreasure.attributeIceDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, intArrayOf(Items.BLOOD_DIAMOND_4670, Items.ICE_DIAMOND_4671, Items.SMOKE_DIAMOND_4672), Scenery.OBELISK_6492) { player, used, with -> + sendMessage(player, "That doesn't appear to be the correct diamond...") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.SHADOW_DIAMOND_4673, Scenery.OBELISK_6492) { player, used, with -> + if (getDynLevel(player, Skills.MAGIC) > 50) { + if (removeItem(player, used)) { + sendMessage(player, "The diamond is absorbed into the pillar.") + setVarbit(player, DesertTreasure.varbitShadowObelisk, 1) + setAttribute(player, DesertTreasure.attributeShadowDiamondInserted, 1) + if (allDiamondsInserted(player)) { + sendMessage(player, "The force preventing access to the Pyramid has now vanished.") + if (getQuestStage(player, DesertTreasure.questName) == 9) { + setQuestStage(player, DesertTreasure.questName, 10) + } + } + } + } else { + sendMessage(player, "You are not a powerful enough mage to breach the protective aura.") + sendMessage(player, "You need a magic level of at least 50 to enter the Pyramid.") + } + return@onUseWith true + } + + // THE DOOR + on(intArrayOf(Scenery.PYRAMID_ENTRANCE_6545, Scenery.PYRAMID_ENTRANCE_6547), SCENERY, "open") { player, node -> + if (allDiamondsInserted(player)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "A mystical power has sealed this door...") + } + return@on true + } + + // THE LADDERS + on(Scenery.LADDER_6497, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2913, 4954, 3)) + return@on true + } + on(Scenery.LADDER_6504, SCENERY, "climb-up") { player, node -> + teleport(player, Location(3233, 2898, 0)) + return@on true + } + + on(Scenery.LADDER_6498, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2846, 4964, 2)) + return@on true + } + on(Scenery.LADDER_6503, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2909, 4963, 3)) + return@on true + } + + on(Scenery.LADDER_6499, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2782, 4972, 1)) + return@on true + } + on(Scenery.LADDER_6502, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2845, 4973, 2)) + return@on true + } + + on(Scenery.LADDER_6500, SCENERY, "climb-down") { player, node -> + teleport(player, Location(3233, 9293, 0)) + return@on true + } + on(Scenery.LADDER_6501, SCENERY, "climb-up") { player, node -> + teleport(player, Location(2783, 4941, 1)) + return@on true + } + + on((6512..6517).toIntArray(), SCENERY, "search") { player, node -> + // Technically there is loot, but I'm lazy as hell. + sendMessage(player, "You don't find anything interesting.") + return@on true + } + + // After Quest + + // Backdoor + on(Scenery.TUNNEL_6481, SCENERY, "enter") { player, node -> + if (isQuestComplete(player, DesertTreasure.questName)){ + teleport(player, Location(3233, 9313, 0)) + } else { + sendMessage(player, "This passage does not seem to lead anywhere...") // https://youtu.be/uNkBucGaqac + } + return@on true + } + + // Portal, which only appears after DT + on(Scenery.PORTAL_6551, SCENERY, "use") { player, node -> + teleport(player, Location(3233, 2887, 0)) + return@on true + } + + } +} + + +// https://www.youtube.com/watch?v=yMwp78OI2y8 + +// Ice Troll +class NorthMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(11322) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 5, 27) + moveCamera(5, 27, 1000) + rotateCamera(6, 27, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Canifis +class NorthEastMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13878) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 47, 31) + moveCamera(47, 31, 1000) + rotateCamera(43, 31, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Smoke Dungeon +class SouthEastMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13102) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 36, 20) + moveCamera(36, 20, 1000) + rotateCamera(44, 20, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Pyramid +class SouthMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(12845) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 33, 41) + moveCamera(33, 41, 1000) + rotateCamera(33, 39, 990) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Bedabin +class SouthWestMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(12590) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 40, 39) + moveCamera(40, 39, 1200) + rotateCamera(10, 39, 1200) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} + +// Rasolo +class NorthWestMirrorLookCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(10037) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 56, 40) + moveCamera(56, 40, 1400) + rotateCamera(56, 35, 1000) + timedUpdate(1) + } + 2 -> { + openInterface(player, 155) + closeOverlay() + timedUpdate(6) + } + 3-> { + closeInterface(player) + fadeToBlack() + timedUpdate(4) + } + 4-> { + end(false){ + fadeFromBlack() + // resetCamera() + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt new file mode 100644 index 000000000..ec4e8a142 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt @@ -0,0 +1,144 @@ +package content.region.desert.quest.deserttreasure + +import content.region.kandarin.quest.templeofikov.TempleOfIkov +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.* +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.prayer.PrayerType +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class DessousMeleeBehavior : NPCBehavior(NPCs.DESSOUS_1914, NPCs.DESSOUS_1915) { + private var disappearing = false; + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean{ + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Dessous returns to his grave, bored of toying with you.") + removeAttribute(player, DesertTreasure.attributeDessousInstance) + } + poofClear(self) + } + + // Dessous just continually hisses independently of projectile fires. + if (self.id == NPCs.DESSOUS_1915 && self.properties.combatPulse.isInCombat) { + animate(self, Animation(1914)) + } + // This is probably the prayer flicking nonsense. + if (self.id == NPCs.DESSOUS_1914 && player != null && player.prayer.get(PrayerType.PROTECT_FROM_MELEE)) { + self.transform(NPCs.DESSOUS_1915) + Graphics.send(Graphics(86), self.location) + } else if (self.id == NPCs.DESSOUS_1915 && player != null && (player.prayer.get(PrayerType.PROTECT_FROM_MAGIC) || player.prayer.get(PrayerType.PROTECT_FROM_MISSILES))) { + self.transform(NPCs.DESSOUS_1914) + Graphics.send(Graphics(86), self.location) + } + return true + } + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + if (self.id == NPCs.DESSOUS_1915) { + // 2 x 5HP (One Magic, One Ranged) + return CombatHandler() + } else { + // Fast melee attack 3 ticks up to 19HP + return original + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + // Teleport nearer, if too far. + if (victim is Player) { + if (victim.location.getDistance(self.location) >= 5) { + Graphics.send(Graphics(86), self.location) + self.properties.teleportLocation = victim.location + Graphics.send(Graphics(86), self.location) + } + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + val player = killer + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 2) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 3) + } + removeAttribute(player, DesertTreasure.attributeDessousInstance) + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> player(FacialExpression.ANGRY, "Well that's Dessous dead, but where is the Diamond he", "was supposed to have?").also { stage++ } + 1 -> playerl(FacialExpression.ANGRY, "If Malak lied to me about it, he is going to pay!").also { + stage = END_DIALOGUE + } + } + } + }) + } + } + + /** Handler for ranged. */ + class CombatHandler : MultiSwingHandler( + SwitchAttack(CombatStyle.MAGIC.swingHandler, null), + SwitchAttack(CombatStyle.RANGE.swingHandler, null) + ) { + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + if (entity is NPC && victim is Player) { + val projectile = Projectile.create( + victim.location.transform(Location(intArrayOf(3, -3).random(),intArrayOf(3, -3).random())), + victim.location, + 350, + 0, + 0, + 0, + 60, + 0, + 255 + ) + // 2 x 5HP (One Magic, One Ranged) + state!!.estimatedHit = 5 + state.secondaryHit = 5 // I have no idea what I'm doing + queueScript(entity, 0, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + sendChat(entity, "Hssssssssssss") + projectile.send() + return@queueScript delayScript(entity, entity.location.getDistance(victim.location).toInt()) + } + 1 -> { + return@queueScript stopExecuting(entity) + } + else -> return@queueScript stopExecuting(entity) + } + } + + } + return super.swing(entity, victim, state) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt new file mode 100644 index 000000000..8105aebf7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfBloodListeners.kt @@ -0,0 +1,134 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfBloodListeners : InteractionListener { + override fun defineListeners() { + + // Silver pot conversions + onUseWith(ITEM, Items.SILVER_POT_4660, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4664) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4660, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4662) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4662, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4666) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SILVER_POT_4664, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.SILVER_POT_4666) + } + return@onUseWith true + } + // Blessed pot conversions + onUseWith(ITEM, Items.BLESSED_POT_4661, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4665) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4661, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4663) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4663, Items.SPICE_2007) { player, used, with -> + sendMessage(player, "You add some spices to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4667) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BLESSED_POT_4665, Items.GARLIC_POWDER_4668) { player, used, with -> + sendMessage(player, "You add some crushed garlic to the pot.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.BLESSED_POT_4667) + } + return@onUseWith true + } + + // You need to crush the garlic. + onUseWith(ITEM, intArrayOf(Items.SILVER_POT_4660, Items.BLESSED_POT_4661, Items.SILVER_POT_4662, Items.BLESSED_POT_4663, Items.SILVER_POT_4664, Items.BLESSED_POT_4665, Items.SILVER_POT_4666, Items.BLESSED_POT_4667 ), Items.GARLIC_1550) { player, used, with -> + sendMessage(player, "You need to crush the garlic before adding it to the pot.") + return@onUseWith true + } + + // Dessous jumps out. + onUseWith(SCENERY, Items.BLESSED_POT_4667, Scenery.VAMPIRE_TOMB_6437) { player, used, with -> + val prevNpc = getAttribute(player, DesertTreasure.attributeDessousInstance, null) + if (prevNpc != null) { + prevNpc.clear() + } + sendMessage(player, "You pour the blood from the pot onto the tomb.") + removeItem(player, used) + val scenery = with.asScenery() + // Swap to a splittable vampire tomb scenery. + replaceScenery(scenery, Scenery.VAMPIRE_TOMB_6438, Animation(1915).duration) + // Vampire Tomb breaks open. + animateScenery(player, scenery, 1915) + // 8 Bat projectiles + spawnProjectile(Location(3570, 3402), Location(3570, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3570, 3400), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3402), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3402), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3404), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3568, 3400), 350, 0, 0, 0, 60, 0) + spawnProjectile(Location(3570, 3402), Location(3572, 3400), 350, 0, 0, 0, 60, 0) + val npc = NPC(NPCs.DESSOUS_1914) + queueScript(player, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + // Projectile gfx for Dessous to jump out. + spawnProjectile(Location(3570, 3402), Location(3570, 3405), 351, 0, 0, 0, 40, 0) + return@queueScript delayScript(player, 1) + } + 1 -> { + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(3570, 3405, 0) + npc.direction = Direction.NORTH + setAttribute(player, DesertTreasure.attributeDessousInstance, npc) + setAttribute(npc, "target", player) + + npc.init() + npc.attack(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + //sendGraphics(350, Location(3570, 3402)) + //sendGraphics(351, Location(3570, 3402)) + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt new file mode 100644 index 000000000..c6a8de304 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfIceListeners.kt @@ -0,0 +1,168 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfIceListeners : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.NPC, Items.CHOCOLATE_CAKE_1897, NPCs.BANDIT_1932 /* should be NPCs.TROLL_CHILD_1932 */) { player, used, with -> + if (removeItem(player, used)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeIceStage, 1) + } + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> player("Hey there little troll...", "Take this and dry those tears...").also { stage++ } + 1 -> npc(FacialExpression.OLD_NEARLY_CRYING, "(sniff)").also { + stage = END_DIALOGUE + } + } + } + }, with as NPC) + } + return@onUseWith true + } + + on(intArrayOf(Scenery.ICE_GATE_5043, Scenery.ICE_GATE_5044), SCENERY, "go-through") { player, node -> + + if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) > 1) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + sendMessage(player, "You squeeze through the large icy bars of the gate.") + // Anim 3272 to squeeze through? + if(player.location.x > 2838) { + teleport(player, Location(2837, 3739, 0)) + } else { + teleport(player, Location(2839, 3739, 0)) + } + } else { + // j_SdwOX1JWg + sendDialogueLines(player, "The bars are frozen tightly shut and a sturdy layer of ice prevents", "you from slipping through.") + } + return@on true + } + + on(Scenery.CAVE_ENTRANCE_6441, SCENERY, "enter") { player, node -> + lock(player, 3) + animate(player, 2796) // Crawling + queueScript(player, 3, QueueStrength.SOFT) { + teleport(player, Location(2874, 3720, 0)) + return@queueScript stopExecuting(player) + } + return@on true + } + + on(Scenery.CAVE_ENTRANCE_6446, SCENERY, "enter") { player, node -> + sendMessage(player, "The entrance to the cave is covered in too much ice to get through.") + return@on true + } + + on(Scenery.CAVE_EXIT_6447, SCENERY, "enter") { player, node -> + lock(player, 3) + animate(player, 2796) // Crawling + queueScript(player, 3, QueueStrength.SOFT) { + teleport(player, Location(2867, 3719, 0)) + return@queueScript stopExecuting(player) + } + return@on true + } + + on(Scenery.ICE_LEDGE_6455, SCENERY, "use") { player, node -> + + if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 3) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + if (inEquipment(player, Items.SPIKED_BOOTS_3107)) { + teleport(player, Location(2838, 3803, 1)) + } else { + sendPlayerDialogue(player, "I don't think I'll make much headway along that icy slope without some spiked boots...") + } + } else { + sendMessage(player, "You have not defeated Kamil yet.") + } + return@on true + } + + on(intArrayOf(Scenery.ICE_GATE_6461, Scenery.ICE_GATE_6462), SCENERY, "go-through") { player, node -> + + teleport(player, Location(2852, 3810, 2)) + return@on true + } + + // This is 1943 as base + on(NPCs.ICE_BLOCK_1944, NPC, "talk-to") { player, node -> + sendDialogueLines(player, "There is a thick layer of ice covering this troll.", "You will have to find some way of shattering it.") + return@on true + } + on(NPCs.ICE_BLOCK_1944, NPC, "smash-ice") { player, node -> + player.attack(node) + return@on true + } + + // This is 1945 as base + on(NPCs.ICE_BLOCK_1946, NPC, "talk-to") { player, node -> + sendDialogueLines(player, "There is a thick layer of ice covering this troll.", "You will have to find some way of shattering it.") + return@on true + } + on(NPCs.ICE_BLOCK_1946, NPC, "smash-ice") { player, node -> + player.attack(node) + return@on true + } + } +} + +/** This is while you are walking up the ice path. **/ +class ComicalTrippingIceArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(2815, 3775, 2880, 3839, 1), + ZoneBorders(2815, 3775, 2880, 3839, 2) + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + if ((1..10).random() == 1) { + lock(entity, 2) + stopWalk(entity) + animate(entity, 767) + } + } + } +} + +class IceAreaAttack : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2850, 3750, 2880, 3770)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player && + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeIceStage) == 2 && + getAttribute(entity, DesertTreasure.attributeKamilInstance, null) == null + ) { + sendMessage(entity, "You can feel an evil presence nearby...") + val npc = NPC.create(NPCs.KAMIL_1913, Location(2857, 3754, 0)) + setAttribute(entity, DesertTreasure.attributeKamilInstance, npc) + setAttribute(npc, "target", entity) + npc.isRespawn = false + npc.init() + npc.attack(entity) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt new file mode 100644 index 000000000..3ca6e6a60 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt @@ -0,0 +1,264 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfShadowListeners : InteractionListener { + + companion object { + + fun roll(player: Player): Boolean { + + val chance = RandomFunction.randomDouble(1.0, 100.0) + val successChance = RandomFunction.getSkillSuccessChance(52.0, 128.0, getDynLevel(player, Skills.THIEVING)) + + return chance < successChance + } + + fun pickAttempt(player: Player, picklockItem: Item?) { + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You attempt to pick the first lock...") + return@queueScript delayScript(player, 1) + } + + 1 -> { + if (roll(player)) { + sendMessage(player, "...You successfully picked the first lock!") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 2 -> { + sendMessage(player, "You attempt to pick the second lock...") + return@queueScript delayScript(player, 1) + } + + 3 -> { + if (roll(player)) { + sendMessage(player, "...You successfully picked the second lock!") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 4 -> { + sendMessage(player, "You attempt to pick the final lock...") + return@queueScript delayScript(player, 1) + } + + 5 -> { + if (roll(player)) { + sendMessage(player, "You managed to pick the final lock.") + return@queueScript delayScript(player, 1) + } else { + if(picklockItem != null) { + removeItem(player, picklockItem) + } else { + removeItem(player, Items.LOCKPICK_1523) + } + sendMessage(player, "...and fail. The locking mechanism has reset itself.") + sendMessage(player, "Your lock pick snapped while attempting to pick the lock.") + impact(player, 3) + applyPoison(player, player, 6) + return@queueScript stopExecuting(player) + } + } + + 6 -> { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 2) + } + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } + } + + override fun defineListeners() { + + // Hidden entrance to the shadow dungeon. + on(Scenery.LADDER_6561, SCENERY, "climb-down") { player, node -> + teleport(player, Location(2630, 5072)) + return@on true + } + + /** This will open up a lot of other places. Maybe have this in a general file? */ + onEquip(Items.RING_OF_VISIBILITY_4657) { player, _ -> + + if((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 3 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + + setVarbit(player, DesertTreasure.varbitRingOfVisibility, 1) + return@onEquip true + } + sendMessage(player, "You need to complete part of Desert Treasure to equip this.") + return@onEquip false + } + + onUnequip(Items.RING_OF_VISIBILITY_4657) { player, _ -> + setVarbit(player, DesertTreasure.varbitRingOfVisibility, 0) + return@onUnequip true + } + + + + on(Scenery.SECURE_CHEST_6448, SCENERY, "open") { player, node -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + if (inInventory(player, Items.LOCKPICK_1523)) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> dialogue("Your skill as a thief allows you to see some kind of elaborate booby", "trapped locking mechanism on this chest.").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes", 2, true), + Topic(FacialExpression.NEUTRAL, "No", END_DIALOGUE, true), + title = "Try to open the chest?" + ) + 2 -> end().also { + if (inInventory(player, Items.LOCKPICK_1523)) { + pickAttempt(player, null) + } else { + sendMessage(player, "You need a lockpick in order to attempt this.") + } + } + } + } + }) + } else { + sendMessage(player, "You need a lockpick in order to attempt this.") + } + } else if ((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 2 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + if (inInventory(player, Items.GILDED_CROSS_4674)) { + sendMessage(player, "The chest is empty.") + } else { + replaceScenery(node as core.game.node.scenery.Scenery, 6449, 2) + sendMessage(player, "Inside the chest, hidden under some rags, you find a Gilded Cross.") + addItemOrDrop(player, Items.GILDED_CROSS_4674) + } + } else { + sendPlayerDialogue(player, "These bandits are hostile enough without me trying to rob them!") + } + return@on true + } + + onUseWith(SCENERY, Items.LOCKPICK_1523, Scenery.SECURE_CHEST_6448) { player, used, with -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 1) { + pickAttempt(player, used as Item) + } else if((DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) >= 2 && + getQuestStage(player, DesertTreasure.questName) >= 9) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + sendMessage(player, "The chest is unlocked.") + } else { + sendPlayerDialogue(player, "These bandits are hostile enough without me trying to rob them!") + } + return@onUseWith true + } + + } +} + +class ShadowDungeonWarning : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2726, 5072, 2728, 5072)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if ( + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 3 && + getAttribute(entity, DesertTreasure.attributeDamisWarning, false) + ) { + sendMessage(entity, "A voice seems to come from the walls around you;") + sendMessage(entity, "'You... do not be... long in this... place") + sendMessage(entity, "Turn... back now, or... prepare... to meet your... doom'") + getAttribute(entity, DesertTreasure.attributeDamisWarning, true) + } else if ( + getQuestStage(entity, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 100 + ) { + if (!inInventory(entity, Items.SHADOW_DIAMOND_4673) && !inBank(entity, Items.SHADOW_DIAMOND_4673)) { + sendMessage(entity, "The Diamond of Shadow seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), Location(2739, 5088, 0), entity) + } + } + } + } +} + +class ShadowDungeonAttack : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2731, 5085, 2748, 5097)) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if (getQuestStage(entity, DesertTreasure.questName) == 9) { + if (DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) == 3 && + getAttribute(entity, DesertTreasure.attributeDamisInstance, null) == null + ) { + val npc = NPC.create(NPCs.DAMIS_1974, Location(2739, 5088, 0)) + setAttribute(entity, DesertTreasure.attributeDamisInstance, npc) + setAttribute(npc, "target", entity) + npc.isRespawn = false + npc.walkRadius = 30 + npc.init() + npc.attack(entity) + sendChat(npc, "You should have listened to me!") + } + } else if (DesertTreasure.getSubStage(entity, DesertTreasure.attributeShadowStage) >= 100) { + if (!inInventory(entity, Items.SHADOW_DIAMOND_4673) && !inBank(entity, Items.SHADOW_DIAMOND_4673)) { + sendMessage(entity, "The Diamond of Shadow seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), Location(2739, 5088, 0), entity) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt new file mode 100644 index 000000000..279b6d17d --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfSmokeListeners.kt @@ -0,0 +1,180 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.timer.RSTimer +import core.game.world.map.Location +import core.tools.secondsToTicks +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class DiamondOfSmokeListeners : InteractionListener { + companion object { + const val timerIdentifierTorchNE = "deserttreasureNEtorch" + const val timerIdentifierTorchSE = "deserttreasureSEtorch" + const val timerIdentifierTorchSW = "deserttreasureSWtorch" + const val timerIdentifierTorchNW = "deserttreasureNWtorch" + + fun checkAllTorchesLit(player: Player) : Boolean { + return getVarbit(player, DesertTreasure.varbitStandingTorchNorthEast) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchSouthEast) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchSouthWest) == 1 && + getVarbit(player, DesertTreasure.varbitStandingTorchNorthWest) == 1 + } + } + + override fun defineListeners() { + on(Scenery.BURNT_CHEST_6420, SCENERY, "open") { player, node -> + if (checkAllTorchesLit(player)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeSmokeStage, 1) + } + } + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) >= 1) { + replaceScenery(node as core.game.node.scenery.Scenery, 6421, 2) + addItemOrDrop(player, Items.WARM_KEY_4656) + sendMessage(player, "You open the chest and take a key.") + } else { + sendDialogueLines(player, "There seems to be no way to open this chest. Engraved where the", "keyhole should be is a message:", "'Light the path, and find the key...'") + } + return@on true + } + + on(intArrayOf(Scenery.GATE_6451, Scenery.GATE_6452), SCENERY, "open") { player, node -> + if (getQuestStage(player, DesertTreasure.questName) == 9) { + // Set attributeUnlockedGate to true if warm key and first time unlocking the gate. + if (!getAttribute(player, DesertTreasure.attributeUnlockedGate, false) && inInventory(player, Items.WARM_KEY_4656)) { + if(removeItem(player, Items.WARM_KEY_4656)) { + sendMessage(player, "You unlock the gate and enter the room.") + setAttribute(player, DesertTreasure.attributeUnlockedGate, true) + } + } + // Fight if unlocked gate, drop recovery diamond if done. + if (getAttribute(player, DesertTreasure.attributeUnlockedGate, false)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) == 1 && + getAttribute(player, DesertTreasure.attributeFareedInstance, null) == null) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + val npc = core.game.node.entity.npc.NPC.create(NPCs.FAREED_1977, Location(3315, 9376, 0)) + setAttribute(player, DesertTreasure.attributeFareedInstance, npc) + setAttribute(npc, "target", player) + npc.isRespawn = false + npc.init() + npc.attack(player) + sendChat(npc, "You dare trespass in my realm?") + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeSmokeStage) >= 100) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + if (!inInventory(player, Items.SMOKE_DIAMOND_4672) && !inBank(player, Items.SMOKE_DIAMOND_4672)) { + sendMessage(player, "The Diamond of Smoke seems to have mystically found its way back here...") + GroundItemManager.create(Item(Items.SMOKE_DIAMOND_4672), Location(3315, 9376, 0), player) + } + } + } else { + sendMessage(player, "The gate is locked.") + } + + } else if (getQuestStage(player, DesertTreasure.questName) in 9..100) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "The gate is locked.") + } + return@on true + } + + + on(Scenery.A_DARK_HOLE_31367, SCENERY, "enter") { player, node -> + // You have to come from the Pollnivneach Slayer Dungeon, then this will tele you back. + sendDialogueLines(player, "This hole leads to a maze of other passages. Without knowing where", "you are headed, there's no chance of reaching anywhere interesting.") + return@on true + } + + // NE Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6406, Scenery.STANDING_TORCH_6413) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchNorthEast, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchNE) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchNE, DesertTreasure.varbitStandingTorchNorthEast)) + return@onUseWith true + } + + // SE Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6408, Scenery.STANDING_TORCH_6414) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchSouthEast, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchSE) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchSE, DesertTreasure.varbitStandingTorchSouthEast)) + return@onUseWith true + } + + // SW Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6410, Scenery.STANDING_TORCH_6415) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchSouthWest, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchSW) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchSW, DesertTreasure.varbitStandingTorchSouthWest)) + return@onUseWith true + } + + // NW Standing Torch + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.STANDING_TORCH_6412, Scenery.STANDING_TORCH_6416) { player, used, with -> + if (getDynLevel(player, Skills.FIREMAKING) < 50) { + sendMessage(player, "You need a firemaking level of 50 to light this torch.") + return@onUseWith true + } + setVarbit(player, DesertTreasure.varbitStandingTorchNorthWest, 1) + sendMessage(player, "You light the torch.") + if (checkAllTorchesLit(player)) { + sendMessage(player, "The path is lit, now claim the key...") + } + removeTimer(player, timerIdentifierTorchNW) + registerTimer(player, StandingTorchTimer(timerIdentifierTorchNW, DesertTreasure.varbitStandingTorchNorthWest)) + return@onUseWith true + } + } +} + +class StandingTorchTimer(private val timerIdentifier: String = "deserttreasureunknowntimer", private val torchVarbit: Int = 0) : RSTimer(secondsToTicks(150), timerIdentifier) { + override fun run(entity: Entity): Boolean { + if (entity is Player) { + when(timerIdentifier) { + DiamondOfSmokeListeners.timerIdentifierTorchNE -> sendMessage(entity, "The North-east torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchSE -> sendMessage(entity, "The South-east torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchSW -> sendMessage(entity, "The South-west torch burns out...") + DiamondOfSmokeListeners.timerIdentifierTorchNW -> sendMessage(entity, "The North-west torch burns out...") + else -> sendMessage(entity, "The torch burns out...") + } + setVarbit(entity, torchVarbit, 0) + } + entity.timers.removeTimer(this) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt new file mode 100644 index 000000000..11608e56c --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/EblisDialogue.kt @@ -0,0 +1,328 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class EblisDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, EblisDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return EblisDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.EBLIS_1923) + } + +} +class EblisDialogueFile : DialogueBuilderFile() { + + companion object { + fun checkAllGiven(player: Player): Boolean { + return (getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) >= 12 && + getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) >= 6 && + getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) >= 6 && + getAttribute(player, DesertTreasure.attributeCountBones, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountAshes, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) >= 1 && + getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) >= 1) + } + } + + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4) + .npcl("Leave us to our fate. We care nothing for the world that betrayed us, or those that come from it.") + .end() + + b.onQuestStages(DesertTreasure.questName, 5,6) + .playerl("Hello. I represent the Museum of Varrock, and I have reason to believe there may be some kinds of artefacts of historical significance in the nearby area...") + .npcl("Ah yes. The only time people care about our existence is when they think they have something to gain from us.") + .npcl("I have nothing to say to you. You and your kind are not welcome here.") + .playerl("Please, if I can just have a few minutes of your time to ask some questions...?") + .npcl("(sigh) I suppose I can spare you that. What do you wish to know about?") + .options() + .let { optionBuilder -> + optionBuilder.option("Why is this village so hostile?") + .playerl("Why are all of the people here so hostile? You would think I was asking you for money instead of just for answers to a few questions...") + .npcl("It is a long story, and I doubt you have much interest in hearing it. Your sort never are, you just take what you can of ours, and then abandon us once more to the desert.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option("No, I want to hear this story.") + .playerl("Actually, I'd be quite interested to hear what it is you have to say to excuse the attitude everybody in this village seems to have.") + .npcl("Ah, it all begun many generations ago, when our ancestors were the proud rulers of these lands...") + .npcl("My ancestors lived far to the North of here, and our lands stretched from the sea in the East to the river Lum, and the mountain of ice. From coast to coast, North to South, our domain was absolute.") + .npcl("Our god was kind to us, and blessed us with prosperity and happiness, and in return we were merciless to his enemies wherever we found them.") + .npcl("Then came the betrayal.") + .npcl("Our god was banished, leaving us helpless to our fates.") + .npcl("Without his protection, we were forced to fend for ourselves once more, against the enemies that sought to destroy us through their petty jealousies.") + .npcl("But we did not succumb without fighting! The spiteful Saradomin and pathetic Zamorak warred with each other, but the hatred they had for each other was as nothing to the hatred they held towards us!") + .npcl("With each battle they waged, we lost more and more land, unable to fight on all fronts, and were pushed further and further South into this gods-forsaken desert.") + .npcl("Our greatest hero, Azzanadra, was finally trapped in a strange stone structure to the South of here, and bound within by terrible powers...") + .npcl("And with that our lands, our homes, our very lives were stolen from us! Too weak to reclaim what was rightfully ours, we made our homes here, knowing that someday Azzanadra will") + .npcl("return with his magnificent power, and bring us back to our former glory...") + .playerl("So you're upset because of something that happened hundreds of years ago?") + .playerl("Seems to me like maybe you should find some closure, and let the past go...") + .npcl("The insults heaped upon my race will never be forgotten, will never be forgiven and will never again be overlooked.") + .npcl("Someday, a harsh wind will blow upon this land, uncovering the wrongs of the past, and we will get back what is rightfully ours. Until such a day we will bide our time here, and will") + .npcl("always be ready with our blades for our righteous vengeance.") + .end() + + optionBuilder2.option("I don't care about your story.") + .playerl("I don't really care what your story is to be honest, there is no excuse for such rudeness or hostility.") + .playerl("I have done nothing wrong to you, but everybody here treats me like I have committed some great crime against the village.") + .npcl("That is because, from our point of view, you have.") + .playerl("What? Just because I entered your village?") + .npcl("You have no right to be here! You have no right to the life you have, for it was taken at our expense!") + .playerl("Whatever... No wonder all you loonies live out here in the desert by yourselves.") + .end() + } + + optionBuilder.option("Do you know anything about treasure near here?") + .playerl("I was wondering if you knew anything about some treasure somewhere around here?") + .playerl("I have some evidence that there might be some kind of treasure hidden very close to this village...") + .npcl("If I knew of any treasure I would not choose to spend my life in this gods-forsaken desert.") + .end() + + optionBuilder.option("Do you know anything about a fortress near here?") + .playerl("Do you know anything about some kind of fortress nearby? I have reason to believe there is, or at least used to be, some kind of fortress very close to here...") + .npcl("Nobody would build anything in this wasteland unless they were forced to, to survive.") + .npcl("I know of no fortress, I know of no reason why anyone would ever bother doing anything out here in the desert.") + .end() + + + // This will only show up after you've talked to the bartender. + optionBuilder.optionIf("Tell me of the four diamonds of Azzanadra.") { player -> + return@optionIf getQuestStage(player, DesertTreasure.questName) == 6 + } + .playerl("So tell me... Did you ever hear of something called the Diamonds of Azzanadra?") + .npcl("This is the treasure which you seek???") + .npcl("Please accept my apologies noble @g[sir,madam]! I thought you were but some opportunistic thief, looking to steal what heritage we have left! Now I see that you are in fact a brave adventurer,") + .npcl("looking to restore our glories back upon us!") + .playerl("Uh... yeah... So anyway, you have heard of them?") + .npcl("Heard of them? Of course I have heard of them! They are the legacy of the great Mahjarrat hero, Azzanadra!") + .playerl("So... do you have any idea where they might be? I have a feeling they will be very valuable.") + .playerl("Uh, valuable as historical artefacts I mean, obviously.") + .npcl("They were stolen by warriors of the false god Zamorak generations ago. When you find the warriors, you will find the diamonds.") + .npcl("I suspect they will not willingly part with such objects of power however.") + .npc("Beware too, for these warriors are very powerful;", "they have taken the powers of the diamonds into themselves!") + .playerl("How do you mean?") + .npcl("Each diamond has an elemental quality...") + .npcl("There is the Diamond of Blood, the Diamond of Ice, the Diamond of Smoke and the Diamond of Shadow.") + .npcl("You should expect the warriors to have taken some aspect of these diamonds as their own...") + .playerl("Do you have any idea how I could track down these warriors somehow, then?") + .npcl("There is an ancient spell I know of that may spy upon such power... But it will require a few ingredients for it to work.") + .npcl("Should you be willing to get these ingredients for me, I will be able to locate the rough area where each of these warriors has taken refuge. The spell is imprecise, but it should help you get on the") + .npcl("right track in your search.") + .npcl("Is your desire for our freedom strong enough? Will you gather the ingredients for this spell for me?") + .options() + .let { optionBuilder2 -> + + optionBuilder2.option("Yes") + // The quest should jump to the next stage here, but I didn't want to write some weird if else here. + .playerl("Sure, what do you need?") + .npcl("For this spell, I will need to make some scrying glasses. I will need enough so that we can view the realm in its entirety.") + .npcl("When enchanted, the scrying glass will be able to let us view any area that has been influenced by the presence of the Diamonds of Azzanadra.") + .playerl("Okay, but what exactly do you need for this spell?") + .npcl("Well, six scrying glasses should be sufficient. For each scrying glass, I will need two magic logs, a steel bar and some molten glass. This makes a total of 12 magic logs, 6 pieces of molten") + .npcl("glass, and 6 steel bars.") + .npcl("In addition, for the actual spell to enchant the glasses, I will require one set of normal bones, some ash, some charcoal and a single blood rune.") + .npcl("Do you understand me, adventurer?") + .playerl("Quick question; what kind of bones do you need?") + .npcl("Standard bones. Other types of bones are of no use to me in this spell.") + .options() + .let { optionBuilder3 -> + optionBuilder3.option("Yes, I will go get those for you.") + .playerl("It's a slightly odd collection of ingredients, but I shouldn't have too much trouble getting those for you.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 6) { + setQuestStage(player, DesertTreasure.questName, 7) + } + } + + optionBuilder3.option("No, please repeat those ingredients.") + .npcl("Before I can complete the spell I will still need the following items;") + .npc("12 magic logs", "6 steel bars", "6 molten glass") + .npc("1 bones,", "1 ashes,", "1 charcoal", "and 1 blood rune.") // This is sic authentic trash dialogue. + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 6) { + setQuestStage(player, DesertTreasure.questName, 7) + } + } + + } + + optionBuilder2.option("No") + .playerl("Actually I don't feel like going on a shopping trip for you right now.") + .npcl("As you wish. I should have known not to get my hopes up that our long cursed life may soon be at an end...") + .end() + } + + optionBuilder.option("Nothing thanks.") + .playerl("Actually, there was nothing I really wanted to ask you about.") + .npcl("Yes, it is exactly like your sort to waste my time in such a way.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 7) + // Branch to check + + .branch { player -> + return@branch if (checkAllGiven(player)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("Excellent! Those are all the ingredients I need to create the scrying glasses.") + .npcl("I will find a suitable spot in the desert to the East of here, and set them up. When you are ready to begin your search, please come and find me there, I will show you how to utilise the") + .npcl("mirrors to find the diamonds.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 7) { + setQuestStage(player, DesertTreasure.questName, 8) + } + } + + branch.onValue(0) + .npcl("Before I can complete the spell I will still need the following items;") + .manualStage { df, player, _, _ -> + df.interpreter!!.sendDialogues(npc!!.id, FacialExpression.NEUTRAL, + "" + (12 - getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0)) + " magic logs", + "" + (6 - getAttribute(player, DesertTreasure.attributeCountSteelBars, 0)) + " steel bars", + "" + (6 - getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0)) + " molten glass") + } + .manualStage { df, player, _, _ -> + df.interpreter!!.sendDialogues(npc!!.id, FacialExpression.NEUTRAL, + "" + (1 - getAttribute(player, DesertTreasure.attributeCountBones, 0)) + " bones,", + "" + (1 - getAttribute(player, DesertTreasure.attributeCountAshes, 0)) + " ashes,", + "" + (1 - getAttribute(player, DesertTreasure.attributeCountCharcoal, 0)) + " charcoal", + "and " + (1 - getAttribute(player, DesertTreasure.attributeCountBloodRune, 0)) + " blood rune.") // This is sic authentic trash dialogue. + } + .end() + } + + b.onQuestStages(DesertTreasure.questName, 8,9,10) + .npcl("Meet me again in the desert East of here, I will use these ingredients to create a scrying glass for you.") + .end() + + b.onQuestStages(DesertTreasure.questName, 100) + .npcl("Meet me again in the desert East of here.") + .end() + + } +} + +class EblisCollectionsListeners : InteractionListener { + override fun defineListeners() { + + onUseWith(IntType.NPC, intArrayOf(Items.MAGIC_LOGS_1513, Items.MAGIC_LOGS_1514), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..11) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) < 12) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountMagicLogs, + getAttribute(player, DesertTreasure.attributeCountMagicLogs, 0) + 1) + sendMessage(player, "You hand over a magic log.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.STEEL_BAR_2353, Items.STEEL_BAR_2354), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..5) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) < 6) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountSteelBars, + getAttribute(player, DesertTreasure.attributeCountSteelBars, 0) + 1) + sendMessage(player, "You hand over a steel bar.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.MOLTEN_GLASS_1775, Items.MOLTEN_GLASS_1776), NPCs.EBLIS_1923) { player, used, with -> + for(i in 0..5) { + if (inInventory(player, used.id)) { + if (getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) < 6) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountMoltenGlass, + getAttribute(player, DesertTreasure.attributeCountMoltenGlass, 0) + 1) + sendMessage(player, "You hand over some molten glass.") + } + } else { + break + } + } else { + break + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.BONES_526, Items.BONES_527), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountBones, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountBones, + getAttribute(player, DesertTreasure.attributeCountBones, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, those are enough bones for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.ASHES_592, Items.ASHES_593), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountAshes, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountAshes, + getAttribute(player, DesertTreasure.attributeCountAshes, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that is enough ash for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.CHARCOAL_973, Items.CHARCOAL_974), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountCharcoal, + getAttribute(player, DesertTreasure.attributeCountCharcoal, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that is enough charcoal for the spell.") + } + } + return@onUseWith true + } + + onUseWith(IntType.NPC, intArrayOf(Items.BLOOD_RUNE_565), NPCs.EBLIS_1923) { player, used, with -> + if (getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) < 1) { + if (removeItem(player, used.id)) { + setAttribute(player, DesertTreasure.attributeCountBloodRune, + getAttribute(player, DesertTreasure.attributeCountBloodRune, 0) + 1) + sendNPCDialogue(player, NPCs.EBLIS_1923, "Thank you, that blood rune should be sufficient for the spell.") + } + } + return@onUseWith true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt new file mode 100644 index 000000000..43bec25f1 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/EblisMirrorsDialogue.kt @@ -0,0 +1,127 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class EblisMirrorsDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, EblisMirrorsDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return EblisMirrorsDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.EBLIS_1924, NPCs.EBLIS_1925) + } +} +class EblisMirrorsDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 8) + .npcl("Ah, so you got here at last.") + .npc("As you may noticed, I have made the mirrors for", "the spell, and cast the enchantment upon them.") + .npc("By simply looking into each mirror, you will be able to", "see the area where the trace magics from the Diamonds", "of Azzanadra are emanating from.") + .npc("Unfortunately, I cannot narrow the search closer with", "this kind of spell, but if you search the areas shown to", "you, you may be able to find some clues leading you to", "the evil warriors of Zamorak who stole the diamonds in") + .npc("the first place.") + .player(FacialExpression.THINKING, "So you can't be anymore specific about where to look", "for these warriors and their diamonds?") + .npc("I'm afraid not, other than the direction that the mirror", "is facing will be approximately the direction you will", "need to head in.") + .npc("Make sure to come and speak to me when you have", "retrieved all four diamonds.") + .endWith { _, player -> + if (getQuestStage(player, DesertTreasure.questName) == 8) { + setQuestStage(player, DesertTreasure.questName, 9) + } + } + + b.onQuestStages(DesertTreasure.questName, 9, 10) + .branch { player -> + return@branch if (DesertTreasure.completedAllSubstages(player)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(1) + .player(FacialExpression.THINKING, "So I have all four of these Diamonds of Azzanadra, now", "what?") + .npc("Azzanadra was our greatest ever hero.", "He was unkillable, and the cowardly traitors who stole", "our lands did not know what to do with him, for his", "hatred for them was as strong as his magics.") + .npc(FacialExpression.SAD, "In the end, they cast a spell upon him, to trap him in", "the stone structure to the South of here.") + .npc(FacialExpression.ANNOYED, "They stole his very life force, the essence of his power,", "and trapped it within four crystals - the very same", "Four Diamonds which you have now recovered from", "the brigands who stole from us.") + .npc(FacialExpression.ANNOYED, "The four pillars surrounding the structure are keeping", "the containment spell intact.", "By placing a diamond into each, you will breach the", "magical defenses and begin to restore Azzanadra's") + .npcl(FacialExpression.ANNOYED, "power, and be able to enter the structure.") + .npcl("Go, place the diamonds, and free my lord Azzanadra!") + .npc(FacialExpression.FRIENDLY, "The path will be hard, for his prison is full of traps", "and danger to prevent his rescue, but he will reward you", "beyond your wildest dreams when freed!") + .npc("Quickly...", "After all these centuries, Lord Azzanadra is nearly free!", "You must spare no time, place the Diamonds upon the", "pillars and enter the pyramid so that you may free him!") + .end() + + branch.onValue(0) + .playerl("So can you give me any help on where to find these warriors and their diamonds?") + .npcl("No, the magic used in this spell is powerful, but inaccurate. The direction the scrying glass faces is roughly the direction you will find the warrior, but I'm afraid I") + .npcl("can't be any more help than that.") + .playerl("I don't understand why there are six mirrors when there are only four diamonds...") + .npcl("As I say, the enchantment is very inaccurate.") + .npcl("I can only focus upon the aura the diamonds have left behind them, so any place where the Diamonds were present for a significant period of time will still be shown - such as the Bandit Camp where I make my home.") + .npcl("My apologies, but magic is an inaccurate art in many respects.") + .npcl("Don't forget to come back here when you have collected all four diamonds.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 100) + .branch { player -> + if (inInventory(player, Items.ANCIENT_STAFF_4675)) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(1) + .playerl("Hello again.") + .npcl("Greetings. I await the return of my Lord Azzanadra and of our god. I do not know why, but I feel this spot has some significance...") + .end() + + branch.onValue(0) + .npcl("So have you spoken to my Lord Azzanadra yet?") + .playerl("Yes I have.") + .npcl("And what words did he have for his followers?") + .playerl("Er... He didn't really mention you at all, but he did teach me some cool new magic spells.") + .npcl("It is understandable perhaps... His poor mind must be addled after all of those years of confinement, he would not willingly ignore his followers...") + .npcl("Anyway, if he has taught you our ancient magics, you may be interested in purchasing an ancient heirloom that was passed down to me. My ancestor fought in the ancient battles using the") + .npcl("magic of our god. This heirloom will help you with the speed of your spell-casting.") + .npcl("Normally I could not bear to part with such a priceless relic, but for your help in freeing my Lord Azzanadra, I will be prepared to sell it to you for a mere 80,000 gold.") + .npcl("Are you interested?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes please") + .branch { player -> + if (inInventory(player, Items.COINS_995, 80000)) { 1 } else { 0 } + } + .let { branch2 -> + branch2.onValue(1) + .npcl("Take care of it, it is the only heirloom from those times I possess, although rumour has it many of our ancient warriors were buried with identical weapons so that they could continue to fight for my Lord in their deaths.") + .endWith { _, player -> + if (removeItem(player, Item(Items.COINS_995, 80000))) { + addItemOrDrop(player, Items.ANCIENT_STAFF_4675) + } + } + + branch2.onValue(0) + .linel("You don't have enough money to buy that.") + .end() + } + + optionBuilder.option("No thanks") + .playerl("No, not really.") + .npcl("As you wish. Bear my offer in mind should you ever change your decision, I will remain here.") + .end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt new file mode 100644 index 000000000..71db43e71 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt @@ -0,0 +1,77 @@ +package content.region.desert.quest.deserttreasure + +import content.global.skill.magic.modern.WaterSpell +import core.api.* +import core.game.container.impl.EquipmentContainer +import core.game.global.action.EquipHandler +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class FareedBehavior : NPCBehavior(NPCs.FAREED_1977) { + private var disappearing = false + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Fareed has lost interest in you, and returned to his flames.") + removeAttribute(player, DesertTreasure.attributeFareedInstance) + } + poofClear(self) + } + return true + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (state.style == CombatStyle.MAGIC && state.spell !is WaterSpell) { + state.neutralizeHits() + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + if (victim is Player) { + if (!inEquipment(victim, Items.ICE_GLOVES_1580)) { + val weapon = getItemFromEquipment(victim, EquipmentSlot.WEAPON) + if (weapon != null) { + EquipHandler.unequip(victim, EquipmentContainer.SLOT_WEAPON, weapon.id) + sendMessage(victim, "The heat from the warrior causes you to drop your weapon.") + } +// val weapon = getItemFromEquipment(victim, EquipmentSlot.WEAPON) +// if(weapon != null && removeItem(victim, weapon.id, Container.EQUIPMENT)) { +// addItemOrDrop(victim, weapon.id) +// } + } + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + addItemOrDrop(killer, Items.SMOKE_DIAMOND_4672) + sendMessage(killer, "You take the Diamond of Smoke from the ashes of the warrior.") + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeSmokeStage) == 1) { + DesertTreasure.setSubStage(killer, DesertTreasure.attributeSmokeStage, 100) + removeAttribute(killer, DesertTreasure.attributeFareedInstance) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt new file mode 100644 index 000000000..5b4eeed6a --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollBehavior.kt @@ -0,0 +1,75 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +class FatherTrollBehavior : NPCBehavior(NPCs.ICE_TROLL_1943 /** WRONG NAME ITS ICE_BLOCK */) { + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return attacker is Player + } + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + self.properties.combatPulse.stop() + attacker.properties.combatPulse.stop() + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + self.skills.lifepoints = self.skills.maximumLifepoints // Reset life of ice block + + setVarbit(attacker, DesertTreasure.varbitFrozenFather, 1) + if (getVarbit(attacker, DesertTreasure.varbitFrozenMother) == 1) { + setVarbit(attacker, DesertTreasure.varbitChildReunite, 4) + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + openDialogue(attacker, ChatFatherAndMotherTrollDialogueFile()) + return@queueScript stopExecuting(self) + } + } else { + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + sendNPCDialogue(attacker, NPCs.TROLL_FATHER_1948, "Oh thank you! It was really cold in there! But please, you must free my wife as well! Our son is depending on us!", FacialExpression.OLD_CALM_TALK2) + return@queueScript stopExecuting(self) + } + } + } + } + } +} + +class MotherTrollBehavior : NPCBehavior(NPCs.ICE_BLOCK_1945) { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return attacker is Player + } + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + self.properties.combatPulse.stop() + attacker.properties.combatPulse.stop() + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + self.skills.lifepoints = self.skills.maximumLifepoints // Reset life of ice block + + setVarbit(attacker, DesertTreasure.varbitFrozenMother, 1) + if (getVarbit(attacker, DesertTreasure.varbitFrozenFather) == 1) { + setVarbit(attacker, DesertTreasure.varbitChildReunite, 4) + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + openDialogue(state.attacker!!.asPlayer(), ChatFatherAndMotherTrollDialogueFile()) + return@queueScript stopExecuting(self) + } + } else { + queueScript(self, 1, QueueStrength.NORMAL) { stage: Int -> + sendNPCDialogue(attacker, NPCs.TROLL_MOTHER_1950, "Wow, thanks for breaking me out of that ice! But please, my husband is still trapped in there!", FacialExpression.OLD_CALM_TALK2) + return@queueScript stopExecuting(self) + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt new file mode 100644 index 000000000..a83ededd6 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FatherAndMotherTrollDialogue.kt @@ -0,0 +1,170 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +// Base: 1943 Iced: 1944 Broke: 1948 Reunion: 1947 +// Base: 1945 Iced: 1946 Broke: 1950 Reunion: 1949 + +@Initializable +class FatherTrollDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3 && + getVarbit(player, DesertTreasure.varbitFrozenFather) == 1 && + getVarbit(player, DesertTreasure.varbitFrozenMother) == 1) { + openDialogue(player!!, ChatFatherAndMotherTrollDialogueFile(), npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Oh thank you! It was really cold in there! But please, you must free my wife as well! Our son is depending on us!").also { stage = END_DIALOGUE } + } + } + }, npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 4) { + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile(), npc) + } else if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Thanks again for freeing me from that ice block! I might be a troll, but it was real uncomfortable in there!").also { stage = END_DIALOGUE } + } + } + }, npc) + } + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return FatherTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(1943) + } +} + +@Initializable +class MotherTrollDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + println(getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3 && + getVarbit(player, DesertTreasure.varbitFrozenFather) == 1 && + getVarbit(player, DesertTreasure.varbitFrozenMother) == 1) { + openDialogue(player!!, ChatFatherAndMotherTrollDialogueFile(), npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 3) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Wow, thanks for breaking me out of that ice! But please, my husband is still trapped in there!").also { stage = END_DIALOGUE } + } + } + }, npc) + } else if (DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) == 4) { + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile(), npc) + } else if ((getQuestStage(player, DesertTreasure.questName) == 9 && + DesertTreasure.getSubStage(player, DesertTreasure.attributeIceStage) >= 5) || + getQuestStage(player, DesertTreasure.questName) >= 10) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_CALM_TALK2, "Thanks again for freeing me from that ice block! I don't know what my little snookums would have done without us!").also { stage = END_DIALOGUE } + } + } + }, npc) + } + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return MotherTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.TROLL_MOTHER_1950) + } +} + +class ChatFatherAndMotherTrollDialogueFile : DialogueFile() { + // We gon do like this as the old way allows to easily jump between npcs. + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Phew! Am I glad to be out of that big ice cube! Are you okay too darling?").also { stage++ } + 1 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Yes, I thought we were done for! Why ever did that nasty Kamil freeze us up there anyway?").also { stage++ } + 2 -> playerl("He must have been trying to protect his Diamond...").also { stage++ } + 3 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "You mean that diamond I found the other day belonged to him? But why didn't he just ask for it back? It's not like I really want it or anything!").also { stage++ } + 4 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "And how did you know we had that diamond anyway, fleshy?").also { stage++ } + 5 -> playerl("Your son told me. That's why I rescued you, it is very important that I have that diamond...").also { stage++ } + 6 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Ooohhhhh, my poor baby! He must have been so worried about us...").also { stage++ } + 7 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Yes, but he certainly inherited his Dad's smarts!").also { stage++ } + 8 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "If he'd told this fleshy that he had the Diamond and not us, we might never have been rescued!").also { stage++ } + 9 -> playerl("Wait... what? That stupid little troll kid had the diamond all along?").also { stage++ } + 10 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "Don't you talk about my baby like that!").also { stage++ } + 11 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Now, now dear, all's well that ends well. We've been freed and this fleshy has certainly earned himself that diamond.").also { stage++ } + 12 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK2, "Let's get out of this terrible place and see our son!").also { + stage++ + if (DesertTreasure.getSubStage(player!!, DesertTreasure.attributeIceStage) == 3) { + DesertTreasure.setSubStage(player!!, DesertTreasure.attributeIceStage, 4) + } + } + + 13 -> { + queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + closeOverlay(player!!) + openOverlay(player!!, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player!!, 6) + } + 1 -> { + teleport(player!!, Location(2836, 3739, 0)) + return@queueScript delayScript(player!!, 1) + } + 2 -> { + openOverlay(player!!, Components.FADE_FROM_BLACK_170) + return@queueScript delayScript(player!!, 6) + } + 3 -> { + closeOverlay(player!!) + openDialogue(player!!, ChatFatherAndMotherTrollAfterDialogueFile()) + return@queueScript stopExecuting(player!!) + } + else -> return@queueScript stopExecuting(player!!) + } + } + end() + } + } + } +} + +class ChatFatherAndMotherTrollAfterDialogueFile : DialogueFile() { + // We gon do like this as the old way allows to easily jump between npcs. + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK2, "Mommy! Daddy! You're free!").also { stage++ } + 1 -> npc(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK1, "That's right son, and it's all thanks to this brave", "adventurer here.", "Now, make sure you hand over that diamond he was", "looking for.").also { stage++ } + 2 -> npcl(NPCs.TROLL_FATHER_1948, FacialExpression.OLD_CALM_TALK1, "It has been nothing but trouble for us, let's just get back to our cave and have dinner.").also { stage++ } + 3 -> npcl(NPCs.TROLL_MOTHER_1950, FacialExpression.OLD_CALM_TALK2, "That's right son, it's your favorite tonight too! A big plate of raw mackerel!").also { stage++ } + 4 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK2, "RAW MACKEREL! YUMMY!").also { stage++ } + 5 -> npcl(NPCs.TROLL_CHILD_1933, FacialExpression.OLD_CALM_TALK1, "Here ya go mister! Thanks for getting my mom and dad away from the bad man!").also { + stage++ + if (DesertTreasure.getSubStage(player!!, DesertTreasure.attributeIceStage) in 3 .. 4) { + addItemOrDrop(player!!, Items.ICE_DIAMOND_4671) + DesertTreasure.setSubStage(player!!, DesertTreasure.attributeIceStage, 100) + } + } + 6 -> playerl("Don't worry about it, just as long as I don't have to go back into that blizzard.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt new file mode 100644 index 000000000..1146559a4 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollBehavior.kt @@ -0,0 +1,35 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +class IceTrollBehavior : NPCBehavior(*iceTrollIds) { + companion object { + private val iceTrollIds = intArrayOf( + NPCs.ICE_TROLL_1936, + NPCs.ICE_TROLL_1937, + NPCs.ICE_TROLL_1938, + NPCs.ICE_TROLL_1939, + NPCs.ICE_TROLL_1940, + NPCs.ICE_TROLL_1941, + NPCs.ICE_TROLL_1942, + ) + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (getQuestStage(killer, DesertTreasure.questName) >= 9) { + val currentIceTrollKill = getAttribute(killer, DesertTreasure.attributeTrollKillCount, 0) + if (currentIceTrollKill < 5) { + setAttribute(killer, DesertTreasure.attributeTrollKillCount, currentIceTrollKill + 1) + setVarbit(killer, DesertTreasure.varbitCaveEntrance, getAttribute(killer, DesertTreasure.attributeTrollKillCount, 0)) + sendMessage(killer, "A chunk of ice falls away from the cave entrance...") + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt new file mode 100644 index 000000000..ac37cb4dd --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/IceTrollDialogue.kt @@ -0,0 +1,35 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class IceTrollDialogue (player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, IceTrollDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return IceTrollDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ICE_TROLL_1935) + } + +} +class IceTrollDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> true } + .npc(FacialExpression.OLD_LAUGH1, "Hur hur hur!", "Well look here, a puny fleshy human!") + .npc(FacialExpression.OLD_LAUGH1, "You should beware of the icy wind that runs through", "this valley, it will bring a fleshy like you to a cold end", "indeed!") + .end() + } + +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt new file mode 100644 index 000000000..f52c0d414 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt @@ -0,0 +1,84 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.* +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +// https://www.youtube.com/watch?v=xeu6Ncmt1fY + +class KamilBehavior : NPCBehavior(NPCs.KAMIL_1913) { + private var disappearing = false + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, (self.walkRadius*1.5).toInt())) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Kamil vanishes on an icy wind...") + removeAttribute(player, DesertTreasure.attributeKamilInstance) + } + poofClear(self) + } + return true + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeIceStage) == 2) { + DesertTreasure.setSubStage(killer, DesertTreasure.attributeIceStage, 3) + removeAttribute(killer, DesertTreasure.attributeKamilInstance) + sendPlayerDialogue(killer, "Well, that must have been the 'bad man' that the troll kid was on about... His parents must be up ahead somewhere.") + } + } + } + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return KamilCombatHandler() + } +} + +// All these combat shit is the most trash level thing to use or decipher. +class KamilCombatHandler: MultiSwingHandler( + SwitchAttack(CombatStyle.MELEE.swingHandler, null), +) { + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + if (victim is Player) { + // This is following RevenantCombatHandler.java, no idea if this is good. + // I can't be bothered to fix fucking frozen. The player can hit through frozen. What the fuck is frozen for then, to glue his fucking legs??? + if (RandomFunction.roll(3) && !hasTimerActive(victim, "frozen") && !hasTimerActive(victim, "frozen:immunity")) { + sendChat(entity as NPC, "Sallamakar Ro!") // Salad maker roll. + impact(victim, if (victim.hasProtectionPrayer(CombatStyle.MAGIC)) 0 else 5) + impact(victim, if (victim.hasProtectionPrayer(CombatStyle.RANGE)) 0 else 5) + registerTimer(victim, spawnTimer("frozen", 7, true)) + sendMessage(victim, "You've been frozen!") + // FIXME: before the below vfx hits, there should be another one that looks kinda like a wind wave exploding at the player's feet. Hope somebody finds the id. + sendGraphics(539, victim.location) + victim.properties.combatPulse.stop() // Force the victim to stop fighting. Whatever. + // FIXME: sfx + }else { + animate(entity!!, Animation(440)) + } + } + super.impact(entity, victim, state) + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt new file mode 100644 index 000000000..37ccfdc53 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/MalakDialogue.kt @@ -0,0 +1,262 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class MalakDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, MalakDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return MalakDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MALAK_1920) + } +} + +class MalakDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .npc("Away from me, dog.", "I have business to discuss with the barkeeper.") + .end() + + + b.onQuestStages(DesertTreasure.questName, 9) + .branch { player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 1) { + return@branch 0 // Same branch as 0 + } + return@branch DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) + }.let { branch -> + branch.onValue(0) + .npcl("A human, eh? Give me one good reason why I should not just take you to Lord Drakan now.") + // There's a ring of charos dialogue here, but its one line and doesn't change anything... + .npcl("You had better make it a good one too, or you will not survive the night, I'll wager.") + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + return@let returnJoin.builder() + .options() + .let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("I am here to worship Zamorak") + .playerl("I am here to worship the almighty Zamorak! Yay! Go Zamorak!") + .npcl("I see. You are a moron. You have probably settled right in with the rest of the idiots in this pathetic excuse for a village.") + .npcl("Unfortunately for you, that is not a good enough reason to explain your presence here.") + .npcl("Now tell me the reason for your coming here, or I will ensure you suffer a horrible fate indeed.") + .goto(returnJoin) + + optionBuilder.option("I am here to praise Lord Drakan") + .playerl("I am here only to serve the mighty Drakan. Yup, Drakan, he's the man.") + .npcl("I see. I would perhaps be more inclined to believe you if I could not smell the death blood of his brother Draynor upon you.") + .npcl("What are you real intentions here?") + // "If the player has not completed Vampyre Slayer" but I'm lazy again + // .npcl("Really? That is interesting, that you would want to give your life by tresspassing in this land for such an unbelievable reason.") + // .npcl("Now speak, what is your purpose in coming here?") + .goto(returnJoin) + + optionBuilder.option("I am here to worship you, oh mighty Malak") + .playerl("I am here only to serve the mighty Drakan.") + .playerl("I came here looking for you, oh mighty Malak, so that I might serve your glory.") + .npcl("Please. Do not think that I am so vain and foolish as to allow you to avoid my question with such obvious sycophancy.") + .npcl("Now tell me the reason behind your being here, or I will ensure that you suffer.") + .goto(returnJoin) + + optionBuilder.option("I am here to kill Lord Drakan") + .playerl("I am here to kill Lord Drakan, and every stinking one of his vampyre brood!") + .npcl("Hah! Most entertaining, human!") + .npcl("Now tell me the reason you are here, or we shall soon see who will be killing whom.") + .goto(returnJoin) + + optionBuilder.option("I am looking for a special Diamond...") + .playerl("I am here looking for a special diamond... I have reason to believe it is somewhere in this vicinity, and it is probably in the possession of a warrior of Zamorak.") + .playerl("I'm fairly sure it will have some kind of magical aura or something too. I don't suppose you've seen it, or know where it might be?") + .npcl("Interesting... Well perhaps we can come to a little... arrangement, human.") + .npcl("I may have information that may assist you, but you in turn will have to do something for me. What do you say? Do you think we could come to some form of") + .npcl("agreement?") + .playerl("Well, what kind of something? No offence, but you're not exactly the trustworthy type...") + .npcl("Ah, you have a healthy sense of paranoia, I see. It is not a particularly unfair request on my part...") + .npcl("All I ask is that you ensure that the current owner of the diamond is killed. For my part, I will let you know his whereabouts, and how exactly to kill him.") + .npcl("When he is dead, you may take the diamond from his corpse and do with it what you will. I have no interest in such baubles.") + .npcl("So what say you? A life for a diamond. As a mark of good faith, I will give you some information free:") + .npcl("The current owner of this diamond is named Dessous.") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 1) + } + } + .options("Agree to this arrangement?") + .let { optionBuilder2 -> + optionBuilder2.option("Yes") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 1) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 2) + } + } + .playerl("Well... I can't see any drawback. Okay, I accept your offer. Now tell me what you know.") + .goto(continuePath) + + optionBuilder2.option("No") + .playerl("I don't trust you, or anything you say. I'm afraid I have to decline.") + .npcl("As you wish human. I doubt anybody near here knows of the diamond you seek, however.") + .npcl("If you wish to claim it as your own, you have little choice but to accept my bargain. I will wait here until such time as you change your mind.") + .end() + } + return@let continuePath.builder() + } + } + .npcl("What I know? Hah! After you have been alive for as long as I, the things I know are enough to fill a library.") + .npcl("I'm afraid you will need to be a little more specific.") + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option("Why do you want Dessous dead?") + .playerl("I don't see exactly how this bargain benefits you... Why exactly do you want Dessous killed anyway?") + .npcl("That is an impertinent question to demand from myself.") + .npcl("However, if it will help seal our deal, I will let you know some of the details.") + .npcl("As you may or may not know, myself and my blood kin are the rulers of this land, all serving under Lord Drakan. However, we tend not to die of old age or similar") + .npcl("natural causes, which means that to gain another Lords tithe or land, there often need to be...") + .npcl("Unnatural causes of death involved. Let us just say, that Dessous is in control of some land that I myself would like some say in, and that it is not in my interest to be seen to be responsible for the") + .npcl("death of a fellow Lord.") + .npcl("It would however be extremely advantageous to myself should some random human adventurer take it upon themself to remove this rival for me... Do you understand?") + .playerl("Yes... I think so...") + .npcl("Good, we understand each other then. We will both benefit from the death of Dessous.") + .goto(returnJoin) + + optionBuilder.option("Where can I find Dessous?") + .playerl("Where can I find this Dessous?") + .npcl("He currently resides in a graveyard to the South-East of here. You will not be able to move the gravestone which he lies beneath however, you will need to find some way to") + .npcl("lure him out from his tomb.") + .playerl("And how exactly would I go about doing that?") + .npcl("Well, he is a vampyre, so fresh blood would almost certainly entice him out.") + .npcl("However, even though he is a frail and decrepit example of our species, he will be able to kill a weakling human such as yourself extremely easily. Having him in a bloodlust as he does so, will not make") + .npcl("your job any easier.") + .goto(returnJoin) + + optionBuilder.option("How can I kill Dessous?") + .playerl("So what advice can you give me on killing Dessous?") + .npcl("As ancient and weak as Dessous is, he is still more than a match for the likes of you.") + .npcl("That is, assuming you were to fight him fairly.") + .npcl("My proposal would be for you to even the odds up a little bit...") + .playerl("How would I go about doing that?") + .npcl("Well, my plan would be as follows. First, take a silver bar to the man living in the sewers in Draynor. He was an assistant to Count Draynor in some of his...") + .npcl("more interesting experiments many years past. Tell him you need a sacrificial offering pot. He will know what you speak of, it is a unique type of container used in various ancient vampyric ceremonies.") + .npcl("Then take the pot to Entrana and get it blessed by the Head Monk. This will lend the pot some holy power.") + .npcl("If you then bring that silver pot back to me, I will provide you with some fresh blood, to put into it. To that pot of blood, you will add some crushed garlic, and some spice to disguise the smell.") + .npcl("Use that pot of blood upon Dessous' tomb, and he will be unable to resist rising and drinking from it.") + .npcl("The combination of garlic, silver, and blessings from Saradomin will act upon him as a poison, and allow you to kill him.") + .npcl("This is just my suggestion of course, you may ignore it if you wish, although I offer no guarantees of your ability to defeat him otherwise.") + .npcl("Was there anything else you wanted?") + .goto(returnJoin) + + optionBuilder.option("Actually, I don't need to know anything.") + .playerl("Never mind, I will figure out all I need to know by myself.") + .npcl("As you wish. Come and see me when you have managed to kill Dessous.") + .end() + } + } + + branch.onValue(2) + .branch { player -> + return@branch if(inInventory(player, Items.BLESSED_POT_4659) || inInventory(player, Items.SILVER_POT_4658)) { 1 } else { 0} + }.let { branch2 -> + branch2.onValue(1) + .player("I found Ruantun in Draynors sewers.", "He made me this pot, now where can I get some fresh", "blood to fill it with?") + .betweenStage { df, player, _, _ -> + sendMessage(player, "Malak cuts you and pours some of your blood into the pot.") + if (removeItem(player, Items.SILVER_POT_4658)) { + addItemOrDrop(player, Items.SILVER_POT_4660) + } else if (removeItem(player, Items.BLESSED_POT_4659)) { + addItemOrDrop(player, Items.BLESSED_POT_4661) + } + animate(npc!!, 1264) + player.impactHandler.manualHit(player, 5, HitsplatType.NORMAL) + } + .linel("Malak cuts you and pours some of your blood into the pot.") // Supposed to be a sendMessage not a dialogue, but why... + .playerl("Ow!") + .npcl("There you go. As fresh as it gets.") + .playerl("Thanks for nothing.") + .npcl("Come and speak to me again when you have managed to kill Dessous.") + .end() + + branch2.onValue(0) + .npcl("Why are you still here? I notice Dessous still lives.") + .options() + .let { optionBuilder -> + optionBuilder.option("Where can I find Dessous?") + .playerl("Where can I find this Dessous?") + .npcl("He currently resides in a graveyard to the South-East of here. You will not be able to move the gravestone which he lies beneath however, you will need to find some way to") + .npcl("lure him out from his tomb.") + .playerl("And how exactly would I go about doing that?") + .npcl("Come and see my when you have prepared a silver ritual pot in the manner I have told you.") + .npcl("I will ensure that you get some fresh blood that you may taint with garlic and spices, to lure our Dessous.") + .end() + + optionBuilder.option("How do I kill Dessous again?") + .playerl("How am I supposed to kill Dessous again?") + .npcl("Take a silver bar to the man in the Draynor sewers. He will fashion a ritualistic pot for you, which you should then take to Entrana and get blessed.") + .npcl("When you have done that, come back here and speak to me, I will provide you with some fresh blood which you will then crush some garlic into, and then add some spices to hide the garlic.") + .npcl("In this way, you will be able to lure him from his tomb and he should be sufficiently weakened to be vulnerable to your attacks.") + .end() + + optionBuilder.option("Actually, I don't need to know anything.") + .playerl("Never mind, I will figure out all I need to know by myself.") + .npcl("As you wish. Come and see me when you have managed to kill Dessous.") + .end() + } + } + + branch.onValue(3) + .npcl("Ah, the wandering hero returns! I take it you have dispatched poor old Dessous for me?") + .playerl("Quit playing games with me, Malak. I want that diamond, and I want it now!") + .betweenStage { df, player, _, _ -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeBloodStage) == 3) { + addItemOrDrop(player, Items.BLOOD_DIAMOND_4670) + DesertTreasure.setSubStage(player, DesertTreasure.attributeBloodStage, 100) + } + } + .npcl("Do not take that tone of voice with me, meat. You should be thankful I have allowed you your life.") + .npcl("Here, take your precious little bauble.") + .npcl("I will take that silver pot from you as well, humans are not meant to possess such artefacts. Now get out of my sight, our deal is complete, and if I see you here again I will not hesistate to take you to") + .npcl("Lord Drakan.") + .npcl("He will be extremely pleased to meet the murderer of poor Dessous, I suspect.") + .end() + + branch.onValue(100) + .branch { player -> + return@branch if(!inInventory(player, Items.BLOOD_DIAMOND_4670)) { 1 } else { 0 } + }.let { branch2 -> + branch2.onValue(1) + .playerl("Where is the Diamond of Blood? I know you have it!") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.BLOOD_DIAMOND_4670) + } + .npcl("Do not take that tone of voice with me, meat. Here, take your bauble. I have no use for it.") + .end() + + branch2.onValue(0) + .npcl("Be lucky I have let you live, meat. Our deal is done, I wish no further dealing with you.") + .end() + + } + } + + b.onQuestStages(DesertTreasure.questName, 10,11,12,13,100) + .npcl("Be lucky I have let you live, meat. Our deal is done, I wish no further dealing with you.") + .end() + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt b/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt new file mode 100644 index 000000000..d5fed9d6e --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/MummyNPC.kt @@ -0,0 +1,18 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class MummyNPC : NPCBehavior(NPCs.MUMMY_1958) { + var clearTime = 0 + + override fun tick(self: NPC): Boolean { + if (clearTime++ > 100) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt b/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt new file mode 100644 index 000000000..2a28610b2 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/PyramidArea.kt @@ -0,0 +1,319 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +class PyramidArea { + companion object { + /** All The Sarcophagus Locations */ + val sarcophagusList = arrayOf( + // Level 1 + Location(2901, 4946, 3), + Location(2902, 4969, 3), + Location(2903, 4961, 3), + Location(2909, 4954, 3), + Location(2913, 4950, 3), + Location(2916, 4954, 3), + Location(2917, 4951, 3), + Location(2917, 4959, 3), + Location(2918, 4963, 3), + // Level 2 + Location(2831, 4962, 2), + Location(2837, 4970, 2), + Location(2843, 4964, 2), + Location(2847, 4947, 2), + Location(2858, 4956, 2), + Location(2858, 4973, 2), + Location(2864, 4942, 2), + Location(2868, 4948, 2), + Location(2869, 4968, 2), + // Level 3 + Location(2759, 4962, 1), + Location(2760, 4956, 1), + Location(2763, 4966, 1), + Location(2764, 4941, 1), + Location(2765, 4936, 1), + Location(2765, 4940, 1), + Location(2767, 4945, 1), + Location(2768, 4947, 1), + Location(2771, 4944, 1), + Location(2774, 4947, 1), + Location(2776, 4941, 1), + Location(2786, 4974, 1), + Location(2787, 4964, 1), + Location(2790, 4968, 1), + Location(2791, 4977, 1), + Location(2798, 4947, 1), + Location(2798, 4952, 1), + Location(2800, 4960, 1), + Location(2802, 4940, 1), + Location(2806, 4936, 1), + Location(2806, 4942, 1), + Location(2810, 4968, 1), + Location(2810, 4975, 1), + // Level 4 + Location(3208, 9315, 0), + Location(3211, 9330, 0), + Location(3217, 9295, 0), + Location(3218, 9281, 0), + Location(3221, 9313, 0), + Location(3221, 9320, 0), + Location(3221, 9324, 0), + Location(3222, 9281, 0), + Location(3225, 9308, 0), + Location(3225, 9310, 0), + Location(3226, 9312, 0), + Location(3226, 9318, 0), + Location(3227, 9288, 0), + Location(3229, 9309, 0), + Location(3231, 9297, 0), + Location(3233, 9309, 0), + Location(3234, 9297, 0), + Location(3234, 9330, 0), + Location(3236, 9302, 0), + Location(3237, 9309, 0), + Location(3240, 9312, 0), + Location(3240, 9318, 0), + Location(3241, 9282, 0), + Location(3242, 9302, 0), + Location(3246, 9282, 0), + Location(3246, 9308, 0), + Location(3246, 9324, 0), + Location(3247, 9323, 0), + Location(3249, 9293, 0), + Location(3250, 9324, 0), + Location(3251, 9323, 0), + Location(3251, 9330, 0), + Location(3251, 9337, 0), + Location(3252, 9330, 0), + Location(3252, 9337, 0), + Location(3253, 9301, 0), + Location(3254, 9324, 0), + Location(3255, 9323, 0), + Location(3255, 9330, 0), + Location(3255, 9337, 0), + Location(3256, 9330, 0), + Location(3256, 9337, 0), + Location(3257, 9289, 0), + Location(3259, 9310, 0), + Location(3259, 9313, 0), + ) + + val safeZone = ZoneBorders(3227, 9310, 3239, 9320) + + // Direction.NORTH - LEFT + // rot 0 - LEFT + // rot 1 - NORTH + // rot 2 - RIGHT + // rot 3 - SOUTH + /** Sarcophagus Opening Location (Note, they are all wrongly mapped due to Scenery) */ + fun getNewLocation(direction: Direction): Location { + return when (direction) { + Direction.NORTH -> Location(-1, 0) + Direction.WEST -> Location(0, -1) + Direction.EAST -> Location(0, 1) + Direction.SOUTH -> Location(1, 0) + else -> Location(0, 0) + } + } + + /** Sarcophagus Opening Facing (Note, they are all wrongly mapped due to Scenery) */ + fun getNewFacing(direction: Direction): Direction { + return when (direction) { + Direction.NORTH -> Direction.NORTH + Direction.WEST -> Direction.NORTH_WEST + Direction.SOUTH -> Direction.WEST + Direction.EAST -> Direction.NORTH_EAST + else -> direction + } + } + + fun nearSarcophagus(loc: Location): Location? { + for (sarcoph in sarcophagusList) { + if(loc.withinDistance(sarcoph, 3)) { + return sarcoph + } + } + return null + } + + /** Trapdoor randomly throws you out of the Pyramid. */ + fun trapdoorTrap(player: Player) { + stopWalk(player) + player.walkingQueue.reset() + forceWalk(player, Location(3233, 2887, 0), "") + lock(player, 8) + sendMessage(player, "You accidentally trigger a trap...") + // addScenery(6521, location) -> animateScenery(scenery, 1939), but 6522 does it for you. + val pitfallScenery = addScenery(6522, player.location) // Scenery - Trapdoor Scenery + animate(player, 1950) // Anim - Player Falling Animation + queueScript(player, 4, QueueStrength.SOFT) { stage -> + player.walkingQueue.reset() + when (stage) { + 0 -> { + sendGraphics(354, player.location) // Gfx - Puff of Smoke + closeOverlay(player) + openOverlay(player, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player, 2) + } + 1 -> { + removeScenery(pitfallScenery) + teleport(player, Location(3233, 2887, 0)) + sendMessage(player, "...and tumble unharmed outside the pyramid.") + closeOverlay(player) + openOverlay(player, Components.FADE_FROM_BLACK_170) + stopWalk(player) + player.walkingQueue.reset() + // animate(player, ??) // Anim - Player Getting Up https://www.youtube.com/watch?v=95OvIPFYCwg + return@queueScript delayScript(player, 3) + } + 2 -> { + player.walkingQueue.reset() + forceWalk(player, Location(3233, 2887, 0), "") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + /** Mummies randomly spawns out of a sarcophagus. */ + fun spawnMummy(player: Player, sarcophagusLocation: Location) { + val sarcophagusScenery = getScenery(sarcophagusLocation) ?: return + val locationInFront = sarcophagusScenery.location.transform(getNewLocation(sarcophagusScenery.direction)) + // There are 6 sarcophagus, 6512 - 6517 with different door designs. + // They map nicely to 6506 - 6511 and act like a door. + // 6505 is the underlying hole scenery after the door opens, but it is done for you. + replaceScenery( + sarcophagusScenery, + sarcophagusScenery.id - 6, + 5, + getNewFacing(sarcophagusScenery.direction), + locationInFront + ) + val mummyNpc = NPC(NPCs.MUMMY_1958) + mummyNpc.isRespawn = false + mummyNpc.isWalks = false + mummyNpc.isAggressive = true + mummyNpc.location = sarcophagusScenery.location + mummyNpc.init() + mummyNpc.walkingQueue.addPath(locationInFront.x, locationInFront.y) + sendChat(mummyNpc, "Rawr!") + lock(player, 1) + stopWalk(player) + queueScript(player, 2, QueueStrength.SOFT) { stage -> + stopWalk(player) + mummyNpc.walkingQueue.addPath(locationInFront.x, locationInFront.y) + mummyNpc.isWalks = true + mummyNpc.isAggressive = true + mummyNpc.attack(player) + return@queueScript stopExecuting(player) + } + + } + + /** Scarabs randomly spawns somewhere near you. */ + fun spawnScarabs(player: Player) { + stopWalk(player) + lock(player, 3) + val scarabsLocation = Location.getRandomLocation(player.location, 3, true) + sendGraphics(356, scarabsLocation) // Gfx - Cracks before scarab appears. + val scarabNpc = NPC(NPCs.SCARABS_1969) + stopWalk(player) + queueScript(player, 2, QueueStrength.STRONG) { stage -> + stopWalk(player) + when (stage) { + 0 -> { + scarabNpc.isRespawn = false + scarabNpc.isWalks = false + scarabNpc.location = scarabsLocation + scarabNpc.init() + animate(scarabNpc, 1949) // Anim - Scarabs appearing from ground. + return@queueScript delayScript(player, 3) + } + 1 -> { + scarabNpc.isWalks = true + scarabNpc.isAggressive = true + scarabNpc.attack(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript stopExecuting(player) + } + } + } +} + +class PyramidAreaFirstThree: MapArea { + + override fun defineAreaBorders(): Array { + return arrayOf( + getRegionBorders(11597), + getRegionBorders(11341), + getRegionBorders(11085), + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + val averageLevel = ( + getDynLevel(entity, Skills.AGILITY) + + getDynLevel(entity, Skills.THIEVING) + ) / 2.0 + val randomValue = RandomFunction.randomDouble(99.5) + + if ((1..20).random() == 1) { + // A mummy would jump out if you walk near a sarcophagus of 2 radius. + val sarcoph = PyramidArea.nearSarcophagus(entity.location) + if (sarcoph != null) { + PyramidArea.spawnMummy(entity, sarcoph) + } + } + + if ((1..80).random() == 2) { + PyramidArea.spawnScarabs(entity) + } + if (randomValue > averageLevel && (1..128).random() == 1) { + PyramidArea.trapdoorTrap(entity) + } + } + } +} + +class PyramidAreaFinal: MapArea { + + override fun defineAreaBorders(): Array { + return arrayOf( + getRegionBorders(12945), + ) + } + + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + if (!PyramidArea.safeZone.insideBorder(entity.location)) { // Safezone is talking to Azzanadra. + if ((1..20).random() == 1) { + // A mummy would jump out if you walk near a sarcophagus of 2 radius. + val sarcoph = PyramidArea.nearSarcophagus(entity.location) + if (sarcoph != null) { + PyramidArea.spawnMummy(entity, sarcoph) + } + } + if ((1..80).random() == 2) { + PyramidArea.spawnScarabs(entity) + } + } + + // No Trapdoor For The Final Level + } + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt new file mode 100644 index 000000000..273dd5d34 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/RasoloDialogue.kt @@ -0,0 +1,217 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +@Initializable +class RasoloDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, RasoloDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return RasoloDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.RASOLO_1972) + } +} + +class RasoloDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .npc("Greetings friend.", "I am Rasolo, the famous merchant.", "Would you care to see my wares?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 0 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npc("Greetings friend.", "I am Rasolo, the famous merchant.", "Would you care to see my wares?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + + optionBuilder.option("Ask about the Diamonds of Azzanadra") + .playerl("No, actually I was looking for something specific...") + .npcl("Hmmmm? And what would that be?") + .playerl("I am looking for one of the Diamonds of Azzanadra. I have reason to believe it may be somewhere around here...") + .npcl("Ahhh... The Shadow Diamond...") + .npcl("I know the object of which you speak. It is guarded by a fearsome warrior known as Damis, they say, who lives in the shadows, invisible to prying eyes...") + .playerl("How can I find this 'Damis' then?") + .npcl("Well now... perhaps we can help each other out here.") + .npcl("I have in my possession a small trinket, a ring, that allows its wearer to see the unseen...") + .playerl("How much do you want for it?") + .npcl("Ah, but it is not for sale... As such...") + .npcl("I am offering to trade it for an item that was rightfully mine, but that was stolen by a bandit named Laheeb.") + .npcl("The item is question is a gilded cross, that has some sentimental value to myself. I wish for you to recover this item for me, and I will happily let you have my ring of visibility.") + .playerl("Where can I find this Laheeb?") + .npcl("Well, as a travelling merchant I have roamed these lands for many years...") + .npcl("To the far east of here there is an area that is dry and barren like the desert... it is called...") + .playerl("Al Kharid?") + .npcl("...Yes, Al Kharid. Now to the south of Al Kharid there is a passageway, it is called the...") + .playerl("Shantay Pass.") + .npcl("...Yes. I didn't realise you had travelled there yourself.") + .npcl("Anyway, when you have gone through the Shantay Pass, you will find yourself in a hostile desert... You will need to bring water with you to keep your life. Now, to the south-west of this pass, you will find a small") + .npcl("village...") + .playerl("The Bedabin camp.") + .npcl(FacialExpression.ANNOYED, "If you know where Laheeb lives, why did you ask me?") + .playerl("I don't. Sorry, please continue.") + .npcl("Well, okay then. Anyway, south of this encampment there is an area where few have ever been... It is a village of murderous bandits, and treacherous") + .npcl("thieves... This is where Laheeb makes his home.") + .npcl("He will have hidden his stolen treasure somewhere in that village, I am sure of it. When you find his loot, you will find my gilded cross. Return it to me, and I will reward you with my ring") + .npcl("of visibility, so that you may find Damis. Does this seem fair to you?") + .options() + .let { optionBuilder2 -> + optionBuilder2.option("Yes") + .playerl("Not a problem. I'll be back with your cross before you know it.") + .endWith { _, player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 0) { + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 1) + } + } + optionBuilder2.option("No") + .playerl("Sounds like too much effort to me. I'll find this Damis by myself.") + .npcl("As you wish.") + .end() + } + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) in 1 .. 2 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npcl("Have you retrieved my gilded cross for me yet?") + .branch { player -> + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 2 && inInventory(player, Items.GILDED_CROSS_4674)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("No, not yet...") + .npcl("Well what seems to be the problem?") + .options() + .let { optionBuilder -> + optionBuilder.option("Where can I find Laheeb?") + .playerl("Where can I find this Laheeb?") + .npcl("Well, as a travelling merchant I have roamed these lands for many years...") + .npcl("To the far east of here there is an area that is dry and barren like the desert... it is called...") + .playerl("Al Kharid?") + .npcl("...Yes, Al Kharid. Now to the south of Al Kharid there is a passageway, it is called the...") + .playerl("Shantay Pass.") + .npcl("...Yes. I didn't realise you had travelled there yourself.") + .npcl("Anyway, when you have gone through the Shantay Pass, you will find yourself in a hostile desert... You will need to bring water with you to keep your life. Now, to the south-west of this pass, you will find a small") + .npcl("village...") + .playerl("The Bedabin camp.") + .npcl("If you know where Laheeb lives, why did you ask me?") + .playerl("I don't. Sorry, please continue.") + .npcl("Well, okay then. Anyway, south of this encampment there is an area where few have ever been... It is a village of murderous bandits, and treacherous") + .npcl("thieves... This is where Laheeb makes his home.") + .end() + optionBuilder.option("Can't I just buy your ring?") + .playerl("Can't I just buy your ring?") + .npcl("No, it is not for sale. Some things are more important than money, and the return of my gilded cross is one of them.") + .end() + optionBuilder.option("Is Damis near here, then?") + .playerl("Is Damis near here, then?") + .npcl("You would be surprised to know just how close he is...") + .end() + } + branch.onValue(1) + .playerl("Yes I have!") + .npcl("Excellent, excellent. Here, take this ring. While you wear it, you will be able to see the things that live in shadows...") + .npcl("And you will be able to find the entrance to Damis' lair.") + .endWith { _, player -> + if (removeItem(player, Items.GILDED_CROSS_4674)) { + if (DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 2) { + addItemOrDrop(player, Items.RING_OF_VISIBILITY_4657) + DesertTreasure.setSubStage(player, DesertTreasure.attributeShadowStage, 3) + } + } + } + } + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) == 3 && + getQuestStage(player, DesertTreasure.questName) >= 9 + } + .npcl("So how goes your quest? Did you managed to find the Diamond you were looking for yet?") + .branch { player -> + if (inInventory(player, Items.RING_OF_VISIBILITY_4657)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("Not yet...") + .npcl("Well, his lair is very close to here. I suggest you look around for it.") + .end() + + branch.onValue(0) + .playerl("I lost that ring you gave me...") + .npcl("Then by all means, take another. Only a foolish merchant would give away his only stock!") + .endWith { _, player -> + addItem(player, Items.RING_OF_VISIBILITY_4657) + } + } + + + b.onPredicate { player -> + DesertTreasure.getSubStage(player, DesertTreasure.attributeShadowStage) > 3 && + getQuestStage(player, DesertTreasure.questName) >= 9 || getQuestStage(player, DesertTreasure.questName) >= 10 + } + .npcl("Many thanks for returning my heirloom to me, adventurer. Would you like to buy something?") + .options() + .let { optionBuilder -> + optionBuilder.option("Yes") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + optionBuilder.option("No") + .playerl("No, not really.") + .npcl("As you wish. I will travel wherever the business takes me.") + .end() + + optionBuilder.optionIf("I lost that ring you gave me...") { player -> + return@optionIf !inInventory(player, Items.RING_OF_VISIBILITY_4657) + } + .playerl("I lost that ring you gave me...") + .npcl("Then by all means, take another. Only a foolish merchant would give away his only stock!") + .endWith { _, player -> + addItem(player, Items.RING_OF_VISIBILITY_4657) + } + } + + } +} + +class RasoloTradeListeners : InteractionListener { + override fun defineListeners() { + on(NPCs.RASOLO_1972, NPC, "trade") { player, node -> + openNpcShop(player, node.id) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt new file mode 100644 index 000000000..89fa635f7 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/RuantunDialogue.kt @@ -0,0 +1,83 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class RuantunDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, RuantunDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return RuantunDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.RUANTUN_1916) + } +} + +class RuantunDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4,5,6,7,8) + .playerl("Hello.") + .npcl(FacialExpression.OLD_NORMAL, "You ssshould not be down here...") + .options() + .let { optionBuilder -> + optionBuilder.option("Who are you?") + .playerl("Who are you?") + .npcl(FacialExpression.OLD_NORMAL, "My name isss unimportant... I live only to ssserve my massster.") + .playerl("Um... Okay then.") + .end() + + optionBuilder.option("Why are you down here?") + .playerl("Why are you down here?") + .npcl(FacialExpression.OLD_NORMAL, "Thisss isss where I belong... Beingsss sssuch as myssself cannot abide in the light... It is in the darknesss where we find our homesss...") + .playerl("Uh... Okay then.") + .end() + + optionBuilder.option("Can I use your anvil?") + .playerl("Can I use your anvil?") + .npcl(FacialExpression.OLD_NORMAL, "Of courssse you may... I have very little ussse for it nowadaysss...") + .playerl("Uh... Thanks, I guess.") + .end() + } + + b.onQuestStages(DesertTreasure.questName, 9, 10, 100) + // Technically should happen after talking to Malak, but nah. + .playerl("Hello.") + .npcl(FacialExpression.OLD_NORMAL, "You sshould not be down here...") + .playerl("Are you an assistant to Count Draynor?") + .npc(FacialExpression.OLD_NORMAL, "I usssed to have that honour...", "Why do you ssseek me?") + .branch { player -> + return@branch if (inInventory(player, Items.SILVER_BAR_2355)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("I have a silver bar with me, I was wondering if you could make it into a 'sacrificial offering pot' for me?") + .betweenStage { df, player, _, _ -> + if (removeItem(player, Items.SILVER_BAR_2355)) { + addItemOrDrop(player, Items.SILVER_POT_4658) + } + } + .npc(FacialExpression.OLD_NORMAL, "Yesss, of courssse...", "There you are, put it to good usssse...") + .end() + + branch.onValue(0) + .playerl("I understand that you can make me a 'sacrificial offering pot' if I bring you a bar of silver?") + .npcl(FacialExpression.OLD_NORMAL, "And where did you hear thisss?") + .playerl("It was from Malak in Canifis.") + .npc(FacialExpression.OLD_NORMAL, "Ah, I sssee...", "Yesss, I know how to make sssuch an item...", "It has been many yearsss sssince I have needed to however...") + .npcl(FacialExpression.OLD_NORMAL, "It is not my wisssh to quessstion your desssire for sssuch an item, I wasss merely sssurprisssed that one sssuch as you would make sssuch a requessst...") + .npcl(FacialExpression.OLD_NORMAL, "I will happily make you thisss pot, but you mussst bring me a bar of sssilver... Alasss, I can no longer collect my own ingredientsss, and mussst remain here...") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt b/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt new file mode 100644 index 000000000..93dd60d7b --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/ScarabNPC.kt @@ -0,0 +1,18 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class ScarabNPC : NPCBehavior(NPCs.SCARABS_1969) { + var clearTime = 0 + + override fun tick(self: NPC): Boolean { + if (clearTime++ > 100) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt b/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt new file mode 100644 index 000000000..6778a0f43 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/TranslationBook.kt @@ -0,0 +1,187 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..87fc7de02 --- /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)) + return@onUseWith addItem(player, Items.DARKLIGHT_6746) + return@onUseWith false + } + } +} diff --git a/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt b/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt index a741f81f9..19af2735d 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,18 @@ 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 +import content.global.handlers.iface.ScrollLine @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 +29,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 +54,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.") @@ -111,321 +108,20 @@ class CuratorHaigHalenGolemDialogue : core.game.dialogue.DialogueBuilderFile() { } } +// https://youtu.be/owPSLUB9E8k?t=261 val LETTER_LINES = arrayOf( - "", - "", - "Dearest Varmen,", - "I hope this finds you well. Here are the books you asked for", - "There has been an exciting development closer to home --", - "another city from the same period has been discovered east", - "of Varrock, and we are starting a huge excavation project", - "here. I don't know if the museum will be able to finance your", - "expedition as well as this one, so I fear your current trip will be", - "the last.", - "May Saradomin grant you a safe journey home", - "Your loving Elissa.", + ScrollLine("Dearest Varmen,", 3), + ScrollLine("I hope this finds you well. Here are the books you asked for", 4), + ScrollLine("There has been an exciting development closer to home --", 5), + ScrollLine("another city from the same period has been discovered east", 6), + ScrollLine("of Varrock, and we are starting a huge excavation project", 7), + ScrollLine("here. I don't know if the museum will be able to finance your", 8), + ScrollLine("expedition as well as this one, so I fear your current trip will be", 9), + ScrollLine("the last.", 10), + ScrollLine("May Saradomin grant you a safe journey home", 11), + ScrollLine("Your loving Elissa.", 12) ) -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..4d637b890 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,12 @@ import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.interaction.InterfaceListener import core.game.world.GameWorld +import content.data.Quests +import content.global.handlers.iface.ScrollInterface +import org.rs09.consts.Components @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 +75,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) } } @@ -111,19 +114,6 @@ class ClayGolemNPC : AbstractNPC { } } -class LetterListener : InterfaceListener { - override fun defineInterfaceListeners() { - onOpen(220) { player, component -> - val lines: Array = player.getAttribute("ifaces:220:lines", arrayOf()) - for(i in 0 until Math.min(lines.size, 15)) { - setInterfaceText(player, lines[i], 220, i+1) - //setInterfaceText(player, "${i}", 220, i+1) - } - return@onOpen true - } - } -} - class DisplayCaseListener : InterfaceListener { override fun defineInterfaceListeners() { onOpen(534) { player, component -> @@ -140,7 +130,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 +147,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 +184,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 +197,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 +254,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 +269,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 +287,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 +321,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 +333,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 } @@ -373,9 +361,8 @@ class TheGolemListeners : InteractionListener { on(34978, IntType.SCENERY, "climb-down") { player, node -> core.game.global.action.ClimbActionHandler.climb(player, core.game.global.action.ClimbActionHandler.CLIMB_DOWN, core.game.global.action.SpecialLadders.getDestination(node.location)); return@on true } on(6372, IntType.SCENERY, "climb-up") { player, node -> core.game.global.action.ClimbActionHandler.climb(player, core.game.global.action.ClimbActionHandler.CLIMB_UP, core.game.global.action.SpecialLadders.getDestination(node.location)); return@on true } on(4615, IntType.ITEM, "read") { player, node -> - player.setAttribute("ifaces:220:lines", LETTER_LINES) player.setAttribute("/save:the-golem:read-elissa-letter", true) - openInterface(player, 220) + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, LETTER_LINES) return@on true } on(35226, IntType.SCENERY, "search") { player, node -> @@ -395,7 +382,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..dda6a88bf --- /dev/null +++ b/Server/src/main/content/region/desert/quest/thegolem/VarmensNotes.kt @@ -0,0 +1,300 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..60cfad2e1 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,9 @@ 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); + SceneryDefinition.forId(20341).getHandlers().put("option:open", this); return this; } @@ -32,13 +39,24 @@ 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; + case 20341: + 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 deleted file mode 100644 index 7e90bf9e9..000000000 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt +++ /dev/null @@ -1,209 +0,0 @@ -package content.region.fremennik.lunarisle.dialogue - -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -/** - * Handles Sirsal banker dialogue tree. - * - * @author vddCore - */ -@Initializable -class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.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, - "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, - "Good day, how may I help you?" - ).also { - if (hasAwaitingGrandExchangeCollections(player)) { - stage++ - } else { - stage += 2 - } - } - } - } else { - playerl(core.game.dialogue.FacialExpression.HALF_WORRIED, "Hi, I...") - stage = 30 - } - - 1 -> npcl( - core.game.dialogue.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), - IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, - "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", - 13, - hasActivatedSecondaryBankAccount(player) - ), - IfTopic( - core.game.dialogue.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), - ) - - 3 -> npcl( - core.game.dialogue.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, - "And what do you do?" - ).also { stage++ } - - 5 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, - "We will look after your items and money for you. " + - "Leave your valuables with us if you want to keep them safe." - ).also { stage = END_DIALOGUE } - - 10 -> { - openBankAccount(player) - end() - } - - 11 -> { - openBankPinSettings(player) - end() - } - - 12 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 13 -> { - toggleBankAccount(player) - - npcl( - core.game.dialogue.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, - "Certainly. We offer secondary accounts to all our customers." - ).also { stage++ } - - 21 -> npcl( - core.game.dialogue.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, - "If your inventory does not contain enough money to cover the costs, we will complement " + - "the amount with the money inside your primary bank account." - ).also { stage++ } - - 23 -> npcl( - core.game.dialogue.FacialExpression.ASKING, - "Knowing all this, would you like to proceed with opening your secondary bank account?" - ).also { stage++ } - - 24 -> showTopics( - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, I am still interested.", 25), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Actually, I've changed my mind.", 26) - ) - - 25 -> { - when (activateSecondaryBankAccount(player)) { - SecondaryBankAccountActivationResult.ALREADY_ACTIVE -> { - npcl( - core.game.dialogue.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, - "I must apologize, the transaction was not successful. Please check your " + - "primary bank account and your inventory - if there's money missing, please " + - "screenshot your chat box and contact the game developers." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> { - npcl( - core.game.dialogue.FacialExpression.NEUTRAL, - "It appears that you do not have the money necessary to cover the costs " + - "associated with opening a secondary bank account. I will be waiting here " + - "until you do." - ).also { stage = END_DIALOGUE } - } - - SecondaryBankAccountActivationResult.SUCCESS -> { - npcl( - core.game.dialogue.FacialExpression.NEUTRAL, - "Your secondary bank account has been opened and can be accessed through any " + - "of the Bank of Gielinor's employees. Thank you for choosing our services." - ).also { stage = END_DIALOGUE } - } - } - } - - 26 -> npcl( - core.game.dialogue.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, - "What are you doing here, Fremennik?!" - ).also { stage++ } - - 31 -> playerl( - core.game.dialogue.FacialExpression.WORRIED, - "I have a Seal of Pass..." - ).also { stage++ } - - 32 -> npcl( - core.game.dialogue.FacialExpression.ANGRY, - "No you don't! Begone!" - ).also { stage = END_DIALOGUE } - - /* TODO: Is the above related to Lunar Diplomacy? */ - } - - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.SIRSAL_BANKER_4519) - } -} diff --git a/Server/src/main/content/region/fremennik/neitiznot/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..b4dfcbdf2 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.kt @@ -0,0 +1,205 @@ +package content.region.fremennik.rellekka.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import content.data.Quests + + +/** + * @author Player Name + */ + +@Initializable +class JarvaldDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + val travelOption = args.size > 1 + val fremname = player.getAttribute("fremennikname","lebron james") + if (npc.id == NPCs.JARVALD_2438) { + // We're on Waterbirth Island + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + if (travelOption) { + npc("So what say you, stay here for the hunt,","or return home to sweet Rellekka to feast","and drink with your tribe?").also { stage = 201 } + } else { + npc("Ah, ${fremname}! Such glorious battle","makes you feel glad of life, does it not?").also { stage = 220 } + } + } else { + if (travelOption) { + npc("Have you had your fill of the hunt and wish to return,", "or are you still feeling the joy of the cull?").also { stage = 201 } + } else { + npc("Ah, you live yet, outerlander!").also { stage = 200 } + } + } + } else { + // We're in Rellekka + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + if (travelOption) { + npc("Of course, ${fremname}! Your presence is more than welcome","on this cull! You wish to leave now?").also { stage = 131 } + } else { + npc("Greetings, ${fremname}!").also { stage = if (fremname == "Jarvald") 100 else 102 } + } + } else { + if (travelOption) { + npc("So do you have the 1,000 coins for my service, and are", "you ready to leave?").also { stage = 41 } + } else if (isQuestInProgress(player, Quests.THE_FREMENNIK_TRIALS, 1, 100)) { + player("Hi, I don't suppose you are a member of", "the council of elders are you?").also { stage = 0 } + } else { + npc("What do you want from me outerlander?", "It is our policy not to associate with those not of our", "tribe.").also { stage = 3 } + } + } + } + return true + } + + fun sail(to: Boolean) { + lock(player, 5) + closeAllInterfaces(player) + openInterface(player, 224) + queueScript(player, 5, QueueStrength.SOFT) { + closeInterface(player) + unlock(player) + if (to) { + player.teleport(Location.create(2544, 3759, 0)) + sendDialogue("The ship arrives at Waterbirth Island.") + } else { + player.teleport(Location.create(2620, 3685, 0)) + sendDialogue("The ship arrives at Rellekka.") + } + return@queueScript stopExecuting(player) + } + } + + override fun handle(interfaceId: Int, buttonID: Int): Boolean { + val fremname = player.getAttribute("fremennikname","lebron james") + when (stage) { + 0 -> npc("No, outerlander, I have never had the honour to be asked.").also { stage++ } + 1 -> npc("I am but a lowly warrior, fighting for my people","wherever threats may appear against us...").also { stage++ } + 2 -> npc("So what do you want from me, outerlander?").also { stage++ } + 3 -> options("Where is your chieftain?", "What Jarvald is doing.", "Nothing").also { stage++ } + 4 -> when (buttonID) { + 1 -> player("Where is your chieftain?", "I find it highly discriminatory to refuse to talk to", "someone on the grounds that they are not part of your", "tribe.").also { stage++ } + 2 -> player("So what are you doing here?").also { stage = if (getVarp(player, 520) == 0) 10 else 40 } + 3 -> player("Actually, I don't think I have","anything to speak to you about...").also { stage = END_DIALOGUE } + } + 5 -> npc("I don't rightly understand your speech outerlander, but", "my loyality is with Chieftain Brundt.").also { stage++ } + 6 -> npc("He resides in our longhall; it is the large building over", "there, you should speak to him for he speaks for us all.").also { stage++ } + 7 -> end().also { stage = END_DIALOGUE } + 10 -> npc("This should not concern you, outerlander.", "I am awaiting other Fremenniks to join me on an", "expedition to Waterbirth Island.").also { stage++ } + 11 -> options("Waterbirth Island?", "Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 12 -> when (buttonID) { + 1 -> npc("It is a small crescent-shaped island","just north-west of here, outerlander.").also { stage++ } + 2 -> player("Can I come?").also { stage = 20 } + 3 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 30 } + 4 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 13 -> npc("We have many legends about it,","such as the tale of the broken sky,","and the day of the green seas.").also { stage++ } + 14 -> npc("The reason I am travelling there is more serious","Fremennik business, however. I doubt an outerlander","would be interested.").also { stage++ } + 15 -> options("Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 16 -> when (buttonID) { + 1 -> player("Can I come?").also { stage = 20 } + 2 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 30 } + 3 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 20 -> npc("An outerlander join us on an honoured hunt???").also { stage++ } + 21 -> npc("Well....", "I guess...", "I might be able to allow you to join us, although it is a", "breach of many of our customs...").also { stage++ } + 22 -> player("Oh, pleeeeease?", "I really LOVE killing stuff!").also { stage++ } + 23 -> npc("Well...", "I remain unconvinced that it would be wise to allow an", "outerlander to join us in such dangerous battle, but", "your ethusiasm seems genuine enough...").also { stage++ } + 24 -> npc("I will allow you to escort us, but you must pay me a", "sum of money first.").also { stage++ } + 25 -> player("What?", "That's outrageous, why charge me money?", "And, uh, how much does it cost me?").also { stage++ } + 26 -> npc("Ah, the outerlanders have stolen from my people for", "many years, in this way you can help my community", "with a small amount of money...").also { stage++ } + 27 -> npc("Let us say...", "1,000 coins.", "Payable in advance, of course.").also { stage++ } + 28 -> npc("For this I will take you to Waterbirth Island on my", "boat, and will bring you back here when you have had", "your fill of the hunt.", "Assuming you are still alive to wish to leave, of course.").also { setVarp(player, 520, 1 shl 13, true) }.also{ stage = END_DIALOGUE } + 29 -> npc("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + 30 -> npc("It is actually a helm, outerlander,","but the sentiment is appreciated nonetheless.").also { stage = END_DIALOGUE } + 40 -> npc("So do you have the 1,000 coins for my service, and are", "you ready to leave?").also { stage++ } + 41 -> options("YES", "NO").also { stage++ } + 42 -> when (buttonID) { + 1 -> if (player.inventory.contains(Items.COINS_995, 1000)) { + if (player.inventory.remove(Item(Items.COINS_995, 1000))) { + sail(true) + } + } else { + player("Sorry, I don't have enough coins.").also { stage = END_DIALOGUE } + } + 2 -> player("No, actually I have some stuff to do here first.").also { stage++ } + } + 43 -> npc("As you wish.", "Come and see me when your bloodlust needs sating.").also { stage = END_DIALOGUE } + 100 -> npc("Ah, and what a glorious name that is! Worthy of only the finest warriors!").also { stage++ } + 101 -> player("Heh. Yup, you're right there.").also { stage++ } + + 102 -> npc("So what brings you back to fair Rellekka?","It has been too long since you have drunk in the longhall","with us and sang of your battles!").also { stage++ } + 103 -> options("What Jarvald is doing.", "Nothing").also { stage++ } + 104 -> when (buttonID) { + 1 -> player("So what are you doing here?").also { stage = if (getVarp(player, 520) and (1 shl 13) == 0) 105 else 130 } + 2 -> player("Actually, I don't think I have","anything to speak to you about...").also { stage = END_DIALOGUE } + } + 105 -> npc("You have not heard, ${fremname}?","I am leading an expedition to Waterbirth Island!").also { stage++ } + 106 -> options("Waterbirth Island?", "Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 107 -> when (buttonID) { + 1 -> npc("You have not ever travelled to Waterbirth Island, ${fremname}?","I am surprised, it is a place of outstanding natural beauty.").also { stage++ } + 2 -> player("Can I come?").also { stage = 130 } + 3 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 140 } + 4 -> player("Wow, you Fremenniks sure know how to party. Well, see ya around.").also { stage = END_DIALOGUE } + } + 108 -> npc("Or at least it used to be! But things have now changed...").also { stage++ } + 109 -> player("Changed? How do you mean, changed?").also { stage++ } + 110 -> npc("It seems as though the sea-beasts known to us as","the dagger-mouths have begun their hatching once again...","And there may be others of their ilk there too.").also { stage++ } + 111 -> player("Dagger-mouths?").also { stage++ } + 112 -> npc("Aye, the dagger-mouths! The vile creatures lived","near here once, but we had thought them all driven back","to the ocean depths many moons past.").also { stage++ } + 113 -> npc("I can only imagine a dagger-mouth queen has nested","somewhere nearby and spawned her foul brood","under the sea once more, and some of them have","migrated to fair Waterbirth Island.").also { stage++ } + 114 -> player("So you're scared they might attack Rellekka?").also { stage++ } + 115 -> npc("Scared? Ha ha ha!").also { stage++ } + 116 -> npc("You wound us with your questioning, ${fremname}!").also { stage++ } + 117 -> npc("We are glad the dagger-mouths have returned to","these shores, for it means we will get the chance","to hunt them once again as our ancestors did!").also { stage++ } + 118 -> npc("When treated in the correct manner, the creatures'","remains can be used to make fine battleworthy armour!").also { setVarp(player, 520, 1 shl 13, true) }.also{ stage++ } + 119 -> options("Can I come?", "Nice hat!", "Ok, 'bye.").also { stage++ } + 120 -> when (buttonID) { + 1 -> player("Can I come?").also { stage = 130 } + 2 -> player("Hey, I have to say, that's a fine looking","hat you are wearing there.").also { stage = 140 } + 3 -> player("Wow, you Fremenniks sure know how to party. Well, see ya around.").also { stage = END_DIALOGUE } + } + 130 -> npc("Of course, ${fremname}! Your presence is more than welcome","on this cull! You wish to leave now?").also { stage++ } + 131 -> options("YES", "NO").also { stage++ } + 132 -> when (buttonID) { + 1 -> sail(true) + 2 -> player("No, actually I have some stuff to do here first.").also { stage = 43 } + } + 140 -> npc("Aye, that it is ${fremname}!","Skulgrimen fashioned it for me from the carcass of one","of the monsters on Waterbirth Island after our last hunt!").also { stage++ } + 141 -> npc("I hope to kill enough creatures to fashion some fine","armour as well when next we leave!").also { stage++ } + 142 -> options("Waterbirth Island?", "Can I come?", "Ok, 'bye.").also { stage++ } + 143 -> when (buttonID) { + 1 -> npc("You have not ever travelled to Waterbirth Island, ${fremname}?","I am surprised, it is a place of outstanding natural beauty.").also { stage = 108 } + 2 -> player("Can I come?").also { stage = 130 } + 3 -> player("Wow, you Fremenniks sure know how to party.","Well, see ya around.").also { stage = END_DIALOGUE } + } + 200 -> npc("Have you had your fill of the hunt and wish to return,", "or are you still feeling the joy of the cull?").also { stage++ } + 201 -> options("I wish to return to Rellekka.", "I want to stay here.").also { stage++ } + 202 -> when (buttonID) { + 1 -> player("I wish to return to Rellekka.").also { stage++ } + 2 -> player("I want to stay here.").also { stage = 210 } + } + 203 -> npc("Then let us away;", "There will be death to bring here another day!").also{ sail(false) } + 210 -> npc("Ha Ha Ha! A true huntsman at heart!").also { stage++ } + 211 -> npc("I myself have killed over a hundred of the dagger-","mouths, and did not think it too many!").also { stage = END_DIALOGUE } + 220 -> npc("So what say you, stay here for the hunt,","or return home to sweet Rellekka to feast","and drink with your tribe?").also { stage = 201 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JarvaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JARVALD_2435, NPCs.JARVALD_2436, NPCs.JARVALD_2437, NPCs.JARVALD_2438) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt index 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..c30737ab4 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt @@ -1,20 +1,20 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials -import core.api.addItem -import core.api.dumpContainer -import core.api.questStage -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 +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Items +import org.rs09.consts.NPCs @Initializable -class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class PeerTheSeerDialogue(player: Player? = null) : DialoguePlugin(player) { val predictionOne = arrayOf("one","two","three","four","five","six","seven","eight","ten") val predictionTwo = arrayOf("black","blue","brown","cyan","green","pink","purple","red","yellow") val predictionThree = arrayOf("fire giant","ghosts","giant","goblin","green dragon","hobgoblin","lesser demon","moss giant","ogre","zombie") @@ -32,232 +32,230 @@ class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialogueP override fun open(vararg args: Any?): Boolean { if(player.inventory.contains(3710,1)){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Can I have a weather forecast now please?") + playerl(FacialExpression.HAPPY,"Can I have a weather forecast now please?") stage = 15 return true } else if(player.inventory.contains(3705,1)){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So, about this forecast...") + playerl(FacialExpression.ASKING,"So, about this forecast...") stage = 20 return true } else if(player.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've got an item to trade but I don't know if it's for you.") + playerl(FacialExpression.ASKING,"I've got an item to trade but I don't know if it's for you.") stage = 26 return true } else if(player.getAttribute("sigmund-steps", 0) == 10){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a brave and powerful warrior to act as a bodyguard?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a brave and powerful warrior to act as a bodyguard?") stage = 8 return true } else if(player.getAttribute("sigmund-steps", 0) == 9){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?") stage = 1 return true } - else if(player.getAttribute("PeerStarted",false) && !player.inventory.isEmpty || !player.equipment.isEmpty){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(player.getAttribute("PeerStarted",false) && !(player.inventory.isEmpty && player.equipment.isEmpty)){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 100 return true } else if(player.getAttribute("PeerStarted",false) && player.inventory.isEmpty && player.equipment.isEmpty){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 110 return true } - else if(player.getAttribute("fremtrials:peer-vote",false)){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:peer-vote",false)) { + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 120 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 150 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 50 return true } - if (questStage(player, "Fremennik Trials") == 0) { - npc(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } + if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) == 0) { + npc(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Er.... Yes, because I AM the Fremennik Seer.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I have a weather forecast then please?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.THINKING,"You require a divination of the weather? This is a simple matter for me, but I will require something in return from you for this small service.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I knew you were going to say that...").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Do not fret, outerlander; it is a fairly simple matter. I require a bodyguard for protection. Find someone willing to offer me this service.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.ASKING,"That's all?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is all.").also { + 1 -> npcl(FacialExpression.ANNOYED,"Er.... Yes, because I AM the Fremennik Seer.").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"Can I have a weather forecast then please?").also { stage++ } + 3 -> npcl(FacialExpression.THINKING,"You require a divination of the weather? This is a simple matter for me, but I will require something in return from you for this small service.").also { stage++ } + 4 -> playerl(FacialExpression.ASKING,"I knew you were going to say that...").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"Do not fret, outerlander; it is a fairly simple matter. I require a bodyguard for protection. Find someone willing to offer me this service.").also { stage++ } + 6 -> playerl(FacialExpression.ASKING,"That's all?").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"That is all.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"If I did, then I would simply have asked them myself now, wouldn't I, outerlander?").also { stage = 1000 } + 10 -> npcl(FacialExpression.ANNOYED,"If I did, then I would simply have asked them myself now, wouldn't I, outerlander?").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I have already told you outerlander; You may have a reading from me when I have a signed contract from a warrior guaranteeing my protection.").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yeah, I know; I have one right here from Thorvald.").also { + 15 -> npcl(FacialExpression.ANNOYED,"I have already told you outerlander; You may have a reading from me when I have a signed contract from a warrior guaranteeing my protection.").also { stage++ } + 16 -> playerl(FacialExpression.HAPPY,"Yeah, I know; I have one right here from Thorvald.").also { removeItem(player,3710) addItem(player,3705) stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"You have not only persuaded one of the Fremennik to act as a servant to me, but you have enlisted the aid of mighty Thorvald himself???").also { stage++ } - 18 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You may take this forecast with my blessing outerlander. You have offered me the greatest security I can imagine.").also { stage = 1000 } + 17 -> npcl(FacialExpression.AMAZED,"You have not only persuaded one of the Fremennik to act as a servant to me, but you have enlisted the aid of mighty Thorvald himself???").also { stage++ } + 18 -> npcl(FacialExpression.HAPPY,"You may take this forecast with my blessing outerlander. You have offered me the greatest security I can imagine.").also { stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Yes, outerlander?").also { stage++ } - 21 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I still don't know why you didn't just let me have one anyway in the first place. Surely it means nothing to you?").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.THINKING,"That is not true, outerlander. Although I see glimpses of the future all of the time, using my powers brings the attention of the gods to me.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Some of the gods are spiteful and cruel, and I fear if I use my powers too much then I will meet with unpredictable accidents.").also { stage++ } - 24 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This is why I needed protection.").also { stage++ } - 25 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Okay... I... think I understand...").also { stage = 1000 } + 20 -> npcl(FacialExpression.THINKING,"Yes, outerlander?").also { stage++ } + 21 -> playerl(FacialExpression.ASKING,"I still don't know why you didn't just let me have one anyway in the first place. Surely it means nothing to you?").also { stage++ } + 22 -> npcl(FacialExpression.THINKING,"That is not true, outerlander. Although I see glimpses of the future all of the time, using my powers brings the attention of the gods to me.").also { stage++ } + 23 -> npcl(FacialExpression.THINKING,"Some of the gods are spiteful and cruel, and I fear if I use my powers too much then I will meet with unpredictable accidents.").also { stage++ } + 24 -> npcl(FacialExpression.HAPPY,"This is why I needed protection.").also { stage++ } + 25 -> playerl(FacialExpression.THINKING,"Okay... I... think I understand...").also { stage = 1000 } - 26 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage++ } + 26 -> npcl(FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage++ } //The Seer's Trial - 50 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 51 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 52 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Hello. I'm looking for members of the council of elders to vote for me to become a Fremennik.").also { stage++ } - 53 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Are you now? Well that is interesting. Usually outerlanders do not concern themselves with our ways like that.").also { stage++ } - 54 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am one of the members of the council of elders, and should you be able to prove to me that you have something to offer my clan I will vote in your favour at the next meeting.").also { stage++ } - 55 -> playerl(core.game.dialogue.FacialExpression.ASKING,"How can I prove that to you?").also { stage++ } - 56 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, I have but a simple test. This building behind me is my house. Inside I have constructed a puzzle.").also { stage++ } - 57 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As a Seer to the clan, I value intelligence very highly, so you may think of it as an intelligence test of sorts.").also { stage++ } - 58 -> playerl(core.game.dialogue.FacialExpression.THINKING,"An intelligence test? I thought barbarians were stupid!").also { stage++ } - 59 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"That is the opinion that outerlanders usually hold of my people, it is true. But that is because people often confuse knowledge with wisdom.").also { stage++ } - 60 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"My puzzle tests not what you know, but what you can work out. All members of our clan have been tested when they took their trials.").also { stage++ } - 61 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So what exactly does this puzzle consist of, then?").also { stage++ } - 62 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, firstly you must enter my house with no items, weapons or armour. Then it is a simple matter of entering through one door and leaving by the other.").also { stage++ } - 63 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I can't take anything in there with me?").also { stage++ } - 64 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander. Everything you need to complete the puzzle you will find inside the building. Nothing more.").also { stage++ } - 65 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"So what say you outerlander? You think you have the wit to earn yourself my vote?").also { stage++ } + 50 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 51 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 52 -> playerl(FacialExpression.ASKING,"Hello. I'm looking for members of the council of elders to vote for me to become a Fremennik.").also { stage++ } + 53 -> npcl(FacialExpression.THINKING,"Are you now? Well that is interesting. Usually outerlanders do not concern themselves with our ways like that.").also { stage++ } + 54 -> npcl(FacialExpression.HAPPY,"I am one of the members of the council of elders, and should you be able to prove to me that you have something to offer my clan I will vote in your favour at the next meeting.").also { stage++ } + 55 -> playerl(FacialExpression.ASKING,"How can I prove that to you?").also { stage++ } + 56 -> npcl(FacialExpression.HAPPY,"Well, I have but a simple test. This building behind me is my house. Inside I have constructed a puzzle.").also { stage++ } + 57 -> npcl(FacialExpression.HAPPY,"As a Seer to the clan, I value intelligence very highly, so you may think of it as an intelligence test of sorts.").also { stage++ } + 58 -> playerl(FacialExpression.THINKING,"An intelligence test? I thought barbarians were stupid!").also { stage++ } + 59 -> npcl(FacialExpression.ANNOYED,"That is the opinion that outerlanders usually hold of my people, it is true. But that is because people often confuse knowledge with wisdom.").also { stage++ } + 60 -> npcl(FacialExpression.ANNOYED,"My puzzle tests not what you know, but what you can work out. All members of our clan have been tested when they took their trials.").also { stage++ } + 61 -> playerl(FacialExpression.ASKING,"So what exactly does this puzzle consist of, then?").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY,"Well, firstly you must enter my house with no items, weapons or armour. Then it is a simple matter of entering through one door and leaving by the other.").also { stage++ } + 63 -> playerl(FacialExpression.ASKING,"I can't take anything in there with me?").also { stage++ } + 64 -> npcl(FacialExpression.HAPPY,"That is correct outerlander. Everything you need to complete the puzzle you will find inside the building. Nothing more.").also { stage++ } + 65 -> npcl(FacialExpression.HAPPY,"So what say you outerlander? You think you have the wit to earn yourself my vote?").also { stage++ } 66 -> options("Yes","No").also { stage++ } 67 -> when(buttonId){ - 1 ->{ playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes, I accept your challenge, I have one small question, however...") + 1 ->{ playerl(FacialExpression.HAPPY,"Yes, I accept your challenge, I have one small question, however...") player?.setAttribute("/save:PeerStarted",true) player?.setAttribute("/save:PeerRiddle", Random.nextInt(0,3)) stage = 70 } - 2 ->{ playerl(core.game.dialogue.FacialExpression.HAPPY,"No, thinking about stuff isn't really my 'thing'. I'd rather go kill something. I'll find someone else to vote for me") + 2 ->{ playerl(FacialExpression.HAPPY,"No, thinking about stuff isn't really my 'thing'. I'd rather go kill something. I'll find someone else to vote for me") stage++ } } //No to challenge - 68 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish, outerlander.").also { stage = 1000 } + 68 -> npcl(FacialExpression.HAPPY,"As you wish, outerlander.").also { stage = 1000 } //Yes to challenge - 70 -> npcl(core.game.dialogue.FacialExpression.ASKING,"Yes outerlander?").also { stage++ } - 71 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well... you say I can bring nothing with me when I enter your house...").also { stage++ } - 72 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Yes outerlander??").also { stage++ } - 73 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well...").also { stage++ } - 74 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Yes, outerlander???").also { stage++ } - 75 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where is the nearest bank?").also { stage++ } - 76 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, I see your problem outerlander. The nearest bank to here is the place known to outerlanders as the Seers Village.").also { stage++ } - 77 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It is some way South. I do however have an alternative, should you wish to take it.").also { stage++ } - 78 -> playerl(core.game.dialogue.FacialExpression.ASKING,"And what is that?").also { stage++ } - 79 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I can store all the weapons, armour and items that you have upon you directly into your bank account.").also { stage++ } - 80 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This will tax what little magic I possess however, so you will have to travel to the bank to withdraw them again.").also { stage++ } - 81 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"What say you outerlander? Do you wish me to do this for you?").also { stage++ } + 70 -> npcl(FacialExpression.ASKING,"Yes outerlander?").also { stage++ } + 71 -> playerl(FacialExpression.THINKING,"Well... you say I can bring nothing with me when I enter your house...").also { stage++ } + 72 -> npcl(FacialExpression.ANNOYED,"Yes outerlander??").also { stage++ } + 73 -> playerl(FacialExpression.THINKING,"Well...").also { stage++ } + 74 -> npcl(FacialExpression.ANGRY,"Yes, outerlander???").also { stage++ } + 75 -> playerl(FacialExpression.ASKING,"Where is the nearest bank?").also { stage++ } + 76 -> npcl(FacialExpression.HAPPY,"Ah, I see your problem outerlander. The nearest bank to here is the place known to outerlanders as the Seers Village.").also { stage++ } + 77 -> npcl(FacialExpression.HAPPY,"It is some way South. I do however have an alternative, should you wish to take it.").also { stage++ } + 78 -> playerl(FacialExpression.ASKING,"And what is that?").also { stage++ } + 79 -> npcl(FacialExpression.HAPPY,"I can store all the weapons, armour and items that you have upon you directly into your bank account.").also { stage++ } + 80 -> npcl(FacialExpression.HAPPY,"This will tax what little magic I possess however, so you will have to travel to the bank to withdraw them again.").also { stage++ } + 81 -> npcl(FacialExpression.HAPPY,"What say you outerlander? Do you wish me to do this for you?").also { stage++ } 82 -> options("Yes","No").also { stage++ } 83 -> when(buttonId){ 1 -> { val slotAmount = player.inventory.itemCount() + player.equipment.itemCount() - if (slotAmount < player.bank.freeSlots()){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"The task is done. I wish you luck with your test, outerlander.") - dumpContainer(player,player.inventory) - dumpContainer(player,player.equipment) + if (slotAmount < player.bank.freeSlots() && slotAmount == dumpContainer(player,player.inventory) + dumpContainer(player,player.equipment)){ + npcl(FacialExpression.HAPPY,"The task is done. I wish you luck with your test, outerlander.") stage = 1000 } else { - npcl(core.game.dialogue.FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you") + npcl(FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you.") stage = 1000 } } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"No thanks. Nobody touches my stuff but me!").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"No thanks. Nobody touches my stuff but me!").also { stage++ } } //No to banking - 84 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish, outerlander. You may attempt my little task when you have deposited your equipment in the bank").also { + 84 -> npcl(FacialExpression.HAPPY,"As you wish, outerlander. You may attempt my little task when you have deposited your equipment in the bank.").also { stage = 1000 } //Yes to banking but cannot bank - 90 -> npcl(core.game.dialogue.FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you").also { + 90 -> npcl(FacialExpression.SAD,"I am sorry outerlander, the spell is not working. I believe you may have some objects that you cannot bank with you.").also { stage = 1000 } //Returning after accepting with items. - 100 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 102 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So I can bring nothing with me when I enter your house?").also { stage++ } - 103 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander, but as I say, I can use my small skill in magic to send your items directly into your bank account from here.").also { stage++ } - 104 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You will need to manually go to the bank to withdraw them again however.").also { stage++ } - 105 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Would you like me to perform this small spell upon you, outerlander?").also { stage = 82 } + 100 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 101 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 102 -> playerl(FacialExpression.ASKING,"So I can bring nothing with me when I enter your house?").also { stage++ } + 103 -> npcl(FacialExpression.HAPPY,"That is correct outerlander, but as I say, I can use my small skill in magic to send your items directly into your bank account from here.").also { stage++ } + 104 -> npcl(FacialExpression.HAPPY,"You will need to manually go to the bank to withdraw them again however.").also { stage++ } + 105 -> npcl(FacialExpression.HAPPY,"Would you like me to perform this small spell upon you, outerlander?").also { stage = 82 } //Returning after accepting without items. - 110 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 111 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } - 112 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So I just have to enter by one door of your house, and leave by the other?").also { stage++ } - 113 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That is correct outerlander. Be warned it is not as easy as it may at first sound...").also { stage = 1000 } + 110 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 111 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that. Hello outerlander. What do you want?").also { stage++ } + 112 -> playerl(FacialExpression.ASKING,"So I just have to enter by one door of your house, and leave by the other?").also { stage++ } + 113 -> npcl(FacialExpression.HAPPY,"That is correct outerlander. Be warned it is not as easy as it may at first sound...").also { stage = 1000 } //After completing the Seer's Trial. - 120 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 121 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that.").also { stage++ } - 122 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"So you will vote for me at the council?").also { stage++ } - 123 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Absolutely, outerlander. Your wisdom in passing my test marks you as worthy in my eyes.").also { stage = 1000 } + 120 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 121 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that.").also { stage++ } + 122 -> playerl(FacialExpression.HAPPY,"So you will vote for me at the council?").also { stage++ } + 123 -> npcl(FacialExpression.HAPPY,"Absolutely, outerlander. Your wisdom in passing my test marks you as worthy in my eyes.").also { stage = 1000 } - //After Fremennik Trials - 150 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"!").also { stage++ } - 151 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahem, sorry about that.").also { - stage = if(player.achievementDiaryManager.getDiary(DiaryType.FREMENNIK).isComplete(0)){ + //After The Fremennik Trials + 150 -> npcl(FacialExpression.AMAZED,"!").also { stage++ } + 151 -> npcl(FacialExpression.HAPPY,"Ahem, sorry about that.").also { + stage = if(anyInEquipment(player, Items.FREMENNIK_SEA_BOOTS_1_14571, Items.FREMENNIK_SEA_BOOTS_2_14572, Items.FREMENNIK_SEA_BOOTS_3_14573) && !hasIronmanRestriction(player, IronmanMode.ULTIMATE)){ 200 }else 152 } - 152 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello Peer.").also { stage++ } - 153 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings to you, brother ${player.getAttribute("fremennikname","dingle")}! What brings you to see me again?").also { stage++ } + 152 -> playerl(FacialExpression.HAPPY,"Hello Peer.").also { stage++ } + 153 -> npcl(FacialExpression.HAPPY,"Greetings to you, brother ${player.getAttribute("fremennikname","dingle")}! What brings you to see me again?").also { stage++ } 154 -> options("Can you tell my future?","Nothing really.").also{stage++} 155 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } + 1 -> playerl(FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } } - 156 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, you would like a prediction? I do not see that that would be so difficult... Wait a moment...").also { + 156 -> npcl(FacialExpression.HAPPY,"Ah, you would like a prediction? I do not see that that would be so difficult... Wait a moment...").also { stage++ } - 157 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here is your prediction: ${PREDICTIONS[prediction]}").also { stage = 1000 } + 157 -> npcl(FacialExpression.HAPPY,"Here is your prediction: ${PREDICTIONS[prediction]}").also { stage = 1000 } - 160 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, hello to you too!").also { stage = 1000 } + 160 -> npcl(FacialExpression.HAPPY,"Well, hello to you too!").also { stage = 1000 } 200 -> options("Deposit service","Can you tell my future?","Nothing really.").also{ stage++ } 201 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Could you deposit some things for me, please?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage = 156 } - 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } + 1 -> playerl(FacialExpression.HAPPY,"Could you deposit some things for me, please?").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"I was wondering if you could give me a reading on my future...?").also { stage = 156 } + 3 -> playerl(FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } } - 202 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Of course, ${player.getAttribute("fremennikname","dingle")}. I am always happy to aid those who have earned the right to wear Fremennik sea boots.").also { + 202 -> npcl(FacialExpression.HAPPY,"Of course, ${player.getAttribute("fremennikname","dingle")}. I am always happy to aid those who have earned the right to wear Fremennik sea boots.").also { player.bank.openDepositBox() stage = 1000 } - 300 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"!").also { stage++ } - 301 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Ahem, sorry about that. I have no interest in talking to you just now outerlander.").also { stage = END_DIALOGUE } + 300 -> npc(FacialExpression.NEUTRAL,"!").also { stage++ } + 301 -> npcl(FacialExpression.NEUTRAL,"Ahem, sorry about that. I have no interest in talking to you just now outerlander.").also { stage = END_DIALOGUE } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PeerTheSeerDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(1288) + return intArrayOf(NPCs.PEER_THE_SEER_1288) } } \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt index 44391fd20..2e0fd8671 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt @@ -1,32 +1,46 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.inInventory +import core.api.setAttribute import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import content.data.Quests @Initializable -class PoisonSalesman(player: Player? = null) : 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.getStage(Quests.MURDER_MYSTERY) + val fremennikTrialsStage = player.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) when (stage) { START_DIALOGUE -> when (buttonId) { - 1 -> { player("Err... nevermind"); stage = END_DIALOGUE } + 1 -> { + when (murderMysteryStage) { + 0 -> npcl(FacialExpression.NEUTRAL, "I'm afraid I'm all sold out of poison at the moment. People know a bargain when they see it!").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "I'm investigating the murder at the Sinclair house.").also { stage = 50 } + 100 -> npcl(FacialExpression.NEUTRAL, "I hear you're pretty smart to have solved the Sinclair Murder!").also { stage = END_DIALOGUE } + } + } 2 -> { player("Hello."); stage = 10 } } - //Fremennik Trials + //The Fremennik Trials 10 -> { /**when (fremennikTrialsStage) { 0 -> { npc("Come see me if you ever need low-alcohol beer!"); stage = END_DIALOGUE } @@ -41,7 +55,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++ } @@ -87,12 +101,48 @@ class PoisonSalesman(player: Player? = null) : core.game.dialogue.DialoguePlugin stage++ } 45 -> { npc("Well come back when you do!"); stage = END_DIALOGUE } + + //Murder Mystery + 50 -> npcl(FacialExpression.NEUTRAL, "There was a murder at the Sinclair house??? That's terrible! And I was only there the other day too! They bought the last of my Patented Multi Purpose Poison!").also { stage++ } + 51 -> showTopics( + Topic("Patented Multi Purpose Poison?", 52), + Topic("Who did you sell Poison to at the house?", 61), + Topic("Can I buy some Poison?", 65), + IfTopic("I have this pot I found at the murder scene...", 69, inInventory(player, Items.PUNGENT_POT_1812)) + ) + 52 -> npcl(FacialExpression.NEUTRAL, "Aaaaah... a miracle of modern apothecaries!").also { stage++ } + 53 -> npcl(FacialExpression.NEUTRAL, "This exclusive concoction has been tested on all known forms of life and been proven to kill them all in varying dilutions from cockroaches to king dragons!").also { stage++ } + 54 -> npcl(FacialExpression.NEUTRAL, "So incredibly versatile, it can be used as pest control, a cleansing agent, drain cleaner, metal polish and washes whiter than white,").also { stage++ } + 55 -> npcl(FacialExpression.NEUTRAL, "all with our uniquely fragrant concoction that is immediately recognisable across the land as Peter Potter's Patented Poison potion!!!").also { stage++ } + 56 -> sendDialogue("The salesman stops for breath.").also { stage ++ } + 57 -> npcl(FacialExpression.NEUTRAL, "I'd love to sell you some but I've sold out recently. That's just how good it is! Three hundred and twenty eight people in this area alone cannot be wrong!").also { stage++ } + 58 -> npcl(FacialExpression.NEUTRAL, "Nine out of Ten poisoners prefer it in controlled tests!").also { stage++ } + 59 -> npcl(FacialExpression.NEUTRAL, "Can I help you with anything else? Perhaps I can take your name and add it to our mailing list of poison users? We will only send you information related to the use of poison and other Peter Potter Products!").also { stage++ } + 60 -> playerl(FacialExpression.NEUTRAL, "Uh... no, it's ok. Really.").also { stage = END_DIALOGUE } + + 61 -> npcl(FacialExpression.HAPPY, "Well, Peter Potter's Patented Multi Purpose Poison is a product of such obvious quality that I am glad to say I managed to sell a bottle to each of the Sinclairs!").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY, "Anna, Bob, Carol, David, Elizabeth and Frank all bought a bottle! In fact they bought the last of my supplies!").also { stage++ } + 63 -> npcl(FacialExpression.HAPPY, "Maybe I can take your name and address and I will personally come and visit you when stocks return?").also { stage++ } + 64 -> playerl(FacialExpression.THINKING, "Uh...no, it's ok.") + .also { setAttribute(player, attributePoisonClue, 1)} + .also { stage = END_DIALOGUE } + + 65 -> npcl(FacialExpression.NEUTRAL, "I'm afraid I am totally out of stock at the moment after my successful trip to the Sinclairs' House the other day.").also { stage++ } + 66 -> npcl(FacialExpression.NEUTRAL, "But don't worry! Our factories are working overtime to produce Peter Potter's Patented Multi Purpose Poison!").also { stage++ } + 67 -> npcl(FacialExpression.NEUTRAL, "Possibly the finest multi purpose poison and cleaner yet available to the general market.").also { stage++ } + 68 -> npcl(FacialExpression.NEUTRAL, "And its unique fragrance makes it the number one choice for cleaners and exterminators the whole country over!").also { stage = END_DIALOGUE } + + 69 -> sendDialogue("You show the poison salesman the pot you found at the murder", "scene with the unusual smell.").also { stage ++ } + 70 -> npcl(FacialExpression.THINKING, "Hmmm... yes, that smells exactly like my Patented Multi Purpose Poison, but I don't see how it could be. It quite clearly says on the label of all bottles").also { stage++ } + 71 -> npcl(FacialExpression.THINKING, "'Not to be taken internally - EXTREMELY POISONOUS'.").also { stage++ } + 72 -> playerl(FacialExpression.THINKING, "Perhaps someone else put it in his wine?").also { stage++ } + 73 -> npcl(FacialExpression.THINKING, "Yes... I suppose that could have happened...").also { stage = END_DIALOGUE } END_DIALOGUE -> end() } return true } - 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..8fd16e3fc 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt @@ -1,43 +1,45 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails import core.game.node.item.Item import core.plugin.Initializable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items @Initializable -class SigliTheHuntsman(player: Player? = null) : 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.") + if(player?.inventory?.contains(Items.CUSTOM_BOW_STRING_3702, 1) == true){ + 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 +49,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 +105,51 @@ 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?.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 { + if (player.inventory.remove(Item(Items.HUNTERS_TALISMAN_3697))) { + player.removeAttribute("fremtrials:draugen-killed") + player.setAttribute("/save:fremtrials:sigli-vote", true) + player.setAttribute("/save:fremtrials:votes", player.getAttribute("fremtrials:votes", 0) + 1) + } + stage = 1000 + } + 150 -> playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map to unspoiled hunting grounds, do you?").also { stage++ } + 151 -> npcl(FacialExpression.HAPPY,"Well, of course I do. I wouldn't be much of a huntsman if I didn't know where to find my prey now, would I outerlander?").also { stage++ } + 152 -> playerl(FacialExpression.ASKING,"No, I guess not. So can I have it?").also { stage++ } + 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 { - removeItem(player,3702) - addItem(player,3701) - stage++ + 165 -> playerl(FacialExpression.HAPPY,"Here. I have your bowstring. Give me your map to the hunting grounds.").also { + if (removeItem(player, Items.CUSTOM_BOW_STRING_3702)) { + addItem(player, Items.TRACKING_MAP_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..947ffee6c 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt @@ -1,6 +1,7 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression @@ -8,9 +9,11 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests +import core.api.addItemOrDrop @Initializable -class SigmundDialogue (player: Player? = null) : 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 +25,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 +65,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 +106,41 @@ 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 { - addItem(player, Items.PROMISSORY_NOTE_3709) - stage = 1000 } + 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 { + addItemOrDrop(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..4ca7edd4f 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt @@ -7,44 +7,46 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests +import org.rs09.consts.Items @Initializable -class SkulgrimenDialogue(player: Player? = null) : 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?") + if(player?.inventory?.contains(Items.UNUSUAL_FISH_3703,1) == true){ + playerl(FacialExpression.HAPPY,"Hi there. I got your fish, so can I have that bowstring for Sigli now?") stage = 20 return true } - else if(player?.inventory?.contains(3702,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So about this bowstring... was it hard to make or something?") + else if(player?.inventory?.contains(Items.CUSTOM_BOW_STRING_3702,1) == true){ + playerl(FacialExpression.ASKING,"So about this bowstring... was it hard to make or something?") stage = 25 return true } 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 +54,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 { - removeItem(player,3703) - addItem(player,3702) - stage++ + 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 { + if (removeItem(player, Items.UNUSUAL_FISH_3703) && addItem(player, Items.CUSTOM_BOW_STRING_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 +94,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..0f7762d8e 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt @@ -1,40 +1,42 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails import core.plugin.Initializable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items @Initializable -class SwensenTheNavigator(player: Player? = null) : 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.") + if(player?.inventory?.contains(Items.WEATHER_FORECAST_3705, 1) == true){ + playerl(FacialExpression.HAPPY,"I would like your map of fishing spots.") stage = 120 return true } - else if(player?.inventory?.contains(3704,1) == true){ - 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?") + else if(player?.inventory?.contains(Items.SEA_FISHING_MAP_3704, 1) == true){ + playerl(FacialExpression.ASKING,"If this map of fishing spots is so valuable, why did you give it away to me so easily?") stage = 125 return true } 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 +49,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 +92,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 +101,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 { - removeItem(player,3705) - addItem(player,3704) - stage++ + 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 { + if (removeItem(player,Items.WEATHER_FORECAST_3705) && addItem(player, Items.SEA_FISHING_MAP_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..e7b48f9d3 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt @@ -21,6 +21,8 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.system.config.ItemConfigParser import core.game.world.GameWorld.Pulser +import content.data.Quests +import core.game.interaction.QueueStrength class TFTInteractionListeners : InteractionListener { @@ -82,8 +84,7 @@ class TFTInteractionListeners : InteractionListener { } onUseWith(IntType.ITEM,TINDERBOX,CHERRY_BOMB){ player, _, _ -> - if(removeItem(player,CHERRY_BOMB)){ - addItem(player,LIT_BOMB) + if (removeItem(player,CHERRY_BOMB) && addItem(player,LIT_BOMB)) { sendMessage(player,"You light the strange object.") } return@onUseWith true @@ -154,7 +155,7 @@ class TFTInteractionListeners : InteractionListener { sendNPCDialogue(player,1278,"Yeah you're good to go through. Olaf tells me you're some kind of outerlander bard here on tour. I doubt you're worse than Olaf is.") core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } - getAttribute(player,"lyreConcertPlayed",false) -> { + getAttribute(player,"lyreConcertPlayed",false) || isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) -> { core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } else -> { @@ -166,17 +167,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 +209,17 @@ class TFTInteractionListeners : InteractionListener { return@on true } - on(SWENSEN_LADDER, IntType.SCENERY, "climb"){ player, _ -> - if(!getAttribute(player,"fremtrials:swensen-accepted",false)){ + on(SWENSEN_LADDER, IntType.SCENERY, "climb-down") { player, _ -> + if (!getAttribute(player,"fremtrials:swensen-accepted",false)) { sendNPCDialogue(player,1283,"Where do you think you're going?", core.game.dialogue.FacialExpression.ANGRY) + return@on true } + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2631, 10006, 0)) return@on true } on(THORVALD_LADDER, IntType.SCENERY, "climb-down") { player, _ -> - if (isQuestComplete(player, "Fremennik Trials") || getAttribute(player, "fremtrials:thorvald-vote", false)) { + if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) || getAttribute(player, "fremtrials:thorvald-vote", false)) { sendMessage(player,"You have no reason to go back down there.") return@on true } else if (!getAttribute(player,"fremtrials:warrior-accepted",false)) { @@ -257,13 +260,8 @@ class TFTInteractionListeners : InteractionListener { return@on true } - on(SWAYING_TREE, IntType.SCENERY, "cut-branch"){ player, node -> - player.pulseManager.run(WoodcuttingSkillPulse(player, node as Scenery)) - return@on true - } - 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 +318,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 +332,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 +350,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 +375,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 +397,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 +431,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 +488,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..e64080988 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt @@ -1,20 +1,23 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem +import core.api.getQuestStage import core.api.removeItem import core.game.node.entity.player.Player import core.plugin.Initializable +import org.rs09.consts.Items import org.rs09.consts.NPCs +import content.data.Quests @Initializable class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { - if(player?.inventory?.contains(3706,1) == true){ + if(player?.inventory?.contains(Items.CHAMPIONS_TOKEN_3706, 1) == true){ playerl(core.game.dialogue.FacialExpression.HAPPY,"I would like your contract to offer your services as a bodyguard.") stage = 215 return true } - else if(player?.inventory?.contains(3710,1) == true){ + else if(player?.inventory?.contains(Items.WARRIORS_CONTRACT_3710, 1) == true){ playerl(core.game.dialogue.FacialExpression.ASKING,"You didn't take much persuading to 'lower' yourself to a bodyguard.") stage = 220 return true @@ -39,27 +42,27 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug stage = 150 return true } - else if (player?.questRepository?.isComplete("Fremennik Trials")!!) { + else if (player?.questRepository?.isComplete(Quests.THE_FREMENNIK_TRIALS)!!) { playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Howdy Thorvald!") stage = 0 return true } - else if(player!!.getAttribute("fremtrials:thorvald-vote", false)!!){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { playerl(core.game.dialogue.FacialExpression.FRIENDLY, "So can I count on your vote at the council of elders now Thorvald?") stage = 160 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ playerl(core.game.dialogue.FacialExpression.HAPPY,"Howdy Thorvald!") stage = 250 return true } - else if(!player.questRepository.hasStarted("Fremennik Trials")){ + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) == 0){ npcl(core.game.dialogue.FacialExpression.ANNOYED, "Leave me be, outerlander. I have nothing to say to the likes of you.") stage = 1000 return true } - else if (!player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && !player!!.getAttribute("fremtrials:thorvald-vote", false)!!) { if (player!!.getAttribute("fremtrials:warrior-accepted", false)!!) { options("What do I have to do again?", "Who is my opponent?", "Can't I do something else?") stage = 100 @@ -75,7 +78,7 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - //After Fremennik Trials + //After The Fremennik Trials 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "And greetings to you too. It is good to see new blood entering the Fremennik; we gain our strength by bringing new warriors into the tribe.").also { stage = 1000 } //Warrior Trial @@ -177,9 +180,10 @@ class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 216 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"It's a good thing I have the Champions' Token right here then, isn't it?").also { stage++ } 217 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah... well this is a different matter. With that token I can claim my rightful place as a champion in the Long hall!").also { stage++ } 218 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here outerlander, I can suffer the indignity of playing babysitter if it means that I can then revel with my warrior equals in the Long Hall afterwards!").also { - removeItem(player,3706) - addItem(player,3710) - stage++ + if (removeItem(player, Items.CHAMPIONS_TOKEN_3706)) { + addItem(player, Items.WARRIORS_CONTRACT_3710) + stage++ + } } 219 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here outerlander, take this contract; I will fulfill it to my utmost.").also { stage = 1000 } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt index 83e782706..3114af8ab 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt @@ -7,46 +7,48 @@ import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable +import content.data.Quests +import org.rs09.consts.Items @Initializable -class YrsaDialogue(player: Player? = null) : 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.") + if(player?.inventory?.contains(Items.FISCAL_STATEMENT_3708,1) == true){ + playerl(FacialExpression.HAPPY,"Hello. Can I have those boots now? Here is a written statement from Brundt outlining future tax burdens upon Fremennik merchants and shopkeepers for the year.") stage = 15 return true } - else if(player?.inventory?.contains(3700,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"Hey, these shoes look pretty comfy. Think you could make me a pair like them?") + else if(player?.inventory?.contains(Items.STURDY_BOOTS_3700,1) == true){ + playerl(FacialExpression.ASKING,"Hey, these shoes look pretty comfy. Think you could make me a pair like them?") stage = 20 return true } 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 +56,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 { - removeItem(player,3708) - addItem(player,3700) - stage++ + 15 -> npcl(FacialExpression.HAPPY,"Certainly! Let me have a look at what he has written here, just give me a moment...").also { + if (removeItem(player, Items.FISCAL_STATEMENT_3708) && addItem(player, Items.STURDY_BOOTS_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/DagannothKingNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java index e928e6179..65f5d4e43 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java @@ -97,7 +97,7 @@ public final class DagannothKingNPC extends AbstractNPC { public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); if (getId() == 2881 || getId() == 2882 || getId() == 2883) { - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); } } 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..e887f5263 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 @@ -145,9 +146,9 @@ public final class SpinolypNPC extends AbstractNPC { super.impact(entity, victim, state); if (super.getType() == CombatStyle.MAGIC && state.getEstimatedHit() > 0) { victim.getSkills().decrementPrayerPoints(1); - } else { + } else if (super.getType() == CombatStyle.RANGE && state.getEstimatedHit() > 0) { 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..96c91c8e1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt @@ -0,0 +1,68 @@ +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.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() + addItemOrDrop(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() + addItemOrDrop(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() + addItemOrDrop(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/hazeelcult/AlomoneDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt new file mode 100644 index 000000000..e6c54d22a --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt @@ -0,0 +1,146 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.HAZEEL_SCROLL +import core.api.* +import core.game.dialogue.* +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.* + +@Initializable +class AlomoneDialogue(player: Player? = null) : DialoguePlugin(player) { + + val hazeel = HazeelNPC(NPCs.HAZEEL_892, Location.create(2607, 9669, 0)) + + override fun handle(componentID: Int, buttonID: Int, ): Boolean { + + val questStage = getQuestStage(player, Quests.HAZEEL_CULT) + + when { + // you can't access Alomone before stage 2 + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage in 2..3) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hi there.").also { stage = 20 } + } else { + npcl(FacialExpression.ANGRY, "How did YOU get in here?").also { stage ++} + } + } + // carnillean arc + 1 -> playerl(FacialExpression.NEUTRAL, "I've come for the Carnillean family armour. Hand it over, or face the consequences.").also { stage++ } + 2 -> npcl(FacialExpression.ANNOYED, "I thought I made it clear to the butler you could not be allowed to interfere with our mission. The incompetent fool must be going soft.").also { stage++ } + 3 -> playerl(FacialExpression.NEUTRAL, "So, the butler's part of your sordid little cult, huh? Why is it ALWAYS the butler? I should have known...").also { stage++ } + 4 -> npcl(FacialExpression.ANNOYED, "Well, you won't live long enough to tell anyone! DIE!!!").also { stage++ } + 5 -> { + end() + setQuestStage(player, Quests.HAZEEL_CULT, 4) + npc!!.attack(player!!) + } + + // mahjarrat arc + 20 -> npcl(FacialExpression.NEUTRAL, "Well well well... So we have a new recruit. Clivet told me of your desire to join us in our glorious task to resurrect the mighty Hazeel from his slumber.").also { stage++ } + 21 -> npcl(FacialExpression.NEUTRAL, "To accomplish this, we require the ancient words of summoning which will restore his shattered body, so that he can bring vengeance upon his enemies.").also { stage++ } + 22 -> npcl(FacialExpression.NEUTRAL, "Hazeel in his mighty cunning anticipated that he might be defeated and secured within his home somewhere a powerful magical scroll that could restore him.").also { stage++ } + 23 -> npcl(FacialExpression.NEUTRAL, "The words to this powerful enchantment are hidden within the Carnillean mansion, right under the nose of his unsuspecting foes. We already have an agent in place there").also { stage++} + 24 -> npcl(FacialExpression.NEUTRAL, "in their Butler Jones, a faithful follower of Hazeel. His loyalty to Hazeel is beyond questioning, yet he has been unsuccessful in locating the scroll containing the spell so far.").also { stage++ } + 25 -> npcl(FacialExpression.NEUTRAL, "Go back to the mansion, and assist us in finding this enchantment, so that together we can restore Hazeel to his true power and glory!").also { stage = END_DIALOGUE}.also { setQuestStage(player, Quests.HAZEEL_CULT, 4) } + } + + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + (questStage in 4..5 && mahjarratArc(player)) -> when (stage) { + 0 -> if(npc.id == NPCs.ALOMONE_891 && !getAttribute(player, "hazeelSpawned", false)) { + playerl(FacialExpression.NEUTRAL, "Hi.").also { stage++ } + } else { + playerl(FacialExpression.NEUTRAL, "Hi.").also { stage = 12 } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Have you brought me the scroll of restoration to complete the ritual?").also { stage++ } + 2 -> if(inInventory(player, HAZEEL_SCROLL)){ + playerl(FacialExpression.NEUTRAL, "Yep. Got it right here.").also { stage++ } + } else { + playerl(FacialExpression.NEUTRAL, "No, not yet.").also { stage = END_DIALOGUE } + } + 3 -> npcl(FacialExpression.NEUTRAL, "FINALLY! Quickly, give it to me and I will begin the ritual!").also { stage++ } + 4 -> sendDialogue(player, "You hand Alomone the scroll of Hazeel.").also { stage++ } + 5 -> npcl(FacialExpression.NEUTRAL, "Yes... YES! With the words contained within this scroll our Lord shall return to this realm and bring destruction to our enemies!").also { stage++ } + 6 -> npcl(FacialExpression.NEUTRAL, "Watch adventurer, and witness the glorious rebirth of Hazeel!").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "Lord Hazeel... Lord Zamorak... I call upon the powers of evil... The forces of chaos... The strength of hatred...").also { stage++ } + 8 -> npcl(FacialExpression.NEUTRAL, "Sentente sillaberi junque dithmento! Ia! Ia! dextrimon encanto! termando... imcando... solly enty rando... sentente! Ia! Ia! Indenti zaggarati g'thxa!").also { stage++ } + 9 -> sendDialogue(player, "Alomone continues to recite the scroll. It is in a language you have never heard and do not understand at all.").also { stage++ } + 10 -> npcl(FacialExpression.NEUTRAL, "Dintenta! Sententa! Retenta! Q'exjta! Ia! Sottottott! Ia! Dysmenta junque fammatio svelken! Sottey! Sentey! SOLOMENT!").also { stage++ } + 11 -> { + sendDialogue(player, "As Alomone finishes reading the scroll you hear a shrill scream emanate from Hazeel's coffin.").also { stage++ } + hazeel.init() + setAttribute(player,"hazeelSpawned", true) + player.face(hazeel) + hazeel.isWalks = false + } + 12 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "My loyal followers. I have pride in you all. Never did I expect to return to this land and I see I have much to attend to.").also { stage++ } + 13 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Soon this world will cower once more at the name Hazeel, and my fury will blaze across mine enemies just as my loyal followers gain their rewards.").also { stage++ } + 14 -> sendDialogue(player, "Hazeel turns to look at you directly.").also{ hazeel.face(player) }.also { stage++ } + 15 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Adventurer. I know that thy efforts were principal in my return to this plain of existence. I offer you my thanks directly.").also { stage++ } + 16 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "You may not be a follower of Zamorak directly but your cunning and treachery mark you as a friend to the Mahjarrat. Rest assured; I will call on your assistance again.").also { stage++ } + 17 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Weak as I am now, my strength shall return, and my followers will be greatly rewarded when my powers peak once more. Join me in my cause as my servant, and all").also { stage++ } + 18 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "the riches of this plane shall be yours to share, in domination over all who oppose my kind. You would be wise to pledge yourself now while you still may.").also { stage++ } + 19 -> playerl(FacialExpression.ANGRY, "I serve nobody but myself.").also { stage++ } + 20 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Your insolence shall be overlooked this time for I am in truth in your debt. Come close: I shall reward you with wealth such as you deserve.").also { stage++ } + 21 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "Although your true reward will be later, when Zamorak and I, together, lay waste to this miserable planet, and you are spared as an ally to us.").also { stage++ } + 22 -> sendDialogue(player, "Hazeel gives you some coins. They seem to be extremely ancient.").also { stage++ } + 23 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "And now I must leave you my loyal subjects for time is short, and I have many pressing matters to address before I make my return. I must leave now, and head Northwards").also { stage++ } + 24 -> sendNPCDialogue(player, NPCs.HAZEEL_892, "to join my fellow mahjarrat and regain my strength and powers. I shall return when I am ready to fight my enemies and much blood shall flow across these lands!").also { stage++ } + 25 -> sendDialogue(player, "The cultists let out a mighty cheer.").also { stage++ } + 26 -> { + end() + removeItem(player, HAZEEL_SCROLL) + val hazeel = player.findNpcInViewport(NPCs.HAZEEL_892) + hazeel?.clear() + removeAttribute(player,"hazeelSpawned") + finishQuest(player, Quests.HAZEEL_CULT) + } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + // also valid for stages 4 and 5 in the carnillean arc. + (questStage in 4..100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hi there.").also { stage++ } + } else { + npcl(FacialExpression.ANNOYED, "You have crossed my path too many times intruder. Leave or face my wrath.").also { stage = 2 } + } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Welcome, adventurer. Know that as a friend to Hazeel, you are always welcome here.").also { stage = END_DIALOGUE } + + 2 -> playerl(FacialExpression.FRIENDLY, "Yeah, whatever.").also { stage = END_DIALOGUE } + } + } + return true + } + + // function used to find any hazeels and remove them at the end of the quest. + // this has the potential to remove other players' hazeels, but the player could still complete the quest by either continuing their + // active dialogue or speaking to alomone, and just dealing with the fact that their hazeel is not on the map. relogging and restarting + // the dialogue would get these players a new hazeel. + private fun Player.findNpcInViewport(npcId: Int): NPC? { + val viewport = viewport ?: return null + for (plane in viewport.viewingPlanes) { + for (npc in plane.npcs) { + if (npc.id == npcId) { + return npc + } + } + } + return null + } + + // Hazeel is in this array because in the event the player quits the dialogue halfway through in the final stage, it can be restarted by speaking to either alomone or hazeel. + override fun getIds(): IntArray = intArrayOf(NPCs.ALOMONE_891, NPCs.HAZEEL_892) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneNPC.kt new file mode 100644 index 000000000..83b28833a --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneNPC.kt @@ -0,0 +1,39 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc +import core.api.isQuestInProgress +import core.api.produceGroundItem +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.* + +/** + * Alomone drops the Carnillean Amour during the Hazeel Cult quest. + */ + +@Initializable +class AlomoneNPC( + id: Int = 0, + location: Location? = null, +) : AbstractNPC(id, location) { + override fun construct( + id: Int, + location: Location, + vararg objects: Any, + ): AbstractNPC = AlomoneNPC(id, location) + + override fun getIds(): IntArray = intArrayOf(NPCs.ALOMONE_891) + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + if (isQuestInProgress(killer, Quests.HAZEEL_CULT, 3, 4) && carnilleanArc(killer)) { + produceGroundItem(killer, Items.CARNILLEAN_ARMOUR_2405, 1, this.location) + } + super.finalizeDeath(killer) + } + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ButlerJonesDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ButlerJonesDialogue.kt new file mode 100644 index 000000000..f8bcc91d3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ButlerJonesDialogue.kt @@ -0,0 +1,80 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.getQuestStage +import core.api.inEquipmentOrInventory +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.* + +@Initializable +class ButlerJonesDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "Hello, adventurer. Such a terrible shame about Scruffy. I wonder if the family will ever fully recover.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Anyway, I hear your quest is going well.").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Really?").also { stage++ } + 4 -> npcl(FacialExpression.ANNOYED, "Oh yes. Do keep up the good work.").also { stage = END_DIALOGUE } + } + + // stage 4 - alomone either fought or he tells you he needs scroll + (questStage == 4 && mahjarratArc(player) && inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406)) -> when (stage) { + 0 -> npcl(FacialExpression.NEUTRAL, "Hello again friend. I see you have the mark now; you should keep it covered up.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "That? Oh, it's nothing. Just an old amulet.").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "You don't have to pretend with me, friend! Our cause is one and the same; the sooner Lord Hazeel is avenged the better for this city... and for us!").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "So do you have any idea where the scroll with the enchantment is?").also { stage++ } + 4 -> npcl(FacialExpression.ANNOYED, "No idea I'm afraid. I KNOW it's somewhere in this house, but for the life of me I can't find it anywhere. I've searched high and low for it!").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "And Sir Ceril doesn't suspect a thing?").also { stage++ } + 6 -> npcl(FacialExpression.ANNOYED, "Ha! That silly old fool? He can't see the forest for the trees.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "I'll keep on looking then...").also { stage = END_DIALOGUE } + } + + // stage 0 - unstarted + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 5 - either returning the armour, or finding the scroll + // also stage 4 if you are carnillean side + (questStage in 0..5) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello. How are you today?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Good thank you, and yourself?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Very well, thank you.").also { stage = END_DIALOGUE } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = 1 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 6 } + } + } + 1 -> npcl(FacialExpression.NEUTRAL, "It's an honour to be in your presence again, adventurer. I hope things are well?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not bad, thanks. Yourself?").also { stage++ } + 3 -> npcl(FacialExpression.ANNOYED, "Unfortunately, I am still forced to deal with this insufferable family. Many generations have passed, but they are still the enemy. As such, they must be kept a close eye on.").also { stage++ } + 4 -> npcl(FacialExpression.ANNOYED, "Still, I have no doubt that the time will soon come for me to leave this place. Our lord will certainly have need of me elsewhere once his current work is complete.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "I see. Well good luck with it all.").also { stage = END_DIALOGUE } + + 6 -> npcl(FacialExpression.ANNOYED, "Why hello there.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "I take it you're the new butler...?").also { stage++ } + 8 -> npcl(FacialExpression.ANNOYED, "That's right. I hear that they had some problems with the last one.").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "Yes, you could say that...").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.BUTLER_JONES_890) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/CerilCarnilleanDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/CerilCarnilleanDialogue.kt new file mode 100644 index 000000000..083e561d5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/CerilCarnilleanDialogue.kt @@ -0,0 +1,147 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.fakeFinish +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +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 org.rs09.consts.* + +@Initializable +class CerilCarnilleanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + when { + // stage 0 - unstarted + (questStage == 0) -> { + when (stage) { + 0 -> npcl(FacialExpression.ANGRY, "Blooming, thieving, weirdo cultists! Why don't they leave me alone? String 'em all up, that's what I say!").also { stage++ } + 1 -> options("What's wrong?", "You probably deserve it.", "You seem uptight, I'll leave you alone.").also { stage++ } + 2 -> when (buttonID) { + 1 -> playerl(FacialExpression.HALF_ASKING, "What's wrong?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "You probably deserve it.").also { stage = 20 } + 3 -> playerl(FacialExpression.FRIENDLY, "You seem uptight, I'll leave you alone.").also { stage = 21 } + } + 3 -> npcl(FacialExpression.ANGRY, "It's those blooming cultists from the forest! Those freaks keep breaking into my house!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Have they taken much?").also { stage++ } + 5 -> npcl(FacialExpression.ANGRY, "They first broke in months ago and stole a suit of armour. The strange thing is that they've broken in four times since but taken nothing.").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "And you are...?").also { stage++ } + 7 -> npcl(FacialExpression.ANGRY, "Why, I am Sir Ceril Carnillean! We really are quite a famous bloodline... who've played a vital role in the politics of Ardougne for many generations.").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "Perhaps you would be able to assist me in returning the stolen armour? For a modest cash reward of course!").also { stage++ } + 9 -> options("Yes, of course, I'd be happy to help.", "No thanks. I've got other plans.").also { stage++ } + 10 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, of course, I'd be happy to help.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "No thanks. I've got other plans.").also { stage = 19 } + } + 11 -> npcl(FacialExpression.FRIENDLY, "That's very noble of you sirrah! I caught a glimpse of the thieves leaving but due to... uh... yeah, my cold I was unable to give chase.").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "They're some kind of crazy cult who dress all in black, and hang out near the cave entrance in the forest south of here.").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY, "How do you know that?").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "My old butler, Higson, once followed them to their hideout there. Unfortunately the next night he died in his sleep.").also { stage++ } + 15 -> playerl(FacialExpression.FRIENDLY, "That's awful!").also { stage++ } + 16 -> npcl(FacialExpression.FRIENDLY, "No, it's okay. A replacement arrived the next day. He's been great - cooks an excellent broth!").also { stage++ } + + 17 -> playerl(FacialExpression.FRIENDLY, "Ok. I'll see what I can do.").also { stage++ } + 18 -> { + end() + setQuestStage(player, Quests.HAZEEL_CULT, 1) + } + 19 -> npcl(FacialExpression.FRIENDLY, "Well no wonder I'm the one with the big house and you're the one on the streets.").also { stage = END_DIALOGUE } + 20 -> npcl(FacialExpression.FRIENDLY, "And who are you to judge me? You look like a peasant anyway. I'm wasting my time talking to you.").also { stage = END_DIALOGUE } + 21 -> npcl(FacialExpression.FRIENDLY, "Yes, I doubt you could help anyway.").also { stage = END_DIALOGUE } + } + } + + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello again.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "Oh the inhumanity... the cruelty... the misery... the pain...").also { stage++ } + 2 -> npcl(FacialExpression.ANNOYED, "My son is a good boy, really, but how could he give his dinner to Scruffy without having the servants test it for poison first? How?").also { stage++ } + 3 -> npcl(FacialExpression.ANNOYED, "How could he be so thoughtless and careless? He knows we are all under threat!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Scruffy?").also { stage++ } + 5 -> npcl(FacialExpression.ANNOYED, "He's been with our family for twenty years... that's 140 in dog years! The poor dog... What did he ever do to deserve such a fate?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Your dog got poisoned? That's not right.").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "I agree! I hope whichever evildoer is responsible gets the full weight of the law brought upon them!").also { stage++ } + 8 -> playerl(FacialExpression.NEUTRAL, "Uh... yeah... me too.").also { stage = END_DIALOGUE } + } + + // stage 4 - alomone either fought or he tells you he needs scroll + (questStage == 4 && carnilleanArc(player) && inInventory(player, Items.CARNILLEAN_ARMOUR_2405)) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Look! I've recovered your armour!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Well done! I must say I am very impressed! Come on, hand it over.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Before we send you on your way with your payment, I'll just get Jones to whip you up a batch of his special broth.").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "I'd rather not if it's all the same to you. I overheard the cultists talking, and apparently Jones is in league with them.").also { stage++ } + 4 -> { + // check if we are upstairs + if(player.location.z == 1) { + npcl(FacialExpression.ANGRY, "W-what? RIGHT! You! We're going to blooming well sort this out right now once and for all!").also { stage++ } + } else { + npcl(FacialExpression.ANGRY, "W-what? RIGHT! You! Come with me upstairs! We're going to blooming-well sort this out, once and for all!").also { stage = END_DIALOGUE } + } + } + + 5 -> npcl(FacialExpression.HALF_ASKING, "Jones! This commoner says you had something to do with the theft of my armour. What do you have to say for yourself about that?").also { stage++ } + 6 -> sendNPCDialogue(player, NPCs.BUTLER_JONES_890, "It wasn't me m'lud. I am, as you know, a loyal servant.").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "Humph. Quite right too. I cannot fathom why this scoundrel would accuse you of such a crime without evidence to back up @g[his,her] accusations.").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "Right, I have decided. I have given my word as a nobleman to reward you for your efforts in retrieving my armour.").also { stage++ } + // it is authentic that the previous line ends in a period and the next line starts with a non-capital 'but' + 9 -> npcl(FacialExpression.FRIENDLY, "but I must also compensate Jones for this terrible slander you have made against him.").also { stage++ } + 10 -> { + sendDialogue(player, "Sir Ceril gives you 5 gold. Sir Ceril gives Jones 1995 gold.") + addItemOrDrop(player, Items.COINS_995, 5) + stage++ + } + 11 -> npcl(FacialExpression.ANGRY, "Now take it and leave you scoundrel! And don't darken my doorstep again!").also { stage++ } + 12 -> sendNPCDialogue(player, NPCs.BUTLER_JONES_890, "Don't worry m'lud, this fool won't be bothering us any more.").also { stage++ } + 13 -> sendDialogue(player, "Jones smirks at you. You are going to need more than words to prove Jones' treachery.").also { stage++ } + 14 -> { + end() + removeItem(player, Items.CARNILLEAN_ARMOUR_2405) + setQuestStage(player, Quests.HAZEEL_CULT, 5) + fakeFinish(player) + } + + } + + // stage 5 - either returning the armour, or finding the scroll + (questStage == 5 && carnilleanArc(player)) -> when (stage) { + // The ceril-jones-player dialogue is continued in the listener for the wardrobe where the player finds the evidence that Jones is bad. + 0 -> npcl(FacialExpression.ANGRY, "Leave, you scoundrel! And don't darken my doorstep again!").also { stage = END_DIALOGUE } + } + + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // also covers stage 4 and 5 if you're playing the mahjarrat side + (questStage in 1..99) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello, Ceril.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "That's Sir Ceril to you, you impudent scamp. Show a bit of respect to your betters. Now, shouldn't you be recovering my armour?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Yeah yeah, I'm on it.").also { stage++ } + 3 -> npcl(FacialExpression.ANNOYED, "Good. I suggest you start with that cave south of the city, near the Clock Tower. That is where the cult is based.").also { stage = END_DIALOGUE } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello, Ceril.").also { stage++ } + 1 -> { + if (mahjarratArc(player)) { + npcl(FacialExpression.FRIENDLY, "Oh... I may be wrong... but ever since I asked for your help, things around here have gone from bad to worse...").also { stage = 3 } + } else { + npcl(FacialExpression.FRIENDLY, "Well hello again adventurer! It's good to see you again! If it wasn't for your quick thinking the treacherous Jones would have poisoned my family and me by now! We are in your debt.").also { stage = 2 } + } + } + 2 -> playerl(FacialExpression.FRIENDLY, "Don't worry about it, a good deed is its own reward. And that 2000 gold didn't hurt either.").also { stage = END_DIALOGUE } + + 3 -> npcl(FacialExpression.NEUTRAL, "I think you'd better keep out of my way. And for the last time, it's Sir Ceril! Sir! It's not that hard to blooming remember!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.CERIL_CARNILLEAN_885) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClausTheChefDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClausTheChefDialogue.kt new file mode 100644 index 000000000..2680171c3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClausTheChefDialogue.kt @@ -0,0 +1,56 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +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 org.rs09.consts.* + +@Initializable +class ClausTheChefDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + when { + + // stage 0 - unstarted + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 3 - poison poured in food (mahjarrat-only stage) + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + (questStage in 0..99) -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Sorry, can't stop to chat! You would be amazed at how many meals this family gets through daily!").also { stage = END_DIALOGUE } + } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> { + when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hey.").also { stage++ } + } else { + playerl(FacialExpression.FRIENDLY, "Hiya.").also { stage = 2 } + } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Oh, hello there. Sorry, but I can't really talk right now. Things haven't been great here recently, and I have a lot of work to do.").also { stage = END_DIALOGUE } + + 2 -> npcl(FacialExpression.HALF_ASKING, "Well hello there adventurer! Are we fit and well?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Yep, fine thanks.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "Glad to hear it.").also { stage = END_DIALOGUE } + } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.CLAUS_THE_CHEF_886) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetDialogue.kt new file mode 100644 index 000000000..34409ef83 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetDialogue.kt @@ -0,0 +1,162 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrCarnillean +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrHazeel +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 org.rs09.consts.* + +@Initializable +class ClivetDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int, ): Boolean { + + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + // stage 0 - unstarted + (questStage == 0) -> when (stage) { + 0 -> npcl(FacialExpression.ANNOYED, "What do you want traveller?").also { stage++ } + 1 -> playerl(FacialExpression.ANNOYED, "Just passing by.").also { stage++ } + 2 -> npcl(FacialExpression.ANNOYED, "You have no business here.").also { stage++ } + 3 -> npcl(FacialExpression.ANGRY, "Leave...now").also { stage = END_DIALOGUE } + } + + // stage 1 - after talking to ceril + (questStage == 1) -> when (stage) { + 0 -> playerl(FacialExpression.ANNOYED, "Do you know the Carnilleans?").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "You mind your business, I'll mind mine.").also { stage++ } + 2 -> playerl(FacialExpression.ANNOYED, "Look I KNOW you're hiding something. I've heard there's a cult hideout down here.").also { stage++ } + 3 -> npcl(FacialExpression.ANGRY, "If you want to stay healthy you'll leave now.").also { stage++ } + 4 -> playerl(FacialExpression.ANNOYED, "I have my orders.").also { stage++ } + 5 -> npcl(FacialExpression.HALF_ASKING, "So... that two faced cold hearted snob has made you fall for his propaganda eh?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Sir Ceril Carnillean is a man of honour!").also { stage++ } + 7 -> npcl(FacialExpression.ANNOYED, "Is he now? Is he REALLY? There is a lot more to the Carnilleans than meets the eye you fool...").also { stage++ } + 8 -> npcl(FacialExpression.ANNOYED, "And none of it is honourable.").also { stage++ } + 9 -> options("What do you mean?", "I've heard enough of your rubbish.").also { stage++ } + 10 -> when (buttonID) { + 1 -> playerl(FacialExpression.HALF_ASKING, "What do you mean?").also { stage++ } + 2 -> playerl(FacialExpression.ANNOYED, "I've heard enough of your rubbish.").also { stage = 22 } + } + 11 -> npcl(FacialExpression.ANNOYED, "The Carnillean home does not belong to them. The builder of the house was Lord Hazeel, one of the Mahjarrat followers of Zamorak. Many years ago there was a civil war").also { stage++ } + 12 -> npcl(FacialExpression.ANNOYED, "in this land, and the hateful Saradominists declared war upon all Zamorakians who lived here. Lord Hazeel nobly would not repent his beliefs, and the Carnilleans harassed").also { stage++ } + 13 -> npcl(FacialExpression.ANNOYED, "Hazeel and his followers for many decades. One fateful night, under cover of darkness, they stormed his home in an angry mob torturing and butchering all loyal").also { stage++ } + 14 -> npcl(FacialExpression.ANNOYED, "Zamorakians they encountered inside. The following morning, the Carnillean forefathers moved into the empty household and claimed it as their own.").also { stage++ } + 15 -> npcl(FacialExpression.ANNOYED, "They have grown fat on the hard work of Lord Hazeel ever since. Unluckily for them Lord Hazeel as a Mahjarrat had access to powers and enchantments they knew nothing").also { stage++ } + 16 -> npcl(FacialExpression.ANNOYED, "about, and made preparations for his return when first they began to storm his home. Soon the day will come when he will return to wreak his vengeance upon the thieves!").also { stage++ } + 17 -> playerl(FacialExpression.ANNOYED, "The politics and histories of Ardougne do not concern me. I have been given a job and intend to see it through to the end.").also { stage++ } + 18 -> npcl(FacialExpression.ANNOYED, "Well then friend, perhaps I can offer you a different job then? Sooner as later Our master WILL return to this land; those faithful to him will be well rewarded.").also { stage++ } + 19 -> npcl(FacialExpression.ANNOYED, "Join our cult and assist us in his restoration, and you will be well rewarded; try and prevent his return and suffer the wrath of Zamorak and the Mahjarrat.").also { stage++ } + 20 -> options("You're crazy, I'd never help you.", "So what would I have to do?").also { stage++ } + 21 -> when (buttonID) { + 1 -> playerl(FacialExpression.DISGUSTED_HEAD_SHAKE, "You're crazy, I'd never help you.").also { stage++ } + 2 -> playerl(FacialExpression.HALF_ASKING, "So what would I have to do?").also { stage = 40 } + } + + // SET CARNILLEAN ARC + 22 -> npcl(FacialExpression.ANNOYED, "Then you are a fool. Go back to your small minded mundane little life; you will never know the glories you could have tasted as one of us!").also { stage++ } + 23 -> { + end() + setAttribute(player, attrCarnillean, true) + setQuestStage(player, Quests.HAZEEL_CULT, 2) + runTask(player, 1) { + npc.isInvisible = true + sendDialogue(player, "The man jumps onto the raft and pushes off down into the sewer system.") + sendMessage(player, "Clivet: You'll never find us...") + stage = END_DIALOGUE + } + } + + // SET MAHJARRAT ARC + 40 -> npcl(FacialExpression.NEUTRAL, "You must prove your loyalty to our cause. Killing one of the Carnillean household should be sufficient proof of your dedication to resurrecting our master Lord Hazeel.").also { stage++ } + 41 -> npcl(FacialExpression.NEUTRAL, "So what say you adventurer? Join our side against the Carnillean thieves?").also { stage++ } + 42 -> options("No. I won't do it.", "Ok, count me in.").also { stage++ } + 43 -> when (buttonID) { + 1 -> playerl(FacialExpression.DISGUSTED_HEAD_SHAKE, "No. I won't do it.").also { stage = 22 } + 2 -> playerl(FacialExpression.NEUTRAL, "Ok, count me in.").also { stage++ } + } + 44 -> npcl(FacialExpression.NEUTRAL, "Excellent. It takes a rare character to see through the cursed Carnillean lies. I can see you are a person of exactly the right character to join the followers of Hazeel.").also { stage++ } + 45 -> npcl(FacialExpression.NEUTRAL, "Here, take this poison. Pour it into their food, and once the deed is done, return here and speak to me once more.").also { stage++ } + 46 -> { + end() + setAttribute(player, attrHazeel, true) + setQuestStage(player, Quests.HAZEEL_CULT, 2) + addItemOrDrop(player, Items.POISON_273) + } + } + + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + (questStage == 2) -> when (stage) { + 0 -> { + if (mahjarratArc(player) && !inInventory(player, Items.POISON_273)) { + playerl(FacialExpression.FRIENDLY, "I need some more poison.").also { stage++ } + } else if (mahjarratArc(player) && inInventory(player, Items.POISON_273)){ + npcl(FacialExpression.FRIENDLY, "You have a mission for us, adventurer. Go to the Carnilliean household and poison Ceril Carnillean's meal to prove your loyalty.").also { stage = END_DIALOGUE } + } else { + playerl(FacialExpression.HALF_ASKING, "WHERE is the cult hideout?").also { stage = 3 } + } + } + 1 -> npcl(FacialExpression.ANNOYED, "Fool! Be more careful with it this time.").also { stage++ } + 2 -> { + end() + addItemOrDrop(player, Items.POISON_273) + } + 3 -> npcl(FacialExpression.ANNOYED, "You're more of a fool than you look if you think you will ever find it. When Lord Hazeel is revived you will be the first to grovel for his mercy!").also { stage = END_DIALOGUE } + } + + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3 && mahjarratArc(player)) -> when (stage) { + 0 -> { + if (!inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406)) { + playerl(FacialExpression.FRIENDLY, "I poured the poison into the Carnillean's meal as requested. It didn't quite go to plan however.").also { stage++ } + } else { + playerl(FacialExpression.HALF_ASKING, "What was I supposed to do again?").also { stage = 6 } + } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Yes, we heard all about it from one of our sources. Hazeel has eyes everywhere! You have now proved your loyalty and will be rewarded when Hazeel is revived!").also { stage++ } + 2 -> playerl(FacialExpression.HALF_ASKING, "Ok. So what's next?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Here. Wear this amulet; it is called The Sign of Hazeel. Not only will it identify you as one of our brotherhood to other followers but it is also the key to finding our hideout.").also { addItemOrDrop(player, Items.HAZEELS_MARK_2406) }.also { stage++ } + 4 -> playerl(FacialExpression.NEUTRAL, "How does this amulet help do that then?").also { stage++ } + 5 -> npcl(FacialExpression.NEUTRAL, "Hazeel in his wisdom when he built this lair did so by making it inaccessible unless the flow of the sewers is controlled exactly by the sewer valves above ground.").also { stage++ } + 6 -> npcl(FacialExpression.NEUTRAL, "Each sewer valve must be correctly set before the water level is such that you may enter. The secret of the correct settings is contained in these amulets which honour him.").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "Starting from left to right, follow the design of the amulet to locate each sewer valve, and then turn each so as to follow the amulet design.").also { stage++ } + 8 -> npcl(FacialExpression.NEUTRAL, "When you solve the sequence you may enter our hideout where our leader, Alomone, awaits you.").also { stage = END_DIALOGUE } + } + + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage in 4..100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + } else { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 4 } + } + } + 1 -> npcl(FacialExpression.FRIENDLY, "It is good to see you once more, adventurer. Glory to Hazeel!").also { stage++ } + 2 -> { + // if player doesn't have hazeel's mark they can get a new copy. + if (hasAnItem(player, Items.HAZEELS_MARK_2406).container == null) { + playerl(FacialExpression.FRIENDLY, "I've lost my Mark of Hazeel.").also { stage++ } + } else { + playerl(FacialExpression.FRIENDLY, "Glory to Hazeel!").also { stage = END_DIALOGUE } + } + } + 3 -> npcl(FacialExpression.ANNOYED, "Here, take this.").also { addItemOrDrop(player, Items.HAZEELS_MARK_2406) }.also { stage = END_DIALOGUE } + + 4 -> npcl(FacialExpression.ANNOYED, "You may have won this battle meddler, but the war rages on. Go bother some goblins or something, for when Hazeel returns your destruction is assured.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.CLIVET_893) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetNPC.kt new file mode 100644 index 000000000..c33424281 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/ClivetNPC.kt @@ -0,0 +1,35 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.* + +/** + * Clivet disappears briefly during the Hazeel Cult quest. + */ + +@Initializable +class ClivetNPC(id: Int = 0, location: Location? = null, ) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any, ): AbstractNPC = ClivetNPC(id, location) + + override fun getIds(): IntArray = intArrayOf(NPCs.CLIVET_893) + + private var invisTicks = 0 + + override fun tick() { + if (isInvisible && invisTicks <= 0) { + invisTicks = 20 + } + + if (invisTicks > 0) { + invisTicks-- + + if (invisTicks == 0) { + isInvisible = false + } + } + + super.tick() + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/GuardHCDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/GuardHCDialogue.kt new file mode 100644 index 000000000..c3e8fd224 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/GuardHCDialogue.kt @@ -0,0 +1,74 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.getQuestStage +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.* + +@Initializable +class GuardHCDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + // stage 0 - unstarted + (questStage == 0) -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello. If you've come to admire the Carnillean family home, I must warn you not to cause any trouble.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Due to recent criminal activities against such a prominent member of Ardougnian politics, I have been sent here on special guard duty to ensure the Carnilleans' security.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Anyone caught interfering with the person or possessions of said Carnilleans will be taken to jail so quickly that their feet won't touch the floor!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Well, I wasn't actually planning on doing anything like that...").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "Glad to hear it. Let's keep it that way, hmmm?").also { stage = END_DIALOGUE } + } + } + + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3) -> { + when (stage) { + 0 -> npcl(FacialExpression.SAD, "Today is a dark day. Those cultists have been back, and this time they've gone further than ever before! Murder! We can't afford to keep letting them get away with this.").also { stage = END_DIALOGUE } + } + } + + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + (questStage in 1..99) -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello there. I hear you're after that cult who broke in the other night. It always gladdens me when civilians assist the law like this.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm just happy to be of help.").also { stage = END_DIALOGUE } + } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 1 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 6 } + } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Hello, adventurer. It's a shame you never found that cult. We have a horrible suspicion that there's been another burglary.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "That's worrying.").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Yes, it is. The thing is, we can't even work out what they've taken! It's all very odd.").also { stage++ } + 4 -> playerl(FacialExpression.HALF_ASKING, "Is there anything more I can do to help?").also { stage++ } + 5 -> npcl(FacialExpression.NEUTRAL, "I don't think so. Sir Ceril says you've done enough.").also { stage = END_DIALOGUE } + + 6 -> npcl(FacialExpression.ANNOYED, "Well well well... if it isn't our very own local hero. Good to see you managed to clear your name. I always had faith in you!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.GUARD_887) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCult.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCult.kt new file mode 100644 index 000000000..c0d0dfa27 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCult.kt @@ -0,0 +1,198 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.HAZEEL_CULT_VARP +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrCarnillean +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrHazeel +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer1R +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer2R +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer3L +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer4R +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCultListeners.Companion.attrSewer5R +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 + +/* HAZEEL CULT + * + * Primary video source: https://www.youtube.com/watch?v=hFBJx_xERQ4 (carnillean playthrough) + * Primary video source: https://www.youtube.com/watch?v=ZYPB823IyRk (mahjarrat playthrough) + * https://web.archive.org/web/20081217064620/http://runescape.salmoneus.net/quests/HazeelCult.html + * https://web.archive.org/web/20090121182721/http://runehq.com/guide.php?type=quest&id=00297 + * https://runescape.wiki/w/Hazeel_Cult?oldid=815270 + * + * Varp 223 - Hazeel Cult progress + * https://chisel.weirdgloop.org/varbs/display?varplayer=223 + * Values Stage Description + * 0 0 unstarted + * 2 1 after talking to ceril + * 3 2 talk to clivet (set attr for carnillean) + * 4 2 talk to clivet (set attr for mahjarrat) + * 5 3 poison poured in food (mahjarrat-only stage) + * 6 4 alomone either fought or he tells you he needs scroll + * 7 5 either returning the armour, or finding the scroll + * 9 100 quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + * + * The varp values just appear to track quest stages, so I don't update the varp at intermediate quest stages. + * + * Good source of half the quest log: https://youtu.be/Kf95y-YdviA?si=kWeA-8oa-22TttE4&t=140 + * + * Original framework of the quest and about half the dialogue is credit to szumaster. + * + */ + +@Initializable +class HazeelCult : Quest(Quests.HAZEEL_CULT, 74, 73, 1, HAZEEL_CULT_VARP, 0, 1, 9) { + + // These functions are used at various points to check whether the player is following the Carnillean's side, or Hazeel's. + companion object { + fun carnilleanArc(player: Player) : Boolean { + return (!getAttribute(player, attrHazeel, false) && getAttribute(player, attrCarnillean, false)) + } + + fun mahjarratArc(player: Player) : Boolean { + return (getAttribute(player, attrHazeel, false) && !getAttribute(player, attrCarnillean, false)) + } + + // the fake ending where you don't prove Jones guilty yet. lol. + fun fakeFinish(player: Player) { + openInterface(player, 277) + + player.packetDispatch.sendString("" + player.getQuestRepository().getPoints() + "", 277, 7) + player.packetDispatch.sendString("You have... kind of... completed the Hazeel Cult Quest!", 277, 4) + + player.packetDispatch.sendItemZoomOnInterface(Items.COINS_995, 230, 277, 5) + + player.packetDispatch.sendString("5 coins", 277, 10) + player.packetDispatch.sendString("", 277, 11) + player.packetDispatch.sendString("", 277, 12) + player.packetDispatch.sendString("", 277, 13) + player.packetDispatch.sendString("", 277, 14) + player.packetDispatch.sendString("", 277, 15) + player.packetDispatch.sendString("", 277, 16) + player.packetDispatch.sendString("", 277, 17) + } + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + + val attributeMahjarrat = mahjarratArc(player) + val attributeCarnillean = carnilleanArc(player) + + if (stage >= 0) { + line(player, "I can start the quest by talking to !!Sir Ceril Carnillean?? at", line++, stage >= 1) + line(player, "the house due !!West?? of !!Ardougne Zoo??.", line++, stage >= 1) + line++ + } + if (stage >= 1) { + line(player, "I spoke to !!Sir Ceril Carnillean?? at his house and agreed to", line++, stage >= 2) + line(player, "help him !!investigate?? the theft of a !!family heirloom??.", line++, stage >= 2) + line++ + } + if (stage >= 2) { + line(player, "I found a !!member of the cult?? called !!Clivet?? at the entrance", line++, stage >= 3) + line(player, "to the !!cult's hideout??, south of !!Ardougne??.", line++, stage >= 3) + line++ + } + + /* --------------- * + * HAZEEL SIDE + * --------------- */ + if (stage >= 3 && attributeMahjarrat && !attributeCarnillean) { + line(player, "Having decided to assist the cult in their mission to revive !!Hazeel??,", line++, stage >= 4) + line(player, "I tried to !!poison?? the !!Carnilleans?? but instead", line++, stage >= 4) + line(player, "only managed to kill their pet dog.", line++, stage >= 4) + line++ + } + if (stage >= 4 && attributeMahjarrat && !attributeCarnillean) { + line(player, "I spoke to !!Clivet?? and he told me that I had failed,", line++, stage >= 5) + line(player, "but he gave me an !!amulet?? anyway.", line++, stage >= 5) + line(player, "Then I managed to take the sewer rafts to the cult hideout.", line++, stage >= 5) + line++ + } + if (stage >= 5 && attributeMahjarrat && !attributeCarnillean) { + line(player, "I found the scroll needed to revive !!Hazeel?? in a", line++, stage >= 100) + line(player, "secret compartment upstairs at the !!Carnillean Mansion??.", line++, stage >= 100) + line++ + } + if (stage == 100 && attributeMahjarrat && !attributeCarnillean) { + line(player, "I gave !!Alomone?? the scroll from the !!Carnillean Mansion?? and", line++, true) + line(player, "helped perform the revivification of !!Hazeel??.", line++, true) + line(player, "I was rewarded for all of my help", line++, true) + line(player, "in returning !!Hazeel?? to his followers.", line++, true) + line++ + } + + /* --------------- * + * CARNILLEAN SIDE + * --------------- */ + if (stage >= 2 && attributeCarnillean && !attributeMahjarrat) { + line(player, "He told me a pack of lies about the !!Carnilleans??,", line++, stage >= 4) + line(player, "then asked me to join the !!cult??. Obviously, I refused.", line++, stage >= 4) + line(player, "I was still no closer to recovering the !!missing armour??.", line++, stage >= 4) + line(player, "After speaking to him, he jumped onto a !!raft?? and headed", line++, stage >= 4) + line(player, "into the !!sewer system??. I need to find a way to follow", line++, stage >= 4) + line++ + } + if (stage >= 4 && attributeCarnillean && !attributeMahjarrat) { + line(player, "I managed to enter !!the hideout??, kill the !!cult leader?? and", line++, stage >= 5) + line(player, "retrieve !!the armour??. I discovered that !!Jones the Butler??", line++, stage >= 5) + line(player, "was secretly a !!member of the cult?? and a !!traitor??.", line++, stage >= 5) + line++ + } + // this stage displays a 'fake' quest ending! + // https://youtu.be/hFBJx_xERQ4?si=ft_1rZGKAHpDjind&t=648 + if (stage >= 5 && attributeCarnillean && !attributeMahjarrat) { + line(player, "I returned !!the armour??, but !!Ceril?? didn't believe !!Jones was involved??", line++, stage >= 100) + line(player, "with the cult and was responsible for the theft.", line++, stage >= 100) + line++ + } + if (stage == 100 && attributeCarnillean && !attributeMahjarrat) { + line(player, "I found undeniable evidence that the !!Butler?? was", line++, true) + line(player, "involved with the cult and gave it to !!Ceril??.", line++, true) + line(player, "My name was cleared and", line++, true) + line(player, "I graciously accepted the reward for all of my help.", line++, true) + line++ + } + + /* --------------- * + * QUEST COMPLETE + * --------------- */ + if (stage == 100) { + line(player, "QUEST COMPLETE!", line, false) + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendItemZoomOnInterface(Items.COINS_995, 230, 277, 5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "2,000 Coins", ln++) + drawReward(player, "1,500 Thieving XP", ln) + + rewardXP(player, Skills.THIEVING, 1500.0) + addItemOrDrop(player, Items.COINS_995, 2000) + + // no attributes need removing on quest completion. the sewer valves are not saved, and which quest arc you chose affects the quest journal and post-quest dialogue. + } + + // use ::setqueststage 74 0 to reset quest. + override fun reset(player: Player) { + removeAttribute(player, attrSewer1R) + removeAttribute(player, attrSewer2R) + removeAttribute(player, attrSewer3L) + removeAttribute(player, attrSewer4R) + removeAttribute(player, attrSewer5R) + removeAttribute(player, attrHazeel) + removeAttribute(player, attrCarnillean) + } + + override fun newInstance(`object`: Any?): Quest = this +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt new file mode 100644 index 000000000..cd6ac6007 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt @@ -0,0 +1,266 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.* +import core.game.dialogue.FacialExpression +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.* + +class HazeelCultListeners : InteractionListener { + + override fun defineListeners() { + on(CLIVET_STAIRS, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, location(2586, 3237, 0)) + sendMessage(player, "You climb up the stairs.") + return@on true + } + + on(CAVE_ENTRANCE, IntType.SCENERY, "enter") { player, _ -> + teleport(player, location(2570, 9682, 0)) + sendMessage(player, "You enter the cave.") + return@on true + } + + on(CLIVET_RAFT, IntType.SCENERY, "board") { player, node -> + // to use the raft, you must also be at either + // Carnillean Arc Stage 2 or greater, + // Mahjarrat Arc Stage 3 with Hazeel's Mark, + // Mahjarrat Arc Stage 4 or greater + val questStage = getQuestStage(player, Quests.HAZEEL_CULT) + val questReq = if( + (carnilleanArc(player) && questStage >= 2) || + (mahjarratArc(player) && inEquipmentOrInventory(player, Items.HAZEELS_MARK_2406) && questStage >= 3) || + (mahjarratArc(player) && questStage >= 4) + ) {true} else false + val raftUnlock = if( + getAttribute(player, attrSewer1R, false) && + getAttribute(player, attrSewer2R, false) && + getAttribute(player, attrSewer3L, false) && + getAttribute(player, attrSewer4R, false) && + getAttribute(player, attrSewer5R, false) && + questReq + ) {true} else false + + + // check which raft this is. + val x = node.location.x + + if (x == 2567) { // raft to cultists. only use if you've turned the valves. + if (!raftUnlock) { + sendNPCDialogue(player, CLIVET_NPC, "Hey! I don't remember saying you could use that raft!", FacialExpression.ANNOYED) + } else { + teleport(player, location(2606, 9692, 0)) + sendDialogue(player, "The raft washes up the sewer, past the islands until it reaches the end of the sewer passage.") + } + } else { // raft away from cultists. no check on this one since the valve attributes aren't saved. + teleport(player, location(2567, 9680, 0)) + sendDialogue(player, "The raft flows back to the cave entrance.") + } + return@on true + } + + /* + * Valve 1 - This is right across the bridge south of the Carnilleans' house. Turn it right. + * Valve 2 - This is right behind Carnilleans' house. Turn it right. + * Valve 3 - This is north of the cave. Turn it left. + * Valve 4 - This is south of the zoo's penguin cage. Turn it right. + * Valve 5 - Go southeast of the fourth valve, past the monk. Turn it right. + * + * All 4 of the below sources turn the valves in different orders. Text quest guides make no mention of needing a specific order, + * even though Clivet's bad-side dialogue makes it seem like it. For this implementation, the player needs to set each valve in the + * correct direction, order of the valves does not matter. + * + * Bad side - turns in order: https://www.youtube.com/watch?v=ZYPB823IyRk + * Good side - turns out of order: https://www.youtube.com/watch?v=OuEvnA6Cyrw + * Good side - turns out of order: https://www.youtube.com/watch?v=JG7E3uVx4rs + * Bad side - turns out of order: https://www.youtube.com/watch?v=T8JyyhDEVSg + * + * I allow the valve attributes to be set even if quest is unstarted, but getting on the raft checks the quest stage. + */ + + // valves 1, 2, 4, 5 should be turned right + on(SEWERS, IntType.SCENERY, "turn-right") { player, node -> + if (!player.location.withinDistance(node.asScenery().location, 2)) return@on true + + val currentSewer = node.id + when (currentSewer) { + SEWER_1 -> setAttribute(player, attrSewer1R, true) + SEWER_2 -> setAttribute(player, attrSewer2R, true) + SEWER_3 -> removeAttribute(player, attrSewer3L) + SEWER_4 -> setAttribute(player, attrSewer4R, true) + SEWER_5 -> setAttribute(player, attrSewer5R, true) + } + + lock(player, 4) + animate(player, TURN_VALVE_ANIMATION) + sendDialogueLines(player, "You turn the large metal valve to the right. Beneath your feet you", "can hear the sudden sound of rushing water from the sewer.") + return@on true + } + + // valve 3 should be turned left + on(SEWERS, IntType.SCENERY, "turn-left") { player, node -> + if (!player.location.withinDistance(node.asScenery().location, 2)) return@on true + + val currentSewer = node.id + when (currentSewer) { + SEWER_1 -> removeAttribute(player, attrSewer1R) + SEWER_2 -> removeAttribute(player, attrSewer2R) + SEWER_3 -> setAttribute(player, attrSewer3L, true) + SEWER_4 -> removeAttribute(player, attrSewer4R) + SEWER_5 -> removeAttribute(player, attrSewer5R) + } + + lock(player, 4) + animate(player, TURN_VALVE_ANIMATION) + sendDialogueLines(player, "You turn the large metal valve to the left. Beneath your feet you", "can hear the sudden sound of rushing water from the sewer.") + return@on true + } + + // this wardrobe contains some poison and an amulet, used to convince ceril that jones is a baddie + on(WARDROBE, IntType.SCENERY, "search") { player, _ -> + openDialogue(player, WardrobeRevealDialogueFile()) + return@on true + } + + // siding with Hazeel and trying to poison the Carnilleans + onUseWith(IntType.SCENERY, POISON, COOKING_RANGE) { player, _, _ -> + if (mahjarratArc(player) && removeItem(player, POISON)) { + sendDialogueLines(player, "You pour the poison into the hot pot.", "The poison dissolves into the soup.") + setQuestStage(player, Quests.HAZEEL_CULT, 3) + } else { + sendNPCDialogue(player, NPCs.CLAUS_THE_CHEF_886, "Oi - I don't want people messing around with my range!") + } + return@onUseWith true + } + + // crate in the basement. they key can only be obtained in the mahjarrat arc, which locks the secret passage, chest, and hazeel scroll to that arc as well. + on(CRATE, IntType.SCENERY, "search") { player, _ -> + if (getQuestStage(player, Quests.HAZEEL_CULT) >= 1 && + mahjarratArc(player) && + !inInventory(player, CHEST_KEY) + ) { + sendDialogue(player, "You search the crate. Under various food packages you find an old rusty key.") + addItemOrDrop(player, CHEST_KEY, 1) + } else { + sendMessage(player, "You search the crate but find nothing.") + } + return@on true + } + + // secret wall on the way up to retrieve the hazeel scroll + on(SECRET_PASSAGE, IntType.SCENERY, "Knock-at") { player, node -> + if (inInventory(player, CHEST_KEY) && player.location.y == 3274) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "There is something odd about the wall here.") + sendMessage(player, "You find a secret passageway") + } else if (player.location.y == 3275) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "Nothing interesting happens.") + } + return@on true + } + + // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. + on(Scenery.CHEST_2856, IntType.SCENERY, "Open") { player, _ -> + sendMessage(player, "This chest is locked shut.") + return@on true + } + + // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. + onUseWith(IntType.SCENERY, CHEST_KEY, Scenery.CHEST_2856) { player, _, _ -> + sendMessage(player, "You unlock the chest.") + animate(player, Animations.HUMAN_OPEN_CHEST_536) + addScenery(Scenery.CHEST_2857,CHEST_LOCATION, 2, 10) + removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2856, Location(2565, 3272, 2))) + return@onUseWith true + } + + // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. + on(Scenery.CHEST_2857, IntType.SCENERY, "Close") { _, _ -> + addScenery(Scenery.CHEST_2856, CHEST_LOCATION, 2, 10) + removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2857, CHEST_LOCATION)) + return@on true + } + + // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. + on(Scenery.CHEST_2857, IntType.SCENERY, "Search") { player, _ -> + val hasScroll = hasAnItem(player, HAZEEL_SCROLL).container != null + if (hasScroll) { + sendDialogue(player, "You already have the scroll from this chest.") + return@on true + } + addItemOrDrop(player, HAZEEL_SCROLL, 1) + sendItemDialogue(player, HAZEEL_SCROLL, "Inside the chest you find the Scroll of Hazeel.") + setQuestStage(player, Quests.HAZEEL_CULT, 5) + return@on true + } + } + + // interact locations for the sewer valves + override fun defineDestinationOverrides() { + setDest(IntType.SCENERY, intArrayOf(SEWER_1), "turn-right", "turn-left") { _, _ -> + return@setDest Location.create(2563, 3246, 0) + } + setDest(IntType.SCENERY, intArrayOf(SEWER_2), "turn-right", "turn-left") { _, _ -> + return@setDest Location.create(2571, 3263, 0) + } + setDest(IntType.SCENERY, intArrayOf(SEWER_3), "turn-right", "turn-left") { _, _ -> + return@setDest Location.create(2586, 3244, 0) + } + setDest(IntType.SCENERY, intArrayOf(SEWER_4), "turn-right", "turn-left") { _, _ -> + return@setDest Location.create(2597, 3262, 0) + } + setDest(IntType.SCENERY, intArrayOf(SEWER_5), "turn-right", "turn-left") { _, _ -> + return@setDest Location.create(2610, 3242, 0) + } + + } + + companion object { + const val HAZEEL_CULT_VARP = 223 + + const val attrSewer1R = "quest:hazeelcult-sewer1" + const val attrSewer2R = "quest:hazeelcult-sewer2" + const val attrSewer3L = "quest:hazeelcult-sewer3" + const val attrSewer4R = "quest:hazeelcult-sewer4" + const val attrSewer5R = "quest:hazeelcult-sewer5" + + const val attrHazeel = "/save:quest:hazeelcult-hazeel" // true when you agree to help Clivet + const val attrCarnillean = "/save:quest:hazeelcult-carnillean" // true when you DON'T agree to help Clivet + + const val CLIVET_NPC = NPCs.CLIVET_893 + const val CLIVET_RAFT = Scenery.RAFT_2849 + + const val CAVE_ENTRANCE = Scenery.CAVE_ENTRANCE_2852 + const val CLIVET_STAIRS = Scenery.STAIRS_2853 + + const val HAZEEL_SCROLL = Items.HAZEEL_SCROLL_2403 + + const val SEWER_1 = Scenery.SEWER_VALVE_2844 // Valve 1 - This is right across the bridge south of the Carnilleans' house. Turn it right. + const val SEWER_2 = Scenery.SEWER_VALVE_2845 // Valve 2 - This is right behind Carnilleans' house. Turn it right. + const val SEWER_3 = Scenery.SEWER_VALVE_2846 // Valve 3 - This is north of the cave. Turn it left. + const val SEWER_4 = Scenery.SEWER_VALVE_2847 // Valve 4 - This is south of the zoo's penguin cage. Turn it right. + const val SEWER_5 = Scenery.SEWER_VALVE_2848 // Valve 5 - Go southeast of the fourth valve, past the monk. Turn it right. + + val SEWERS = intArrayOf(SEWER_1, SEWER_2, SEWER_3, SEWER_4, SEWER_5) + + const val WARDROBE = Scenery.WARDROBE_2851 + + const val TURN_VALVE_ANIMATION = 4861 + + const val CRATE = Scenery.CRATE_34585 + const val CHEST_KEY = Items.CHEST_KEY_709 + const val POISON = Items.POISON_273 + const val COOKING_RANGE = Scenery.COOKING_RANGE_2859 + + const val SECRET_PASSAGE = Scenery.WALL_26940 + + val CHEST_LOCATION = Location(2565, 3272, 2) + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultistDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultistDialogue.kt new file mode 100644 index 000000000..cb986e49d --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultistDialogue.kt @@ -0,0 +1,45 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.getQuestStage +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.* + +@Initializable +class HazeelCultistDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int, ): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + (questStage in 1..99) -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "What? Who are you and why did you come here? It doesn't matter, leave now adventurer. While you still can.",).also { stage = END_DIALOGUE } + } + + (questStage == 100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 1 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 2 } + } + } + 1 -> npcl(FacialExpression.FRIENDLY, "The adventurer returns! Glory to you for your efforts in bringing about the return of Lord Hazeel!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.ANNOYED, "It's the meddler! The one who blew Jones' cover! I don't know why you came to this place but it is here that you will die.").also { stage++ } + 3 -> { + end() + npc.attack(player) + } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.HAZEEL_CULTIST_894) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelNPC.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelNPC.kt new file mode 100644 index 000000000..ae249d3ea --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelNPC.kt @@ -0,0 +1,17 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.* + +/** + * Hazeel is spawned in briefly during the Mahjarrat end of Hazeel Cult quest. + */ + +@Initializable +class HazeelNPC(id: Int = 0, location: Location? = null, ) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any, ): AbstractNPC = HazeelNPC(id, location) + + override fun getIds(): IntArray = intArrayOf(NPCs.HAZEEL_892) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HenryetaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HenryetaDialogue.kt new file mode 100644 index 000000000..bef204d6a --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HenryetaDialogue.kt @@ -0,0 +1,64 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.getQuestStage +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.* + +@Initializable +class HenryetaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(componentID: Int, buttonID: Int): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3) -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello. Are you okay?").also { stage++ } + 1 -> npcl(FacialExpression.SAD, "No, no I am very far from OK. Those hooligans slaughtered my precious Scruffy! I shall never recover! I am emotionally scarred for life!").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.NEUTRAL, "It WAS only a dog you know...").also { stage = END_DIALOGUE } + } + } + + // stage 0 - unstarted + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + (questStage in 0..99) -> { + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Oh, hello. I'm afraid I'm not very presentable at the moment. I've been under incredible stress of late! It's ruining my complexion!").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Why? What's wrong?").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "It's those awful cultists! I just don't feel safe here anymore!").also { stage = END_DIALOGUE } + } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 1 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 3 } + } + } + + 1 -> npcl(FacialExpression.HALF_ASKING, "Oh, it's you. I'm very disappointed that you never found those awful cultists. You're quite useless really, aren't you?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY, "Yup, I guess I am.").also { stage = END_DIALOGUE } + + 3 -> npcl(FacialExpression.FRIENDLY, "Hello again adventurer! Since you dealt with those horrible hooligan cultists, things have vastly improved for us around here!").also { stage++ } + 4 -> playerl(FacialExpression.HAPPY, "My pleasure, Lady Henryeta.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.HENRYETA_CARNILLEAN_889) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/PhilipeDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/PhilipeDialogue.kt new file mode 100644 index 000000000..a3135eebe --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/PhilipeDialogue.kt @@ -0,0 +1,80 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.mahjarratArc +import core.api.getQuestStage +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.* + +@Initializable +class PhilipeDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle( + componentID: Int, + buttonID: Int, + ): Boolean { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + + when { + // stage 0 - unstarted + (questStage == 0) -> { + when (stage) { + 0 -> npcl(FacialExpression.CHILD_NORMAL, "Hello, how are you today?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Good thank you. And yourself?").also { stage++ } + 2 -> npcl(FacialExpression.CHILD_NORMAL, "Fine and dandy.").also { stage = END_DIALOGUE } + } + } + + // stage 3 - poison poured in food (mahjarrat-only stage) + (questStage == 3) -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.CHILD_SAD, "Someone killed Scruffy. I liked Scruffy. He never told me off.").also { stage++ } + 2 -> playerl(FacialExpression.HALF_GUILTY, "Yeah... it's a real shame.").also { stage++ } + 3 -> npcl(FacialExpression.CHILD_SAD, "I want my mommy.").also { stage = END_DIALOGUE } + } + } + + // stage 1 - after talking to ceril + // stage 2 - talk to clivet (set attr for carnillean) + // stage 2 - talk to clivet (set attr for mahjarrat) + // stage 4 - alomone either fought or he tells you he needs scroll + // stage 5 - either returning the armour, or finding the scroll + (questStage in 1..99) -> { + when (stage) { + 0 -> npcl(FacialExpression.CHILD_NORMAL, "Mommy said you're here to kill all the nasty people that keep breaking in.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Something like that.").also { stage++ } + 2 -> npcl(FacialExpression.CHILD_FRIENDLY, "Can I watch?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "No!").also { stage = END_DIALOGUE } + } + } + + // stage 100 - quest complete - either presenting proof that jones is a bad guy (duh) or resurrecting hazeel + (questStage == 100) -> when (stage) { + 0 -> { + if (mahjarratArc(player)) { + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 1 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello youngster.").also { stage = 4 } + } + } + + 1 -> npcl(FacialExpression.CHILD_NORMAL, "What have you brought me? I want some more toys!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm afraid I don't have any toys.").also { stage++ } + 3 -> npcl(FacialExpression.CHILD_SAD, "Toys! I want toys!").also { stage = END_DIALOGUE } + + 4 -> npcl(FacialExpression.CHILD_NORMAL, "Daddy says you don't like Mr. Jones. Mr. Jones is nice. He brings me toys and sweets.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "Jones is a bad man, Philipe.").also { stage++ } + 6 -> npcl(FacialExpression.CHILD_NORMAL, "You're a bad " + (if (player.isMale) "man" else "lady") + " I don't like you!").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "I'll try and console myself about that disappointment somehow.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.PHILIPE_CARNILLEAN_888) +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/WardrobeRevealDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/WardrobeRevealDialogue.kt new file mode 100644 index 000000000..22234e521 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/WardrobeRevealDialogue.kt @@ -0,0 +1,81 @@ +package content.region.kandarin.ardougne.quest.hazeelcult + +import content.data.Quests +import content.region.kandarin.ardougne.quest.hazeelcult.HazeelCult.Companion.carnilleanArc +import core.api.* +import core.game.dialogue.DialogueFile +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 + +/** + * This dialogue is triggered at stage 5 of Hazeel Cult during the Carnillean Arc by searching the wardrobe for evidence. + */ + +@Initializable +class WardrobeRevealDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, WardrobeRevealDialogueFile()) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return WardrobeRevealDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf() // dialogue is triggered manually + } +} + +class WardrobeRevealDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.HAZEEL_CULT) + when { + (questStage == 5 && carnilleanArc(player!!)) -> { + when (stage) { + // note that it's authentic for the 'wardrobe' to be referred to as 'cupboard' in the dialogue. it used to be a cupboard pre-graphical update. https://youtu.be/CH7i06Noc18?si=zAdxBNlIS92cseBR&t=606 + 0 -> sendDialogue(player!!, "You search the cupboard thoroughly. You find a bottle of poison and a mysterious amulet. You pass your discovery on to Ceril.").also { stage++ } + 1 -> sendPlayerDialogue(player!!, "Ceril!").also { stage++ } + 2 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "What do you want now you scoundrel?", FacialExpression.ANGRY).also { stage++ } + 3 -> sendPlayerDialogue(player!!, "Look what I've found in the cupboard!", FacialExpression.ANGRY).also { stage++ } + 4 -> sendDialogue(player!!, "You hand Ceril the bottle of poison.").also { stage++ } + 5 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "I... I don't believe it! Poison! JONES!").also { stage++ } + 6 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "You called m'lud?").also { stage++ } + 7 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "JONES! Just WHAT is this POISON doing here???").also { stage++ } + 8 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "P-p-poison??? Yeah... uh... well, hmm... Oh, it's for Killing rats.").also { stage++ } // '...for Killing...' is authentic + 9 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "Yes, that's right. Killing rats. I am but a loyal butler sir.").also { stage++ } + 10 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Rats eh? Yes, fair enough. Sorry to have doubted you old boy.").also { stage++ } + 11 -> sendPlayerDialogue(player!!, "Then how about this?").also { stage++ } + 12 -> sendDialogue(player!!, "You show Ceril the amulet from the cupboard.").also { stage++ } + 13 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Wh-what??? I've seen this amulet before... The thieves that broke in... One of them was wearing an identical amulet! Jones! I don't believe it!").also { stage++ } + 14 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Jones! We trusted you! We took you into our home!").also { stage++ } + 15 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "You senile old fool... It was all too easy! I should have killed you and your pathetic family weeks ago!", FacialExpression.ANGRY).also { stage++ } + 16 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "To think we took you in and trusted you... and this is how you repay us...").also { stage++ } + 17 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "GUARD!").also { stage++ } + 18 -> sendDialogue(player!!, "A guard rushes into the house.").also { stage++ } + 19 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "Take him away. Attempted murder and burglary.").also { stage++ } + 20 -> sendNPCDialogue(player!!, NPCs.BUTLER_JONES_890, "Don't think this is the last you have heard of me Ceril. You and your family will pay dearly for this. GLORY TO HAZEEL! ALL PRAISE ZAMORAK!").also { stage++ } + 21 -> sendNPCDialogue(player!!, NPCs.GUARD_887, "Now now. Come along quietly. There'll be no revenge where YOU'RE going - Port Sarim Jail.").also { stage++ } + 22 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "It looks like I am indebted to you sirrah!").also { stage++ } + 23 -> sendPlayerDialogue(player!!, "No problem. We all make mistakes.").also { stage++ } + 24 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "But if it weren't for you... My whole family... we could have been... I apologize for my harshness before.").also { stage++ } + 25 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "The very least I can do is to reward you for your noble efforts, and to offer my sincerest apologies as a Lord and a gentleman.").also { stage++ } + 26 -> sendPlayerDialogue(player!!, "Thank you Lord Ceril.").also { stage++ } + 27 -> sendNPCDialogue(player!!, NPCs.CERIL_CARNILLEAN_885, "No, no, thank YOU! Feel free to stop by anytime adventurer!" + ).also { + end() + finishQuest(player!!, Quests.HAZEEL_CULT) + } + } + } + + (questStage in 0..100) -> { + when(stage) { + 0 -> sendDialogue(player!!, "You search the cupboard thoroughly but find nothing of interest.").also {stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file 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..9c5866a59 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCityListeners.kt @@ -0,0 +1,435 @@ +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{ + setAttribute(player, ARDOUGNE_TELE_ATTRIBUTE, true) + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendItemDialogue(player, Items.A_MAGIC_SCROLL_1505, "You memorise what is written on the scroll.").also { stage++ } + 1 -> sendDialogue(player, "You can now cast the Ardougne Teleport spell provided you have the required runes and magic level.").also { stage = END_DIALOGUE } + } + } + }) + } + 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++ } + 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.handleAutowalkDoor(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.") + } + } + + // https://youtu.be/AG81LH51WME?t=302 + private fun scruffyNote(player: Player) { + val scruffynotes = + arrayOf( + "Got a bncket of nnilk", + "Tlen qrind sorne lhoculate", + "vnith a pestal and rnortar", + "ald the grourd dlocolate to tho milt", + "fnales add 5cme snape gras5", + ) + setInterfaceText(player, scruffynotes.joinToString("
"), 222, 4) + } + + 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/BindingBook.kt b/Server/src/main/content/region/kandarin/dialogue/BindingBook.kt new file mode 100644 index 000000000..6c1aa1171 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/BindingBook.kt @@ -0,0 +1,151 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..fd294e309 100644 --- a/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt +++ b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt @@ -1,8 +1,10 @@ package content.region.kandarin.dialogue +import core.ServerConstants import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic import core.game.node.entity.player.Player import core.game.node.entity.player.link.IronmanMode import core.plugin.Initializable @@ -13,19 +15,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 +39,112 @@ 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), + IfTopic(FacialExpression.NEUTRAL, "I'd like to open a secondary bank account.", 5, ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player)), + IfTopic(FacialExpression.NEUTRAL, "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", 40, hasActivatedSecondaryBankAccount(player)), + Topic(FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 31), + Topic(FacialExpression.NEUTRAL, "I'd like to see my collection box.", 32), + IfTopic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE, !ServerConstants.SECOND_BANK) ) + 5 -> npcl(FacialExpression.ASKING, "Sorry, ${if (player.isMale) "sir" else "ma'am"}, the bank didn't license me for that.").also { stage++ } + 6 -> playerl(FacialExpression.HALF_GUILTY, "Oh, okay, I'll ask a banker who is stationed in an actual bank.").also { stage = END_DIALOGUE } + 10 -> npcl( - 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, - "I'm sorry to hear that, dear ${if (player.isMale) "sir" else "madam"}. " + 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 } @@ -158,10 +165,15 @@ class EniolaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin openInterface(player, Components.BANK_CHARGE_ZMI_619) end() } + + 40 -> { + toggleBankAccount(player) + npcl( FacialExpression.NEUTRAL, "Your active bank account has been switched. You can now access your ${getBankAccountName(player)} account.").also { stage = END_DIALOGUE } + } } return true } override fun getIds(): IntArray = intArrayOf(NPCs.ENIOLA_6362) -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt b/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt new file mode 100644 index 000000000..54fe778fb --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt @@ -0,0 +1,340 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..3ec20f532 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ShamansTomeBook.kt @@ -0,0 +1,57 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..ed2bc611e --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ThormacDialogue.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.dialogue + +import content.region.kandarin.quest.scorpioncatcher.SCThormacDialogue +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs +import content.data.Quests + +@Initializable +class ThormacDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds(): IntArray { + return intArrayOf(NPCs.THORMAC_389) + } + + companion object { + const val COMPLETED_QUEST = 1000 + const val ENCHANT_DIALOGUE = 2000 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (isQuestComplete(player, Quests.SCORPION_CATCHER)){ + npc(FacialExpression.HAPPY, "Thank you for rescuing my scorpions.").also {stage = COMPLETED_QUEST} + } + else{ + openDialogue(player, SCThormacDialogue(getQuestStage(player, Quests.SCORPION_CATCHER)), npc) + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + COMPLETED_QUEST -> showTopics( + Topic(FacialExpression.HAPPY, "That's okay.", END_DIALOGUE), + Topic(FacialExpression.NEUTRAL, "You said you'd enchant my battlestaff for me", ENCHANT_DIALOGUE) + ) + + ENCHANT_DIALOGUE -> { + npcl(FacialExpression.HAPPY, "Yes, it'll cost you 40,000 coins for the materials needed though. " + + "Which sort of staff did you want enchanting?").also { stage++ } + } + ENCHANT_DIALOGUE + 1 -> { + end() + player.interfaceManager.openComponent(332) + } + + } + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/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..efff701ef 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt @@ -153,7 +153,7 @@ class ChompyBirdNPC : AbstractNPC, InteractionListener { val bird = node.asNpc() if (!bird.getAttribute("plucked", false)) { - addItem(player, Items.FEATHER_314, RandomFunction.random(25, 32)) + addItemOrDrop(player, Items.FEATHER_314, RandomFunction.random(25, 32)) produceGroundItem(player, Items.BONES_526, 1, bird.location) produceGroundItem(player, Items.RAW_CHOMPY_2876, 1, bird.location) bird.clear() @@ -164,7 +164,7 @@ class ChompyBirdNPC : AbstractNPC, InteractionListener { return@on true } - on(Items.OGRE_BOW_2883, IntType.ITEM, "check kills") { player, _ -> + on(intArrayOf(Items.OGRE_BOW_2883, Items.COMP_OGRE_BOW_4827), IntType.ITEM, "check kills") { player, _ -> val amount = player.getAttribute("chompy-kills", 0) sendDialogue(player, "You have killed $amount chompy birds.") return@on true diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt index 94173377d..f0f1ca4ff 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt @@ -8,6 +8,7 @@ import core.plugin.Initializable import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player import core.game.world.map.Location +import content.data.Quests @Initializable class RantzNPC : AbstractNPC { @@ -27,7 +28,7 @@ class RantzNPC : AbstractNPC { val chompy = findLocalNPC(this, NPCs.CHOMPY_BIRD_1550) as? ChompyBirdNPC ?: return val owner = getAttribute(chompy, "owner", null) ?: return - val quest = owner.questRepository.getQuest("Big Chompy Bird Hunting") + val quest = owner.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) if (quest.getStage(owner) !in 40..50 || chompy.getAttribute("attacked", false)) return diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt new file mode 100644 index 000000000..b93f3a8ed --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/BartenderDialogueFile.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import org.rs09.consts.Items + +class BartenderDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> dialogueNum == 1 } + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeAskedAboutTankard, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .playerl("Hello there, I found this tankard in an ogre tomb cavern. It has the emblem of this Inn on it and I wondered if you knew anything about it?") + .npcl("Oh yes, this is Brentle's mug...I'm surprised he left it just lying around down some cave. He's quite protective of it.") + .playerl("Brentle you say? So you knew him then?") + .npcl("Yeah, this belongs to 'Brentle Vahn', he's quite a common customer, though I've not seen him in a while.") + .npcl(FacialExpression.THINKING, "He was talking to some shifty looking wizard the other day. I don't know his name, but I'd recognise him if I saw him.") + .playerl("Hmm, I'm sorry to tell you this, but Brentle Vahn is dead - I believe he was murdered.") + .npcl(FacialExpression.EXTREMELY_SHOCKED, "Noooo! I'm shocked...") + .npcl("...but not surprised. He was a good customer...but I knew he would sell his sword arm and do many a dark deed if paid enough.") + .npcl(FacialExpression.SAD, "If you need help bringing the culprit to justice, you let me know.") + .endWith { _, player -> + setAttribute(player, ZogreFleshEaters.attributeAskedAboutTankard, true) + } + branch.onValue(1) + .playerl("Hello again. Can you tell me what you know about this tankard again please?") + .npcl("Oh yes, Brentle's tankard. Yeah, you've shown me this already. It belonged to Brentle Vahn, he was quite a common customer, though I've not seen him in a while.") + .npcl("He was talking to some shifty looking wizard the other day. I don't know his name, but I'd recognise him if I saw him.") + .npcl(FacialExpression.SAD, "If you need help bringing the culprit to justice, you let me know.") + .end() + } + + b.onPredicate { _ -> dialogueNum == 2 } + .iteml(Items.SITHIK_PORTRAIT_4814, "You show the portrait to the Inn keeper.") + .npcl("Yeah, that's the guy who was talking to Brentle Vahn the other day! Look at those eyes, never a more shifty looking pair will you ever see!") + .playerl("Hmm, you've just identified the man who I think sent Brentle Vahn to his death.") + .playerl("I'm trying to bring him to justice with the Wizards' Guild grand secretary. Do you think you could sign this portrait to say that he was talking to Brentle Vahn.") + .npcl("I can and I will!") + .betweenStage { df, player, _, _ -> + if (removeItem(player, Items.SITHIK_PORTRAIT_4814)) { + addItemOrDrop(player, Items.SIGNED_PORTRAIT_4816) + } + } + .iteml(Items.SIGNED_PORTRAIT_4816, "The Dragon Inn bartender signs the portrait.") + .playerl("Many thanks for your help, it's really very good of you.") + .npcl("Not at all, just doing my part.") + .end() + + + b.onPredicate { _ -> dialogueNum == 3 } + .iteml(Items.SITHIK_PORTRAIT_4815, "You show the sketch to the Inn keeper.") + .npcl("Who's that? I mean, I guess it's a picture of a person isn't it? Sorry...you've got me? And before you ask, you're not putting it up on my wall!") + .playerl("It's a portrait of Sithik Ints...don't you recognise him?") + .npcl("I'm sorry, I really am, but I just don't see it...can you make a better picture?") + .playerl("I'll try...") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt new file mode 100644 index 000000000..7e518f0ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrishDialogue.kt @@ -0,0 +1,184 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.region.kandarin.quest.templeofikov.TempleOfIkov +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GrishDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GrishDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GrishDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRISH_2038) + } +} +class GrishDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0) + .playerl(FacialExpression.FRIENDLY, "Hello there, what's going on here?") + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...wha's you's doing here? Yous be cleverer to be running so da sickies from da zogres don't dead ya.") + + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("I'm just looking around thanks.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature won'ts see muchly in dis place...just da zogries coming wiv da sickies.") + .goto(returnJoin) + optionBuilder.option_playerl("What do you mean sickies?") + .npc(FacialExpression.OLD_NORMAL, "Da zogries comin wiv da sickies...yous get bashed by da", "zogries and get da sickies...den you gonna be like da", "zogries.") + .playerl(FacialExpression.FRIENDLY, "Sorry, I just don't understand...") + .betweenStage { df, player, _, _ -> + animate(npc!!, 2090) + setAttribute(player, ZogreFleshEaters.attributeAskedAboutSickies, true) + } + .npc(FacialExpression.OLD_NORMAL, "Da sickies is when yous creature goes like orange till", "green and then goes 'Urggghhhh!'", "~ Grish imitates falling down with only the white of his", "eyes visible. ~") + .goto(returnJoin); + optionBuilder.option_playerl("What are Zogres?") + .npcl(FacialExpression.OLD_NORMAL, "a Zogres are da bigun nasties wiv da sickies, deys old pals of Grish but deys jig in Jiggig when dey's full home is deep in da dirt, dey's is not da same dead'uns like was before.") + .goto(returnJoin); + optionBuilder.optionIf("Can I help in any way?") { player -> return@optionIf getAttribute(player, ZogreFleshEaters.attributeAskedAboutSickies, false) } + .playerl(FacialExpression.FRIENDLY, "Can I help in any way?") + .branch { player -> + return@branch if (ZogreFleshEaters.requirements(player)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Sorry, yous creatures, but yous is too green behind da ears for dis job Grish finks.") + .playerl(FacialExpression.ANGRY, "No, I'm not!") + .npcl(FacialExpression.OLD_ANGRY1, "Yes you are!") + .playerl(FacialExpression.ANGRY, "No, I'm not!") + .npcl(FacialExpression.OLD_ANGRY1, "Yes you are and that's final!") + .end() + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Yes creatures...yous does good fings for Grish and learn why Zogries at Jiggig and den get da Zogries back in da ground.") + .playerl("Oh, so you want me to find out why the Zogres have appeared and then find a way of burying them?") + .npcl(FacialExpression.OLD_NORMAL, "Is what Grish says! But dis is da biggy danger fing yous creatures...yous be geddin' sickies most surely...yous needs be ready..wiv da foodies un da glug-glugs.") + .playerl("Right, so you think there's a good chance that I can get ill from this, so I need to get some food and something to drink?") + .npcl(FacialExpression.OLD_NORMAL, "Yea creatures, yous just say what Grish says...not know own wordies creature?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hmm, sorry, it sounds a bit too dangerous.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature is not a stoopid one...stays out of dere, like clever Grish. Yous can paint circles on chest and be da Shaman too!") + .playerl("Hmm, is it too late to reconsider?") + .end() + optionBuilder.option_playerl("Ok, I'll check things out then and report back.") + .npcl(FacialExpression.OLD_NORMAL, "Is yous creatures really, really sure yous wanna do dis creatures..we's got no glug-glugs for da sickies? We's knows nuffin for da going of da sickies?") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Yes, I'm really sure!") + .npcl(FacialExpression.OLD_NORMAL,"Dats da good fing yous creature...yous does Grish a good fing. But yous know dat yous get sickies and mebe get dead!") + .playerl("If that's your idea of a pep talk, I have to say that it leaves a lot to be desired.") + .npcl(FacialExpression.OLD_NORMAL,"Yous creatures is alus says funny stuff...speaks proper like Grish!") + .manualStage() { df, player, _, _ -> + sendDoubleItemDialogue(player, Items.COOKED_CHOMPY_2878, Items.SUPER_RESTORE3_3026, "Grish hands you some food and two potions.") + } + .npcl(FacialExpression.OLD_NORMAL,"Der's yous go creatures...da best me's do for yous...and be back wivout da sickies.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 0) { + // Trying to prevent players from spamming to get more super restores. + addItemOrDrop(player, Items.COOKED_CHOMPY_2878, 3) + addItemOrDrop(player, Items.SUPER_RESTORE3_3026, 2) + setQuestStage(player, ZogreFleshEaters.questName, 1) + } + } + optionBuilder2.option_playerl("Hmm, sorry, it sounds a bit too dangerous.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creature is not a stoopid one...stays out of dere, like clever Grish. Yous can paint circles on chest and be da Shaman too!") + .end() + } + } + + } + optionBuilder.option_playerl("Sorry, I have to go.") + .end() + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 1,2,3,4,5,6) + .npcl(FacialExpression.OLD_NORMAL, "Yous creature dun da fing yet? Da zogries going in da dirt full home?") + .playerl("Nope, I haven't figured out why the zogres are here yet.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 7) + .npcl(FacialExpression.OLD_NORMAL,"Yous creature dun da fing yet? Da zogries going in da ground?") + .playerl("I found who's responsible for the Zogres being here.") + .npcl(FacialExpression.OLD_NORMAL,"Where is da creature? Me's wants to squeeze him till he's a deadun...") + .playerl("The person responsible is a wizard named 'Sithik Ints' and he's going to be in serious trouble. He told me that the spell which raised the zogres from the ground will last forever.") + .playerl("I'm sorry to say, but you'll have to move the site of your ceremonial dancing somewhere else.") + .npcl(FacialExpression.OLD_NORMAL,"Dat is da bad fing creature...we's needs new Jiggig for da fallin' down jig.") + .playerl("Yes, that's right, you'll need to create a new ceremonial dance area.") + .npcl(FacialExpression.OLD_NORMAL,"Urghhh...not good fing creature, yous gotta get da ogrish old fings for da making new jiggig special. You's creature needs da key for getting in da low bury place.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.OGRE_GATE_KEY_4839) + } + .iteml(Items.OGRE_GATE_KEY_4839, "Grish gives you a crudely crafted key.") + .playerl("Oh, so you want me to go back in there and look for something for you?") + .npcl(FacialExpression.OLD_NORMAL,"Yeah creature, yous gotta get da ogrish old fings for da making new jiggig and proper in da special way.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 7) { + setQuestStage(player, ZogreFleshEaters.questName, 8) + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 8) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .branch { player -> + return@branch if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("Nope, not yet.") + .npcl(FacialExpression.OLD_NORMAL, "Yous gets 'em quick tho, cos we'ze wonna do da new Jiggig place...") + .end() + branch.onValue(0) + .playerl("I lost the key you gave me.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.OGRE_GATE_KEY_4839) + } + .iteml(Items.OGRE_GATE_KEY_4839, "Grish gives you a crudely crafted key.") + .end() + } + + b.onQuestStages(ZogreFleshEaters.questName, 9) + .branch { player -> + return@branch if (inInventory(player, Items.OGRE_ARTEFACT_4818)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .playerl("No sorry, I don't have them yet.") + .npcl(FacialExpression.OLD_NORMAL, "Yous creatures get dem for me soon doh, yes?") + .end() // There's all the default dialogue here, but I'm lazy again. + + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Hey, you's creature got da old fings?") + .playerl("Yeah, I have them here!") + .npcl(FacialExpression.OLD_NORMAL, "Dat is da goodly fing yous creature, now's we's can make da new Jiggig place away from zogries! Yous been da big helpy fing yous creature, Grish wishin' yous good stuff for da next fings for creature.") + .npcl(FacialExpression.OLD_HAPPY, "~ Grish seems very pleased about the return of the artefacts. ~") + .playerl("Thanks, that's very nice of you!") + .endWith { _, player -> + if (removeItem(player, Items.OGRE_ARTEFACT_4818)) { + if (getQuestStage(player, ZogreFleshEaters.questName) == 9) { + finishQuest(player, ZogreFleshEaters.questName) + } + } + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 100) + .playerl("How's everything going now?") + .npcl(FacialExpression.OLD_NORMAL,"All da zogries stayin' in da oldie Jiggig, we's gonna do da new Jiggig someways else. Yous creature da good- un for geddin' da oldie fings...") + // More default dialogue, but lazy. + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt new file mode 100644 index 000000000..6081288cf --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/GrugDialogue.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class GrugDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GrugDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GrugDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GRUG_2041) + } +} +class GrugDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npcl(FacialExpression.OLD_NORMAL, "Ukk...I's dun fer...me's don't feel legsies anymore!") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt new file mode 100644 index 000000000..718e26efc --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/JiggigListeners.kt @@ -0,0 +1,27 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.interaction.InteractionListener +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class JiggigListeners : InteractionListener { + override fun defineListeners() { + on(Scenery.OGRE_COFFIN_6848, SCENERY, "open") { player, node -> + // https://youtu.be/HnRcW2iM8es + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.OGRE_COFFIN_6890, 10) + return@on true + } + + on(NPCs.UGLUG_NAR_2039, NPC, "trade") { player, node -> + if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { + openNpcShop(player, NPCs.UGLUG_NAR_2039) + } else { + sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.", FacialExpression.OLD_NORMAL) + } + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt new file mode 100644 index 000000000..6f5193687 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/OgreGuardDialogue.kt @@ -0,0 +1,73 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class OgreGuardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return OgreGuardDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, OgreGuardDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OGRE_GUARD_2042) + } +} +class OgreGuardDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(ZogreFleshEaters.questName, 0) + .npcl(FacialExpression.OLD_NORMAL, "Yous needs ta stay away from dis place... yous get da sickies and mebe yous goes to dead if yous da unlucky fing.") + .playerl("Ok, thanks.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 1) + .npcl(FacialExpression.OLD_NORMAL, "Yous needs ta stay away from dis place... yous get da sickies and mebe yous goes to dead if yous da unlucky fing.") + .playerl(FacialExpression.FRIENDLY, "But Grish has asked me to look into this place and find out why all the undead ogres are here.") + .npcl(FacialExpression.OLD_NORMAL, "Ok, dat is da big, big scary, danger fing! You's sure you's wants to go in?") + .playerl(FacialExpression.FRIENDLY, "Yes, I'm sure.") + .npcl(FacialExpression.OLD_NORMAL, "Ok, I opens da stoppa's for yous creature.") + .endWith { _, player -> + lock(player, 4) + face(npc!!, Location(2456, 3049, 0)) + // Lesson learnt here, endWith kills the npc object so tie the queueScript with the npc instead, not the player. + queueScript(npc!!, 2, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animate(npc!!, 2102) + return@queueScript delayScript(npc!!, Animation(2102).duration) + } + 1 -> { + if(getQuestStage(player, ZogreFleshEaters.questName) == 1) { + setQuestStage(player, ZogreFleshEaters.questName, 2) + } + setVarbit(player, ZogreFleshEaters.varbitGateBashed, 1) + unlock(player) + face(npc!!, player.location) + sendNPCDialogue(player, NPCs.OGRE_GUARD_2042, "Ok der' yous goes!", FacialExpression.OLD_NORMAL) + return@queueScript stopExecuting(npc!!) + } + else -> return@queueScript stopExecuting(npc!!) + } + } + } + + b.onQuestStages(ZogreFleshEaters.questName, 2,3,4,5,6,7,8,9,10,100) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous tryin' not to get da sickies else yous be da sick-un and mebe get to be a dead-un if yous be da unlucky fing.") + .playerl(FacialExpression.FRIENDLY, "Don't worry, I know how to take care of myself.") + .end() + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt new file mode 100644 index 000000000..6eee48feb --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/PilgDialogue.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class PilgDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return PilgDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PilgDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PILG_2040) + } +} +class PilgDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npcl(FacialExpression.OLD_NORMAL, "Dey got me in da belly, mees gutsies feel like had a dead dead dog dinner.") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt new file mode 100644 index 000000000..789251861 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SithikIntsDialogue.kt @@ -0,0 +1,388 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +/** This NPC is a scenery. */ +//@Initializable +//class SithikIntsDialogue (player: Player? = null) : DialoguePlugin(player) { +// override fun newInstance(player: Player): DialoguePlugin { +// return SithikIntsDialogue(player) +// } +// override fun handle(interfaceId: Int, buttonId: Int): Boolean { +// openDialogue(player, SithikIntsDialogueFile(), npc) +// return false +// } +// override fun getIds(): IntArray { +// return intArrayOf(NPCs.SITHIK_INTS_2061, NPCs.SITHIK_INTS_2062) +// } +//} +class SithikIntsDialogue : InteractionListener { + override fun defineListeners() { + on(Scenery.SITHIK_INTS_6888, SCENERY, "talk-to") { player, node -> + openDialogue(player, SithikIntsDialogueFile(), NPC(NPCs.SITHIK_INTS_2061)) + return@on true + } + on(Scenery.SITHIK_INTS_6889, SCENERY, "talk-to") { player, node -> + openDialogue(player, SithikIntsOgreFormDialogueFile(), NPC(NPCs.SITHIK_INTS_2062)) + return@on true + } + + on(Scenery.DRAWERS_6875, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && + (!inInventory(player, Items.BOOK_OF_PORTRAITURE_4817) || + !inInventory(player, Items.PAPYRUS_970) || + !inInventory(player, Items.CHARCOAL_973) + )) { + if (hasSpaceFor(player, Item(Items.BOOK_OF_PORTRAITURE_4817, 3))) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + if (!inInventory(player, Items.PAPYRUS_970) && !inInventory(player, Items.CHARCOAL_973)) { + sendDoubleItemDialogue(player, Items.CHARCOAL_973, Items.PAPYRUS_970, "You find some charcoal and papyrus.") + addItemOrDrop(player, Items.PAPYRUS_970) + addItemOrDrop(player, Items.CHARCOAL_973) + stage++ + } else if (!inInventory(player, Items.PAPYRUS_970)) { + sendItemDialogue(player, Items.PAPYRUS_970, "You find some papyrus.") + addItemOrDrop(player, Items.PAPYRUS_970) + stage++ + } else if (!inInventory(player, Items.CHARCOAL_973)) { + sendItemDialogue(player, Items.CHARCOAL_973, "You find some charcoal.") + addItemOrDrop(player, Items.CHARCOAL_973) + stage++ + } else { + sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You also find a book on portraiture.") + addItemOrDrop(player, Items.BOOK_OF_PORTRAITURE_4817) + stage = END_DIALOGUE + } + } + 1 -> { + sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You also find a book on portraiture.") + addItemOrDrop(player, Items.BOOK_OF_PORTRAITURE_4817) + stage = END_DIALOGUE + } + } + } + }) + } else { + sendDialogue(player, "You see some items in the drawer, but you need 3 free inventory spaces to take them.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + on(Scenery.CUPBOARD_6876, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && !inInventory(player, Items.NECROMANCY_BOOK_4837)) { + if (hasSpaceFor(player, Item(Items.NECROMANCY_BOOK_4837))) { + addItemOrDrop(player, Items.NECROMANCY_BOOK_4837) + sendItemDialogue(player, Items.NECROMANCY_BOOK_4837, "You find a book on Necromancy.") + setAttribute(player, ZogreFleshEaters.attributeFoundNecromanticBook, true) + } else { + sendDialogue(player, "You see an item in the cupboard, but you don't have space to put it in your inventory.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + onUseWith(IntType.SCENERY, Items.NECROMANCY_BOOK_4837, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Aha! A necromantic book! What's this doing here then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.NECROMANCY_BOOK_4837, "You show the Necromantic book to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh..I'm not quite sure actually...where did you find that then?").also { stage++ } + 3 -> sendPlayerDialogue(player,"I found it in this cupboard! What do you have to say for yourself?").also { stage++ } + 4 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh yes, that's right...I remember now. It's for my research, there's nothing really dangerous about it, unless it falls into the wrong hands. I'm sure it's pretty safe with me.").also { stage++ } + 5 -> sendPlayerDialogue(player,"Hmmm, likely story!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + on(Scenery.WARDROBE_6877, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) <= 2) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendPlayerDialogue(player, "Erk! I'd better not start rifling through peoples things without permission.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getQuestStage(player, ZogreFleshEaters.questName) in 3..4 && !inInventory(player, Items.BOOK_OF_HAM_4829)) { + if (hasSpaceFor(player, Item(Items.BOOK_OF_HAM_4829))) { + addItemOrDrop(player, Items.BOOK_OF_HAM_4829) + sendItemDialogue(player, Items.BOOK_OF_HAM_4829, "You find a book on Philosophy written by the 'Human's Against Monsters' leader, Johanhus Albrect.") + setAttribute(player, ZogreFleshEaters.attributeFoundHamBook, true) + } else { + sendDialogue(player, "You see an item in the wardrobe, but you don't have space to put it in your inventory.") + } + } else { + sendMessage(player, "You search but find nothing of significance.") + } + return@on true + } + + onUseWith(IntType.SCENERY, Items.BOOK_OF_HAM_4829, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"What's this then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.BOOK_OF_HAM_4829, "You show the HAM book to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "What do you mean? It's a book by the respected HAM leader Johanhus Ulsbrecht, that man speaks for a lot of people who are unhappy with the current state of affairs.").also { stage++ } + 3 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Can you honestly tell me that you've not had to fight for your life against the odd monster or two?").also { stage++ } + 4 -> sendPlayerDialogue(player,"Hmm, that may be true, but I don't universally hate all monsters, whereas I have a sneaking suspicion that you do...and ogres in particular!").also { stage++ } + 5 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmm, that's an interesting theory, care to back it up with any facts?").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.PAPYRUS_970, Scenery.SITHIK_INTS_6888) { player, used, with -> + if(inInventory(player, Items.CHARCOAL_973)) { + if (removeItem(player, used)) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Oh lovely! You're making my portrait! Let me see it afterwards!", FacialExpression.FRIENDLY).also { stage++ } + 1 -> sendDialogue(player, "You begin sketching the irritable Sithik.").also { animate(player, 909); stage++ } + 2 -> { + val skill = Skills.CRAFTING + val level: Int = getDynLevel(player, skill) + getFamiliarBoost(player, skill) + val ratio = RandomFunction.getSkillSuccessChance(0.0, 80.0, level) + + if (ratio > 0.5) { + // Passed + sendItemDialogue(player, Items.SITHIK_PORTRAIT_4814, "You get a portrait of Sithik.") + addItemOrDrop(player, Items.SITHIK_PORTRAIT_4814) + } else { + // Failed + sendItemDialogue(player, Items.SITHIK_PORTRAIT_4815, "You get a portrait of Sithik.") + addItemOrDrop(player, Items.SITHIK_PORTRAIT_4815) + } + setAttribute(player, ZogreFleshEaters.attributeMadePortrait, true) + stage = END_DIALOGUE + } + } + } + }) + } + } else { + sendDialogue(player, "You have no charcoal with which to sketch this subject.") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BOOK_OF_PORTRAITURE_4817, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Oh, so explain this then?").also { stage++ } + 1 -> sendItemDialogue(player, Items.BOOK_OF_PORTRAITURE_4817, "You show the book on portraiture to Sithik.").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "It's my hobby...I'm interested in portraiture, but all art in general. It's fun, you should try it.").also { stage++ } + 3 -> sendPlayerDialogue(player,"How do I do it...").also { stage++ } + 4 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Well...you could start by reading the book!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.SITHIK_PORTRAIT_4814, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player, "Here you go, what do you think?").also { stage++ } + 1 -> sendItemDialogue(player, Items.SITHIK_PORTRAIT_4814, "You show the sketch...").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmmm, well it's not the most flattering of portraits, but I like the 'honesty' of the work...well done.").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.SITHIK_PORTRAIT_4815, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player, "Here you go, what do you think?").also { stage++ } + 1 -> sendItemDialogue(player, Items.SITHIK_PORTRAIT_4815, "You show the sketch...").also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hmmm, well it's an interesting interpretation, but not really classic realist representation is it? It's not my favourite, but I like the 'truth' of the work...well done.").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + on(Items.CUP_OF_TEA_4838, ITEM, "take") { player, node -> + sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Hey! What do you think you're doing? Leave my tea alone!", FacialExpression.ANNOYED) + return@on true + } + + onUseWith(IntType.SCENERY, Items.STRANGE_POTION_4836, Scenery.SITHIK_INTS_6888) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendPlayerDialogue(player,"Here, try some of this potion, it'll make you feel better!").also { stage++ } + 1 -> sendNPCDialogue(player, NPCs.SITHIK_INTS_2061, "Err, yuck...no way am I taking any potions or medication off you...I don't trust you!").also { stage = END_DIALOGUE } + } + } + }) + return@onUseWith true + } + + onUseWith(IntType.GROUNDITEM, Items.STRANGE_POTION_4836, Items.CUP_OF_TEA_4838) { player, _, with -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 5) { + if (removeItem(player, Items.STRANGE_POTION_4836)) { + sendItemDialogue(player, Items.STRANGE_POTION_4836, "You pour some of the potion into the cup. Zavistic said it may take some time to have an effect.") + addItemOrDrop(player, Items.SAMPLE_BOTTLE_3377) + } + setQuestStage(player, ZogreFleshEaters.questName, 6) + } + return@onUseWith true + } + } +} + +class SithikIntsDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0, 1, 2) + .npcl(FacialExpression.ANNOYED, "Hey... who gave you permission to come in here! Get out, get out I say.") + .playerl("Alright, alright... keep your night cap on.") + .end() + + b.onQuestStages(ZogreFleshEaters.questName, 3, 4, 5, 6) + .npcl(FacialExpression.ANNOYED, "Hey... who gave you permission to come in here! Get out, get out I say.") + .playerl("Zavistic Rarve said that I could come and talk to you and ask you a few questions.") + .betweenStage { df, player, _, _ -> + if (getQuestStage(player, ZogreFleshEaters.questName) == 3) { + setQuestStage(player, ZogreFleshEaters.questName, 4) + } + } + .npcl(FacialExpression.ANNOYED, "Oh, Zavistic...why...why would he send you to me?") + + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Do you know anything about the undead ogres at Jiggig?") + .npcl("Er...undead ogres...no, sorry, no idea what you're talking about there.") + .playerl("Hmm, is that right...") + .npcl("Well, yes, yes it is. If I knew something, I'd tell you.") + .npcl("Anyway, dead ogres you say? How strange? That must be a strange sight?") + .playerl("Very well, if you don't know anything about it, you won't mind if I look around then?") + .npcl("Well,err....well, actually yes I do mind...it's my place and I don't want strangers going through my things.") + .goto(returnJoin) + + optionBuilder.option_playerl("What do you do?") + .npcl("I'm a scholarly student of the magical arts. When I was younger I used to be an adventurer, probably just like yourself. But I lost interest in the constant fighting, looting and gaining abilities.") + .npcl("Instead I decided to focus my attention and time to study the purer form of the lost arts.") + .playerl("The lost arts? What are they?") + .npcl("Ignorant people call them the 'dark arts'. I'm talking about Necromancy, the power to bring the dead back to life - the power of the gods! Surely the most awesome power known to man.") + .playerl("Hmm, well I guess I must be an ignorant person then, because bringing the dead back to life sounds very unnatural.") + .goto(returnJoin) + + optionBuilder.option_playerl("Do you mind if I look around?") + .npcl("Well,err....well, actually yes I do mind...it's my place and I don't want strangers going through my things.") + .playerl("Well, I'm going to have a look around anyway, if you're not involved in this whole thing, you won't have anything to hide.") + .npcl("Why, if I was a few years younger I'd give you a good hiding!") + .playerl("I'm sure!") + .goto(returnJoin) + + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + builder.goto(returnJoin) + } + } +} + +class SithikIntsOgreFormDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(ZogreFleshEaters.questName, 7,8,9,100) + /* + There are some after first time dialogue, but who reads this... + .npcl("Arghhhh..what do you want now...you've turned me into a beast!") + .playerl("I've got some questions for you...and you'd better answer them well or else!") + .npcl("Ok, ok, I'll tell you anything, just turn me back into a human again!") + */ + .npcl(FacialExpression.OLD_DEFAULT, "Arghhhh..what's happened to me...you beast!") + .playerl("It's your own fault, you shouldn't have lied about your involvement with the undead Ogres at Jiggig. The potion will wear off once you've told the truth!") + .npcl(FacialExpression.OLD_DEFAULT, "Ok, ok, I admit it, I got Brentle Vahn to cast the spell to put an end to those awful Ogres...they're just disgusting creatures...") + .playerl("Ok, that's a start...now I want some answers.") + .let { builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option("How do I remove the effects of the spell from the area?") + .playerl("How do I remove the effects of the spell from the area? The ogres want to get their ceremonial dance area back and can't do that with undead walking all over it.") + .npcl(FacialExpression.OLD_DEFAULT, "Unfortunately you can't. The spell is permanent, it will last forever, the only option you have is to move the ceremonial area.") + .playerl("You're an evil man and I'm going to make you pay for this...you can stay like that forever as far as I'm concerned.") + .npcl(FacialExpression.OLD_DEFAULT, "No...no, let me try to make amends...please I can help you. Just don't leave me like this.") + .goto(returnJoin) + + optionBuilder.option_playerl("How do I get rid of the undead ogres?") + .npcl(FacialExpression.OLD_DEFAULT, "Ok, similar spells have been cast before and the only way to deal with the resulting creatures is to cordon off the area and not go in there again.") + .npcl(FacialExpression.OLD_DEFAULT, "The undead creatures usually manifest some sort of disease so it's best to attack them from a distance with a ranged weapon.") + .npcl(FacialExpression.OLD_DEFAULT, "Normal missiles like arrows and darts do very little damage to them because they're designed to destroy internal organs. This is a waste of time with undead creatures like undead ogres.") + .playerl("Yeah, clearly so what should we use?") + .npcl(FacialExpression.OLD_DEFAULT, "From my research it looks like a flat ended arrow was designed called a 'Brutal arrow'. This does large amounts of crushing damage to the creature. You can make them by using larger arrows.") + .npcl(FacialExpression.OLD_DEFAULT, "I think some Ogre hunters make them. But instead of adding an arrow tip, you hammer a large nail into the end of the shaft.") + .goto(returnJoin) + + optionBuilder.option_playerl("How do I get rid of the disease?") + .npcl(FacialExpression.OLD_DEFAULT, "My research shows that two jungle based herbs can be used, one is found near river tributaries and looks like a vine, the other is found in caves and grows on the wall.") + .npcl(FacialExpression.OLD_DEFAULT, "It's quite well camouflaged so it's unlikely that you'll find it.") + .playerl("We'll see about that!") + .goto(returnJoin) + + optionBuilder.option_playerl("Sorry, I have to go.") + .npcl(FacialExpression.OLD_DEFAULT, "But...you can't just leave me here like this!") + .end() + } + builder.goto(returnJoin) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt new file mode 100644 index 000000000..54363e66d --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt @@ -0,0 +1,48 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.getOrStartTimer +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class SkogreBehavior : NPCBehavior(*skogreIds) { + companion object { + @JvmField + val skogreIds = intArrayOf( + NPCs.SKOGRE_2050, + NPCs.SKOGRE_2056, + NPCs.SKOGRE_2057, + ) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt new file mode 100644 index 000000000..95fab62bc --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt @@ -0,0 +1,100 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.global.handlers.item.equipment.special.DragonfireSwingHandler +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.timer.impl.Disease +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class SlashBashBehavior : NPCBehavior(NPCs.SLASH_BASH_2060) { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + if (victim is Player) { + val disease = getOrStartTimer(victim, 25) + disease.hitsLeft = 25 + } + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { + super.onDropTableRolled(self, killer, drops) + if (killer is Player && getQuestStage(killer, ZogreFleshEaters.questName) == 8) { + drops.add(Item(Items.ZOGRE_BONES_4812, 2)) + drops.add(Item(Items.OURG_BONES_4834, 3)) + drops.add(Item(Items.OGRE_ARTEFACT_4818)) + setQuestStage(killer, ZogreFleshEaters.questName, 9) + removeAttribute(killer, ZogreFleshEaters.attributeSlashBashInstance) + } + } + + var clearTime = 0 + override fun tick(self: NPC): Boolean { + val player: Player? = getAttribute(self, "target", null) + // You have 500 ticks to kill this guy + if (clearTime++ > 500) { + poofClear(self) + clearTime = 0 + if (player != null) { + removeAttribute(player, ZogreFleshEaters.attributeSlashBashInstance) + } + + } + return true + } + + /** MELEE Swing */ + private val COMBAT_HANDLER = MultiSwingHandler(SwitchAttack(CombatStyle.MELEE.swingHandler, Animation(359))) + /** RANGE Swing (Projectile) */ + private val COMBAT_HANDLER_FAR = MultiSwingHandler(SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(359), Graphics(499))) + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + val victim = self.properties.combatPulse.getVictim() ?: return original + if (victim !is Player) return original + + return if (victim.location.getDistance(self.location) >= 2) + COMBAT_HANDLER_FAR + else + COMBAT_HANDLER + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt new file mode 100644 index 000000000..aab0bfee9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/UglugNarDialogue.kt @@ -0,0 +1,78 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class UglugNarDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return UglugNarDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, UglugNarDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.UGLUG_NAR_2039) + } +} +class UglugNarDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 0) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hey, what's going on here?") + .npcl(FacialExpression.OLD_NORMAL, "Dem's dead ogre's come out of the ground...dey's makin' da rest of us into sick-ums ...and dead-uns.") + .playerl("That doesn't sound good!") + .npcl(FacialExpression.OLD_NORMAL, "Grish want's da person go down der - see what's what!") + .end() + optionBuilder.option_playerl("What are you selling?") + .npcl(FacialExpression.OLD_NORMAL, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.") + .end() + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + + b.onQuestStages(ZogreFleshEaters.questName, 1,2,3,4,5,6,7,8,9,10,100) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Hello again.") + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...yous did good fings gedin that glug-glugs for da sickies! All is ogries pepels are not gettin dead cos of you.") + .end() + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Hey yous creature...yous still here?") + .playerl("Yeah, I'm going to help Grish by figuring out what went on here.") + .npcl(FacialExpression.OLD_NORMAL, "If yous finds somefin for da sickies, yous brings to me...and I's give you bright pretties, den me make more for alls pepels.") + .playerl("Hmm, ok. I'll try to bear that in mind.") + .end() + } + + optionBuilder.option_playerl("What are you selling?") + .branch { player -> + return@branch if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl(FacialExpression.OLD_NORMAL, "Me's showin' you da stufsies for yous creatures!") + .endWith { _, player -> + openNpcShop(player, npc!!.id) + } + branch.onValue(0) + .npcl(FacialExpression.OLD_NORMAL, "Me's not got no glug-glugs to sell, yous bring me da sickies glug-glug den me's open da stufsies for ya.") + .end() + } + + optionBuilder.option_playerl("Ok, thanks.") + .end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt new file mode 100644 index 000000000..1954334b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZavisticRarveDialogueFile.kt @@ -0,0 +1,290 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Items + +class ZavisticRarveDialogueFile : DialogueBuilderFile() { + + companion object { + private fun hasEvidences(player: Player): Int { + var count = 0 + if (inInventory(player, Items.NECROMANCY_BOOK_4837)) { count++ } + if (inInventory(player, Items.BOOK_OF_HAM_4829)) { count++ } + if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { count++ } + if (inInventory(player, Items.SIGNED_PORTRAIT_4816)) { count++ } + return count + } + + fun dialogueBlackPrismAndTornPage(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("There's some undead ogre activity over at Jiggig, I've found some clues, I wondered if you'd have a look at them.") + .manualStage() { df, player, _, _ -> + sendDoubleItemDialogue(player, Items.BLACK_PRISM_4808, Items.TORN_PAGE_4809, "You show the prism and the necromantic half page to the aged wizard.") + } + .npcl("Hmmm, now this is interesting! Where did you get these from?") + .playerl("I got them from a nearby Ogre tomb, it's recently been infested with zombie ogres and I'm trying to work out what happened there.") + .npcl("This is very troubling @name, very troubling indeed. While it's permitted for learned members of our order to research the 'dark arts', it's absolutely forbidden to make use of such magic.") + .playerl("Do you have any leads on people that I might talk to regarding this?") + .npcl("Well a wizard by the name of 'Sithik Ints' was doing some research in this area. He may know something about it. He's lodged at that guest house to the North, though he's ill and isn't able to leave his room.") + .npcl("Why not go and talk to him, poke around a bit and see if anything comes up. Let me know how you get on. However, I doubt that 'Sithik' had anything to do with it.") + .npcl("Hmm, that black prism seems to have some magical protection. Once you've finished with this item, bring it back to me would you. I may have a reward for you.") + } + + fun dialogueGoodPortrait(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Look, I made a portrait of Sithik.") + .iteml(Items.SITHIK_PORTRAIT_4814, "You show the portrait of Sithik to Zavistic.") + .npcl("Hmm, great...but I already know what he looks like!") + } + + fun dialogueBadPortrait(builder: DialogueBuilder): DialogueBuilder { + return builder + .playerl("Look, I made a portrait of Sithik.") + .iteml(Items.SITHIK_PORTRAIT_4815, "You show the sketch...") + .npcl("Who the demonikin is that? Is it meant to be a portrait of Sithik, it doesn't look anything like him!") + } + + // Too lazy to do this, but this gets mentioned whenever you've shown him one of the evidence (but not all). + fun dialogueSeenEvidenceBefore(builder: DialogueBuilder): DialogueBuilder { + return builder + .npcl("Yeah, you've shown me this before...if this is all the evidence you have?") + .playerl("Please just look at it again...") + .npcl("Ok, let me look then.") + } + } + + override fun create(b: DialogueBuilder) { + b.onQuestStages(ZogreFleshEaters.questName, 2) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .branch { player -> + return@branch if (inInventory(player, Items.BLACK_PRISM_4808) && inInventory(player, Items.TORN_PAGE_4809) ) { + 3 + } else if (inInventory(player, Items.BLACK_PRISM_4808) ) { + 2 + } else if (inInventory(player, Items.TORN_PAGE_4809) ) { + 1 + } else { + 0 + } + }.let { branch -> + branch.onValue(3) + .let{ dialogueBlackPrismAndTornPage(it) } + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) == 2) { + setQuestStage(player, ZogreFleshEaters.questName, 3) + } + } + + branch.onValue(2) + .playerl("There's some undead ogre activity over at 'Jiggig', and the ogres have asked me to look into it. I think I've found a clue and I wonder if you could take a look at it for me?") + .iteml(Items.BLACK_PRISM_4808, "You show the black prism to the aged wizard.") + .npcl("Hmmm, well this is an uncommon spell component. On its own it's useless, but with certain necromantic spells it can be very powerful. Did you find anything else there?") + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { 1 } else { 0 } + }.let { branch2 -> + val returnJoin = b.placeholder() + branch2.onValue(0) + .goto(returnJoin) + branch2.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the tankard to Zavistic.") + .playerl("Well, I found this...") + .npcl("Hmmm, no, that's not really associated with this to be honest. Did you find anything else there?") + .goto(returnJoin) + return@let returnJoin.builder() + } + .playerl("Not really.") + .npcl("I don't know what to say then, there isn't enough to go on with the clues you've shown me so far. I'd suggest going back to search a bit more, but you may just be wasting your time?") + .npcl("Hmm, but this prism does seem to have some magical protection. Once you've finished with this item, bring it back to me would you? I may have a reward for you!") + .playerl("Sure...I mean, I'll try if I remember.") + .end() + + branch.onValue(1) + .playerl("There's some undead ogre activity over at Jiggig, I've found a clue that you may be able to help with.") + .iteml(Items.TORN_PAGE_4809, "You show the necromantic half page to the aged wizard.") + .npcl("Hmm, this is a half torn spell page, it requires another spell component to be effective. Did you find anything else there?") + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811)) { 1 } else { 0 } + }.let { branch2 -> + val returnJoin = b.placeholder() + branch2.onValue(0) + .goto(returnJoin) + branch2.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the tankard to Zavistic.") + .playerl("Well, I found this...") + .npcl("Hmmm, no, that's not really associated with this to be honest. Did you find anything else there?") + .goto(returnJoin) + return@let returnJoin.builder() + } + .playerl("Not really.") + .npcl("I don't know what to say then, there isn't enough to go on with the clues you've shown me so far. I'd suggest going back to search a bit more, but you may just be wasting your time?") + .end() + + branch.onValue(0) + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + } + + + b.onQuestStages(ZogreFleshEaters.questName, 3,4) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .let { builder -> + val returnJoin = b.placeholder() + builder.goto(returnJoin) + return@let returnJoin.builder().options() + .let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option("What did you say I should do?") + .playerl("What did you say I should do?") + .npcl("You should go and have a chat with Sithik Ints, he's in that house just to the north. He's a lodger and has a room upstairs. Just tell him that I sent you to see him. He should be fine once you've mentioned my name.") + .goto(returnJoin) + optionBuilder.option_playerl("Where is Sithik?") + .npcl("He's in that house just to the north, less than a few seconds walk away. He's a lodger and has a room upstairs...he's not very well though.") + .goto(returnJoin) + optionBuilder.optionIf("I have an item that I'd like you to look at.") { player -> return@optionIf hasEvidences(player) == 1 } + .goto(continuePath) + optionBuilder.optionIf("I have an item that I'd like you to look at.") { player -> return@optionIf hasEvidences(player) > 1 } + .goto(continuePath) + optionBuilder.option("I want to ask about the magic guild") + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(it) } + optionBuilder.option_playerl("Sorry, I have to go.") + .end() + + return@let continuePath.builder() + } + } + .branch { player -> + return@branch if (inInventory(player, Items.NECROMANCY_BOOK_4837) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.NECROMANCY_BOOK_4837, "You show the Necromancy book to Zavistic.") + .playerl("I have this necromancy book as evidence that Sithik is involved with the undead ogres at Jiggig.") + .npcl("Ok, so he's researching necromancy...it doesn't mean anything in itself.") + .playerl("Yes, but if you look, you can see that there is a half torn page which matches the page I found at Jiggig.") + .npcl("Hmm, yes, but someone could have stolen that from him and then gone and cast it without his permission or to try and deliberately implicate him.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.BOOK_OF_HAM_4829) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.BOOK_OF_HAM_4829, "You show the HAM book to Zavistic.") + .playerl("Look, this book proves that Sithik hates all monsters and most likely Ogres with a passion.") + .npcl("So what, hating monsters isn't a crime in itself...although I suppose that it does give a motive if Sithik was involved. On its own, it's not enough evidence though.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.DRAGON_INN_TANKARD_4811) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.DRAGON_INN_TANKARD_4811, "You show the dragon Inn Tankard to Zavistic.") + .playerl("This is the tankard I found on the remains of Brentle Vahn!") + .npcl("That doesn't mean anything in itself, you could have gotten that from anywhere. Even from the Dragon Inn tavern! There isn't anything to link Brentle Vahn with Sithik Ints.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (inInventory(player, Items.SIGNED_PORTRAIT_4816) ) { 1 } else { 0 } + }.let { branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .iteml(Items.SIGNED_PORTRAIT_4816, "You show the signed portrait of Sithik to Zavistic.") + .playerl("This is a portrait of Sithik, signed by the landlord of the Dragon Inn saying that he saw Sithik and Brentle Vahn together.") + .npcl("Hmmm, well that is interesting.") + .goto(continuePath) + branch.onValue(0) + .goto(continuePath) + return@let continuePath.builder() + } + .branch { player -> + return@branch if (hasEvidences(player) == 4) { 1 } else { 0 } + }.let { branch -> + branch.onValue(0) + .npcl("However, there isn't enough evidence for me to take the issue further at this point. If you find any further evidence bring it to me.") + .end() + return@let branch + } + .onValue(1) + .npcl("And I'm starting to think that Sithik may be involved. Here, take this potion and give some to Sithik. It'll bring on a change which should solicit some answers - tell him the effects won't revert until he's told the truth.") + .iteml(Items.STRANGE_POTION_4836 ,"Zavistic hands you a strange looking potion bottle and takes all the evidence you've accumulated so far.") + .endWith { _, player -> + if(getQuestStage(player, ZogreFleshEaters.questName) in 3..4) { + if (removeItem(player, Items.NECROMANCY_BOOK_4837) && + removeItem(player, Items.BOOK_OF_HAM_4829) && + removeItem(player, Items.DRAGON_INN_TANKARD_4811) && + removeItem(player, Items.SIGNED_PORTRAIT_4816)) { + addItemOrDrop(player, Items.STRANGE_POTION_4836) + } + setQuestStage(player, ZogreFleshEaters.questName, 5) + } + } + b.onQuestStages(ZogreFleshEaters.questName, 5) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .npcl("Have you used that potion yet?") + .branch { player -> + return@branch if (inInventory(player, Items.STRANGE_POTION_4836) ) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .playerl("No, not yet, what was I supposed to do again?") + .npcl("Try to use the potion on Sithik somehow, he should undergo an interesting transformation, though you'll probably want to leave the house in case there are any side effects. Then go back and question Sithik and tell") + .npcl("him the effects won't wear off until he tells the truth. In fact, that's not exactly true, but I'm sure it'll be an extra incentive to get him to be honest.") + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + branch.onValue(0) + .playerl("Well, actually, I've lost it, could I have another one please?") + .npcl("Sure, but don't lose it this time.") + .iteml(Items.STRANGE_POTION_4836 ,"Zavistic hands you a bottle of strange potion.") + .endWith { _, player -> + addItemOrDrop(player, Items.STRANGE_POTION_4836) + } + } + + + b.onQuestStages(ZogreFleshEaters.questName, 6,7,8,9) + .let { content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.dialogueInitialTalk(it) } + .npcl("Don't you worry about Sithik, he's not likely to be moving from his bed for a long time. When he eventually does get better, he's going to be sent before a disciplinary tribunal, then we'll sort out what's what.") + .playerl("Thanks for your help with all of this.") + .npcl("Ooohh, no thanks required. It's I who should be thanking you my friend...your investigative mind has shown how vigilant we really should be for this type of evil use of the magical arts.") + .let { builder -> + content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile.defaultTalk(builder) + } + } +} + +/** Dialogues when you use stuff on him. */ +class ZavisticRarveUseItemsDialogueFile(private val dialogueNum: Int = 0) : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + // SITHIK_PORTRAIT_4814 + b.onPredicate { _ -> dialogueNum == 3 } + .let{ZavisticRarveDialogueFile.dialogueGoodPortrait(it)} + .end() + + // SITHIK_PORTRAIT_4815 + b.onPredicate { _ -> dialogueNum == 4 } + .let{ZavisticRarveDialogueFile.dialogueBadPortrait(it)} + .end() + + // SIGNED_PORTRAIT_4816 + b.onPredicate { _ -> dialogueNum == 5 } + // That would continue the conversation as above if you normally talk to him. + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt new file mode 100644 index 000000000..871266c92 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt @@ -0,0 +1,56 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.getOrStartTimer +import core.api.inEquipment +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.spell.SpellType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class ZogreBehavior : NPCBehavior(*zogreIds) { + companion object { + @JvmField + val zogreIds = intArrayOf( + NPCs.ZOGRE_2044, + NPCs.ZOGRE_2045, + NPCs.ZOGRE_2046, + NPCs.ZOGRE_2047, + NPCs.ZOGRE_2048, + NPCs.ZOGRE_2049, + NPCs.ZOGRE_2051, + NPCs.ZOGRE_2052, + NPCs.ZOGRE_2053, + NPCs.ZOGRE_2054, + NPCs.ZOGRE_2055, + ) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (inEquipment(attacker, Items.COMP_OGRE_BOW_4827)) { + return + } + if (state.spell != null && state.spell.type == SpellType.CRUMBLE_UNDEAD) { + state.estimatedHit = (state.estimatedHit * 0.5).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.5).toInt() + } + return + } + state.estimatedHit = (state.estimatedHit * 0.25).toInt() + if (state.secondaryHit > 0) { + state.secondaryHit = (state.secondaryHit * 0.25).toInt() + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt new file mode 100644 index 000000000..6ed8c9010 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEaters.kt @@ -0,0 +1,353 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Zogre Flesh Eaters Quest + * + * 1 - Talked to Grish + * 2 - Smashed Barricade + * 2 Cont' - Collected Stuff Underground + * A - Black Prism from Coffin + * B - Half Torn Page from Broken Lecturn + * C - Tankard from Backpack (Kill the Zombie which turns out to be Brentle Vahn) + * 3 - Talked to Zavistic Rarve with A & B only, points to where Sithik Ints is + * 4 - Talked to Sithik and challenged to incriminate him + * 4 Cont' - Collected Evidence + * A - Tankard from 2C after talking to Innkeeper + * B - Portrait of Sithik using papyrus and charcoal with questionable drawing skills + * C - Book of HAM philosophy from drawer + * D - Necromantic book + * 5 - Incriminate Sithik to Zavistic Rarve and given potion + * 6 - Turned Sithik into an Ogre with potion + * 7 - Talked to Grish + * 8 - Get Key from Grish + * 9 - Killed Slash Bash + * 100 - Returned to Grish with Ogre Artifact + * + * This quest journal is the worst; there's typos, and it is out of order. + * + * if (VARPBIT[487] > 12) return 2; if (VARPBIT[487 == 0) return 0; return 1; }; + * define_varbit 487 455 0 4 + * + */ +@Initializable +class ZogreFleshEaters : Quest(Quests.ZOGRE_FLESH_EATERS, 40, 39, 1, 455, 0, 1, 13) { + companion object { + val questName = Quests.ZOGRE_FLESH_EATERS + const val varbitGateBashed = 496 + const val varbitOgreCoffin = 488 + const val varbitSithikOgre = 495 + + const val attributeAskedAboutSickies = "quest:zogreflesheaters-askedaboutsickies" + + // 2A + const val attributeSearchedCoffin = "/save:quest:zogreflesheaters-searchedcoffin" + const val attributeBrokeLockCoffin = "/save:quest:zogreflesheaters-brokelockcoffin" + const val attributeOpenedCoffin = "/save:quest:zogreflesheaters-openedcoffin" // You can fail apparently... + const val attributeFoundBlackPrism = "/save:quest:zogreflesheaters-foundblackprism" + // 2B + const val attributeFoundHalfTornPage = "/save:quest:zogreflesheaters-foundhalftornpage" + // 2C + const val attributeFoughtZombie = "/save:quest:zogreflesheaters-foughtzombie" + const val attributeFoundTankard = "/save:quest:zogreflesheaters-foundtankard" + // 4A + const val attributeAskedAboutTankard = "quest:zogreflesheaters-askedabouttankard" + // 4B + const val attributeMadePortrait = "/save:quest:zogreflesheaters-madeportrait" + // 4C + const val attributeFoundHamBook = "/save:quest:zogreflesheaters-foundhambook" + // 4D + const val attributeFoundNecromanticBook = "/save:quest:zogreflesheaters-foundnecromanticbook" + + const val attributeSlashBashInstance = "/save:quest:zogreflesheaters-slashbashinstance" + + // Open Uglug Nar Shop with Relicym balm + const val attributeOpenUglugNarShop = "/save:quest:zogreflesheaters-openuglugnarshop" + + fun requirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.RANGE, 30), + hasLevelStat(player, Skills.SMITHING, 4), + hasLevelStat(player, Skills.HERBLORE, 8), + isQuestComplete(player, Quests.JUNGLE_POTION), + isQuestComplete(player, Quests.BIG_CHOMPY_BIRD_HUNTING), + ).all { it } + } + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if (!started) { + line(player, "I can !!start?? this quest by talking to !!Grish?? at the Ogrish", line++, false) + line(player, "ceremonial dance place called !!Jiggig??.", line++, false) + line(player, "To start this !!quest?? I should complete these quests:-", line++, false) + line(player, "!!Jungle Potion.??", line++, isQuestComplete(player, Quests.JUNGLE_POTION)) + line(player, "!!Big Chompy Bird Hunting.??", line++, isQuestComplete(player, Quests.BIG_CHOMPY_BIRD_HUNTING)) + line(player, "It would help if I had the following skills levels:-", line++, false) + line(player, "!!Ranged level : 30??", line++, hasLevelStat(player, Skills.RANGE, 30)) + line(player, "!!Fletching level : 30??", line++, hasLevelStat(player, Skills.FLETCHING, 30)) + line(player, "!!Smithing level : 4??", line++, hasLevelStat(player, Skills.SMITHING, 4)) + line(player, "!!Herblore level : 8??", line++, hasLevelStat(player, Skills.HERBLORE, 8)) + line(player, "Must be able to defeat a !!level 111?? foe.", line++, false) + } else if (stage < 100) { + + line(player, "I started this quest by talking to Grish, he asked me to", line++, true) + line(player, "check out the underground area where some Zombie ogres", line++, true) + line(player, "(Zogres) were coming from.", line++, true) + + if (stage >= 2) { + line(player, "I have to find a way into the ceremonial dance area and", line++, true) + line(player, "then underground.", line++, true) + line(player, "I persuaded a guard to let me past, I only had to mention", line++, true) + line(player, "Grish's name and the guard smashed the barricade down. I", line++, true) + line(player, "can enter now.", line++, true) + } else if (stage >= 1) { + line(player, "I have to find a way into the ceremonial dance area and", line++, false) + line(player, "then underground.", line++, false) + } + + if (stage >= 2) { + // Set 2A (Stays) + if (getAttribute(player, attributeFoundBlackPrism, false) || stage >= 3) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a coffin, maybe I can open it", line++, true) + line(player, "now?", line++, true) + line(player, "I've !!opened?? the !!coffin?? and retrieved a !!black prism??, this", line++, stage >= 3) + line(player, "may be useful.", line++, stage >= 3) + } else if (getAttribute(player, attributeOpenedCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a coffin, maybe I can open it", line++, true) + line(player, "now?", line++, true) + line(player, "I've !!opened?? the !!coffin??, maybe there's something in it.", line++, false) + } else if (getAttribute(player, attributeBrokeLockCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + line(player, "I have forced the lock on a !!coffin??, maybe I can open it", line++, false) + line(player, "now?", line++, false) + } else if (getAttribute(player, attributeSearchedCoffin, false)) { + line(player, "I have searched a coffin, it had a funny looking hole at the", line++, true) + line(player, "side.", line++, true) + } + } + + if (stage >= 2) { + // Set 2B (Stays) + if (getAttribute(player, attributeFoundHalfTornPage, false) || stage >= 3) { + line(player, "I found a !!half torn page?? from a !!necromatic spellbook??,", line++, stage >= 3) + line(player, "maybe this is a !!clue???", line++, stage >= 3) + } + } + + // 4 - This is the weirdest shit that is out of place. (Stays) This appears after talking to Sithik Int. + if (stage >= 4) { + line(player, "I have shown the prism and torn page to the grand", line++, true) + line(player, "secretary of the wizards guild.", line++, true) + } + + // Set 2C (Does not stay) + if (stage in 2..4 && getAttribute(player, attributeFoundTankard, false)) { + line(player, "I killed a !!human zombie?? which dropped a !!backpack??. The", line++, stage >= 3) + line(player, "!!backpack?? had the name !!'B. Vahn'?? on it, inside the !!backpack??", line++, stage >= 3) + line(player, "I found a !!tankard??.", line++, stage >= 3) + } else if (stage in 2..4 && getAttribute(player, attributeFoughtZombie, false)) { + line(player, "I killed a !!human zombie?? which dropped a !!backpack??.", line++, stage >= 3) + } + + // Stays until you find all the 3X stuff above. + if (stage in 2..4 && !(getAttribute(player, attributeFoundBlackPrism, false) && + getAttribute(player, attributeFoundHalfTornPage, false) && + getAttribute(player, attributeFoundTankard, false))) { + line(player, "I need to find out what happened here.", line++, false) // Cleared when the 3 sets above are done. + } + + // Set 4A (Does not stay) + if (stage in 2..4 && getAttribute(player, attributeAskedAboutTankard, false)) { + line(player, "The 'Dragon Inn' !!Innkeeper?? says the tankard belongs to", line++, false) + line(player, "one of his locals called !!Brentle Vahn??. He was seen talking", line++, false) + line(player, "to a !!wizard?? the other day.", line++, false) // Cleared after handed in with the rest + } + + // 3 Zavistic Rarve seen prism and page - lines disappears right after... + if (stage == 3) { + line(player, "I have shown the !!prism?? and the !!necromantic page?? to", line++, false) + line(player, "Zavistic Rarve. He's told me about a !!wizard?? named", line++, false) + line(player, "!!Sithik Ints?? who might have some information.", line++, false) + } + + // 4 Spoken with Sithik + if (stage >= 4) { + line(player, "I've spoken to !!Sithik??, I need to see if he was !!involved??", line++, stage >= 5) + line(player, "with the !!Undead Ogres at 'Jiggig'?? in some way.", line++, stage >= 5) + } + + // Only stays for 4 + if (stage == 4) { + if (getAttribute(player, attributeMadePortrait, false)) { + line(player, "I've made a !!portrait?? of !!Sithik??...not sure what this will do?", line++, false) + } + if (getAttribute(player, attributeFoundHamBook, false)) { + line(player, "I've found a !!book?? on !!HAM philosophy??...what does this prove?", line++, false) + } + if (getAttribute(player, attributeFoundNecromanticBook, false)) { + line(player, "I've found a !!necromantic book??...what does this prove?", line++, false) + } + } + + // 4 - Who the hell knows why this is here. (Stays) This appears after getting the potion. + if (stage >= 5) { + line(player, "I've talked to Zavistic Rarve regarding the prism and the", line++, true) + line(player, "torn page, he gave some information on a student called", line++, true) + line(player, "Sithik Ints, he may know more about what's happening", line++, true) + line(player, "here.", line++, true) + } + + // Beyond this is legit + if (stage >= 5) { + line(player, "Zavistic has given me some sort of !!potion??, apparently I", line++, stage >= 6) + line(player, "need to give it to !!Sithik??.", line++, stage >= 6) + } + + if (stage >= 7) { + line(player, "I came back into Sithik's room to find that he had been", line++, true) + line(player, "turned into an Ogre!", line++, true) + } else if (stage >= 6) { + line(player, "I have put some of the !!potion?? into !!Sithik's tea??, the !!potion??", line++, false) + line(player, "will take some time to act. Perhaps I should !!get out of??", line++, false) + line(player, "!!here?? in case there are any !!side effects???", line++, false) + } + + if (stage >= 8) { + line(player, "Sithik has told me how to make 'brutal arrows', which", line++, true) + line(player, "should be more effective against Zogres.", line++, true) + + line(player, "Sithik has given me some pointers on how I can make a", line++, true) + line(player, "cure disease potion, though I'm still not sure exactly which", line++, true) + line(player, "herbs I should use.", line++, true) + } else if (stage >= 7) { + line(player, "!!Sithik?? has told me that there is no way I can remove the", line++, false) + line(player, "effects of the !!necromantic curse spell?? from the !!Jiggig??", line++, false) + line(player, "area. I'll have to go back and let !!Grish?? know.", line++, false) + + line(player, "!!Sithik?? has told me how to make !!'brutal arrows'??, which", line++, false) + line(player, "should be more !!effective?? against !!Zogres??.", line++, false) + + line(player, "!!Sithik?? has given me some pointers on how I can make a", line++, false) + line(player, "!!cure disease potion??, though I'm still not sure exactly which", line++, false) + line(player, "!!herbs?? I should use.", line++, false) + } + + if (stage >= 9) { + line(player, "I've told Grish to relocated the dance area, but he needs", line++, true) + line(player, "me to get something from the tomb to so that he can do", line++, true) // "tomb to so" is authentic [sic] + line(player, "this.", line++, true) + line(player, "I need to go back into the tomb and look for some 'old'", line++, true) + line(player, "items that Grish has asked for.", line++, true) + line(player, "I should return the !!artifact?? to !!Grish??.", line++, false) + } else if (stage >= 8) { + line(player, "I've told Grish to relocated the dance area, but he needs", line++, true) + line(player, "me to get something from the tomb to so that he can do", line++, true) // "tomb to so" is authentic [sic] + line(player, "this.", line++, true) + line(player, "I need to go back into the !!tomb?? and look for some !!'old'??", line++, false) + line(player, "!!items?? that !!Grish?? has asked for.", line++, false) + } + + } else { + // The ending is COMPLETELY replaced from this entire shitshow of a quest log. + line(player, "I talked to Grish in the Jiggig area which is swarming with", line++, true) + line(player, "Zombie Ogres (Zogres) These disgusting creatures carry", line++, true) + line(player, "disease and are quite dangerous so the Ogres weren't", line++, true) + line(player, "too keen to try and sort them out.", line++, true) + + line(player, "I talked to an ogre called Grish who asked me to look into", line++, true) + line(player, "the problem. After some searching around in a tomb, I", line++, true) + line(player, "found some clues which pointed me to the human", line++, true) + line(player, "habitation of Yannile.", line++, true) + + line(player, "With the help of Zavistic Rarve, the grand secretary of", line++, true) + line(player, "the Wizards guild I was able to piece the clues together", line++, true) + line(player, "and discover that a Wizard named 'Sithik Ints' was", line++, true) + line(player, "responsible.", line++, true) + + line(player, "Unfortunately I couldn't remove the curse from the area,", line++, true) + line(player, "however, I was able to return some important artefacts to", line++, true) + line(player, "Grish, who can now set up a new ceremonial dance area for", line++, true) + line(player, "the ogres of Gu' Tanoth.", line++, true) + + line(player, "Sithik Ints also told me how to make Brutal arrows which are", line++, true) + line(player, "more effective against Zogres, and he also told me how to", line++, true) + line(player, "make a disease balm.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line) + } + } + + override fun reset(player: Player) { + setVarp(player, varbitGateBashed, 0, true) + removeAttribute(player, attributeAskedAboutSickies) + removeAttribute(player, attributeSearchedCoffin) + removeAttribute(player, attributeBrokeLockCoffin) + removeAttribute(player, attributeOpenedCoffin) + removeAttribute(player, attributeFoundBlackPrism) + removeAttribute(player, attributeFoundHalfTornPage) + removeAttribute(player, attributeFoughtZombie) + removeAttribute(player, attributeFoundTankard) + removeAttribute(player, attributeAskedAboutTankard) + removeAttribute(player, attributeMadePortrait) + removeAttribute(player, attributeFoundHamBook) + removeAttribute(player, attributeFoundNecromanticBook) + removeAttribute(player, attributeOpenUglugNarShop) + + } + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Zogre Flesh Eaters!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.OGRE_ARTEFACT_4818, 240, 277, 5) + + drawReward(player,"1 Quest Point.", ln++) + drawReward(player,"Can now make Brutal Arrows", ln++) + drawReward(player,"and cure disease potions.", ln++) + drawReward(player,"2000 Ranged, Fletching and", ln++) + drawReward(player,"Herblore XP", ln++) + + player.skills.addExperience(Skills.RANGE, 2000.0) + player.skills.addExperience(Skills.FLETCHING, 2000.0) + player.skills.addExperience(Skills.HERBLORE, 2000.0) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + override fun updateVarps(player: Player) { + if(getQuestStage(player, questName) >= 2) { + setVarbit(player, varbitGateBashed, 1, true) + } else { + setVarbit(player, varbitGateBashed, 0, true) + } + if(getQuestStage(player, questName) >= 7) { + setVarbit(player, varbitSithikOgre, 1, true) + } else { + setVarbit(player, varbitSithikOgre, 0, true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt new file mode 100644 index 000000000..bd16a02b6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreFleshEatersListeners.kt @@ -0,0 +1,382 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Direction +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class ZogreFleshEatersListeners : InteractionListener { + + companion object { + @JvmStatic + fun ladderMakesSithikTurnIntoOgre(player: Player) { + if(getQuestStage(player, ZogreFleshEaters.questName) == 6) { + setQuestStage(player, ZogreFleshEaters.questName, 7) + setVarbit(player, ZogreFleshEaters.varbitSithikOgre, 1) + } + } + } + + override fun defineListeners() { + // Stairs and doors + on(Scenery.STAIRS_6841, SCENERY, "climb-down") { player, node -> + sendMessage(player, "You climb down the steps.") + if (node.location == Location(2443, 9417, 2)) { + teleport(player, Location(2442, 9417, 0)) + } else { + teleport(player, Location(2477, 9437, 2)) + } + return@on true + } + on(Scenery.STAIRS_6842, SCENERY, "climb-up") { player, node -> + sendMessage(player, "You climb up the steps.") + if (node.location == Location(2443, 9417, 0)) { + teleport(player, Location(2447, 9417, 2)) + } else { + teleport(player, Location(2485, 3045, 0)) + } + return@on true + } + on(Scenery.OGRE_STONE_DOOR_6871, SCENERY, "open") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 9) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You use the Ogre Tomb Key to unlock the door.") + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + + on(Scenery.OGRE_STONE_DOOR_6872, SCENERY, "open") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 9) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (inInventory(player, Items.OGRE_GATE_KEY_4839)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You use the Ogre Tomb Key to unlock the door.") + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + + // Stage 2 + on(Scenery.CRUSHED_BARRICADE_6881, SCENERY, "climb-over") { player, node -> + if (player.location.x < 2456) { + val distance = player.location.getDistance(Location(2457, 3049, 0)).toInt() + forceMove(player, player.location, Location(2457, 3049, 0), 0, distance * 15, null, 1236) + } else { + val distance = player.location.getDistance(Location(2455, 3049, 0)).toInt() + forceMove(player, player.location, Location(2455, 3049, 0), 0, distance * 15, null, 1236) + } + return@on true + } + on(Scenery.CRUSHED_BARRICADE_6882, SCENERY, "climb-over") { player, node -> + if (player.location.x < 2456) { + val distance = player.location.getDistance(Location(2457, 3048, 0)).toInt() + forceMove(player, player.location, Location(2457, 3048, 0), 0, distance * 15, null, 1236) + } else { + val distance = player.location.getDistance(Location(2455, 3048, 0)).toInt() + forceMove(player, player.location, Location(2455, 3048, 0), 0, distance * 15, null, 1236) + } + return@on true + } + + // Stage 2A + on(Scenery.OGRE_COFFIN_6844, SCENERY, "search") { player, node -> + if (getVarbit(player, ZogreFleshEaters.varbitOgreCoffin) == 0) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogueLines(player, "You search the coffin and find a small geometrically shaped hole in", "the side. It looks as if this hole was made with a considerable amount", "of force, maybe the thing which made the hole is still inside?").also { stage++ } + 1 -> sendDialogueLines(player, "The lock looks quite crude, with some skill and a slender blade, you", "may be able to force it.").also { stage = END_DIALOGUE } + } + } + }) + } else if (getVarbit(player, ZogreFleshEaters.varbitOgreCoffin) == 1) { + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogueLines(player, "The lid looks heavy, but now that you've unlocked it, you may be", "able to lift it. You prepare yourself.").also { stage++ } + 1 -> playerl("Urrrgggg.").also { sendChat(player, "Urrrgggg."); stage++ } + 2 -> playerl("Aarrrgghhh!").also { sendChat(player, "Aarrrgghhh!"); stage++ } + // Supposed to have a failure state here. + 3 -> playerl("Raarrrggggg! Yes!").also { sendChat(player, "Raarrrggggg! Yes!"); stage++ } + 4 -> sendDialogueLines(player, "You eventually manage to lift the lid.").also { + setVarbit(player, ZogreFleshEaters.varbitOgreCoffin, 3, true) + stage = END_DIALOGUE + } + } + } + }) + } + return@on true + } + onUseWith(IntType.SCENERY, Items.KNIFE_946, Scenery.OGRE_COFFIN_6844) { player, _, _ -> + sendItemDialogue(player, Items.KNIFE_946, + "With some skill you manage to slide the blade along the lock edge and click into place the teeth of the primitive mechanism.") + setVarbit(player, ZogreFleshEaters.varbitOgreCoffin, 1, true) + return@onUseWith true + } + + on(Scenery.OGRE_COFFIN_6845, SCENERY, "search") { player, node -> + sendItemDialogue(player, Items.BLACK_PRISM_4808, + "You find a creepy looking black prism inside.") + addItemOrDrop(player, Items.BLACK_PRISM_4808) + return@on true + } + + on(Items.BLACK_PRISM_4808, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.BLACK_PRISM_4808, + "It looks like a smokey black gem of some sort...very creepy. Some magical force must have prevented it from being shattered when it hit the coffin.") + return@on true + } + + // Stage 2B + on(Scenery.BROKEN_LECTURN_6846, SCENERY, "search") { player, node -> + sendMessage(player, "You search the broken down lecturn.") + if (inInventory(player, Items.TORN_PAGE_4809)) { + sendMessage(player, "You find nothing.") + } else { + sendItemDialogue(player, Items.TORN_PAGE_4809, + "You find a half torn page...it has spidery writing all over it.") + addItemOrDrop(player, Items.TORN_PAGE_4809) + } + return@on true + } + + on(Items.TORN_PAGE_4809, ITEM, "read") { player, node -> + sendDialogue(player, "You don't manage to understand all of it as there is only a half page here. But it seems the spell was used to place a curse on an area and for all time raise the dead.") + return@on true + } + + // Stage 2C + on(Scenery.SKELETON_6893, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 2){ + if (getAttribute(player, ZogreFleshEaters.attributeFoughtZombie, false)) { + + if (inInventory(player, Items.RUINED_BACKPACK_4810)) { + sendMessage(player, "You find nothing on the corpse.") + } else { + sendMessage(player, "You find another backpack.") + addItemOrDrop(player, Items.RUINED_BACKPACK_4810) + } + } else { + // Zombie time. + sendMessage(player, "Something screams into life right in front of you.") + val npc = NPC(NPCs.ZOMBIE_1826) + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(2442, 9459, 2) + npc.direction = Direction.NORTH + npc.init() + npc.attack(player) + } + } else { + // At no point should this be reached since you need to start the quest anyway. + sendMessage(player, "You find nothing on the corpse.") + } + return@on true + } + on(Items.RUINED_BACKPACK_4810, ITEM, "open") { player, node -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + sendItemDialogue(player, Items.RUINED_BACKPACK_4810, + "Just before you open the backpack, you notice a small leather patch with the moniker: 'B.Vahn', on it.").also { stage++ } + sendItemZoomOnInterface(player, 241, 1, Items.RUINED_BACKPACK_4810, 230) + } + 1 -> sendItemDialogue(player, Items.DRAGON_INN_TANKARD_4811, + "You find an interesting looking tankard.").also { + setAttribute(player, ZogreFleshEaters.attributeFoundTankard, true) + if(removeItem(player, node)) { + addItem(player, Items.ROTTEN_FOOD_2959) + addItem(player, Items.KNIFE_946) + addItem(player, Items.DRAGON_INN_TANKARD_4811) + } + stage++ + } + 2 -> sendDoubleItemDialogue(player, Items.KNIFE_946, Items.ROTTEN_FOOD_2959, "You find a knife and some rotten food, the backpack is ripped to shreds.").also { + sendMessage(player, "You find a knife and some rotten food.") + sendMessage(player, "You find an interesting looking tankard.") + stage = END_DIALOGUE + } + } + } + }) + return@on true + } + + + on(Items.DRAGON_INN_TANKARD_4811, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.DRAGON_INN_TANKARD_4811, + "A stout ceramic tankard with a Dragon Emblem on the side, the words, 'Ye Olde Dragon Inn' are inscribed in the bottom.") + return@on true + } + + // Zavistic Bell, Stage 2,3,4,5,6 and on (Actually should be standalone. + on(Scenery.BELL_6847, SCENERY, "ring") { player, node -> + sendMessage(player, "You ring the bell.") + // TODO: Make Zavistic appear at the bell area. + openDialogue(player, content.region.kandarin.yanille.dialogue.ZavisticRarveDialogueFile(), NPC(NPCs.ZAVISTIC_RARVE_2059)) + return@on true + } + + // Stage 4A + onUseWith(IntType.NPC, Items.DRAGON_INN_TANKARD_4811, NPCs.BARTENDER_739) { player, used, with -> + openDialogue(player, BartenderDialogueFile(1), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4814, NPCs.BARTENDER_739) { player, used, with -> + // The good portrait + openDialogue(player, BartenderDialogueFile(2), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4815, NPCs.BARTENDER_739) { player, used, with -> + // The bad portrait + openDialogue(player, BartenderDialogueFile(3), with as NPC) + return@onUseWith true + } + on(Items.SIGNED_PORTRAIT_4816, ITEM, "look-at") { player, node -> + sendItemDialogue(player, Items.SIGNED_PORTRAIT_4816, + "You see an image of Sithik with a message underneath 'I, the bartender of the Dragon Inn, do swear that this is a true likeness of the wizzy who was talking to Brentle Vahn, my customer the other day.'") + return@on true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4814, NPCs.ZAVISTIC_RARVE_2059) { player, used, with -> + // The good portrait + openDialogue(player, ZavisticRarveUseItemsDialogueFile(3), with as NPC) + return@onUseWith true + } + onUseWith(IntType.NPC, Items.SITHIK_PORTRAIT_4815, NPCs.ZAVISTIC_RARVE_2059) { player, used, with -> + // The bad portrait + openDialogue(player, ZavisticRarveUseItemsDialogueFile(4), with as NPC) + return@onUseWith true + } + + + // Stage 4B + on(Items.BOOK_OF_PORTRAITURE_4817, ITEM, "read") { player, node -> + sendDialogueLines(player, + "All interested artisans should really consider taking up the hobby of", + "portraiture. To do so, one uses a piece of papyrus on the intended", + "subject to initiate a likeness drawing activity.") + return@on true + } + + // Stage 4C + on(Items.BOOK_OF_HAM_4829, ITEM, "read") { player, node -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> { + sendDialogue(player, + "You read this book for a while, it seems to be some sort of political "+ + "manifesto about how the king doesn't do enough to safeguard the "+ + "citizens of the realm from the monsters that still thrive within the "+ + "borders. ").also { stage++ } + // This is the original, but it is too big for this to handle. +// sendDialogueLines(player, +// "You read this book for a while, it seems to be some sort of political", +// "manifesto about how the king doesn't do enough to safeguard the", +// "citizens of the realm from the monsters that still thrive within the", +// "borders. It sends out a rallying to all people who would want to", +// "stop monsters, to join the HAM movement.").also { stage++ } + } + 1 -> sendDialogue(player, "It sends out a rallying to all people who would want to stop monsters, to join the HAM movement.").also { stage++ } + 2 -> sendPlayerDialogue(player, "Hmm, Sithik must really hate monsters then, I wonder if he hates ogres in particular?").also { + stage = END_DIALOGUE + } + } + } + }) + return@on true + } + + // Stage 4D + on(Items.NECROMANCY_BOOK_4837, ITEM, "read") { player, node -> + sendDialogueLines(player, + "This book uses very strange language and some", + "incomprehensible symbols. It has a very dark and evil feeling to", + "it. As you're looking through the book, you notice that", + "one of the pages has been torn and half of it is missing.") + return@on true + } + + onUseWith(IntType.ITEM, Items.NECROMANCY_BOOK_4837, Items.TORN_PAGE_4809) { player, _, _ -> + sendDoubleItemDialogue(player, Items.NECROMANCY_BOOK_4837, Items.TORN_PAGE_4809, + "The torn page matches exactly the part where a torn out page is missing from the book. You feel sure that this page came from this book.") + return@onUseWith true + } + + // Uglug Nar Shop + onUseWith(IntType.NPC, intArrayOf(Items.RELICYMS_BALM1_4848, Items.RELICYMS_BALM2_4846, Items.RELICYMS_BALM3_4844, Items.RELICYMS_BALM4_4842), NPCs.UGLUG_NAR_2039) { player, used, with -> + openDialogue(player, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendItemDialogue(player, used.id,"You show the potion to Uglug Nar.").also { stage++ } + 1 -> playerl("Hey, here you go! I brought you some of the potion which should cure the disease. You said that you would buy some from me.").also { + if (getAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, false)) { + stage = 2 + } else { + stage = 3 + } + } + 2 -> sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Yous creatures is da funny ones... yous already solds me's ones now..and us can now sell un to yous!", FacialExpression.OLD_NORMAL).also { stage = END_DIALOGUE } + 3 -> sendNPCDialogue(player, NPCs.UGLUG_NAR_2039, "Yous creatures done da good fing...yous get many bright pretties for dis...!", FacialExpression.OLD_NORMAL).also { stage++ } + 4 -> sendDoubleItemDialogue(player, Item(Items.COINS_995, 1000), used as Item, "You sell the potion and get 1000 coins in return.").also { + if (removeItem(player, used)) { + addItemOrDrop(player, Items.COINS_995, 1000) + setAttribute(player, ZogreFleshEaters.attributeOpenUglugNarShop, true) + } + stage = END_DIALOGUE + } + } + } + }) + return@onUseWith true + } + + // Stage 8 to 9 + on(Scenery.STAND_6897, SCENERY, "search") { player, node -> + if (getQuestStage(player, ZogreFleshEaters.questName) == 8 && + getAttribute(player, ZogreFleshEaters.attributeSlashBashInstance, null) == null + ) { + // Zombie time. + sendMessage(player, "Something stirs behind you!") + val npc = NPC(NPCs.SLASH_BASH_2060) + setAttribute(player, ZogreFleshEaters.attributeSlashBashInstance, npc) + setAttribute(npc, "target", player) + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(2478, 9446, 0) + npc.direction = Direction.EAST + npc.init() + npc.attack(player) + } else if (getQuestStage(player, ZogreFleshEaters.questName) > 8) { + if (inInventory(player, Items.OGRE_ARTEFACT_4818)) { + sendMessage(player, "You find nothing on the stand.") + } else { + sendMessage(player, "You find another artifact.") + addItemOrDrop(player, Items.OGRE_ARTEFACT_4818) + } + } else { + // At no point should this be reached since you need to start the quest anyway. + sendMessage(player, "You find nothing on the stand.") + } + return@on true + } + } +} diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt new file mode 100644 index 000000000..1db9325ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogrePotionAndFletchingListeners.kt @@ -0,0 +1,109 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import kotlin.math.min + +public enum class BrutalArrows(val nailItem: Int, val level: Int, val product: Int, val exp: Double) { + BRONZE_BRUTAL(Items.BRONZE_NAILS_4819, 7, Items.BRONZE_BRUTAL_4773, 8.4), + IRON_BRUTAL(Items.IRON_NAILS_4820, 18, Items.IRON_BRUTAL_4778, 15.6), + STEEL_BRUTAL(Items.STEEL_NAILS_1539, 33, Items.STEEL_BRUTAL_4783, 30.6), + BLACK_BRUTAL(Items.BLACK_NAILS_4821, 38, Items.BLACK_BRUTAL_4788, 39.0), + MITHRIL_BRUTAL(Items.MITHRIL_NAILS_4822, 49, Items.MITHRIL_BRUTAL_4793, 45.0), + ADAMANT_BRUTAL(Items.ADAMANTITE_NAILS_4823, 62, Items.ADAMANT_BRUTAL_4798, 61.2), + RUNE_BRUTAL(Items.RUNE_NAILS_4824, 77, Items.RUNE_BRUTAL_4803, 75.0) + ; + + companion object { + @JvmField + val nailItemMap = values().associateBy { it.nailItem } + val nailItemArray = nailItemMap.values.map { it.nailItem }.toIntArray() + } +} +/** + * This handles potions and fletching related to zogre flesh eaters. + * + * Relicym's Balm is unique to zogre flesh eaters. + * Maybe move this to herblore when it can handle quest requirements better. + */ +class ZogrePotionAndFletchingListeners : InteractionListener { + + override fun defineListeners() { + // ROGUES_PURSE_POTIONUNF_4840 is already in UnfinishedPotion.java + onUseWith(IntType.ITEM, Items.ROGUES_PURSE_POTIONUNF_4840, Items.CLEAN_SNAKE_WEED_1526) { player, used, with -> + if (!hasLevelStat(player, Skills.HERBLORE, 8)) { + sendMessage(player, "You need a herblore level of 8 to make this mix.") + return@onUseWith true + } + if (getQuestStage(player, ZogreFleshEaters.questName) < 7) { + sendMessage(player, "You need to have partially completed Zogre Flesh Eaters to make this mix.") + return@onUseWith true + } + + if(removeItem(player, used) && removeItem(player, with)) { + sendMessage(player, "You add the snake weed to the rogues purse solution and make Relicyms Balm.") + addItem(player, Items.RELICYMS_BALM3_4844) + rewardXP(player, Skills.HERBLORE, 40.0) + } + return@onUseWith true + } + + // FletchingListeners.kt + // ACHEY_TREE_LOGS_2862 -> UNSTRUNG_COMP_BOW_4825 -> COMP_OGRE_BOW_4827 + // Requirement to wield comp ogre bow. + onEquip(Items.COMP_OGRE_BOW_4827) { player, _ -> + if (getQuestStage(player, ZogreFleshEaters.questName) >= 8){ + return@onEquip true + } + sendMessage(player, "You need to complete part of Zogre Flesh Eaters to equip this.") + return@onEquip false + } + + + onUseWith(IntType.ITEM, BrutalArrows.nailItemArray, Items.FLIGHTED_OGRE_ARROW_2865) { player, used, with -> + fun getMaxAmount(_unused: Int = 0): Int { + val tips = amountInInventory(player, used.id) + val shafts = amountInInventory(player, with.id) + return min(tips, shafts) + } + + fun process() { + val amountThisIter = min(6, getMaxAmount()) + if (removeItem(player, Item(used.id, amountThisIter)) && removeItem(player, Item(with.id, amountThisIter))) { + addItem(player, BrutalArrows.nailItemMap[used.id]!!.product, amountThisIter) + sendMessage(player, "You make $amountThisIter brutal arrows.") + rewardXP(player, Skills.FLETCHING, BrutalArrows.nailItemMap[used.id]!!.exp) + } + } + + if (getQuestStage(player, ZogreFleshEaters.questName) < 7) { + sendMessage(player, "You need to complete part of Zogre Flesh Eaters to make these.") + return@onUseWith true + } + + if (getStatLevel(player, Skills.FLETCHING) < BrutalArrows.nailItemMap[used.id]!!.level) { + sendMessage(player, "You need a Fletching level of " + BrutalArrows.nailItemMap[used.id]!!.level + " to make these.") + return@onUseWith true + } + + sendSkillDialogue(player) { + create { id, amount -> + runTask( + player, + delay = 2, + repeatTimes = min(amount, getMaxAmount() / 6 + 1), + task = ::process + ) + } + calculateMaxAmount(::getMaxAmount) + withItems(Item(BrutalArrows.nailItemMap[used.id]!!.product, 5)) + } + return@onUseWith true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt new file mode 100644 index 000000000..d24485447 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZombieBrentleVahnBehavior.kt @@ -0,0 +1,40 @@ +package content.region.kandarin.feldip.quest.zogreflesheaters + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.timer.impl.Disease +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** The zombie you have to fight when you click on the skeleton. */ +class ZombieBrentleVahnBehavior : NPCBehavior(NPCs.ZOMBIE_1826) { + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + val disease = getOrStartTimer(victim, 10) + disease.hitsLeft = 10 + } + + override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { + super.onDropTableRolled(self, killer, drops) + // Drops backpack when killed. + if (killer is Player && getQuestStage(killer, ZogreFleshEaters.questName) in 2..4) { + drops.add(Item(Items.RUINED_BACKPACK_4810)) + setAttribute(killer, ZogreFleshEaters.attributeFoughtZombie, true) + } + } + + var clearTime = 0 + override fun tick(self: NPC): Boolean { + // You have 400 ticks to kill this guy + if (clearTime++ > 400) { + clearTime = 0 + poofClear(self) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/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/gnomestronghold/dialogue/GnomeBarmanDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeBarmanDialogue.kt new file mode 100644 index 000000000..40b59d721 --- /dev/null +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeBarmanDialogue.kt @@ -0,0 +1,110 @@ +package content.region.kandarin.gnomestronghold.dialogue + +import core.api.addItem +import core.api.inInventory +import core.api.openDialogue +import core.api.openNpcShop +import core.api.removeItem +import core.api.sendMessage +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +private const val INGREDIENT_COST = 20 +private const val NOT_ENOUGH_MONEY_MESSAGE = "You do not have enough money to buy that." + +/** + * Handles the dialogue for the Gnome Barman NPCs in the Tree Gnome Stronghold. + * @author Broseki + */ +@Initializable +class GnomeBarmanDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return GnomeBarmanDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GnomeBarmanDialogueStart(), npc) + return false + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARMAN_849) + } +} + +class GnomeBarmanDialogueStart : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.OLD_HAPPY, "Good day to you. What can I get you to drink?") + options( + DialogueOption("store", "What do you have?", expression = ChatAnim.FRIENDLY, spokenText = "What do you have."), + DialogueOption("nothing", "Nothing thanks.", expression = ChatAnim.FRIENDLY), + DialogueOption( + "ingredients", "Can I buy some ingredients?", + spokenText = "I was just wanting to buy a cocktail ingredient actually.", + expression = ChatAnim.FRIENDLY + ), + ) + + label("store") + npc(ChatAnim.OLD_HAPPY, "Here, take a look at our menu.") + exec { player, npc -> + openNpcShop(player, npc.id) + } + + label("nothing") + npc(ChatAnim.OLD_HAPPY, "Okay, take it easy.") + + label("ingredients") + npc(ChatAnim.OLD_HAPPY, "Sure thing, what did you want?") + options( + DialogueOption("lemon", "A lemon.", expression = ChatAnim.FRIENDLY), + DialogueOption("orange", "An orange.", expression = ChatAnim.FRIENDLY), + DialogueOption("shaker", "A cocktail shaker.", expression = ChatAnim.FRIENDLY), + DialogueOption( + "buynothing", "Nothing thanks.", + spokenText = "Actually nothing thanks.", + expression = ChatAnim.FRIENDLY + ), + ) + + label("lemon") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.LEMON_2102) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("orange") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.ORANGE_2108) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("shaker") + npc(ChatAnim.OLD_HAPPY, "$INGREDIENT_COST coins please.") + exec { player, _ -> + if (removeItem(player, Item(Items.COINS_995, INGREDIENT_COST))) { + addItem(player, Items.COCKTAIL_SHAKER_2025) + } else { + sendMessage(player, NOT_ENOUGH_MONEY_MESSAGE) + } + } + + label("buynothing") + // Just end the conversation here without any further dialogue + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt new file mode 100644 index 000000000..c6f7fd2fd --- /dev/null +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeWomanDialogue.kt @@ -0,0 +1,104 @@ +package content.region.kandarin.gnomestronghold.dialogue + +import core.api.addItemOrDrop +import core.api.openDialogue +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handled the dialogue for the Gnome Woman NPCs in the Tree Gnome Stronghold. + * @author Broseki + */ +@Initializable +class GnomeWomanDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return GnomeWomanDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Pick a random number between 0 - 5 to determine the dialogue tree to use + val randomDialogue = (0..5).random() + + when (randomDialogue) { + 0 -> openDialogue(player, GnomeWomanDialogueFile0(), npc) + 1 -> openDialogue(player, GnomeWomanDialogueFile1(), npc) + 2 -> openDialogue(player, GnomeWomanDialogueFile2(), npc) + 3 -> openDialogue(player, GnomeWomanDialogueFile3(), npc) + 4 -> openDialogue(player, GnomeWomanDialogueFile4(), npc) + else -> openDialogue(player, GnomeWomanDialogueFile5(), npc) + } + return false + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_WOMAN_168, NPCs.GNOME_WOMAN_169) + } +} + +class GnomeWomanDialogueFile0 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hello adventurer. Here are some wise words:") + player(ChatAnim.FRIENDLY, "OK.") + npc(ChatAnim.OLD_CALM_TALK1, "Happiness is inward and not outward. So it does not depend on what we have but on what we are!") + } +} + +class GnomeWomanDialogueFile1 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hi. I've never seen so many humans in my life.") + player(ChatAnim.LAUGH, "I've never seen so many gnomes!") + npc(ChatAnim.OLD_LAUGH1, "So we're both learning.") + } +} + +class GnomeWomanDialogueFile2 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Hello traveller. Are you eating properly? You look tired.") + player(ChatAnim.FRIENDLY, "I think so.") + npc(ChatAnim.OLD_CALM_TALK1, "Here, get this worm down you. It'll do you the world of good.") + item(Item(Items.KING_WORM_2162), "The gnome gives you a worm.") + exec { player, npc -> + addItemOrDrop(player, Items.KING_WORM_2162) + } + player(ChatAnim.HAPPY, "Thanks!") + } +} + +class GnomeWomanDialogueFile3 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_HAPPY, "Well good day to you kind sir. Are you new to these parts?") + player(ChatAnim.FRIENDLY + , "Kind of.") + npc(ChatAnim.OLD_HAPPY, "Well if you're looking for a good night out: Blurberry's cocktail bar is great!") + } +} + +class GnomeWomanDialogueFile4 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + npc(ChatAnim.OLD_CALM_TALK1, "Some people grumble because roses have thorns. I'm thankful that thorns have roses!") + player(ChatAnim.HAPPY, "Good attitude!") + } +} + +class GnomeWomanDialogueFile5 : DialogueLabeller() { + override fun addConversation() { + player(ChatAnim.FRIENDLY, "Hello.") + player(ChatAnim.FRIENDLY, "How are you?") + npc(ChatAnim.OLD_HAPPY, "Not bad, a little worn out.") + player(ChatAnim.FRIENDLY, "Maybe you should have a lie down.") + npc(ChatAnim.OLD_HAPPY, "With three kids to feed I've no time for naps!") + player(ChatAnim.HAPPY, "Sounds like hard work!") + npc(ChatAnim.OLD_HAPPY, "It is but they're worth it.") + } +} diff --git a/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java index 7cec187ec..1322bf19c 100644 --- a/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java @@ -17,6 +17,7 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; import content.global.travel.EssenceTeleport; +import content.data.Quests; /** * Represents the wizard guild plugin. @@ -35,7 +36,6 @@ public final class WizardGuildPlugin extends OptionHandler { SceneryDefinition.forId(2155).getHandlers().put("option:open", this); SceneryDefinition.forId(1722).getHandlers().put("option:climb-up", this); new WizardDistentorDialogue().init(); - new ZavisticRarveDialogue().init(); new ProfessorImblewynDialogue().init(); new WizardFrumsconeDialogue().init(); new RobeStoreDialogue().init(); @@ -74,7 +74,7 @@ public final class WizardGuildPlugin extends OptionHandler { } break; case "teleport": - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { player.getPacketDispatch().sendMessage("You need to have completed the Rune Mysteries Quest to use this feature."); return true; } @@ -133,7 +133,7 @@ public final class WizardGuildPlugin extends OptionHandler { stage = 2; break; case 2: - if (!player.getQuestRepository().isComplete("Rune Mysteries")) { + if (!player.getQuestRepository().isComplete(Quests.RUNE_MYSTERIES)) { player("Nothing thanks, I'm just looking around."); stage = 4; return true; @@ -175,68 +175,6 @@ public final class WizardGuildPlugin extends OptionHandler { } - /** - * Represents the dialogue used for zavistic rarve. - * @author 'Vexia - * @version 1.0 - */ - public final class ZavisticRarveDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ZavisticRarveDialogue} {@code Object}. - */ - public ZavisticRarveDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZavisticRarveDialogue} {@code Object}. - * @param player the player. - */ - public ZavisticRarveDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZavisticRarveDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("What are you doing...Oh, it's you...sorry...didn't", "realise... what can I do for you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Thanks for your help with all of this."); - stage = 1; - break; - case 1: - npc("Ooohh, no thanks required. It's I who should be", "thanking you my friend...your investigative mind has", "shown how vigilant we really should be for this type of", "evil use of the magical arts."); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2059 }; - } - - } - /** * Represents the wizard distentor dialogue. * @author 'Vexia diff --git a/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt b/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt new file mode 100644 index 000000000..84d32fdbc --- /dev/null +++ b/Server/src/main/content/region/kandarin/handlers/GnomeTortoiseNPC.kt @@ -0,0 +1,284 @@ +package content.region.kandarin.handlers + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.impl.Animator.Priority +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.npc.agg.AggressiveBehavior +import core.game.node.entity.npc.agg.AggressiveHandler +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/* + * Behavior is based on the following sources: + * https://www.youtube.com/watch?v=u2A-_ihV_2w (november 2008) + * https://www.youtube.com/watch?v=O0EIZu7-iys (august 2009) + * https://runescape.wiki/w/Tortoise?oldid=815524 https://web.archive.org/web/20090308094532/http://runescape.wikia.com/wiki/Tortoise + * + * todo fix att, str, def? I'm not sure what is the typical way to calculate these when they are unknown. + * level 79: HP 101, max hit 12, "low attack but good defence" + * level 92: HP 121, max hit 12, "low attack but good defence" + * + * https://runescape.wiki/w/Gnome_Archer?oldid=949978 https://web.archive.org/web/20090929124104/http://runescape.wikia.com/wiki/Gnome_archer + * https://runescape.wiki/w/Gnome_Driver?oldid=1235409 https://web.archive.org/web/20090929124109/http://runescape.wikia.com:80/wiki/Gnome_driver + * https://runescape.wiki/w/Gnome_Mage?oldid=1425756 https://web.archive.org/web/20090928131013/http://runescape.wikia.com:80/wiki/Gnome_mage + */ + +@Initializable +class GnomeTortoiseNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeTortoiseNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TORTOISE_3808) + } + + fun spawnGnomes(location: Location, direction: Direction) { + + //todo: sometimes the child spawns or direction is wrong on death. does a move trigger right before it dies? + + var archerLoc = location + var driverLoc = location + var mageLoc = location + + // X D X 3x3 turtle, C center + // M C A D driver, M mage, A archer + // X X X + + // If I was smart I could probably do this with vectors, but I'm dumb so just doing the possibilities by hand. + if(direction == Direction.NORTH) { + archerLoc = location.transform(1,0,0) + driverLoc = location.transform(0,1,0) + mageLoc = location.transform(-1,0,0) + }else if(direction == Direction.NORTH_EAST) { + archerLoc = location.transform(1,-1,0) + driverLoc = location.transform(1,1,0) + mageLoc = location.transform(-1,1,0) + }else if(direction == Direction.EAST) { + archerLoc = location.transform(0,-1,0) + driverLoc = location.transform(1,0,0) + mageLoc = location.transform(0,1,0) + }else if(direction == Direction.SOUTH_EAST) { + archerLoc = location.transform(-1,-1,0) + driverLoc = location.transform(1,-1,0) + mageLoc = location.transform(1,1,0) + }else if(direction == Direction.SOUTH) { + archerLoc = location.transform(-1,0,0) + driverLoc = location.transform(0,-1,0) + mageLoc = location.transform(1,0,0) + }else if(direction == Direction.SOUTH_WEST) { + archerLoc = location.transform(-1,1,0) + driverLoc = location.transform(-1,-1,0) + mageLoc = location.transform(1,-1,0) + }else if(direction == Direction.WEST) { + archerLoc = location.transform(0,1,0) + driverLoc = location.transform(-1,0,0) + mageLoc = location.transform(0,-1,0) + }else if(direction == Direction.NORTH_WEST) { + archerLoc = location.transform(1,1,0) + driverLoc = location.transform(-1,1,0) + mageLoc = location.transform(-1,-1,0) + } + + val npcArcher = GnomeArcherNPC(NPCs.GNOME_ARCHER_3814, archerLoc) + npcArcher.sendChat("Argh!") + npcArcher.init() + + val npcDriver = GnomeDriverNPC(NPCs.GNOME_DRIVER_3815, driverLoc) + npcDriver.sendChat("Nooooo! Dobbie's dead!") + npcDriver.init() + + val npcMage = GnomeMageNPC(NPCs.GNOME_MAGE_3816, mageLoc) + npcMage.sendChat("Kill the infidel!") + npcMage.init() + } + + override fun finalizeDeath(killer: Entity?) { + val turtleLoc = this.centerLocation + val turtleDir = this.direction + spawnGnomes(turtleLoc, turtleDir) + super.finalizeDeath(killer) + // todo remove this debug if not needed. It's just telling me the "direction" the tortoise dies in so I can verify the direction that the child NPCs should spawn. + if (killer is Player) { + killer.debug(direction.toString()) + } + } +} + +//handles the attack switching +class GnomeTortoiseBehavior : NPCBehavior(NPCs.TORTOISE_3808) { + + private val combatHandler = MultiSwingHandler( + true, + // per wiki source, melee has a max hit of 12 + SwitchAttack( + CombatStyle.MELEE.swingHandler, + Animation(3953, Priority.HIGH) + ), + // todo correct the projectile locations (they should originate from the range or mage gnome, not the center). not sure how to do this. + SwitchAttack( + CombatStyle.RANGE.swingHandler, + Animation(3954, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 10, //bronze arrow + 35, + 30, + 10, + 50, + 14, + 255 + ) + ), + // per wiki source above, spell should be water strike with the sounds of ice barrage + SwitchAttack( + CombatStyle.MAGIC.swingHandler, + Animation(3955, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 94, //water strike + 35, + 30, + 10, + 50, + 14, + 255 + ) + ) + ) + + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return combatHandler + } +} + +// Handles Gnome Archer behavior +class GnomeArcherNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeArcherNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_ARCHER_3814) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeArcherBehavior : NPCBehavior(NPCs.GNOME_ARCHER_3814) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} + +// Handles Gnome Mage behavior +class GnomeMageNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeMageNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_MAGE_3816) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeMageBehavior : NPCBehavior(NPCs.GNOME_MAGE_3816) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} + +// Handles Gnome Driver behavior +class GnomeDriverNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GnomeDriverNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GNOME_DRIVER_3815) + } + + override fun init() { + super.init() + this.isRespawn = false + this.isAggressive = true + this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() { + override fun ignoreCombatLevelDifference(): Boolean { + return true + } + }) + } +} + +class GnomeDriverBehavior : NPCBehavior(NPCs.GNOME_DRIVER_3815) { + override fun onCreation(self: NPC) { + // stops the entity from instantly moving. + delayEntity(self, 1) + setAttribute(self, "despawn-time", GameWorld.ticks + 25) + } + + override fun tick(self: NPC): Boolean { + if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)) + self.clear() + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java b/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java deleted file mode 100644 index b75407700..000000000 --- a/Server/src/main/content/region/kandarin/handlers/RasoloNPC.java +++ /dev/null @@ -1,84 +0,0 @@ -package content.region.kandarin.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class RasoloNPC extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable { - new RosaloDialouge().init(); - NPCDefinition.forId(1972).getHandlers().put("option:talk-to",this); - NPCDefinition.forId(1972).getHandlers().put("option:trade",this); - return null; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if(option.equals("trade")){ - new NPC(1972).openShop(player); - } else { - player.getDialogueInterpreter().open(1972); - } - return false; - } - - /** - * Rasolo npc - * @author ceik - */ - - public class RosaloDialouge extends DialoguePlugin{ - public RosaloDialouge(){ - /** - * - */ - } - public RosaloDialouge(Player player){super(player);} - - @Override - public DialoguePlugin newInstance(Player player) { - return new RosaloDialouge(player); - } - - @Override - public boolean open(Object... args){ - npc("Hello, would you like to see my wares?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage){ - case 0: - player.getDialogueInterpreter().sendOptions("Select one", "Yes, please", "No, thanks"); - stage++; - break; - case 1: - switch(buttonId){ - case 1: - end(); - new NPC(1972).openShop(player); - break; - case 2: - player("No, thanks"); - stage++; - break; - } - break; - case 2: - end(); - break; - } - return true; - } - @Override - public int[] getIds() {return new int[] {1972};} - } -} diff --git a/Server/src/main/content/region/kandarin/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..b1233b698 100644 --- a/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt @@ -1,115 +1,101 @@ package content.region.kandarin.pisc.dialogue -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.player.Player +import core.ServerConstants +import core.api.addItemOrDrop +import core.api.hasActivatedSecondaryBankAccount +import core.api.hasIronmanRestriction +import core.api.isUsingSecondaryBankAccount +import core.api.openBankAccount +import core.api.openBankPinSettings +import core.api.openGrandExchangeCollectionBox +import core.api.openNpcShop +import core.api.toggleBankAccount +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.IronmanMode -import core.plugin.Initializable +import core.game.node.item.Item import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.game.dialogue.IfTopic -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE -/** - * Provides the regular dialogue for Arnold Lydspor. - * TODO: Swan Song quest will need a special case handling. - * - * @author vddCore - */ -@Initializable -class ArnoldLydsporDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - START_DIALOGUE -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Ah, you come back! What you want from Arnold, heh?" - ).also { stage++ } - - 1 -> showTopics( - IfTopic( - core.game.dialogue.FacialExpression.ASKING, - "Can you open my bank account, please?", - 2, - !hasIronmanRestriction(player, IronmanMode.ULTIMATE) - ), - - IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, - "I'd like to check my bank PIN settings.", - 3, - !hasIronmanRestriction(player, IronmanMode.ULTIMATE) - ), - IfTopic( - core.game.dialogue.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) - ) - - 2 -> { - openBankAccount(player) - end() - } - - 3 -> { - openBankPinSettings(player) - end() - } - - 4 -> { - openGrandExchangeCollectionBox(player) - end() - } - - 5 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, - "Ja, I have wide range of stock..." - ).also { stage++ } - - 6 -> { - openNpcShop(player, NPCs.ARNOLD_LYDSPOR_3824) - end() - } - - 7 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, - "Ah, that is nice - always I like to chat, but " + - "Herr Caranos tell me to get back to work! " + - "Here, you been nice, so have a present." - ).also { stage++ } - - 8 -> sendItemDialogue( - player, - Items.CABBAGE_1965, - "Arnold gives you a cabbage." - ).also { - addItemOrDrop(player, Items.CABBAGE_1965) - stage++ - } - - 9 -> playerl( - core.game.dialogue.FacialExpression.HALF_THINKING, - "A cabbage?" - ).also { stage++ } - - 10 -> npcl( - core.game.dialogue.FacialExpression.HAPPY, - "Ja, cabbage is good for you!" - ).also { stage++ } - - 11 -> playerl( - core.game.dialogue.FacialExpression.NEUTRAL, - "Um... Thanks!" - ).also { stage = END_DIALOGUE } +class ArnoldLydsporDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, ArnoldLydsporLabellerFile(), node as NPC) + return@on true + } + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "bank") { player, _ -> + openBankAccount(player) + return@on true + } + on(NPCs.ARNOLD_LYDSPOR_3824, IntType.NPC, "collect") { player, _ -> + openGrandExchangeCollectionBox(player) + return@on true } - - return true } - override fun getIds(): IntArray = intArrayOf(NPCs.ARNOLD_LYDSPOR_3824) -} \ No newline at end of file + class ArnoldLydsporLabellerFile : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.FRIENDLY, "Ah, you come back! What you want from Arnold, heh?") + goto("main options") + + label("main options") + options( + DialogueOption("access", "Can you open my bank account please?", expression = ChatAnim.ASKING) { player, _ -> !hasIronmanRestriction(player, IronmanMode.ULTIMATE) }, + DialogueOption("buy second bank", "I'd like to open a secondary bank account.") { player, _ -> return@DialogueOption !hasIronmanRestriction(player, IronmanMode.ULTIMATE) && ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) }, + DialogueOption("switch second bank", "I'd like to switch to my primary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && isUsingSecondaryBankAccount(player) }, + DialogueOption("switch second bank", "I'd like to switch to my secondary bank account.") { player, _ -> return@DialogueOption hasActivatedSecondaryBankAccount(player) && !isUsingSecondaryBankAccount(player) }, + DialogueOption("pin", "I'd like to check my PIN settings.", expression = ChatAnim.NEUTRAL) { player, _ -> !hasIronmanRestriction(player, IronmanMode.ULTIMATE) }, + DialogueOption("collect", "I'd like to collect items.", expression = ChatAnim.NEUTRAL) { player, _ -> !hasIronmanRestriction(player, IronmanMode.STANDARD) }, + DialogueOption("trade", "Would you like to trade?", expression = ChatAnim.ASKING), + DialogueOption("chat", "Nothing, I just came to chat.", expression = ChatAnim.FRIENDLY) { player, _ -> return@DialogueOption hasIronmanRestriction(player, IronmanMode.STANDARD) || !ServerConstants.SECOND_BANK } + ) + + label("access") + exec { player, _ -> openBankAccount(player) } + goto("nowhere") + + label("buy second bank") + npc(ChatAnim.GUILTY, "I'm so sorry! My little shop does not have the fibre-optic connections to be able to open a second account. Try asking again in a brick-and-mortar building in a big city!") + player(ChatAnim.FRIENDLY, "That's okay, I understand. I will ask again in a big bank like Varrock's.") + goto("nowhere") + + label("switch second bank") + exec { + player, _ -> toggleBankAccount(player) + loadLabel(player, if (isUsingSecondaryBankAccount(player)) "now secondary" else "now primary") + } + label("now primary") + npc("Your active bank account has been switched. You can now access your primary account.") + goto("main options") + label("now secondary") + npc("Your active bank account has been switched. You can now access your secondary account.") + goto("main options") + + label("pin") + exec { player, _ -> openBankPinSettings(player) } + goto("nowhere") + + label("collect") + exec { player, _ -> openGrandExchangeCollectionBox(player) } + goto("nowhere") + + label("trade") + npc(ChatAnim.FRIENDLY, "Ja, I have wide range of stock...") + exec { player, _ -> openNpcShop(player, NPCs.ARNOLD_LYDSPOR_3824) } + goto("nowhere") + + label("chat") + npc("Ah, that is nice - always I like to chat, but Herr Caranos tell me to get back to work! Here, you been nice, so have a present.") + exec { player, _ -> addItemOrDrop(player, Items.CABBAGE_1965) } + item(Item(Items.CABBAGE_1965), "Arnold gives you a cabbage.") + player(ChatAnim.HALF_THINKING, "A cabbage?") + npc(ChatAnim.HAPPY, "Ja, cabbage is good for you!") + player(ChatAnim.NEUTRAL, "Um... Thanks!") + goto("nowhere") + } + } +} diff --git a/Server/src/main/content/region/kandarin/pisc/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/dialogue/MathiasFlaconryDialogue.java b/Server/src/main/content/region/kandarin/pisc/dialogue/MathiasFlaconryDialogue.java index dd3a27bd2..b926d7fde 100644 --- a/Server/src/main/content/region/kandarin/pisc/dialogue/MathiasFlaconryDialogue.java +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/MathiasFlaconryDialogue.java @@ -8,6 +8,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 MathiasFlaconryDialogue dialogue. @@ -49,7 +50,8 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { } break; case 323: - end(); + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "Well, you're welcome to come back if you change", "your mind."); + stage = 967; break; case 95: if (player.getBank().containsItem(FALCON) || player.getEquipment().containsItem(FALCON) || player.getInventory().containsItem(FALCON)) { @@ -58,7 +60,7 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { return true; } if (player.getEquipment().get(EquipmentContainer.SLOT_HANDS) != null || player.getEquipment().get(EquipmentContainer.SLOT_SHIELD) != null || player.getEquipment().get(EquipmentContainer.SLOT_WEAPON) != null) { - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Sorry, free your hands, weapon, and shield slot first."); + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Sorry, you really need both hands free for falconry. I'd", "suggest that you put away your weapons and gloves before", "we start."); stage = 99; break; } @@ -69,11 +71,11 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { stage = 97; } else { end(); - player.getPacketDispatch().sendMessage("You need 500 gold goins."); + player.getPacketDispatch().sendMessage("You need 500 gold coins."); } break; case 97: - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Don't worry: I'll keep and eye on you to make sure", "you don't upset it roo much."); + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Don't worry; I'll keep an eye on you to make sure", "you don't upset it too much."); stage = 99; break; case 99: @@ -84,22 +86,109 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { stage = 501; break; case 501: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could I have a go with your bird?"); + interpreter.sendOptions("Select an Option", "Do you have any quests I could do?", "What is this place?", "Could I have a go with your bird?"); stage = 502; break; case 502: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you have any quests I could do?"); + stage = 600; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "What is this place?"); + stage = 700; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Could I have a go with your bird?"); + stage = 800; + break; + } + break; + case 600: + interpreter.sendDialogues(5093, FacialExpression.ASKING, "A quest? What a strange notion. Do you normally go", "around asking complete strangers for quests?"); + stage = 601; + break; + case 601: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Er, yes, now you come to mention it."); + stage = 602; + break; + case 602: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Oh, ok then. Well, no, I don't; sorry."); + stage = 967; + break; + + case 700: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "A good question; straight and to the point. My name is ", "Matthias, I am a falconer, and this is where I train", "my birds."); + stage = 701; + break; + case 701: + interpreter.sendOptions("Select an Option", "Do you have any quests I could do?", "That sounds like fun; could I have a go?", "That doesn't sound like my sort of thing.", "What's this falconry thing all about then?"); + stage = 702; + break; + case 702: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you have any quests I could do?"); + stage = 600; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "That sounds like fun; could I have a go?"); + stage = 800; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That doesn't sound like my sort of thing."); + stage = 720; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's this falconry thing all about then?"); + stage = 750; + break; + } + break; + case 720: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Fair enough; it does require a great deal of patience and", "skill, so I can understand if you might feel intimidated."); + stage = 967; + break; + case 750: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "Well, some people see it as a sport, although such a term", "does not really convey the amount of patience and", "dedication required to be proficient at the task."); + stage = 751; + break; + case 751: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "Putting it simply, it is the training and use of birds of", "prey in hunting quarry."); + stage = 752; + break; + case 752: + interpreter.sendDialogues(player, FacialExpression.ASKING, "So it's like keeping a pet then?"); + stage = 753; + break; + case 753: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "Not exactly, no. Such a bird can never really be", "considered tame in the same way that a dog can."); + stage = 754; + break; + case 754: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "They can be trained to associate people or places with", "food though, and, as such, a good falconer can get a", "trained bird to do as he wishes."); + stage = 701; + break; + + case 800: if (player.getSkills().getLevel(Skills.HUNTER) < 43) { npc("Try coming back when you're more experienced", "I wouldn't want my birds being injured."); stage = 967; return true; } - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Training falcons is a lot of work and I", "doubt you're up to the task. However, I suppose", "I could let you try hunting with one."); - stage = 503; + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Training falcons is a lot of work and I doubt you're up", "to the task. However, I suppose I could let you try", "hunting with one."); + stage = 801; break; - case 503: - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "I have some tamer birds that I occasionally lend to rich", "noblemen who consider it a sufficiently refined sport for", "their tastes. and you look like the kind who might", "appreciate a good hunt."); + case 801: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "I have some tamer birds that I occasionally lend to rich", "noblemen who consider it a sufficiently refined sport for", "their tastes, and you look like the kind who might", "appreciate a good hunt."); + stage = 802; + break; + case 802: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "I'd have to request a small fee, mind you; how does", "500 gold pieces sound?"); stage = 90; break; + case 900: interpreter.sendOptions("Select an Option", "Yes, please.", "No thank you."); stage = 901; @@ -125,7 +214,48 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { case 967: end(); break; + + case 1000: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Ah, you're back. How are you getting along with her then?"); + stage = 1001; + break; + case 1001: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "It's certainly harder than it looks."); + stage = 1002; + break; + case 1002: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Sorry, but I was talking to the falcon, not you. But yes it", "is. Have you had enough yet?"); + stage = 1003; + break; + case 1003: + interpreter.sendOptions("Select an Option", "Actually, I'd like to keep trying a little longer.", "I think I'll leave it for now."); + stage = 1004; + break; + case 1004: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Actually, I'd like to keep trying a little longer."); + stage = 1010; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I think I'll leave it for now."); + stage = 1020; + break; + } + break; + case 1010: + interpreter.sendDialogues(5093, FacialExpression.NEUTRAL, "Ok then, just come talk to me when you're done."); + stage = 967; + break; + case 1020: + player.getInventory().remove(FALCON); + player.getEquipment().remove(FALCON, true); + + interpreter.sendDialogue("You give the falcon and glove back to Matthias."); + stage = 967; + break; } + return true; } @@ -145,6 +275,11 @@ public class MathiasFlaconryDialogue extends DialoguePlugin { stage = 900; return true; } + if (player.getEquipment().contains(10024, 1) || player.getInventory().contains(10024, 1)) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + stage = 1000; + return true; + } if (args.length == 2) quick = true; if (quick) { 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..876f9cfda 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. @@ -40,6 +44,7 @@ public class DwarfCannonPlugin extends OptionHandler { SceneryDefinition.forId(3).getHandlers().put("option:open", this); SceneryDefinition.forId(5).getHandlers().put("option:inspect", this); SceneryDefinition.forId(2).getHandlers().put("option:enter", this); + SceneryDefinition.forId(11).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(13).getHandlers().put("option:climb-over", this); SceneryDefinition.forId(15601).getHandlers().put("option:inspect", this); for (int i = 15; i < 21; i++) { @@ -57,14 +62,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 +86,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))) { @@ -106,6 +111,13 @@ public class DwarfCannonPlugin extends OptionHandler { player.animate(Animation.create(845)); player.teleport(new Location(2619, 9797, 0), 2); break; + case 11: + if (node.getLocation().equals(new Location(2570, 3443, 1)) + && quest.getStage(player) == 20 + && !player.hasItem(DwarfCannon.DWARF_REMAINS)) { + setVarp(player, 0, 3, true); + } + return ClimbActionHandler.climbLadder(player, node.asScenery(), option); case 13: ClimbActionHandler.climb(player, new Animation(828), new Location(2623, 3391, 0)); break; @@ -120,6 +132,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 +177,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 +203,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 +215,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 +303,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 +314,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 +327,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 +370,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 +448,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..9923b7426 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionsNotes.kt @@ -0,0 +1,41 @@ +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.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..cf4ac3326 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCManual.kt @@ -0,0 +1,97 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..186d85616 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt @@ -0,0 +1,88 @@ +package content.region.kandarin.quest.grandtree + +import core.ServerConstants +import core.api.sendChat +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.world.update.flag.context.Animation +import org.rs09.consts.NPCs + +// Source video https://www.youtube.com/watch?v=LS1Xtuz0sLA +class BlackDemonCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location.create(2491, 9864, 0)) + if(player.settings.isRunToggled){ + player.settings.toggleRun() + } + loadRegion(9882) + addNPC(NPCs.GLOUGH_671, 48, 8, Direction.WEST) + } + + 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)!!) + addNPC(NPCs.BLACK_DEMON_677, 39, 8, Direction.EAST) + move(getNPC(NPCs.BLACK_DEMON_677)!!, 41, 8) + 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.OLD_ANGRY1, "You really are becoming a headache! Well, at least now you can die knowing you were right, it will save me having to hunt you down like all the other human filth of " + ServerConstants.SERVER_NAME + "!") + } + 7 -> { + playerDialogueUpdate(FacialExpression.SCARED, "You're crazy, Glough!") + } + 8 -> { + dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.OLD_ANGRY1, "Bah! Well, soon you'll see, the gnomes are ready to fight. In three weeks this tree will be dead wood, in ten weeks it will be 30 battleships! Finally we will rid the world of the disease called humanity!") + } + 9 -> { + playerDialogueUpdate(FacialExpression.SCARED, "What makes you think I'll let you get away with it?") + } + 10 -> { + moveCamera(55, 8, 700) + rotateCamera(48, 8) + val demon = getNPC(NPCs.BLACK_DEMON_677)!! + move(demon, 46, 8) + demon.animate(Animation(64, 140)) + dialogueUpdate(NPCs.GLOUGH_671, FacialExpression.OLD_ANGRY1, "Fool...meet my little friend!") + } + 11 -> { + end(fade = false){ + 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..a0dd8b258 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt @@ -0,0 +1,215 @@ +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 +import content.global.handlers.iface.ScrollInterface +import content.global.handlers.iface.ScrollLine +import org.rs09.consts.Components + +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), + ) + + // https://youtu.be/-jfueZpRhzU?t=184 + val hazelmereScrollText = arrayOf( + ScrollLine("Es lemanto meso pro eis prit ta Cinqo mond.", 6), + ScrollLine("Mi lovos ta lemanto Daconia arpos", 7), + ScrollLine("et Daconia arpos eto meriz ta priw!", 8) + ) + + 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(Items.HAZELMERES_SCROLL_786, IntType.ITEM, "read") { player, node -> + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, hazelmereScrollText) + 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 + } + } +} + 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..2a4bc37c5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/TranslationBook.kt @@ -0,0 +1,141 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt new file mode 100644 index 000000000..7998584f2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/AnnaDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonAnna +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class AnnaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, AnnaDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return AnnaDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ANNA_814, NPCs.ANNA_6192) + } +} + +class AnnaDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "Oh really? What do you want to know then?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 6), + IfTopic("Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 10, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("It was clearly an intruder.").also { stage++ } + 4 -> playerl("Well, I don't think it was.").also { stage++ } + 5 -> npcl("It was one of our lazy servants then.").also { stage = END_DIALOGUE } + + 6 -> npcl("In the library. No one else was there so you'll just have to take my word for it.").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show Anna the thread from the study.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1809)) stage++ else stage = 9 } + 8 -> npcl("It's some Green thread. It's not exactly uncommon is it? My trousers are made of the same material.").also { stage = END_DIALOGUE } + + 9 -> npcl("Not really, no. Thread is fairly common.").also { stage = END_DIALOGUE } + + 10 -> npcl(FacialExpression.ANNOYED, "That useless Gardener Stanford let his compost heap fester. It's an eyesore to the garden! So I bought some poison from a travelling salesman so that I could kill off some of the wildlife living in it.") + .also { setAttribute(player!!, attributeAskPoisonAnna, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt new file mode 100644 index 000000000..75455b51d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/BobDialogue.kt @@ -0,0 +1,61 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonBob +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BobDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, BobDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return BobDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BOB_815, NPCs.BOB_6193) + } +} + +class BobDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("I suppose I had better talk to you then.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic(FacialExpression.THINKING, "Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 10, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't really care as long as no one thinks it's me. Maybe it was that strange poison seller who headed towards the seers village.").also { stage = END_DIALOGUE } + + 4 -> npcl("I was walking by myself in the garden.").also { stage++ } + 5 -> playerl("And can anyone vouch for that?").also { stage++ } + 6 -> npcl("No. But I was.").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show him the thread you discovered.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1808)) stage++ else stage = 9 } + 8 -> npcl("It's some red thread. I suppose you think that's some kind of clue? It looks like the material my trousers are made of.").also { stage = END_DIALOGUE } + + 9 -> npcl(FacialExpression.THINKING, "It's some thread. Great clue. No, really.").also { stage = END_DIALOGUE } + + 10 -> npcl(FacialExpression.THINKING, "What's it to you anyway?").also { stage++ } + 11 -> npcl(FacialExpression.ANGRY, "If you absolutely must know, we had a problem with the beehive in the garden, and as all of our servants are so pathetically useless, I decided I would deal with it myself. So I did.") + .also { setAttribute(player!!, attributeAskPoisonBob, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt new file mode 100644 index 000000000..66b283ccf --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/CarolDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonCarol +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class CarolDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, CarolDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return CarolDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.CAROL_816, NPCs.CAROL_6194) + } +} + +class CarolDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("Well, ask what you want to know then.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic("Do you recognise this thread?", 5, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 8, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't know. I think it's very convenient that you have arrived here so soon after it happened. Maybe it was you.").also { stage = END_DIALOGUE } + + 4 -> npcl("Why? Are you accusing me of something? You seem to have a very high opinion of yourself. I was in my room if you must know, alone.").also { stage = END_DIALOGUE } + + 5 -> sendDialogue(player!!, "You show Carol the thread found at the crime scene.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1808)) stage++ else stage = 7 } + 6 -> npcl("It's some red thread... it kind of looks like the Same material as my trousers. But obviously it's not.").also { stage = END_DIALOGUE } + + 7 -> npcl("It's some thread. Sorry, do you have a point here? Or do you just enjoy wasting peoples time?").also { stage = END_DIALOGUE } + + 8 -> npcl(FacialExpression.THINKING, "I don't see what on earth it has to do with you, but the drain outside was").also { stage++ } + 9 -> npcl(FacialExpression.ANNOYED, "blocked, and as nobody else here has the intelligence to even unblock a simple drain I felt I had to do it myself.") + .also { setAttribute(player!!, attributeAskPoisonCarol, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt new file mode 100644 index 000000000..b25862064 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/DavidDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonDavid +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class DavidDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DavidDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DavidDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DAVID_817, NPCs.DAVID_6195) + } +} + +class DavidDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("And? Make this quick. I have better things to do than be interrogated by halfwits all day.").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 5), + IfTopic("Do you recognise this thread?", 6, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("I don't really know or care. Frankly, the old man deserved to die.").also { stage++ } + 4 -> npcl("There was a suspicious red headed man who came to the house the other day selling poison now I think about it. Last I saw he was headed towards the tavern in the Seers village.").also { stage = END_DIALOGUE } + + 5 -> npcl("That is none of your business. Are we finished now, or are you just going to stand there irritating me with your idiotic questions all day?").also { stage = END_DIALOGUE } + + 6 -> sendDialogue(player!!, "You show him the thread you found on the study window.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1809)) stage++ else stage = 8 } + 7 -> npcl("It's some Green thread, like my trousers are made of. Are you finished? I'm not sure which I dislike more bout you, your face or your general bad odour.").also { stage = END_DIALOGUE } + + 8 -> npcl("No. Can I go yet? Your face irritates me.").also { stage = END_DIALOGUE } + + 9 -> npcl(FacialExpression.ANGRY, "There was a nest of spiders upstairs between the two servants' quarters. Obviously I had to kill them before our pathetic servants whined at my father some more.").also { stage++ } + 10 -> npcl(FacialExpression.THINKING, "Honestly, it's like they expect to be treated like royalty! If I had my way I would fire the whole workshy lot of them!") + .also { setAttribute(player!!, attributeAskPoisonDavid, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt new file mode 100644 index 000000000..28fae9bbf --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/DonovanDialogue.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class DonovanDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DonovanDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DonovanDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONOVAN_THE_FAMILY_HANDYMAN_806) + } +} + +class DonovanDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("I have no interest in talking to gawkers.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl("I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("Oh... I really couldn't say. I wouldn't really want to point any fingers at anybody. If I had to make a guess I'd have to say it was probably Bob though.").also { stage++ } + 4 -> npcl("I saw him arguing with Lord Sinclair about some missing silverware from the Kitchen. It was a very heated argument.").also { stage = END_DIALOGUE } + + 5 -> npcl("Me? I was sound asleep here in the servants Quarters. It's very hard work as a handyman around here. There's always something to do!").also { stage = END_DIALOGUE } + + 6 -> npcl("Hmmm... No, I didn't, but I sleep very soundly at night.").also { stage++ } + 7 -> playerl("So you didn't hear any sounds of a struggle or any barking from the guard dog next to his study window?").also { stage++ } + 8 -> npcl("Now you mention it, no. It is odd I didn't hear anything like that. But I do sleep very soundly as I said and wouldn't necessarily have heard it if there was any such noise.").also { stage = END_DIALOGUE } + + 9 -> npcl("Well, I do know Frank bought some poison recently to clean the family crest that's outside.").also { stage++ } + 10 -> npcl("It's very old and rusty, and I couldn't clean it myself, so he said he would buy some cleaner and clean it himself. He probably just got some from that Poison Salesman who came to the door the other day...").also { stage++ } + 11 -> npcl("You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} + diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt new file mode 100644 index 000000000..faacbbe69 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/ElizabethDialogue.kt @@ -0,0 +1,64 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonElizabeth +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ElizabethDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ElizabethDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return ElizabethDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELIZABETH_818, NPCs.ELIZABETH_6196) + } +} + +class ElizabethDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("What's so important you need to bother me with then?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you when the murder happened?", 4), + IfTopic("Do you recognise this thread?", 7, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 3 -> npcl("Could have been anyone. The old man was an idiot. He's been asking for it for years.").also { stage = END_DIALOGUE } + + 4 -> npcl("I was out.").also { stage++ } + 5 -> playerl("Care to be any more specific?").also { stage++ } + 6 -> npcl("Not really. I don't have to justify myself to the likes of you, you know. I know the King personally you know. Now are we finished here?").also { stage = END_DIALOGUE } + + 7 -> sendDialogue(player!!, "You show her the thread from the study window.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1810)) stage++ else stage = 11 } + 8 -> npcl(" Looks like Blue thread to me. If you can't work that out for yourself I don't hold much hope of you solving this crime.").also { stage++ } + 9 -> playerl("It looks a lot like the material your trousers are made of doesn't it?").also { stage++ } + 10 -> npcl("I suppose it does. So what?").also { stage = END_DIALOGUE } + + 11 -> npcl(" It's some thread. You're not very good at this whole investigation thing are you?").also { stage = END_DIALOGUE } + + 12 -> npcl("There was a nest of mosquitos under the fountain in the garden, which I killed with poison the other day. You can see for yourself if you're capable of managing that, which I somehow doubt.").also { stage++ } + 13 -> playerl(FacialExpression.ANNOYED, "I hate mosquitos!").also { stage++ } + 14 -> npcl("Doesn't everyone?") + .also { setAttribute(player!!, attributeAskPoisonElizabeth, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt new file mode 100644 index 000000000..952be8a03 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FlypaperDialogue.kt @@ -0,0 +1,26 @@ +package content.region.kandarin.quest.murdermystery + +import core.api.* +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +class FlypaperDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogue(player!!, "There's some flypaper in there. Should I take it?").also { stage++ } + 1 -> options("Yes, it might be useful.", "No, I don't see any need for it.").also { stage++ } + 2 -> when(buttonID) { + 1 -> if (addItem(player!!, Items.FLYPAPER_1811)){ + sendDialogue(player!!, "You take a piece of fly paper. There is still plenty of fly paper left.") + .also { stage = END_DIALOGUE } + } + else { + sendDialogue(player!!, "You don't have enough space in your inventory.") + .also { stage = END_DIALOGUE } + } + 2 -> sendDialogue(player!!, "You leave the paper in the sack.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt new file mode 100644 index 000000000..95ef1d79e --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FountainDialogue.kt @@ -0,0 +1,15 @@ +package content.region.kandarin.quest.murdermystery + +import core.api.* +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE + +class FountainDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogue(player!!, "The fountain is swarming with mosquitos. There's a nest of them underneath the fountain.").also { stage++ } + 1 -> playerl("I hate mosquitos, they're so annoying!").also { stage++ } + 2 -> sendDialogue(player!!, "It's certainly clear nobody's used poison here.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt new file mode 100644 index 000000000..15e8af9bb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/FrankDialogue.kt @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonFrank +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class FrankDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, FrankDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return FrankDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.FRANK_819, NPCs.FRANK_6197) + } +} + +class FrankDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl("Good for you. Now what do you want?").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "...And can you spare me any money? I'm a little short...").also { stage++ } + 3 -> showTopics( + Topic("Who do you think is responsible?", 4), + Topic( "Where were you when the murder happened?", 5), + IfTopic("Do you recognise this thread?", 6, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810)), + IfTopic("Why'd you buy poison the other day?", 9, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + 4 -> npcl("I don't know. You don't know how long it takes an inheritance to come through do you? I could really use that money pretty soon...").also { stage = END_DIALOGUE } + + 5 -> npcl("I don't know, somewhere around here probably. Could you spare me a few coins? I'll be able to pay you back double tomorrow it's just there's this poker night tonight in town...").also { stage = END_DIALOGUE } + + 6 -> sendDialogue(player!!, "Frank examines the thread from the crime scene.") + .also { if (inInventory(player!!, Items.CRIMINALS_THREAD_1810)) stage++ else stage = 8 } + 7 -> npcl("It kind of looks like the same material as my trousers are made of... same colour anyway. Think it's worth anything? Can I have it? Or just some money?").also { stage = END_DIALOGUE } + + 8 -> npcl("It looks like thread to me, but I'm not exactly an expert. Is it worth something? Can I have it? Actually, can you spare me a few gold?").also { stage = END_DIALOGUE } + + 9 -> npcl("Would you like to buy some? I'm kind of strapped for cash right now. I'll sell it to you cheap. It's hardly been used at all.").also { stage++ } + 10 -> npcl("I just used a bit to clean that family crest outside up a bit. Do you think I could get much money for the family crest, actually? It's cleaned up a bit now.") + .also { setAttribute(player!!, attributeAskPoisonFrank, true)} + .also { stage = END_DIALOGUE } + } + 100 -> npcl("Apparently you aren't as stupid as you look.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt new file mode 100644 index 000000000..095fd175d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/GossipDialogue.kt @@ -0,0 +1,133 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +@Initializable +class GossipDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GossipDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return GossipDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOSSIP_813) + } +} + +class GossipDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("There's some kind of commotion up at the Sinclair place I hear. Not surprising all things considered.").also { stage = END_DIALOGUE} + 1 -> when (stage) { + 0 -> playerl("I'm investigating the murder up at the Sinclair place.").also { stage++ } + 1 -> npcl("Murder is it? Well, I'm not really surprised...").also { stage++ } + 2 -> options("What can you tell me about the Sinclairs?", "Who do you think was responsible?", "I think the butler did it.", "I am so confused about who did it.").also { stage++ } + 3 -> when(buttonID) { + 1 -> playerl("What can you tell me about the Sinclairs?").also { stage = 4 } + 2 -> playerl("Who do you think was responsible?").also { stage = 40 } + 3 -> playerl("I think the butler did it.").also { stage = 42 } + 4 -> playerl("I am so confused about who did it.").also { stage = 43 } + } + 4 -> npcl("Well, what do you want to know?").also { stage++ } + 5 -> options("Tell me about Lord Sinclair.", "Why do the Sinclairs live so far from town?", "What can you tell me about his sons?", "What can you tell me about his daughters?").also { stage++ } + 6 -> when(buttonID) { + 1 -> playerl("Tell me about Lord Sinclair.").also { stage = 7 } + 2 -> playerl("Why do the Sinclairs live so far from town?").also { stage = 10 } + 3 -> playerl("What can you tell me about his sons?").also { stage = 12 } + 4 -> playerl("What can you tell me about his daughters?").also { stage = 15 } + } + 7 -> npcl("Old Lord Sinclair was a great man with a lot of respect around these parts. More than his worthless children have anyway.").also { stage++ } + 8 -> playerl("His children? They have something to gain by his death?").also { stage++ } + 9 -> npcl("Yes. You could say that. Not that I am one to gossip.").also { stage = END_DIALOGUE } + + 10 -> npcl("Well, they used to live in the big castle, but old Lord Sinclair gave it up so that those strange knights could live there instead. So the king built him a new house to the North.").also { stage++ } + 11 -> npcl("It's more cramped than his old place, but he seemed to like it. His children were furious at him for doing it though!").also { stage = END_DIALOGUE } + + 12 -> npcl("His sons eh? They all have their own skeletons in their cupboards. You'll have to be more specific. Who are you interested in exactly?").also { stage++ } + 13 -> options("Tell me about Bob.", "Tell me about David.", "Tell me about Frank.").also { stage++ } + 14 -> when(buttonID) { + 1 -> playerl("Tell me about Bob.").also { stage = 18 } + 2 -> playerl("Tell me about David.").also { stage = 23 } + 3 -> playerl("Tell me about Frank.").also { stage = 27 } + } + + 15 -> npcl("His daughters eh? They're all nasty pieces of work, which of them specifically did you want to know about?").also { stage++ } + 16 -> options("Tell me about Anna.", "Tell me about Carol.", "Tell me about Elizabeth.").also { stage++ } + 17 -> when(buttonID) { + 1 -> playerl("Tell me about Anna.").also { stage = 30 } + 2 -> playerl("Tell me about Carol.").also { stage = 33 } + 3 -> playerl("Tell me about Elizabeth.").also { stage = 36 } + } + + 18 -> npcl("Bob is an odd character indeed... I'm not one to gossip, but I heard Bob is addicted to Tea. He can't make").also { stage++ } + 19 -> npcl("it through the day without having at least 20 cups!").also { stage++ } + 20 -> npcl("You might not think that's such a big thing, but he has spent thousands of gold to feed his habit!").also { stage++ } + 21 -> npcl("At one point he stole a lot of silverware from the kitchen and pawned it just so he could afford to buy his daily tea allowance.").also { stage++ } + 22 -> npcl("If his father ever found out, he would be in so much trouble... he might even get disowned!").also { stage = END_DIALOGUE } + + 23 -> npcl("David... oh David... not many people know this, but David really has an anger problem. He's always screaming and shouting").also { stage++ } + 24 -> npcl("at the household servants when he's angry, and they live in a state of fear, always walking on eggshells around him, but none of them have the courage").also { stage++ } + 25 -> npcl("to talk to his father about his behaviour. If they did, Lord Sinclair would almost certainly").also { stage++ } + 26 -> npcl("kick him out of the house, as some of the servants have been there longer than he has, and he definitely has no right to treat them like he does... but I'm not one to gossip about people.").also { stage = END_DIALOGUE } + + 27 -> npcl("I'm not one to talk ill of people behind their back, but Frank is a real piece of work. He is an absolutely terrible gambler...he can't pass 2 dogs in the street without putting a bet on which one will bark first!").also { stage++ } + 28 -> npcl("He has already squandered all of his allowance, and I heard he had stolen a number of paintings of his fathers to sell to try and cover his debts, but he still owes a lot of people a lot of").also { stage++ } + 29 -> npcl("money. If his father ever found out, he would stop his income, and then he would be in serious trouble!").also { stage = END_DIALOGUE } + + 30 -> npcl("Anna... ah yes... Anna has 2 great loves:").also { stage++ } + 31 -> npcl("Sewing and gardening. But one thing she has kept secret is that she once had an affair with Stanford the gardener, and tried to get him fired when they broke up,").also { stage++ } + 32 -> npcl("by killing all of the flowers in the garden. If her father ever found out she had done that he would be so furious he would probably disown her.").also { stage = END_DIALOGUE } + + 33 -> npcl("Oh Carol... she is such a fool. You didn't hear this from me, but I heard a while ago she was conned out of a lot of money by a travelling salesman who sold her a box full").also { stage++ } + 34 -> npcl("of beans by telling her they were magic. But they weren't. She sold some rare books from the library to cover her debts, but").also { stage++ } + 35 -> npcl("her father would be incredibly annoyed if he ever found out - he might even throw her out of the house!").also { stage = END_DIALOGUE } + + 36 -> npcl("Elizabeth? Elizabeth has a strange problem... She cannot help herself, but is always stealing small objects - it's pretty sad that she is rich enough to afford to buy things, but would rather steal them instead.").also { stage++ } + 37 -> npcl("Now, I don't want to spread stories, but I heard she even stole a silver needle from her father that had great sentimental value for him.").also { stage++ } + 38 -> npcl("He was devastated when it was lost, and cried for a week thinking he had lost it!").also { stage++ } + 39 -> npcl("If he ever found out that it was her who had stolen it he would go absolutely mental, maybe even disowning her!").also { stage = END_DIALOGUE } + + 40 -> npcl("Well, I guess it could have been an intruder, but with that big guard dog of theirs I seriously doubt it. I suspect it was someone closer to home...").also { stage ++ } + 41 -> npcl("Especially as I heard that the poison salesman in the Seers' village made a big sale to one of the family the other day.") + .also { setAttribute(player!!, attributeNoiseClue, true)} + .also { stage = END_DIALOGUE } + + 42 -> npcl("And I think you've been reading too many cheap detective novels. Hobbes is kind of uptight, but his loyalty to old Lord Sinclair is beyond question.").also { stage = END_DIALOGUE } + + 43 -> playerl("Think you could give me any hints?") + .also { when (RandomFunction.random(4)) { //RS3 wiki seems to indicate this is fully random, not based off of progression + 0 -> stage = 44 + 1 -> stage = 46 + 2 -> stage = 47 + 3 -> stage = 48 + 4 -> stage = 51 + } + } + 44 -> npcl("Well, I don't know if it's related, but I heard from that Poison Salesman in town that he sold some poison to one of the Sinclair family").also { stage++ } + 45 -> npcl("the other day. I don't think he has any stock left now though...").also { stage = END_DIALOGUE } + + 46 -> npcl("Well, I don't know how much help this is, but I heard their guard dog will bark loudly at anyone it doesn't recognise. Maybe you should find out if anyone heard anything suspicious?") + .also { stage = END_DIALOGUE } + + 47 -> npcl("Well, this might be of some help to you. My father was in the guards when he was younger and he always said that there isn't a crime that can't be solved through careful examination of the crime scene and all surrounding areas.").also { stage = END_DIALOGUE } + + 48 -> npcl("I don't know how much help this is to you, but my dad was in the guard once and he told me the marks on your hands are totally unique. He calls them 'finger prints'.").also { stage++ } + 49 -> npcl("He said you can find them easily on any shiny metallic surface, by using a fine powder to mark out where the marks are and then using some sticky paper to lift the print from the object.").also { stage++ } + 50 -> npcl("I bet if you could find a way to get everyone's 'finger prints' you could solve the crime pretty easily!").also { stage = END_DIALOGUE } + + 51 -> npcl("My father used to be in the guard, he always wrote himself notes on a piece of paper so he could keep track of information easily.").also { stage++ } + 52 -> npcl("Maybe you should try that? Don't forget to thank me if I help you solve the case!").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt new file mode 100644 index 000000000..d661a6aae --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/GuardDialogue.kt @@ -0,0 +1,220 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeRandomMurderer +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.clueCount +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.solvedMystery +import core.ServerConstants.Companion.SERVER_NAME +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class GuardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GuardDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return GuardDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_812, NPCs.GUARD_6191) + } +} + +class GuardDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val KILLER = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 0 -> "Anna" + 1 -> "Bob" + 2 -> "Carol" + 3 -> "David" + 4 -> "Elizabeth" + 5 -> "Frank" + else -> "Anna" + } + val POISONSPOT = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 0 -> "compost heap" + 1 -> "beehive" + 2 -> "drain" + 3 -> "spiders nest" + 4 -> "fountain" + 5 -> "Sinclair Crest" + else -> "compost heap" + } + val KILLERMALE = when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 1, 3, 5 -> true + else -> false + } + val EVIDENCE = (1796..1822).toList().toIntArray() //All Murder Mystery related items are between these item ids + + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "What's going on here?").also { stage++ } + 1 -> npcl(FacialExpression.SAD, "Oh, it's terrible! Lord Sinclair has been murdered and we don't have any clues as to who or why. We're totally baffled!").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "If you can help us we will be very grateful.").also { stage++ } + 3 -> options("Sure, I'll help.", "You should do your own dirty work.").also { stage++ } + 4 -> when (buttonID) { + 1 -> playerl(FacialExpression.HAPPY, "Sure, I'll help.").also { stage++ } + 2 -> playerl("You should do your own dirty work.").also { stage = 8 } + } + 5 -> npcl(FacialExpression.HAPPY, "Thanks a lot!").also { stage++ } + 6 -> playerl(FacialExpression.NEUTRAL, "What should I be doing to help?").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "Look around and investigate who might be responsible. The Sarge said every murder leaves clues to who done it, but frankly we're out of our depth here.") + .also { setAttribute(player!!, attributeRandomMurderer, RandomFunction.random(5))} + .also { setQuestStage(player!!, Quests.MURDER_MYSTERY, 1) } + .also { stage = END_DIALOGUE } + + 8 -> npcl("Get lost then, this is private property! ...Unless you'd like to be taken in for questioning yourself?").also { stage = END_DIALOGUE } + } + 1 -> when (stage) { + 0 -> options("What should I be doing to help again?", "How did Lord Sinclair die?", "I know who did it!").also { stage++ } + 1 -> when(buttonID) { + 1 -> playerl("What should I be doing to help?").also { stage = 2 } + 2 -> playerl("How did Lord Sinclair die?").also { stage = 3 } + 3 -> playerl(FacialExpression.HAPPY, "I know who did it!") + .also { + stage = if (solvedMystery(player!!)) 46 + else if (clueCount(player!!) == 1) { + if (inInventory(player!!, Items.KILLERS_PRINT_1815)) { + 41 + } + else if (getAttribute(player!!, attributePoisonClue,0) == 2) { + 37 + } + else 33 + } + else if (clueCount(player!!) > 1) 32 + else 6 + } + } + 2 -> npcl("Look around and investigate who might be responsible. The Sarge said every murder leaves clues to who done it, but frankly we're out of our depth here.").also { stage = END_DIALOGUE } + + 3 -> npcl("Well, it's all very mysterious. Mary, the maid, found the body in the study next to his bedroom on the east wing of the ground floor.").also { stage++ } + 4 -> npcl("The door was found locked from the inside, and he seemed to have been stabbed, but there was an odd smell in the room. Frankly, I'm stumped.").also { stage = END_DIALOGUE } + + 5 -> npcl("Really? That was quick work! Who?").also { stage++ } + 6 -> options("It was an intruder!", "The butler did it!", "It was one of the servants!", "It was one of his family!").also { stage++ } + 7 -> when (buttonID) { + 1 -> playerl("It was an intruder!").also { stage = 10 } + 2 -> playerl("The butler did it!").also { stage = 8 } + 3 -> playerl("It was one of the servants!").also { stage = 13 } + 4 -> playerl("It was one of his family!").also { stage = 20 } + } + 8 -> npcl("I hope you have proof to that effect. We have to arrest someone for this and it seems to me that only the actual murderer would gain by falsely accusing someone.").also { stage++ } + 9 -> npcl("Although having said that the butler is kind of shifty looking...").also { stage = END_DIALOGUE } + + 10 -> npcl("That's what we were thinking too. That someone broke in to steal something, was discovered by Lord Sinclair, stabbed him and ran.").also { stage++ } + 11 -> npcl("It's odd that apparently nothing was stolen though... Find out something has been stolen,").also { stage++ } + 12 -> npcl("and the case is closed, but the murdered man was a friend of the King and it's more than my job's worth not to investigate fully.").also { stage = END_DIALOGUE } + + 13 -> npcl("Oh really? Which one?").also { stage++ } + 14 -> options("It was one of the women.", "It was one of the men.").also { stage++ } + 15 -> when (buttonID) { + 1 -> playerl("It was one of the women.").also { stage = 16 } + 2 -> playerl("It was one of the men.").also { stage = 18 } + } + 16 -> options("It was so obviously Louisa The Cook.", "It must have been Mary The Maid.").also { stage++ } + 17 -> when (buttonID) { + 1 -> playerl("It was so obviously Louisa The Cook.").also { stage = 27 } + 2 -> playerl("It must have been Mary The Maid.").also { stage = 27 } + } + + 18 -> options("It can only be Donovan the Handyman.", "Pierre the Dog Handler. No question", "Hobbes the Butler. The butler *always* did it.", "You must know it was Stanford The Gardener.").also { stage++ } + 19-> when (buttonID) { + 1 -> playerl("It can only be Donovan the Handyman.").also { stage = 27 } + 2 -> playerl("Pierre the Dog Handler. No question.").also { stage = 27 } + 3 -> playerl("Hobbes the Butler. The butler *always* did it.").also { stage = 8 } + 4 -> playerl("You must know it was Stanford The Gardener.").also { stage = 27 } + } + + 20 -> npcl("Oh really? Which one?").also { stage++ } + 21 -> options("It was one of the women.", "It was one of the men.").also { stage++ } + 22 -> when (buttonID) { + 1 -> playerl("It was one of the women.").also { stage = 23 } + 2 -> playerl("It was one of the men.").also { stage = 25 } + } + 23 -> options("I know it was Anna.", "I am so sure it was Carol.", "I'll bet you anything it was Elizabeth.").also { stage++ } + 24 -> when (buttonID) { + 1 -> playerl("I know it was Anna.").also { stage = 27 } + 2 -> playerl("I am so sure it was Carol.").also { stage = 27 } + 3 -> playerl("I'll bet you anything it was Elizabeth.").also { stage = 27 } + } + 25 -> options("I'm certain it was Bob.", "It was David. No doubt about it.", "If it wasn't Frank I'll eat my shoes.").also { stage++ } + 26 -> when (buttonID) { + 1 -> playerl("I'm certain it was Bob.").also { stage = 27 } + 2 -> playerl("It was David. No doubt about it.").also { stage = 27 } + 3 -> playerl("If it wasn't Frank I'll eat my shoes.").also { stage = 27 } + } + 27 -> sendDialogue(player!!, "You tell the guard who you suspect of the crime.").also { stage++ } + 28 -> npcl("Great work, show me the evidence and we'll take them to the dungeons.").also { stage++ } + 29 -> npcl("You *DO* have evidence of their crime, right?").also { stage++ } + 30 -> playerl("Uh...").also { stage++ } + 31 -> npcl("Tch. You wouldn't last a day in the guards with sloppy thinking like that. Come see me when you have some proof of your accusations.").also { stage = END_DIALOGUE } + + 32 -> showTopics( + IfTopic("I have proof that it wasn't any of the servants.", 33, inInventory(player!!, Items.CRIMINALS_THREAD_1808) || inInventory(player!!, Items.CRIMINALS_THREAD_1809) || inInventory(player!!, Items.CRIMINALS_THREAD_1810), true), + IfTopic("I have proof one of the family lied about the poison.", 37, getAttribute(player!!, attributePoisonClue, 0) == 2, true), + IfTopic("I have the finger prints of the culprit.", 41, inInventory(player!!, Items.KILLERS_PRINT_1815), true) + ) + 33 -> playerl("I have proof that it wasn't any of the servants!").also { stage++ } + 34 -> sendDialogue(player!!, "You show the guard the thread you found on the window.").also { stage ++ } + 35 -> playerl("All the servants dress in black so it couldn't have been one of them.").also { stage++ } + 36 -> npcl("That's some good work there. I guess it wasn't a servant. You still haven't proved who did do it though.").also { stage = END_DIALOGUE } + + 37 -> playerl("I have proof that $KILLER is lying about the poison.").also { stage++ } + 38 -> npcl("Oh really? How did you get that?").also { stage++ } + 39 -> sendDialogue(player!!, "You tell the guard about the " + + when (getAttribute(player!!, attributeRandomMurderer, 0)) { + 4 -> "mosquitos at the fountain." + 5 -> "tarnished family crest." + else -> "$POISONSPOT." + }).also { stage++ } + 40 -> npcl("Hmm. That's some good detective work there, we need more evidence before we can close the case though. Keep up the good work.").also { stage = END_DIALOGUE } + + 41 -> playerl("I have the fingerprints of the culprit!").also { stage++ } + 42 -> playerl("I have $KILLER's fingerprints here, you can see for yourself they match the fingerprints on the murder weapon exactly.").also { stage++ } + 43 -> sendDialogue(player!!,"You show the guard the finger prints evidence.").also { stage++ } + 44 -> npcl("... I'm impressed. How on earth did you think of something like that? I've never heard of such a technique for finding criminals before!").also { stage++ } + 45 -> npcl("This will come in very handy in the future but we can't arrest someone on just this. I'm afraid you'll still need to find more evidence before we can close this case completely.").also { stage = END_DIALOGUE } + + 46 -> playerl(FacialExpression.HAPPY, "I have conclusive proof who the killer was.").also { stage++ } + 47 -> npcl(FacialExpression.HAPPY, "You do? That's excellent work. Let's hear it then.").also { stage++ } + 48 -> playerl("I don't think it was an intruder, and I don't think Lord Sinclair was killed by being stabbed.").also { stage++ } + 49 -> npcl("Hmmm? Really? Why not?").also { stage++ } + 50 -> playerl(FacialExpression.HAPPY, "Nobody heard the guard dog barking, which it would have if it had been an intruder who was responsible.").also { stage++ } + 51 -> playerl("Nobody heard any signs of a struggle either. I think the knife was there to throw suspicion away from the real culprit.").also { stage++ } + 52 -> npcl("Yes, that makes sense. But who did do it then?").also { stage++ } + 53 -> sendDialogue(player!!, "You prove to the guard the thread matches $KILLER's clothes.").also { stage++ } + 54 -> npcl("Yes, I'd have to agree with that... but we need more evidence!").also { stage++ } + 55 -> sendDialogue(player!!, "You prove to the guard $KILLER did not use the poison on the $POISONSPOT.").also { stage++ } + 56 -> npcl("Excellent work - have you considered a career as a detective? But I'm afraid it's still not quite enough...").also { stage++ } + 57 -> sendDialogue(player!!, "You match $KILLER's finger prints with those on the dagger found in the body of Lord Sinclair.").also { stage++ } + 58 -> npcl(FacialExpression.HAPPY, "Yes. There's no doubt about it. It must have been $KILLER who killed " + if (KILLERMALE) {"his"} else {"her"} + " father. All of the guards must congratulate you on your excellent work in helping us to solve this case.").also { stage++ } + 59 -> npcl("We don't have many murders here in $SERVER_NAME and I'm afraid we wouldn't have been able to solve it by ourselves. We will hold " + if (KILLERMALE) {"him"} else {"her"} + " here under house arrest until such time as we bring " + if (KILLERMALE) {"him"} else {"her"} + " to trial.").also { stage++ } + 60 -> npcl("You have our gratitude, and I'm sure the rest of the family's as well, in helping to apprehend the murderer. I'll just take the evidence from you now.").also { stage++ } + 61 -> sendDialogue(player!!, "You hand over all the evidence.").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY, "Please accept this reward from the family!").also { stage++ } + 63 -> finishQuest(player!!, Quests.MURDER_MYSTERY) + .also { + player!!.inventory.removeAll(EVIDENCE) + player!!.bank.removeAll(EVIDENCE) + player!!.equipment.removeAll(EVIDENCE) + } + .also { stage = END_DIALOGUE } + } + 100 -> when (stage) { + 0 -> npcl("Excellent work on solving the murder! All of the guards I know are very impressed, and don't worry, we have the murderer under guard until they can be taken to trial.").also { stage++ } + 1 -> playerl("Is there anything else I can do? Seems awfully quiet up at the house, considering their sibling has just been arrested.").also { stage++ } + 2 -> npcl("Nothing right now, we have it all under control.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt new file mode 100644 index 000000000..75e67d2d6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/HobbesDialogue.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class HobbesDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, HobbesDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return HobbesDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.HOBBES_808) + } +} + +class HobbesDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("This is private property! Please leave!").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 6), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Well, in my considered opinion it must be David. The man is nothing more than a bully And I happen to know that poor Lord Sinclair and David had a massive argument in the living").also { stage++ } + 4 -> npcl("room about the way he treats the staff, the other day. I did not intend to overhear their conversation, but they were shouting so loudly I could not help but Overhear it. David definitely used the words").also { stage++ } + 5 -> npcl("'I am going to kill you!' as well. I think he should be the prime suspect. He has a nasty temper that one.").also { stage = END_DIALOGUE } + + 6 -> npcl("I was assisting the cook with the evening meal. I gave Mary His Lordships' dinner, and sent her to take it to him, then heard the scream as she found the body.").also { stage = END_DIALOGUE } + + 7 -> npcl("How do you mean 'suspicious'?").also { stage++ } + 8 -> playerl("Any sounds of a struggle with Lord Sinclair?").also { stage++ } + 9 -> npcl("No, I definitely didn't hear anything like that.").also { stage++ } + 10 -> playerl("How about the guard dog barking at all?").also { stage++ } + 11 -> npcl("You know, now you come to mention it I don't believe I did. I suppose that is Proof enough that it could not have been an intruder who is responsible.").also { stage = END_DIALOGUE } + + 12 -> npcl("Well, I do know that Elizabeth was extremely annoyed by the mosquito nest under the fountain in the garden, and was going to do something about it. I suspect any poison she bought would have been").also { stage++ } + 13 -> npcl ("enough to get rid of it. A Good job, too. I hate mosquitos.").also { stage++ } + 14 -> playerl("Yeah, so do I.").also { stage++ } + 15 -> npcl("You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} + diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt new file mode 100644 index 000000000..f8c7306a7 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/LouisaDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class LouisaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LouisaDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return LouisaDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.LOUISA_809) + } +} + +class LouisaDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("I'm far too upset to talk to random people right now.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 6), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Elizabeth.").also { stage ++ } + 4 -> npcl("Her father confronted her about her constant petty thieving, and was devastated to find she had stolen a silver needle which had meant a lot to him.").also { stage++ } + 5 -> npcl("You could hear their argument from Lumbridge!").also { stage = END_DIALOGUE } + + 6 -> npcl("I was right here with Hobbes and Mary. You can't suspect me surely!").also { stage = END_DIALOGUE } + + 7 -> npcl("Suspicious? What do you mean suspicious?").also { stage++ } + 8 -> playerl("Any sounds of a struggle with an intruder for example?").also { stage++ } + 9 -> npcl("No, I'm sure I don't recall any such thing.").also { stage++ } + 10 -> playerl("How about the guard dog barking at an intruder?").also { stage++ } + 11 -> npcl("No, I didn't. If you don't have anything else to ask can You go and leave me alone now? I have a lot of cooking to do for this evening.").also { stage = END_DIALOGUE } + + 12 -> npcl("I told Carol to buy some from that strange poison salesman and clean the drains before they began to smell any worse. She was the one who blocked them in the first place with a load").also { stage++ } + 13 -> npcl("of beans that she bought for some reason. There were far too many to eat, and they were almost rotten when she bought them anyway! You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt new file mode 100644 index 000000000..07388707c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MaryDialogue.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class MaryDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, MaryDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return MaryDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MARY_810) + } +} + +class MaryDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> sendDialogue(player!!, "They are ignoring you.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 7, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 12, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Oh I don't know... Frank was acting kind of funny... After that big argument him and the Lord had the other day by the beehive... so").also { stage ++ } + 4 -> npcl("I guess maybe him... but it's really scary to think someone here might have been responsible. I actually hope it was a burglar...").also { stage = END_DIALOGUE } + + 5 -> npcl("I was with Hobbes and Louisa in the Kitchen helping to prepare Lord Sinclair's meal, and then when I took it to his study... I saw... oh, it was horrible... he was...").also { stage++ } + 6 -> sendDialogue(player!!, "She seems to be on the verge of crying. You decide not to push her anymore for details.").also { stage = END_DIALOGUE} + + 7 -> npcl("I don't really remember hearing anything out of the ordinary.").also { stage++ } + 8 -> playerl("No sounds of a struggle then?").also { stage++ } + 9 -> npcl("No, I don't remember hearing anything like that.").also { stage++ } + 10 -> playerl("How about the guard dog barking?").also { stage++ } + 11 -> npcl("Oh that horrible dog is always barking at noting but I don't think I did...").also { stage = END_DIALOGUE } + + 12 -> npcl("I overheard Anna saying to Stanford that if he didn't do something about the state of his compost heap, she was going to.").also { stage++ } + 13 -> npcl("She really doesn't get on well with Stanford, I really have no idea why. You'd really have to ask her though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt new file mode 100644 index 000000000..23ec627b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMystery.kt @@ -0,0 +1,156 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +// https://www.youtube.com/watch?v=f1Eou5i5hBo at 2:29 +// https://www.youtube.com/watch?v=BwO4JhZdQKo at 25:20 +// https://www.youtube.com/watch?v=u5Osw_jas4A at 26:35 +// https://www.youtube.com/watch?v=f1Eou5i5hBo +// https://www.youtube.com/watch?v=9Q2aLUg44SQ at 10:02 +// https://www.youtube.com/watch?v=_lTUessdfOM 6:18 + +/** + * Murder Mystery Quest + */ +@Initializable +class MurderMystery : Quest(Quests.MURDER_MYSTERY, 93, 92, 3, 192, 0, 1, 2) { + + companion object { + const val questName = "Murder Mystery" + + const val attributeRandomMurderer = "/save:quest:murdermystery-randommurderer" //Alphabetical, 0 for Anna, 1 for Bob, 2 for Carol, 3 for David, 4 for Elizabeth, 5 for Frank + const val attributeTakenThread = "/save:quest:murdermystery-takenthread" //true after taking thread for the first time from window + const val attributeNoiseClue = "/save:quest:murdermystery-noiseclue" //true on learning of the barking dog for the first time + const val attributePoisonClue = "/save:quest:murdermystery-poisonclue" //1 after learning poison was bought, 2 after finding liar + const val attributeAskPoisonAnna = "/save:quest:murdermystery-askpoisonanna" //true after asking Anna about poison + const val attributeAskPoisonBob = "/save:quest:murdermystery-askpoisonbob" //true after asking Bob about poison + const val attributeAskPoisonCarol = "/save:quest:murdermystery-askpoisoncarol" //true after asking Carol about poison + const val attributeAskPoisonDavid = "/save:quest:murdermystery-askpoisondavid" //true after asking David about poison + const val attributeAskPoisonElizabeth = "/save:quest:murdermystery-askpoisonelizabeth" //true after asking Elizabeth about poison + const val attributeAskPoisonFrank = "/save:quest:murdermystery-askpoisonfrank" //true after asking Anna about poison + + fun clueCount(player: Player) : Int { + var count = 0 + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + count++ + } + if (inInventory(player, Items.KILLERS_PRINT_1815)) { + count ++ + } + if (getAttribute(player, attributePoisonClue,0) == 2) { + count ++ + } + return count + } + + fun solvedMystery(player: Player) : Boolean { + return ( + ( inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810) ) + && inInventory(player, Items.KILLERS_PRINT_1815) + && getAttribute(player, attributePoisonClue,0) == 2 + && getAttribute(player, attributeNoiseClue, false) + ) + } + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.MURDER_MYSTERY) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to one of the !!Guards?? at", line++, false) + line(player, "the !!Sinclair Mansion??, North of the !!Seer's Village??.", line++, false) + } else if (stage < 100) { + line(player, "Lord Sinclair, a prominent nobleman, had been horribly", line++, true) + line(player, "murdered at his mansion. The guards had been sent to", line++, true) + line(player, "investigate his murder, but have been completely stuck.", line++, true) + + if (solvedMystery(player)) { + line(player, "One of the guards asked me for my help in solving the", line++, true) + line(player, "murder. After careful examination of the crime scene and", line++, true) + line(player, "interrogating all suspects, I worked out who was guilty.", line++, true) + } else { + line(player, "One of the !!guards?? has asked me for my help in solving the", line++, false) + line(player, "murder. I should !!examine the crime scene?? very closely for", line++, false) + line(player, "evidence, and !!interrogate everybody?? in the area carefully.", line++, false) + } + + // This may not be a stage but an attribute when all the evidence is collected. + if (solvedMystery(player)) { + line(player, "I have !!indisputable evidence?? of who the murderer must be.", line++, false) + line(player, "I should take it to one of the !!Guards?? immediately.", line++, false) + } else { + line++ + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + line(player, "I have found some !!coloured thread??. It might be useful.", line++, false) + } + if (inInventory(player, Items.CRIMINALS_DAGGER_1813) || inInventory(player, Items.CRIMINALS_DAGGER_1814)) { + line(player, "I have taken the !!murder weapon??. I think it might help me.", line++, false) + } + if (inInventory(player, Items.PUNGENT_POT_1812)) { + line(player, "I have a !!strange smelling pot??. It seems like a clue.", line++, false) + } + } + } else { + line(player, "One of the guards asked me for my help in solving the", line++, true) + line(player, "murder. After careful examination of the crime scene and", line++, true) + line(player, "interrogating all suspects, I worked out who was guilty.", line++, true) + line(player, "I took the evidence I had collected to the Guards and", line++, true) + line(player, "explained how it could identify the killer. Impressed", line++, true) + line(player, "with my deductions, the killer was arrested and I was", line++, true) + line(player, "given a fair reward for my help in solving the crime.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line) + } + + } + + override fun reset(player: Player) { + removeAttribute(player, attributeRandomMurderer) + removeAttribute(player, attributeNoiseClue) + removeAttribute(player, attributePoisonClue) + removeAttribute(player, attributeTakenThread) + removeAttribute(player, attributeAskPoisonAnna) + removeAttribute(player, attributeAskPoisonBob) + removeAttribute(player, attributeAskPoisonCarol) + removeAttribute(player, attributeAskPoisonDavid) + removeAttribute(player, attributeAskPoisonElizabeth) + removeAttribute(player, attributeAskPoisonFrank) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Murder Mystery quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.COINS_617, 230, 277, 5) + + drawReward(player, "3 Quest Points", ln++) + drawReward(player, "2000 coins", ln++) + drawReward(player, "1406 Crafting XP", ln++) + + addItem(player, Items.COINS_995, 2000) + rewardXP(player, Skills.CRAFTING, 1406.0) + + removeAttribute(player, attributeNoiseClue) + removeAttribute(player, attributePoisonClue) + removeAttribute(player, attributeTakenThread) + removeAttribute(player, attributeAskPoisonAnna) + removeAttribute(player, attributeAskPoisonBob) + removeAttribute(player, attributeAskPoisonCarol) + removeAttribute(player, attributeAskPoisonDavid) + removeAttribute(player, attributeAskPoisonElizabeth) + removeAttribute(player, attributeAskPoisonFrank) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt new file mode 100644 index 000000000..bf9184584 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/MurderMysteryListeners.kt @@ -0,0 +1,442 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonAnna +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonBob +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonCarol +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonDavid +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonElizabeth +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeAskPoisonFrank +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeRandomMurderer +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeTakenThread +import core.api.* +import core.game.interaction.InteractionListener +import core.game.node.item.GroundItem +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class MurderMysteryListeners : InteractionListener { + companion object { + val UNDUSTEDEVIDENCE = intArrayOf( + Items.CRIMINALS_DAGGER_1813, + Items.PUNGENT_POT_1812, + Items.SILVER_NECKLACE_1796, + Items.SILVER_CUP_1798, + Items.SILVER_BOTTLE_1800, + Items.SILVER_BOOK_1802, + Items.SILVER_NEEDLE_1804, + Items.SILVER_POT_1806 + ) + val DUSTEDEVIDENCE = intArrayOf( + Items.CRIMINALS_DAGGER_1814, + Items.SILVER_NECKLACE_1797, + Items.SILVER_CUP_1799, + Items.SILVER_BOTTLE_1801, + Items.SILVER_BOOK_1803, + Items.SILVER_NEEDLE_1805, + Items.SILVER_POT_1807 + ) + val SILVERBARRELS = intArrayOf( + Scenery.ANNA_S_BARREL_2656, + Scenery.BOB_S_BARREL_2657, + Scenery.CAROL_S_BARREL_2658, + Scenery.DAVID_S_BARREL_2659, + Scenery.ELIZABETH_S_BARREL_2660, + Scenery.FRANK_S_BARREL_2661 + ) + val POISONSPOTS = intArrayOf( + Scenery.SINCLAIR_FAMILY_COMPOST_HEAP_26120, + Scenery.SINCLAIR_FAMILY_BEEHIVE_26121, + Scenery.SINCLAIR_MANSION_DRAIN_2843, + Scenery.SPIDERS__NEST_26109, + Scenery.SINCLAIR_FAMILY_FOUNTAIN_2654, + Scenery.SINCLAIR_FAMILY_CREST_2655 + ) + val SUSPECTPRINTS = intArrayOf( + Items.ANNAS_PRINT_1816, + Items.BOBS_PRINT_1817, + Items.CAROLS_PRINT_1818, + Items.DAVIDS_PRINT_1819, + Items.ELIZABETHS_PRINT_1820, + Items.FRANKS_PRINT_1821 + ) + } + + override fun defineListeners() { + on(Items.CRIMINALS_DAGGER_1813, GROUNDITEM, "take") { player, groundItem -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + if (inInventory(player, Items.CRIMINALS_DAGGER_1813) || inInventory(player, Items.CRIMINALS_DAGGER_1814)) { + sendDialogue(player, "I already have the murder weapon.") + } + else if (addItem(player, Items.CRIMINALS_DAGGER_1813)) { + removeGroundItem(groundItem as GroundItem) + sendDialogue(player, "This knife doesn't seem sturdy enough to have killed Lord Sinclair.") + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Items.PUNGENT_POT_1812, GROUNDITEM, "take") { player, groundItem -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + if (inInventory(player, Items.PUNGENT_POT_1812)) { + sendDialogue(player, "I already have the poisoned pot.") + } else if (addItem(player, Items.PUNGENT_POT_1812)) { + removeGroundItem(groundItem as GroundItem) + sendDialogue(player, "It seems like Lord Sinclair was drinking from this before he died.") + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(intArrayOf(Scenery.STURDY_WOODEN_GATE_2664, Scenery.STURDY_WOODEN_GATE_2665), SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + sendDialogue(player, "As you approach the gate the guard dog starts barking loudly at you. There is no way an intruder could have committed the murder. It must have been someone the dog knew to get past it quietly.") + setAttribute(player, attributeNoiseClue, true) + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Scenery.SMASHED_WINDOW_26110, SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + sendDialogue(player, "Some thread seems to have been caught on a loose nail on the window.") + if (inInventory(player, Items.CRIMINALS_THREAD_1808) || inInventory(player, Items.CRIMINALS_THREAD_1809) || inInventory(player, Items.CRIMINALS_THREAD_1810)) { + sendMessage(player, "You have already taken the thread.") + } + else if (getAttribute(player, attributeTakenThread, false)) { + if (hasSpaceFor(player, Item(Items.CRIMINALS_THREAD_1808))) { + when (getAttribute(player, attributeRandomMurderer, 0)) { + 1, 2 -> addItem(player, Items.CRIMINALS_THREAD_1808) + 0, 3 -> addItem(player, Items.CRIMINALS_THREAD_1809) + else -> addItem(player, Items.CRIMINALS_THREAD_1810) + } + sendMessage(player, "Lucky for you there's some thread left. You should be less careless in the future.") + } + } + else { + if (hasSpaceFor(player, Item(Items.CRIMINALS_THREAD_1808))) { + when (getAttribute(player, attributeRandomMurderer, 0)) { + 1, 2 -> addItem(player, Items.CRIMINALS_THREAD_1808) + 0, 3 -> addItem(player, Items.CRIMINALS_THREAD_1809) + else -> addItem(player, Items.CRIMINALS_THREAD_1810) + } + sendMessage(player, "You take the thread.") + setAttribute(player, attributeTakenThread, true) + } + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(Scenery.SMASHED_WINDOW_26110, SCENERY, "break") { player, _ -> + sendDialogue(player, "You don't want to damage evidence!") + return@on true + } + on(Scenery.SACKS_2663, SCENERY, "investigate") { player, _ -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + openDialogue(player, FlypaperDialogue()) + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(SILVERBARRELS, SCENERY, "search") { player, node -> + when (getQuestStage(player, Quests.MURDER_MYSTERY)) { + 1 -> { + when (node.id) { + Scenery.ANNA_S_BARREL_2656 -> { + if (inInventory(player, Items.SILVER_NECKLACE_1796) || inInventory(player, Items.SILVER_NECKLACE_1797)) { + sendDialogue(player, "I already have Anna's Necklace.") + } + else if (addItem(player, Items.SILVER_NECKLACE_1796)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Anna's Silver Necklace.") + } + } + Scenery.BOB_S_BARREL_2657 -> { + if (inInventory(player, Items.SILVER_CUP_1798) || inInventory(player, Items.SILVER_CUP_1799)) { + sendDialogue(player, "I already have Bob's cup.") + } + else if (addItem(player, Items.SILVER_CUP_1798)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Bob's silver cup.") + } + } + Scenery.CAROL_S_BARREL_2658 -> { + if (inInventory(player, Items.SILVER_BOTTLE_1800) || inInventory(player, Items.SILVER_BOTTLE_1801)) { + sendDialogue(player, "I already have Carol's bottle.") + } + else if (addItem(player, Items.SILVER_BOTTLE_1800)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Carol's silver bottle.") + } + } + Scenery.DAVID_S_BARREL_2659 -> { + if (inInventory(player, Items.SILVER_BOOK_1802) || inInventory(player, Items.SILVER_BOOK_1803)) { + sendDialogue(player, "I already have David's book.") + } + else if (addItem(player, Items.SILVER_BOOK_1802)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take David's silver book.") + } + } + Scenery.ELIZABETH_S_BARREL_2660 -> { + if (inInventory(player, Items.SILVER_NEEDLE_1804) || inInventory(player, Items.SILVER_NEEDLE_1805)) { + sendDialogue(player, "I already have Elizabeth's Needle.") + } + else if (addItem(player, Items.SILVER_NEEDLE_1804)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Elizabeth's silver needle.") + } + } + Scenery.FRANK_S_BARREL_2661 -> { + if (inInventory(player, Items.SILVER_POT_1806) || inInventory(player, Items.SILVER_POT_1807)) { + sendDialogue(player, "I already have Frank's pot.") + } + else if (addItem(player, Items.SILVER_POT_1806)){ + sendDialogue(player, "There's something shiny hidden at the bottom. You take Frank's silver pot.") + } + } + } + return@on true + } + else -> { + sendDialogue(player, "You need the guards' permission to do that.") + return@on true + } + } + } + on(POISONSPOTS, SCENERY, "investigate") { player, node -> + when (node.id) { + Scenery.SINCLAIR_FAMILY_COMPOST_HEAP_26120 -> { + if (!getAttribute(player, attributeAskPoisonAnna, false)) { + sendDialogue(player, "It's a heap of compost.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 0) { + sendDialogue(player, "The compost is teeming with maggots. Somebody should really do something about it. It's certainly clear nobody's used poison here.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "There is a faint smell of poison behind the smell of the compost.") + } + } + Scenery.SINCLAIR_FAMILY_BEEHIVE_26121 -> { + if (!getAttribute(player, attributeAskPoisonBob, false)) { + sendDialogue(player, "It's a very old beehive.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 1) { + sendDialogue(player, "The beehive buzzes with activity. These bees definitely don't seem poisoned at all.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The hive is empty. There are a few dead bees and a faint smell of poison.") + } + } + Scenery.SINCLAIR_MANSION_DRAIN_2843 -> { + if (!getAttribute(player, attributeAskPoisonCarol, false)) { + sendDialogue(player, "It's the drains from the kitchen.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 2) { + sendDialogue(player, "The drain is totally blocked. It really stinks. No, it REALLY smells bad. It's certainly clear nobody's cleaned it recently.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The drain seems to have been recently cleaned. You can still smell the faint aroma of poison.") + } + } + Scenery.SPIDERS__NEST_26109 -> { + if (!getAttribute(player, attributeAskPoisonDavid, false)) { + sendDialogue(player, "It looks like a spiders' nest of some kind...") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 3) { + sendDialogue(player, "There is a spiders' nest here. You estimate there must be at least a few hundred spiders ready to hatch. It's certainly clear nobody's used poison here.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "A faint smell of poison and a few dead spiders is all that remains of the spiders nest.") + } + } + Scenery.SINCLAIR_FAMILY_FOUNTAIN_2654 -> { + if (!getAttribute(player, attributeAskPoisonElizabeth, false)) { + sendDialogue(player, "A fountain with large numbers of insects around the base.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 4) { + openDialogue(player, FountainDialogue()) + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "There are a lot of dead mosquitos around the base of the fountain. A faint smell of poison is in the air, but the water seems clean.") + } + } + Scenery.SINCLAIR_FAMILY_CREST_2655 -> { + if (!getAttribute(player, attributeAskPoisonFrank, false)) { + sendDialogue(player, "The Sinclair Family Crest is hung up here.") + } + else if (getAttribute(player, attributeRandomMurderer, 0) == 5) { + sendDialogue(player, "It looks like the Sinclair family crest but it is very dirty. You can barely make it out under all of the grime. It's certainly clear nobody's cleaned it recently.") + setAttribute(player, attributePoisonClue, 2) + } + else { + sendDialogue(player, "The Sinclair family crest. It's shiny and freshly polished and has a slight smell of poison.") + } + } + + } + return@on true + } + onUseWith(ITEM, UNDUSTEDEVIDENCE, Items.POT_OF_FLOUR_1933) { player, used, _ -> + when (used.id) { + Items.CRIMINALS_DAGGER_1813 -> { + removeItem(player, used) + addItem(player, Items.CRIMINALS_DAGGER_1814) + sendMessage(player, "You sprinkle a small amount of flour on the murder weapon.") + sendMessage(player, "The murder weapon is now coated with a thin layer of flour.") + } + Items.PUNGENT_POT_1812 -> { + sendMessage(player, "You sprinkle a small amount of flour on the strange smelling pot.") + sendMessage(player, "The surface isn't shiny enough to take a fingerprint from.") + } + Items.SILVER_NECKLACE_1796 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NECKLACE_1797) + sendMessage(player, "You sprinkle the flour on Anna's necklace.") + sendMessage(player, "The necklace is now coated with a thin layer of flour.") + } + Items.SILVER_CUP_1798 -> { + removeItem(player, used) + addItem(player, Items.SILVER_CUP_1799) + sendMessage(player, "You sprinkle the flour on Bob's cup.") + sendMessage(player, "The cup is now coated with a thin layer of flour.") + } + Items.SILVER_BOTTLE_1800 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOTTLE_1801) + sendMessage(player, "You sprinkle the flour on Carol's bottle.") + sendMessage(player, "The bottle is now coated with a thin layer of flour.") + } + Items.SILVER_BOOK_1802 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOOK_1803) + sendMessage(player, "You sprinkle the flour on David's book.") + sendMessage(player, "The Book is now coated with a thin layer of flour.") + } + Items.SILVER_NEEDLE_1804 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NEEDLE_1805) + sendMessage(player, "You sprinkle the flour on Elizabeth's needle.") + sendMessage(player, "The Needle is now coated with a thin layer of flour.") + } + Items.SILVER_POT_1806 -> { + removeItem(player, used) + addItem(player, Items.SILVER_POT_1807) + sendMessage(player, "You sprinkle the flour on Frank's pot") + sendMessage(player, "The Pot is now coated with a thin layer of flour.") + } + } + removeItem(player, Items.POT_OF_FLOUR_1933) + addItem(player, Items.EMPTY_POT_1931) + return@onUseWith true + } + onUseWith(ITEM, DUSTEDEVIDENCE, Items.FLYPAPER_1811) { player, used, _ -> + removeItem(player, Items.FLYPAPER_1811) + when (used.id) { + Items.CRIMINALS_DAGGER_1814 -> { + removeItem(player, used) + addItem(player, Items.CRIMINALS_DAGGER_1813) + addItem(player, Items.UNKNOWN_PRINT_1822) + sendMessage(player, "You use the flypaper on the floury dagger.") + sendMessage(player, "You have a clean impression of the murderer's finger prints.") + } + Items.SILVER_NECKLACE_1797 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NECKLACE_1796) + addItem(player, Items.ANNAS_PRINT_1816) + sendMessage(player, "You use the flypaper on the flour covered necklace.") + sendMessage(player, "You have a clean impression of Anna's finger prints.") + } + Items.SILVER_CUP_1799 -> { + removeItem(player, used) + addItem(player, Items.SILVER_CUP_1798) + addItem(player, Items.BOBS_PRINT_1817) + sendMessage(player, "You use the flypaper on the flour covered cup.") + sendMessage(player, "You have a clean impression of Bob's finger prints.") + } + Items.SILVER_BOTTLE_1801 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOTTLE_1800) + addItem(player, Items.CAROLS_PRINT_1818) + sendMessage(player, "You use the flypaper on the flour covered bottle.") + sendMessage(player, "You have a clean impression of Carol's finger prints.") + } + Items.SILVER_BOOK_1803 -> { + removeItem(player, used) + addItem(player, Items.SILVER_BOOK_1802) + addItem(player, Items.DAVIDS_PRINT_1819) + sendMessage(player, "You use the flypaper on the flour covered book.") + sendMessage(player, "You have a clean impression of David's finger prints.") + } + Items.SILVER_NEEDLE_1805 -> { + removeItem(player, used) + addItem(player, Items.SILVER_NEEDLE_1804) + addItem(player, Items.ELIZABETHS_PRINT_1820) + sendMessage(player, "You use the flypaper on the flour covered needle.") + sendMessage(player, "You have a clean impression of Elizabeth's finger prints.") + } + Items.SILVER_POT_1807 -> { + removeItem(player, used) + addItem(player, Items.SILVER_POT_1806) + addItem(player, Items.FRANKS_PRINT_1821) + sendMessage(player, "You use the flypaper on the flour covered pot.") + sendMessage(player, "You have a clean impression of Frank's finger prints.") + } + } + return@onUseWith true + } + onUseWith(ITEM, SUSPECTPRINTS, Items.UNKNOWN_PRINT_1822) { player, used, with -> + val SUSPECT = when (used.id) { + Items.ANNAS_PRINT_1816 -> "Anna" + Items.BOBS_PRINT_1817 -> "Bob" + Items.CAROLS_PRINT_1818 -> "Carol" + Items.DAVIDS_PRINT_1819 -> "David" + Items.ELIZABETHS_PRINT_1820 -> "Elizabeth" + Items.FRANKS_PRINT_1821 -> "Frank" + else -> "Anna" + } + if (used.id == getAttribute(player, attributeRandomMurderer, 0) + Items.ANNAS_PRINT_1816) { + sendDialogue(player, "The fingerprints are an exact match to $SUSPECT's.") + removeItem(player, with.id) + addItem(player, Items.KILLERS_PRINT_1815) + } + else { + sendDialogue(player, "They don't seem to be the same. I guess that clears $SUSPECT of the crime. You destroy the useless fingerprint.") + removeItem(player, used.id) + } + return@onUseWith true + } + onUseWith(SCENERY, intArrayOf(Items.SILVER_POT_1806, Items.PUNGENT_POT_1812), Scenery.BARREL_OF_FLOUR_26122) { player, _, _ -> + sendDialogue(player, "You probably shouldn't use evidence from a crime scene to keep flour in.") + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt new file mode 100644 index 000000000..7d94c0677 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/PierreDialogue.kt @@ -0,0 +1,58 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class PierreDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PierreDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return PierreDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PIERRE_807) + } +} + +class PierreDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("The Guards told me not to talk to anyone.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 11, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("Honestly? I think it was Carol.").also { stage++ } + 4 -> npcl("I saw her in a huge argument with Lord Sinclair in the library the other day. It was something to do with stolen books. She definitely seemed upset enough to have done it afterwards.").also { stage = END_DIALOGUE } + + 5 -> npcl("I was in town at the inn. When I got back the house was swarming with guards who told me what had happened. Sorry.").also { stage = END_DIALOGUE } + + 6 -> npcl("Well, like what?").also { stage++ } + 7 -> playerl("Any sounds of a struggle with Lord Sinclair?").also { stage++ } + 8 -> npcl("No, I don't remember hearing anything like that.").also { stage++ } + 9 -> playerl("How about the guard dog barking at all?").also { stage++ } + 10 -> npcl("I hear him bark all the time. It's one of his favourite things to do. I can't say I did the night of the murder though as I wasn't close enough to hear either way.").also { stage = END_DIALOGUE } + + 11 -> npcl("Well, I know David said that he was going to do something about the spiders' nest that's between the two servants' quarters upstairs.").also { stage++ } + 12 -> npcl("He made a big deal about it to Mary the Maid, calling her useless and incompetent. I felt quite sorry for her actually. You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt b/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt new file mode 100644 index 000000000..ea70f1a92 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/murdermystery/StanfordDialogue.kt @@ -0,0 +1,58 @@ +package content.region.kandarin.quest.murdermystery + +import content.data.Quests +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributeNoiseClue +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class StanfordDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, StanfordDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return StanfordDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.STANFORD_811) + } +} + +class StanfordDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.MURDER_MYSTERY)){ + 0 -> npcl("Have you no shame? We are all grieving at the moment.").also { stage = END_DIALOGUE } + 1 -> when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "I'm here to help the guards with their investigation.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "How can I help?").also { stage++ } + 2 -> showTopics( + Topic("Who do you think is responsible?", 3), + Topic( "Where were you at the time of the murder?", 5), + IfTopic("Did you hear any suspicious noises at all?", 6, getAttribute(player!!, attributeNoiseClue, false)), + IfTopic("Do you know why so much poison was bought recently?", 11, getAttribute(player!!, attributePoisonClue, 0) > 0) + ) + + 3 -> npcl("It was Anna. She is seriously unbalanced. She trashed the garden once then tried to blame it on me! I bet it was her. It's just the kind of thing she'd do!").also { stage++ } + 4 -> npcl("She really hates me and was arguing with Lord Sinclair about trashing the garden a few days ago.").also { stage = END_DIALOGUE } + + 5 -> npcl("Right here, by my little shed. It's very cosy to sit and think in.").also { stage = END_DIALOGUE } + + 6 -> npcl("Not that I remember.").also { stage++ } + 7 -> playerl("So no sounds of a struggle between Lord Sinclair and an intruder?").also { stage++ } + 8 -> npcl("Not to the best of my recollection.").also { stage++ } + 9 -> playerl("How about the guard dog barking?").also { stage++ } + 10 -> npcl("Not that I can recall.").also { stage = END_DIALOGUE } + + 11 -> npcl("Well, Bob mentioned to me the other day he wanted to get rid of the bees in that hive over there. I think I saw him buying poison").also { stage++ } + 12 -> npcl("from that poison salesman the other day. I assume it was to sort out those bees. You'd really have to ask him though.").also { stage = END_DIALOGUE } + } + 100 -> npcl("Thank you for all your help in solving the murder.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt new file mode 100644 index 000000000..9af08c1b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/AstronomyBook.kt @@ -0,0 +1,171 @@ +package content.region.kandarin.quest.observatoryquest + +import content.global.handlers.iface.* +import core.ServerConstants +import core.api.storeBookInHouse +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Astronomy Book + */ +class AstronomyBook : InteractionListener { + companion object { + private const val TITLE = "THE TALE OF SCORPIUS" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("THE TALE OF", 55), + BookLine("SCORPIUS: ", 56), + BookLine("", 57), + BookLine("A History of Astronomy", 58), + BookLine("in ${ServerConstants.SERVER_NAME}.", 59), + BookLine("", 60), + BookLine("At the start of the 4th", 61), + BookLine("Age, a learned man by", 62), + BookLine("the name of Scorpius,", 63), + BookLine("known well for his powers", 64), + BookLine("of vision and magic,", 65), + ), + Page( + BookLine("sought communion with", 66), + BookLine("the gods of the world. So", 67), + BookLine("many unanswered", 68), + BookLine("questions had he that he", 69), + BookLine("devoted his entire lif to", 70), + BookLine("the cause.", 71), + BookLine("", 72), + BookLine("After many years of", 73), + BookLine("study, seeking knowledge", 74), + BookLine("from the wise of that", 75), + BookLine("time, he developed a", 76), + ) + ), + PageSet( + Page( + BookLine("machine infused with", 55), + BookLine("magical power, infused", 56), + BookLine("with the ability to pierce", 57), + BookLine("into the very heavens - a", 58), + BookLine("huge eye that gave the", 59), + BookLine("user incredible sight, like", 60), + BookLine("never before.", 61), + BookLine("", 62), + BookLine("", 63), + BookLine("", 64), + BookLine("", 65), + ), + Page( + BookLine("As time passed, Scorpius", 66), + BookLine("grew adept at using his", 67), + BookLine("specialized skills, and", 68), + BookLine("followed the movements of", 69), + BookLine("the stars in ${ServerConstants.SERVER_NAME}", 70), + BookLine("which he mapped and", 71), + BookLine("named, and are still used", 72), + BookLine("to this very day.", 73), + BookLine("", 74), + BookLine("Before long, Scorpius", 75), + BookLine("used his knowledge for", 76), + ) + ), + PageSet( + Page( + BookLine("predicting the future,", 55), + BookLine("and, in turn, he called", 56), + BookLine("upon the dark knowledge", 57), + BookLine("of Zamorakian", 58), + BookLine("worshippers to further his", 59), + BookLine("cause. Living below", 60), + BookLine("ground, the followers of", 61), + BookLine("the dark god remained", 62), + BookLine("until the civilisation of", 63), + BookLine("Ardougne grew in", 64), + BookLine("strength and control.", 65), + ), + Page( + BookLine("", 66), + BookLine("", 67), + BookLine("", 68), + BookLine("", 69), + BookLine("", 70), + BookLine("", 71), + BookLine("The kings of that time", 72), + BookLine("worked to banish the", 73), + BookLine("Zamorakian followers in", 74), + BookLine("the area, hiding all", 75), + BookLine("references to Scorpius's", 76), + ) + ), + PageSet( + Page( + BookLine("invention, due to its 'evil", 55), + BookLine("nature'.", 56), + BookLine("", 57), + BookLine("Years after, when the", 58), + BookLine("minds of the kings lent", 59), + BookLine("more towards the", 60), + BookLine("research of the unknown,", 61), + BookLine("the plans of Scorpius", 62), + BookLine("were uncovered and the", 63), + BookLine("heavenly eye constructed", 64), + BookLine("again.", 65), + ), + Page( + BookLine("Since then, many have", 66), + BookLine("studied the ways of the", 67), + BookLine("astronomer, Scorpius, and", 68), + BookLine("in his memory a grave", 69), + BookLine("was constructed near the", 70), + BookLine("Observatory.", 71), + BookLine("", 72), + BookLine("", 73), + BookLine("", 74), + BookLine("", 75), + BookLine("", 76), + ) + ), + + PageSet( + Page(), + Page( + BookLine("Some claim his ghost still", 66), + BookLine("wanders nearby, in", 67), + BookLine("torment as he seeks the", 68), + BookLine("secrets of the heavens", 69), + BookLine("that can never be solved.", 70), + BookLine("Tales tell that he will", 71), + BookLine("grant those adept in the", 72), + BookLine("arts of the astronomer a", 73), + BookLine("blessing of unusual", 74), + BookLine("power.", 75), + BookLine("", 76), + ) + ), + PageSet( + Page( + BookLine("Here ends the tale of how", 55), + BookLine("astronomy entered the", 56), + BookLine("known world.", 57), + ), + Page() + ), + ) + private fun display(player:Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + BookInterface.setModelOnPage(player,2, 27071, BookInterface.FANCY_BOOK_3_49, 33, 34, 1020, 0, 0) + BookInterface.setModelOnPage(player,4, 27069, BookInterface.FANCY_BOOK_3_49, 17, 18, 1224, 0, 0) + return true + } + } + + override fun defineListeners() { + on(Items.ASTRONOMY_BOOK_600, IntType.ITEM, "read") { player, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt new file mode 100644 index 000000000..5d676d5de --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinDialogues.kt @@ -0,0 +1,152 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.dialogue.* +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class GoblinDialogues : InteractionListener { + override fun defineListeners() { + on(NPCs.GREASYCHEEKS_6127, NPC, "talk-to") { player, node -> + openDialogue(player, GreasycheeksDialogueFile(), node as NPC) + return@on true + } + on(NPCs.SMELLYTOES_6128, NPC, "talk-to") { player, node -> + openDialogue(player, SmellytoesDialogueFile(), node as NPC) + return@on true + } + on(NPCs.CREAKYKNEES_6129, NPC, "talk-to") { player, node -> + openDialogue(player, CreakykneesDialogueFile(), node as NPC) + return@on true + } + } +} + +class GreasycheeksDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.GREASYCHEEKS_6127) + player("Hello.") + npc(ChatAnim.OLD_NORMAL, "Shush! I'm concentrating.") + player("Oh, sorry.") + } +} + +class SmellytoesDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SMELLYTOES_6128) + player("Hi there.") + npc(ChatAnim.OLD_NORMAL, "Hey, ids me matesh!") + player("Sorry, have we met?") + npc(ChatAnim.OLD_NORMAL, "Yeah! you wazsh wiv me in dat pub overy by hill!") + player("I have no idea what you're going on about.") + npc(ChatAnim.OLD_NORMAL, "Glad yeeash remembers.") + } +} + +class CreakykneesDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.CREAKYKNEES_6129) + player("Where did you get that lens?") + npc(ChatAnim.OLD_NORMAL, "From that strange metal thing up on the hill.") + player(ChatAnim.ANGRY, "You should give that back!") + npc(ChatAnim.OLD_NORMAL, "Even if it's cracked?") + player("Ah, well, I suppose it's of no use. But, still.") + } +} + +@Initializable +class GreasycheeksNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return GreasycheeksNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GREASYCHEEKS_6127) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Cook, cook, cook!", + "I'm so hungry!", + "This is gonna taste sooo good!", + ).random()) + } + } +} + +@Initializable +class SmellytoesNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return SmellytoesNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SMELLYTOES_6128) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Doh ray meeee laa doh faaa!", + "La la la! Do di dum dii!", + ).random()) + } + } +} + +@Initializable +class CreakykneesNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return CreakykneesNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CREAKYKNEES_6129) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Was that a spark?", + "Come on! Please light!", + ).random()) + } + } +} +@Initializable +class LostGoblinNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return LostGoblinNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOBLIN_6125) + } + + override fun handleTickActions() { + super.handleTickActions() + if (RandomFunction.roll(12)) { + sendChat(this, arrayOf( + "Which way should I go?", + "These dungeons are such a maze.", + "Where's the exit?!?", + "This is the fifth time this week. I'm lost!", + "I've been wandering around down here for hours.", + "How do you get back to the village?", + "I hate being so lost!", + "How could I be so disoriented?", + "Where am I? I'm so lost.", + "I know the exit's around here, somewhere." + ).random()) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt new file mode 100644 index 000000000..963438628 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/GoblinGuardNPC.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class GoblinGuardNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GoblinGuardNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SLEEPING_GUARD_6122, NPCs.GOBLIN_GUARD_489) + } + + override fun finalizeDeath(entity: Entity) { + if (entity is Player) { + val player = entity.asPlayer() + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 4) { + setAttribute(player, ObservatoryQuest.attributeKilledGuard, true) + } + super.finalizeDeath(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt new file mode 100644 index 000000000..5c7f1f7eb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryAssistantDialogue.kt @@ -0,0 +1,238 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedWine +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ObservatoryAssistantDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ObservatoryAssistantDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ObservatoryAssistantDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OBSERVATORY_ASSISTANT_6118, 10022) + } +} + +class ObservatoryAssistantDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.OBSERVATORY_ASSISTANT_6118) + + exec { player, npc -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 0) { + loadLabel(player, "observatoryQuest0") + } else if (getAttribute(player, attributeReceivedWine, false)) { + loadLabel(player, "receivedwine") + } else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 100) { + loadLabel(player, "observatoryqueststage100") + } else { + loadLabel(player, "randomStart" + (1..4).random()) + } + } + + label("observatoryQuest0") + player(FacialExpression.ASKING, "Hi, are you busy?") + npc("Me? I'm always busy.") + npc("See that man there? That's the professor. If he had his way, I think he'd never let me sleep!") + npc("Anyway, how might I help you?") + options( + DialogueOption("wonderingwhat", "I was wondering what you do here."), + DialogueOption("justlooking", "Just looking around, thanks."), + DialogueOption("looktelescope", "Can I have a look through that telescope?", expression = FacialExpression.ASKING) + ) + + label("wonderingwhat") + npc("Glad you ask. This is the Observatory reception. Up on the cliff is the Observatory dome, from which you can view the heavens. Usually...") + player(FacialExpression.ASKING, "What do you mean, 'usually'?") + npc("*Ahem*. Back to work, please.") + npc("I'd speak with the professor. He'll explain.") + + label("justlooking") + npc("Okay, just don't break anything. If you need any help, let me know.") + line("The assistant continues with his work.") + + label("looktelescope") + npc("You can. You won't see much though.") + player(FacialExpression.ASKING, "And that's because?") + npc("Just talk to the professor. He'll fill you in.") + + label("randomStart1") + player(FacialExpression.ASKING, "Can I interrupt you?") + npc("I suppose so. Please be quick though.") + goto("afterRandomStart") + + label("randomStart2") + player(FacialExpression.ASKING, "Might I have a word?") + npc("Sure, how can I help you?") + goto("afterRandomStart") + + label("randomStart3") + player("Hello there.") + npc("Yes?") + goto("afterRandomStart") + + label("randomStart4") + player(FacialExpression.ASKING, "Can I speak with you?") + npc("Why, of course. What is it?") + goto("afterRandomStart") + + + label("afterRandomStart") + exec { player, npc -> + loadLabel(player, "observatoryqueststage" + getQuestStage(player, Quests.OBSERVATORY_QUEST)) + } + + label("observatoryqueststage1") + exec { player, _ -> + if (inInventory(player, Items.PLANK_960, 3)) { + loadLabel(player, "enoughplanks") + } + else if (inInventory(player, Items.PLANK_960)) { + loadLabel(player, "someplanks") + } + else { + loadLabel(player, "notenoughplanks") + } + } + + label("notenoughplanks") + player(FacialExpression.ASKING,"Where can I find planks of wood? I need some for the telescope's base.") + npc("I understand planks can be found at Port Khazard, to the east of here. There are some at the Barbarian Outpost, too.") + npc("Failing that, you could always ask the Sawmill Operator. He's to the north-east of Varrock, by the Lumber Yard.") + + label("enoughplanks") + player("I've got some planks for the telescope's base.") + npc("Good work! The professor has been eagerly awaiting them.") + + label("someplanks") + player("I've got a plank.") + npc("That's nice.") + player("You know, for the telescope's base.") + npc("Well done. Remember that you'll need three in total.") + + label("observatoryqueststage2") + exec { player, _ -> + if(inInventory(player, Items.BRONZE_BAR_2349)) { + loadLabel(player, "hasbronzebar") + } else { + loadLabel(player, "nohasbronzebar") + } + } + + label("nohasbronzebar") + player(FacialExpression.ASKING, "Can you help me? How do I go about getting a bronze bar?") + npc("You'll need to use tin and copper ore on a furnace to produce this metal.") + player("Right you are.") + + label("hasbronzebar") + player("The bronze bar is ready, and waiting for the professor.") + npc("He'll surely be pleased. Go ahead and give it to him.") + + label("observatoryqueststage3") + exec { player, _ -> + if(inInventory(player, Items.MOLTEN_GLASS_1775)) { + loadLabel(player, "hasmoltenglass") + } else { + loadLabel(player, "nohasmoltenglass") + } + } + + label("nohasmoltenglass") + player(FacialExpression.ASKING, "What's the best way for me to get molten glass?") + npc("There are many ways, but I'd suggest making it yourself. Get yourself a bucket of sand and some soda ash, which you can get from using seaweed with a furnace. Use the soda ash and sand together in a") + npc("furnace and bang - molten glass is all yours. There's a book about it on the table if you want to know more.") + player("Thank you!") + + label("hasmoltenglass") + player("I've managed to get hold of some molten glass.") + npc("I suggest you have a word with the professor, in that case.") + + label("observatoryqueststage4") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + loadLabel(player, "haslensmould") + } else { + loadLabel(player, "nohaslensmould") + } + } + + label("nohaslensmould") + player(FacialExpression.ASKING, "Where can I find this lens mould you mentioned?") + npc("I'm sure I heard one of those goblins talking about it. I bet they've hidden it somewhere. Probably using it for some strange purpose, I'm sure.") + player(FacialExpression.ASKING, "What makes you say that?") + npc("I had a nice new star chart, until recently. I went out to do an errand for the professor the other day, only to see a goblin using it...") + npc("...as some kind of makeshift hankey to blow his nose!") + player("Oh dear.") + npc("You may want to look through the dungeon they have under their little village.") + player("Thanks for the advice.") + + label("haslensmould") + player("I have the lens mould.") + npc("Well done on finding that! I am honestly quite impressed. Make sure you take it straight to the professor.") + player("Will do.") + + label("observatoryqueststage5") + exec { player, _ -> + if(inInventory(player, Items.OBSERVATORY_LENS_603)) { + loadLabel(player, "haslens") + } else { + loadLabel(player, "nohaslens") + } + } + + label("nohaslens") + player(FacialExpression.ASKING, "How should I make this lens?") + npc("Just use the molten glass with the mould. Simple.") + player("Thanks!") + + label("haslens") + player("Do you like this lens? Good, huh?") + npc("Nice. What's that scratch?") + player("Oh, erm, that's a feature.") + player("Yes, that's it! Indubitably, it facilitates the triangulation of photonic illumination to the correct...") + npc("Stop! You can't confuse me with big words. Just pray the professor doesn't notice.") + + label("observatoryqueststage6") + player("Hello again.") + npc("Ah, it's the telescope repairman! The professor is waiting for you in the Observatory.") + player("How can I get to the Observatory?") + npc("Well, since the bridge was ruined, you will have to travel through the dungeon under the goblins' settlement.") + + label("observatoryqueststage100") + player("Hi assistant.") + player("Wait a minute.") + player(FacialExpression.ASKING, "What's your real name?") + npc(FacialExpression.ASKING, "My real name?") + player(FacialExpression.ASKING, "I only know you as the professor's assistant. What's your actual name?") + npc("Patrick.") + player("Hi Patrick, I'm @name.") + npc("Well, hello again, and thanks for helping out the professor.") + npc(" Oh, and, believe it or not, his name is Mambo-duna-roona, but don't tell him I told you.") + player(FacialExpression.AMAZED, "You made that up!") + npc("No, honest! Anyways, you've made my life much easier. Have a drink on me!") + item(Item(Items.JUG_OF_WINE_1993), "The assistant gives you some wine.") + exec { player, _ -> + addItemOrDrop(player, Items.JUG_OF_WINE_1993) + setAttribute(player, attributeReceivedWine, true) + } + player("Thanks very much.") + npc("No problem. Scorpius would be proud.") + player(FacialExpression.ASKING, "Sorry?") + npc("You may want to check out that book on the table, and perhaps look around for a grave...") + + label("receivedwine") + npc(FacialExpression.ASKING, "How was the wine?") + player(FacialExpression.DRUNK, "That was good stuff, *hic*! Wheresh the professher?") + npc(FacialExpression.ASKING, "The professor? He's up in the Observatory. Since the bridge was ruined, you will have to travel through the dungeon under the goblins' settlement.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt new file mode 100644 index 000000000..0eae2b1ab --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryCutscene.kt @@ -0,0 +1,199 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.activity.Cutscene +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import org.rs09.consts.Animations +import org.rs09.consts.NPCs +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeFinishedCutscene +import core.game.world.map.Location + +class ObservatoryCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location(2438, 3163)) + loadRegion(9777) + addNPC(PROFESSOR, 7, 25, Direction.SOUTH) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(5) + } + 1 -> { + teleport(player, 6, 27) + face(player, getNPC(PROFESSOR)!!.location) + rotateCamera(9, 25, 300) + moveCamera(25, 22, 1800) + timedUpdate(5) + } + 2 -> { + fadeFromBlack() + dialogueLinesUpdate("~ The Observatory ~", "The great eye into the heavens.") + } + 3 -> { + moveCamera(14, 10, 1800, 10) + timedUpdate(3) + } + 4 -> { + moveCamera(6, 10, 1800, 10) + timedUpdate(3) + } + 5 -> { + moveCamera(0, 32, 1800, 10) + timedUpdate(3) + } + 6 -> { + moveCamera(4, 36, 1600, 10) + timedUpdate(3) + } + 7 -> { + rotateCamera(7, 25, 300, 5) + moveCamera(13, 40, 1800, 10) + timedUpdate(3) + } + 8 -> { + moveCamera(12, 36, 1800, 5) + timedUpdate(5) + } + 9 -> { + fadeToBlack() + timedUpdate(5) + } + 10 -> { + moveCamera(5, 21, 500) + rotateCamera(6, 27, 100) + timedUpdate(3) + } + 11 -> { + fadeFromBlack() + timedUpdate(5) + } + 12 -> { + playerDialogueUpdate(FacialExpression.NEUTRAL, "Hi professor!") + } + 13 -> { + face(getNPC(PROFESSOR)!!, player.location) + timedUpdate(3) + } + 14 -> { + animate(getNPC(PROFESSOR)!!, WAVE) + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "Oh, hi there.") + } + 15 -> { + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "I'm just adding the finishing touches.") + } + 16 -> { + playerDialogueUpdate(FacialExpression.HAPPY, "Okay, don't let me interrupt.") + } + 17 -> { + dialogueUpdate(PROFESSOR, FacialExpression.NEUTRAL, "Thank you.") + } + 18 -> { + face(getNPC(PROFESSOR)!!, Location(136, 25)) + dialogueUpdate(PROFESSOR, FacialExpression.NEUTRAL, "Right, let's get this finished.") + } + 19 -> { + animate(getNPC(PROFESSOR)!!, THINKING) + sendChat(getNPC(PROFESSOR)!!, "Hmmmm...") + timedUpdate(5) + } + 20 -> { + move(getNPC(PROFESSOR)!!, 7, 23) + rotateCamera(9, 23, 100) + moveCamera(12, 30, 300) + timedUpdate(3) + } + 21 -> { + move(getNPC(PROFESSOR)!!, 10, 26) + timedUpdate(10) + } + 22 -> { + face(getNPC(PROFESSOR)!!, Location(135, 25)) + animate(getNPC(PROFESSOR)!!, FIX) + sendChat(getNPC(PROFESSOR)!!, "Bit of a tap here...") + timedUpdate(3) + } + 23 -> { + move(getNPC(PROFESSOR)!!, 11, 26) + timedUpdate(5) + } + 24 -> { + face(getNPC(PROFESSOR)!!, player.location) + dialogueUpdate(PROFESSOR, FacialExpression.HAPPY, "@name, I'm just going upstairs to finish off.") + } + 25 -> { + playerDialogueUpdate(FacialExpression.HAPPY, "Right-oh.") + } + 26 -> { + face(getNPC(PROFESSOR)!!, Location(140, 25)) + timedUpdate(3) + } + 27 -> { + teleport(getNPC(PROFESSOR)!!, 11, 22, 1) + teleport(player, 14,29,1) + face(getNPC(PROFESSOR)!!, Location(2442, 3158, 1)) + moveCamera(6, 20, 400) + rotateCamera(10, 24, 400) + timedUpdate(5) + } + 28 -> { + move(getNPC(PROFESSOR)!!, 7, 27) + moveCamera(10, 20, 400, 1) + rotateCamera(7, 27, 50, 20) + timedUpdate(10) + } + 29 -> { + sendChat(getNPC(PROFESSOR)!!, "In goes the lens.") + timedUpdate(3) + } + 30 -> { + animate(getNPC(PROFESSOR)!!, TUNEUP) + timedUpdate(20) + } + 31 -> { + sendChat(getNPC(PROFESSOR)!!, "And one final adjustment.") + move(getNPC(PROFESSOR)!!, 10, 29) + timedUpdate(6) + } + 32 -> { + move(getNPC(PROFESSOR)!!, 10, 25) + face(getNPC(PROFESSOR)!!, Location(137, 26)) + timedUpdate(6) + } + 33 -> { + face(getNPC(PROFESSOR)!!, Location(137, 26)) + animate(getNPC(PROFESSOR)!!, CRANK) + sendChat(getNPC(PROFESSOR)!!, "And all our work pays off.") + timedUpdate(6) + } + 34 -> { + animate(getNPC(PROFESSOR)!!, CHEER) + timedUpdate(10) + } + 35 -> { + moveCamera(6, 27, 500, 5) + rotateCamera(10, 25, 50, 5) + timedUpdate(3) + } + 36 -> { + end { + setAttribute(player, attributeFinishedCutscene, true) + } + } + } + } + + companion object { + val PROFESSOR = NPCs.OBSERVATORY_PROFESSOR_6121 + val WAVE = Animations.HUMAN_WAVE_863 + val THINKING = 6844 + val FIX = 6847 + val TUNEUP = 6848 + val CRANK = 6845 + val CHEER = Animations.HUMAN_CHEER_862 + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt new file mode 100644 index 000000000..380334049 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryProfessorDialogue.kt @@ -0,0 +1,676 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedWine +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ObservatoryProfessorDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ObservatoryProfessorDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ObservatoryProfessorDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.OBSERVATORY_ASSISTANT_6119, NPCs.OBSERVATORY_ASSISTANT_6120) + } +} + +class ObservatoryProfessorDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.OBSERVATORY_PROFESSOR_488) + + npc("What would you like to talk about?") + options( + DialogueOption("observatoryquest", "Talk about the Observatory Quest.", skipPlayer = true), + DialogueOption("treasuretrails", "Talk about the Treasure Trails.", skipPlayer = true), + ) + + label("observatoryquest") + exec { player, npc -> + loadLabel(player, "observatoryqueststage" + getQuestStage(player, Quests.OBSERVATORY_QUEST)) + } + + label("observatoryqueststage0") + player("Hi, I was...") + npc(FacialExpression.FRIENDLY, "Welcome to the magnificent wonder of the Observatory, where wonder is all around you, where the stars can be clutched from the heavens!") + player("Wow, nice intro.") + npc(FacialExpression.FRIENDLY, "Why, thanks! How might I help you?") + options( + DialogueOption("totallylost", "I'm totally lost."), + DialogueOption("whatwhat", "An Observatory?", expression = FacialExpression.THINKING), + DialogueOption("nah", "I'm just passing through."), + ) + + label("totallylost") + npc(FacialExpression.THINKING, "Lost? It must have been those pesky goblins that led you astray.") + npc("Head north-east to find the city of Ardougne.") + player("I'm sure I'll find the way.") + player("Thanks for all your help.") + npc(FacialExpression.FRIENDLY, "No problem at all. Come and visit again!") + line("The professor carries on with his studies.") + + label("nah") + npc("Fair enough. Not everyone is interested in this place, I suppose.") + + label("whatwhat") + npc(FacialExpression.FRIENDLY, "Of course. We have a superb telescope up in the Observatory, on the hill.") + npc(FacialExpression.FRIENDLY, "A truly marvellous invention, the likes of which you'll never behold again.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Well, it would be if it worked.") + npc(FacialExpression.ANGRY, "Don't interrupt!") + player(FacialExpression.THINKING, "What? It doesn't work?") + npc("Oh, no, no, no. Don't listen to him, he's joking. Aren't you, my FAITHFUL assistant?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Nope, dead serious. Hasn't been working for a long time.") + npc(FacialExpression.ANGRY, "Arghhh! Get back to work and stop sticking your nose in!") + player("So, it's broken. How come?") + npc(FacialExpression.SAD, "Oh, I suppose there's no use keeping it secret. Did you see those houses outside?") + player("Up on the hill? Yes, I've seen them.") + npc("It's a horde of goblins.") + npc(FacialExpression.SAD, "Since they moved here they have caused nothing but trouble.") + npc("Last week, my telescope was tampered with.") + npc(FacialExpression.ANGRY, "Now, parts need replacing before it can be used again. They've even been messing around in the dungeons under this area. Something needs to be done.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Strikes me that this visitor could help us.") + npc(FacialExpression.ANGRY, "Stop being so rude.") + npc("...") + npc("Although, he has a point. What do you say?") + player(FacialExpression.THINKING, "What, me?") + options( + DialogueOption("yestoquest", "Sounds interesting. How can I help?", "Sounds interesting, what can I do for you?", FacialExpression.FRIENDLY), + DialogueOption("notoquest", "Oh, sorry, I don't have time for that."), + ) + + label("notoquest") + npc("Oh dear. I really do need some help.") + npc("If you see anyone who can help then please send them my way.") + + label("yestoquest") + exec { player, _ -> + if(getQuestStage(player, Quests.OBSERVATORY_QUEST) == 0) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 1) + } + } + npc(FacialExpression.FRIENDLY, "Oh, thanks so much.") + npc(FacialExpression.FRIENDLY, "I shall need some materials for the telescope, so it can be used again.") + npc("Let's start with three planks of wood for the telescope base. My assistant will help with obtaining these, won't you?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "As if I don't have enough work to do. Seems I don't have a choice.") + npc(FacialExpression.FRIENDLY, "Go talk to him if you need some advice.") + player(FacialExpression.FRIENDLY, "Okay, I'll be right back.") + + label("observatoryqueststage1") + player(FacialExpression.FRIENDLY, "Hi again!") + npc(FacialExpression.FRIENDLY, "It's my helping hand, back again.") + npc(FacialExpression.THINKING, "Do you have the planks yet?") + exec { player, _ -> + if(inInventory(player, Items.PLANK_960, 3)) { + loadLabel(player, "enoughplanks") + } else { + loadLabel(player, "notenoughplanks") + } + } + + label("notenoughplanks") + player("Sorry, not yet. Three planks was it?") + npc("It was indeed.") + + label("enoughplanks") + player("Yes, I've got them. Here they are.") + exec { player, _ -> + if(removeItem(player, Item(Items.PLANK_960, 3))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 1) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 2) + } + } + } + npc(FacialExpression.FRIENDLY, "Well done. This will make a big difference.") + npc(FacialExpression.FRIENDLY, "Now, the bronze for the tube. Oh, assistant!") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Okay, okay, ask me if you need any help, @name.") + + label("observatoryqueststage2") + player("Hi.") + npc("The traveller returns!") + player("Still working hard?") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Some of us are.") + npc(FacialExpression.ANGRY, "What did I tell you about speaking when spoken to?") + npc(FacialExpression.THINKING, "So, @name, you have the bronze bar?") + exec { player, _ -> + if(inInventory(player, Items.BRONZE_BAR_2349)) { + loadLabel(player, "hasbronzebar") + } else { + loadLabel(player, "nohasbronzebar") + } + } + + label("nohasbronzebar") + player("Not yet.") + npc("Please bring me one, then.") + + label("hasbronzebar") + player(FacialExpression.FRIENDLY, "I certainly do. Here you go.") + exec { player, _ -> + if(removeItem(player, Item(Items.BRONZE_BAR_2349))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 2) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 3) + } + } + } + npc(FacialExpression.FRIENDLY, "Great. Now all I need is the lens made.") + npc(FacialExpression.FRIENDLY, "Please get me some molten glass.") + npc("Oi! Lazy bones!") + player(FacialExpression.AMAZED, "What? I'm not lazy.") + npc("Not you! I'm talking to my assistant.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Calm down old man, I heard. @name, I'm here if you need any help.") + npc("Thank you. Wait a minute, who are you calling 'old'?") + + label("observatoryqueststage3") + npc("How are you getting on finding me some molten glass?") + exec { player, _ -> + if(inInventory(player, Items.MOLTEN_GLASS_1775)) { + loadLabel(player, "hasmoltenglass") + } else { + loadLabel(player, "nohasmoltenglass") + } + } + + label("nohasmoltenglass") + player("Still working on it.") + npc("I really need it. Please hurry.") + + label("hasmoltenglass") + player(FacialExpression.FRIENDLY, "Here it is.") + exec { player, _ -> + if(removeItem(player, Item(Items.MOLTEN_GLASS_1775))) { + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 3) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 4) + } + } + } + npc(FacialExpression.FRIENDLY, "Excellent work, let's make the lens.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "It'll need to be made to an exact shape and size.") + npc("Well, obviously, hence why we have a lens mould.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Not any more. One of those goblins took it.") + npc(FacialExpression.SAD, "Great, just what I need. @name, I don't suppose you could find it?") + player(FacialExpression.FRIENDLY, "I'll have a look - where should I start?") + npc(FacialExpression.FRIENDLY, "No idea. You could ask my USELESS assistant if you want.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "What have I done to deserve this?") + + label("observatoryqueststage4") + npc(FacialExpression.THINKING, "Did you bring me the mould?") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + loadLabel(player, "haslensmould") + } else { + loadLabel(player, "nohaslensmould") + } + } + + label("nohaslensmould") + player("Still looking for it.") + npc("Please try and find it; my assistant may be able to help.") + + label("haslensmould") + player(FacialExpression.HAPPY, "I certainly have. You'll never guess what they were doing with it.") + npc("Well, from the smell I'd guess cooking some vile concoction.") + player(FacialExpression.HAPPY, "Wow, good guess. Well, here you go.") + npc(FacialExpression.NEUTRAL, NPCs.OBSERVATORY_ASSISTANT_6118, "Please don't give that to him. Last time he tried any Crafting, I had to spend a week cleaning up after the explosion.") + player("Explosion?") + npc(FacialExpression.SUSPICIOUS, "Erm, yes. I think in this instance you had probably better do it.") + player("I suppose it's better I don't ask.") + npc(FacialExpression.HAPPY,"You can use the mould with molten glass to make a new lens.") + exec { player, _ -> + if(inInventory(player, Items.LENS_MOULD_602)) { + addItemOrDrop(player, Items.MOLTEN_GLASS_1775) + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 4) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 5) + } + } + } + item(Item(Items.MOLTEN_GLASS_1775), "The professor gives you back the molten glass.") + + + label("observatoryqueststage5") + npc("Is the lens finished?") + exec { player, _ -> + if(inInventory(player, Items.OBSERVATORY_LENS_603)) { + loadLabel(player, "haslens") + } else { + loadLabel(player, "nohaslens") + } + } + + label("nohaslens") + player("How do I make it again?") + npc("Use the molten glass with the mould.") + player("Huh. Simple.") + + label("haslens") + player("Yes, here it is. You may as well take this mould too.") //No source if no mould present + line("The player hands the observatory professor the observatory lens.") + npc("Wonderful, at last I can fix the telescope.") + npc("Would you accompany me to the Observatory? You simply must see the telescope in operation.") + player("Sounds interesting. Count me in.") + npc("Superb. You'll have to go via the dungeon under the goblin settlement, seeing as the bridge is broken. You'll find stairs up to the Observatory from there.") + player("Okay. See you there.") + exec { player, _ -> + setVarbit(player, ObservatoryQuest.telescopeVarbit, 1, true) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + openOverlay(player, Components.FADE_TO_BLACK_120) + return@queueScript delayScript(player, 4) + } + 1 -> { + if(removeItem(player, Item(Items.OBSERVATORY_LENS_603))) { + removeItem(player, Item(Items.LENS_MOULD_602)) + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 5) { + setQuestStage(player, Quests.OBSERVATORY_QUEST, 6) + } + } + return@queueScript delayScript(player, 1) + } + 2 -> { + openOverlay(player, Components.FADE_FROM_BLACK_170) + return@queueScript delayScript(player, 2) + } + 3 -> { + sendDialogueLines(player, "The professor has gone ahead to the Observatory dome. Best you", "follow him to see the finished telescope.") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + + + } + + label("observatoryqueststage6") + npc("Hello, friend.") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) != null) { + loadLabel(player, "hasviewtelescope") + } else { + loadLabel(player, "nohasviewtelescope") + } + } + label("nohasviewtelescope") + // http://youtu.be/cK5suqcw3qU + player("Hi, this really is impressive.") + npc("Certainly is. Please, take a look through the telescope and tell me what you see.") + + label("hasviewtelescope") + player("I've had a look through the telescope.") + npc("What did you see? If you're not sure, you can find out by looking at the star charts dotted around the walls downstairs.") + player("It was...") + goto("page1") + + + label("page1") + options( + DialogueOption("Aquarius", "Aquarius", skipPlayer = true), + DialogueOption("Capricorn", "Capricorn", skipPlayer = true), + DialogueOption("Sagittarius", "Sagittarius", skipPlayer = true), + DialogueOption("Scorpio", "Scorpio", skipPlayer = true), + DialogueOption("page2", "~ next ~", skipPlayer = true), + ) + + label("page2") + options( + DialogueOption("page1", "~ previous ~", skipPlayer = true), + DialogueOption("Libra", "Libra", skipPlayer = true), + DialogueOption("Virgo", "Virgo", skipPlayer = true), + DialogueOption("Leo", "Leo", skipPlayer = true), + DialogueOption("page3", "~ next ~", skipPlayer = true), + ) + + label("page3") + options( + DialogueOption("page2", "~ previous ~", skipPlayer = true), + DialogueOption("Cancer", "Cancer", skipPlayer = true), + DialogueOption("Gemini", "Gemini", skipPlayer = true), + DialogueOption("Taurus", "Taurus", skipPlayer = true), + DialogueOption("page4", "~ next ~", skipPlayer = true), + ) + + label("page4") + options( + DialogueOption("page3", "~ previous ~", skipPlayer = true), + DialogueOption("Aries", "Aries", skipPlayer = true), + DialogueOption("Pisces", "Pisces", skipPlayer = true), + ) + + label("Aquarius") + player("Aquarius!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 19) { + loadLabel(player, "Aquariuscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Aquariuscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Aquarius, the water-bearer.") + npc("It seems suitable, then, to award you with water runes!") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.WATER_RUNE_555, 25) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Aries") + player("Aries!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 20) { + loadLabel(player, "Ariescorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Ariescorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Aries, the ram.") + npc("A fierce fighter. I'm sure he'll look down on you and improve your Attack for such insight.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.ATTACK, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Cancer") + player("Cancer!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 21) { + loadLabel(player, "Cancercorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Cancercorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Cancer, the crab.") + npc("An armoured creature - I think I shall reward you with an amulet of protection.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.AMULET_OF_DEFENCE_1729) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Capricorn") + player("Capricorn!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 22) { + loadLabel(player, "Capricorncorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Capricorncorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Capricorn, the goat.") + npc("Capricorn will surely reward your insight with an increase to your Strength.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.STRENGTH, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Gemini") + player("Gemini!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 23) { + loadLabel(player, "Geminicorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Geminicorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Gemini, the twins.") + npc("With the double nature of Gemini, I can't offer you anything more suitable than a two-handed weapon.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.BLACK_2H_SWORD_1313) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Leo") + player("Leo!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 24) { + loadLabel(player, "Leocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Leocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Leo, the lion.") + npc("I think the majestic power of the lion will raise your Hitpoints.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.HITPOINTS, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Libra") + player("Libra!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 25) { + loadLabel(player, "Libracorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Libracorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Libra, the scales.") + npc("Hmmm, balance, law, order - I shall award you with law runes!") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.LAW_RUNE_563, 25) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Pisces") + player("Pisces!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 26) { + loadLabel(player, "Piscescorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Piscescorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Pisces, the fish.") + npc("What's more suitable as a reward than some tuna?") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.TUNA_361, 3) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Sagittarius") + player("Sagittarius!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 27) { + loadLabel(player, "Sagittariuscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Sagittariuscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Sagittarius, the centaur.") + npc("As you've spotted the archer, I shall reward you with a maple longbow.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.MAPLE_LONGBOW_851) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Scorpio") + player("Scorpio!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 28) { + loadLabel(player, "Scorpiocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Scorpiocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Scorpio, the scorpion.") + npc("I think weapon poison would make a suitable reward.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.WEAPON_POISON_187) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Taurus") + player("Taurus!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 29) { + loadLabel(player, "Tauruscorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Tauruscorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Taurus, the bull.") + npc("This Strength potion should be a suitable reward.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + addItemOrDrop(player, Items.SUPER_STRENGTH1_161) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("Virgo") + player("Virgo!") + exec { player, _ -> + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == 30) { + loadLabel(player, "Virgocorrect") + } else { + loadLabel(player, "wrongstar") + } + } + + label("Virgocorrect") + npc("That's exactly it!") + player("Yes! Woo hoo!") + npc("That's Virgo, the virtuous.") + npc("Virgo will surely provide you with an increase to Defence.") + // Usually it is given here, but I don't want loopholes. + npc("By Saradomin's earlobes! You must be a friend of the gods indeed.") + npc("Look in your backpack for your reward, in payment for your work.") + exec { player, _ -> + rewardXP(player, Skills.DEFENCE, 875.0) + addItemOrDrop(player, Items.UNCUT_SAPPHIRE_1623) + finishQuest(player, Quests.OBSERVATORY_QUEST) + } + + label("wrongstar") + npc("I'm afraid not. Have another look. Remember, you can check the star charts on the walls for reference.") + + // http://youtu.be/Z5RRnZl2vTg + label("observatoryqueststage100") + npc("Thanks for all your help with the telescope. What can I do for you?") + options( + DialogueOption("needmorehelp", "Do you need any more help with the telescope?", expression = FacialExpression.ASKING), + DialogueOption("mambodunaroona", "Is it true your name is Mambo-duna-roona?", expression = FacialExpression.ASKING) { player, _ -> + return@DialogueOption getAttribute(player, attributeReceivedWine, false) + }, + DialogueOption("nevermind", "Nothing, thanks."), + ) + + label("needmorehelp") + npc("Not right now,") + npc("but the stars may hold a secret for you.") + + label("nevermind") + npc("Okay, no problem. See you again.") + + label("treasuretrails") + npc("Welcome back! How can I help you today?") + player("Can you teach me to solve treasure trail clues?") + npc("Ah, I get asked about treasure trails all the time! Listen carefully and I shall tell you what I know...") + npc("Lots of clues have degrees and minutes written on them. These are the coordinates of the place where the treasure is buried.") + npc("You have to walk to the correct spot, so that your coordinates are exactly the same as the values written on the clue scroll.") + npc("To do this, you must use a sextant, a watch and a chart to find your own coordinates.") + npc("Once you know the coordinates of the place where you are, you know which way you have to walk to get to the place where the treasure is!") + player("Riiight. So where do I get those items from?") + npc("I think Murphy, the owner of the fishing trawler moored south of Ardougne, might be able to spare you a sextant. Then the nearest Clock Tower is south of Ardougne - you could probably get a watch there. I've") + npc("got plenty of charts myself, here have one.") + + label("mambodunaroona") + npc(FacialExpression.AMAZED, "How do you know tha-") + npc(FacialExpression.SUSPICIOUS, "I mean, of course not, what a silly idea.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt new file mode 100644 index 000000000..ce6ba2513 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuest.kt @@ -0,0 +1,159 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import core.api.* +import core.api.setVarp +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +// http://youtu.be/TWIkCRRea8A The initial quest log. +// http://youtu.be/onHNm9Z5L-o The best full quest log. +// http://youtu.be/cq-jGTXXHuE +// http://youtu.be/ekYr30h43ag Scorpius. +/** + * Observatory Quest + */ +@Initializable +class ObservatoryQuest : Quest(Quests.OBSERVATORY_QUEST, 96, 95, 2,112, 0, 1, 7) { + + companion object { + val questName = Quests.OBSERVATORY_QUEST + const val observatoryVarp = 112 + const val goblinStoveVarbit = 3837 + const val telescopeVarbit = 3838 + + const val starChartsInterface = 104 + const val telescopeInterface = 552 + const val dungeonWarning = 560 + + const val attributeKilledGuard = "/save:quest:observatoryquest-killedguard" + const val attributeUnlockedGate = "/save:quest:observatoryquest-unlockedgate" + const val attributeTelescopeStar = "/save:quest:observatoryquest-telescopestar" // NULL - not seen telescope, 19 - 30 one of the random star patterns + const val attributeReceivedWine = "/save:quest:observatoryquest-receivedwine" + const val attributeReceivedMould = "/save:quest:observatoryquest-receivedmould" + const val attributeRandomChest = "/save:quest:observatoryquest-randomchest" + const val attributeFinishedCutscene = "/save:quest:observatoryquest-finishedcutscene" + + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, questName) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to the !!professor?? in the", line++, false) + line(player, "!!Observatory reception, south-west of Ardougne.??", line++, false) + } else { + line(player, "I can start this quest by speaking to the professor in the", line++, true) + line(player, "Observatory reception, south-west of Ardougne.", line++, true) + + if (stage >= 5) { + line(player, "I should take the letter the Examiner has given me to the", line++, true) + line(player, "Curator of Varrock Museum, for his approval.", line++, true) + line++ + } else if (stage >= 1) { + line(player, "Seems the observatory telescope needs repairing, due to", line++) + line(player, "the nearby goblins. The !!professor?? wants me to help by", line++) + line(player, "getting the following, with the help of his !!assistant??:", line++) + line++ + } + if (stage >= 2) { + line(player, "!!3 plain wooden planks??", line++, true) + } else if (stage >= 1) { + line(player, "!!3 plain wooden planks??", line++, inInventory(player, Items.PLANK_960, 3)) + } + if (stage >= 3) { + line(player, "!!1 bronze bar??", line++, true) + } else if (stage >= 2) { + line(player, "!!1 bronze bar??", line++, inInventory(player, Items.BRONZE_BAR_2349)) + } + if (stage >= 4) { + line(player, "!!1 molten glass??", line++, true) + } else if (stage >= 3) { + line(player, "!!1 molten glass??", line++, inInventory(player, Items.MOLTEN_GLASS_1775)) + } + if (stage >= 5) { + line(player, "!!1 lens mould??", line++, true) + } else if (stage >= 4) { + line(player, "!!1 lens mould??", line++) + } + + if (stage >= 6) { + line(player, "The professor was pleased to have all the pieces needed", line++, true) + line(player, "to fix the telescope. Apparently, the professor's last", line++, true) + line(player, "attempt at Crafting ended in disaster. So, he wants me to", line++, true) + line(player, "create the lens by using the molten glass with the mould.", line++, true) + line(player, "Fine by me!", line++, true) + } else if (stage >= 5) { + line(player, "The !!professor?? was pleased to have all the pieces needed", line++) + line(player, "to fix the telescope. Apparently, the professor's last", line++) + line(player, "attempt at Crafting ended in disaster. So, he wants me to", line++) + line(player, "create the !!lens?? by using the !!molten glass?? with the !!mould??.", line++) + line(player, "Fine by me!", line++) + } + + if (stage >= 100) { + line(player, "The professor has gone ahead to the Observatory. He", line++, true) + line(player, "wants me to meet him there by travelling through the", line++, true) + line(player, "dungeon below it.", line++, true) + } else if (stage >= 6) { + line(player, "The !!professor?? has gone ahead to the !!Observatory??. He", line++) + line(player, "wants me to meet him there by travelling through the", line++) + line(player, "!!dungeon?? below it.", line++) + } + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Observatory Quest!", 277, 4) + // This image is special since it isn't an item, but a standalone model. + player.packetDispatch.sendModelOnInterface(1174, 277, 5, 0) // Scenery 2210, Model 1174 + player.packetDispatch.sendAngleOnInterface(277, 5, 2040, 0, 1836) + + drawReward(player, "2 Quest Points", ln++) + drawReward(player, "2,250 Crafting XP", ln++) + drawReward(player, "A payment depending on", ln++) + drawReward(player, "which constellation you", ln++) + drawReward(player, "observed", ln++) + + rewardXP(player, Skills.CRAFTING, 2250.0) + } + + override fun updateVarps(player: Player) { + setVarp(player, observatoryVarp, getQuestStage(player, questName), true) + if(getQuestStage(player, questName) >= 6) { + setVarbit(player, telescopeVarbit, 1, true) + } else { + setVarbit(player, telescopeVarbit, 0, true) + } + if(getQuestStage(player, questName) >= 7) { + setVarp(player, observatoryVarp, 7, true) + } + } + + override fun reset(player : Player) { + removeAttribute(player, attributeKilledGuard) + removeAttribute(player, attributeUnlockedGate) + removeAttribute(player, attributeTelescopeStar) + removeAttribute(player, attributeReceivedWine) + removeAttribute(player, attributeReceivedMould) + removeAttribute(player, attributeRandomChest) + removeAttribute(player, attributeFinishedCutscene) + setVarbit(player, 3837, 0, true) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt new file mode 100644 index 000000000..a6627b98a --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestInterfaces.kt @@ -0,0 +1,59 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.world.map.Location + +class ObservatoryQuestInterfaces : InterfaceListener { + + companion object { + + val buttonToNameMapping = mapOf( + 19 to "Aquarius", + 20 to "Aries", + 21 to "Cancer", + 22 to "Capricorn", + 23 to "Gemini", + 24 to "Leo", + 25 to "Libra", + 26 to "Pisces", + 27 to "Sagittarius", + 28 to "Scorpio", + 29 to "Taurus", + 30 to "Virgo", + ) + + val buttonToStarObjectMapping = mapOf( + 19 to 27064, + 20 to 27066, + 21 to 27067, + 22 to 27061, + 23 to 27068, + 24 to 27058, + 25 to 27057, + 26 to 27062, + 27 to 27056, + 28 to 27055, + 29 to 27059, + 30 to 27060, + ) + } + + override fun defineInterfaceListeners() { + on(ObservatoryQuest.starChartsInterface) { player, component, opcode, buttonID, slot, itemID -> + if(buttonToStarObjectMapping.contains(buttonID)){ + // 55 57 + player.packetDispatch.sendModelOnInterface(buttonToStarObjectMapping[buttonID]!!, ObservatoryQuest.starChartsInterface, 55, 0) + setInterfaceText(player, buttonToNameMapping[buttonID]!!, ObservatoryQuest.starChartsInterface, 57) + } + return@on true + } + on(ObservatoryQuest.dungeonWarning) { player, _, _, buttonID, _, _ -> + when (buttonID) { + 17 -> teleport(player, Location(2355, 9394)).also { closeInterface(player) } + 18 -> closeInterface(player) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt new file mode 100644 index 000000000..c718f9a10 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/ObservatoryQuestListeners.kt @@ -0,0 +1,294 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeFinishedCutscene +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeRandomChest +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class ObservatoryQuestListeners : InteractionListener { + + companion object { + val chestMap = mapOf( + Scenery.CHEST_2191 to Scenery.CHEST_2194, + Scenery.CHEST_25385 to Scenery.CHEST_25386, + Scenery.CHEST_25387 to Scenery.CHEST_25388, + Scenery.CHEST_25389 to Scenery.CHEST_25390, + Scenery.CHEST_25391 to Scenery.CHEST_25392 + ) + + val reverseChestMap = chestMap.map { (k, v) -> v to k }.toMap() + + /* Comments show only the default order BEFORE randomization. A randomized + number (0-10) is calculated the first time the player goes down the stairs, + and is then added to the map value modulo 11, rotating the map. + */ + + val chestLocations = mapOf( + Location(2333, 9405) to 0, // key + Location(2312, 9400) to 1, // spider + Location(2310, 9374) to 2, // spider + Location(2348, 9383) to 3, // empty + Location(2356, 9380) to 4, // spider + Location(2359, 9376) to 5, // empty + Location(2360, 9366) to 6, // empty + Location(2351, 9361) to 7, // spider + Location(2364, 9355) to 8, // antipoison + Location(2335, 9374) to 9, // spider + Location(2326, 9360) to 10) // empty + } + + override fun defineListeners() { + + on(Scenery.SIGNPOST_25397, SCENERY, "read") { player, _ -> + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val servername = ServerConstants.SERVER_NAME + when(stage){ + 0 -> sendDialogueLines(player, "~ The Observatory ~", "Step ahead to the reception if you wish to explore $servername's most", "magnificent invention.").also { + stage++ + } + 1 -> player(FacialExpression.NEUTRAL, "Magnificent invention? I've seen some pretty magnificent", "things in my time. It'll have to be pretty impressive.").also { stage = END_DIALOGUE } + } + } + }) + return@on true + } + + on(Scenery.ORRERY_25401, SCENERY, "view") { player, _ -> + sendChat(player, "Oooooh, bizarre!") + return@on true + } + + on(Scenery.STAIRS_25432, SCENERY, "climb-down") { player, _ -> + openInterface(player, 560) + if (getAttribute(player, attributeRandomChest, null) == null) { + setAttribute(player, attributeRandomChest, RandomFunction.random(11)) + } + return@on true + } + // http://youtu.be/Kg84MYXgo-M -> You can always go to the observatory, no matter which stage. + on(Scenery.STAIRS_25429, SCENERY, "climb up") { player, node -> + if (node.location == Location(2335, 9351)) { + if (!getAttribute(player, attributeFinishedCutscene, false) && getQuestStage(player, Quests.OBSERVATORY_QUEST) == 6) { + ObservatoryCutscene(player).start() + } + else teleport(player, Location(2439, 3164)) + } else { + teleport(player, Location(2457, 3186)) + } + return@on true + } + + on(Scenery.STAIRS_25434, SCENERY, "climb-down") { player, _ -> + teleport(player, Location(2335, 9350)) + return@on true + } + + on(Scenery.STAIRS_25431, SCENERY, "climb-up") { player, _ -> + teleport(player, Location(2443, 3160, 1)) + return@on true + } + + on(Scenery.STAIRS_25437, SCENERY, "climb-down") { player, _ -> + teleport(player, Location(2444, 3162, 0)) + return@on true + } + + // All chests + on(chestMap.keys.toIntArray(), SCENERY, "open") { player, node -> + animate(player, 536) + sendMessage(player, "You open the chest.") + chestMap[node.id]?.let { replaceScenery(node as core.game.node.scenery.Scenery, it, 240) } + return@on true + } + on(chestMap.values.toIntArray(), SCENERY, "close") { player, node -> + animate(player, 535) + reverseChestMap[node.id]?.let { replaceScenery(node as core.game.node.scenery.Scenery, it, -1) } + return@on true + } + + on(chestMap.values.toIntArray(), SCENERY, "search") { player, node -> + val chest = chestLocations[node.location]?.plus(getAttribute(player, attributeRandomChest, 0))?.mod(11) + when (chest) { + 0 -> { + if (inInventory(player, Items.GOBLIN_KITCHEN_KEY_601) || getQuestStage(player, Quests.OBSERVATORY_QUEST) != 4 || getAttribute(player, ObservatoryQuest.attributeUnlockedGate, false)) { + sendMessage(player, "You search the chest.") + sendMessage(player, "The chest is empty.") + } else { + lock(player,2) + queueScript(player, 2, QueueStrength.STRONG) { + if (inInventory(player, Items.GOBLIN_KITCHEN_KEY_601)) { + return@queueScript stopExecuting(player) + } + addItemOrDrop(player, Items.GOBLIN_KITCHEN_KEY_601) + sendItemDialogue(player, Items.GOBLIN_KITCHEN_KEY_601,"You find a kitchen key.") + return@queueScript stopExecuting(player) + } + } + animate(player, 537) + return@on true + } + 1, 2, 4, 7, 9 -> { + sendMessage(player, "You search the chest.") + if (findLocalNPC(player, NPCs.POISON_SPIDER_1009) != null) { + sendMessage(player, "The chest is empty.") + animate(player, 537) + return@on true + } + sendMessage(player, "The chest contains a poisonous spider.") + val npc = NPC(NPCs.POISON_SPIDER_1009) + npc.location = player.location + npc.init() + npc.isRespawn = false + npc.moveStep() + npc.face(player) + animate(player, 537) + return@on true + } + 8 -> { + sendMessage(player, "You search the chest.") + lock(player,2) + queueScript(player, 2, QueueStrength.STRONG) { + addItemOrDrop(player, Items.ANTIPOISON1_179) + sendMessage(player,"This chest contains some antipoison.") + return@queueScript stopExecuting(player) + } + animate(player, 537) + return@on true + } + else -> { + sendMessage(player, "You search the chest.") + sendMessage(player, "The chest is empty.") + animate(player, 537) + return@on true + } + } + } + + on(intArrayOf(Scenery.KITCHEN_GATE_2199, Scenery.KITCHEN_GATE_2200), SCENERY, "open") { player, node -> + if (getAttribute(player, ObservatoryQuest.attributeUnlockedGate, false)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else if (getAttribute(player, ObservatoryQuest.attributeKilledGuard, false)) { + if (removeItem(player, Items.GOBLIN_KITCHEN_KEY_601)) { + sendMessage(player, "The gate unlocks.") + sendMessage(player, "The key is useless now. You discard it.") + setAttribute(player, ObservatoryQuest.attributeUnlockedGate, true) + sendPlayerDialogue(player, "I had better be quick, there may be more guards about.") + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + // http://youtu.be/ZkUF-0eonls + sendMessage(player, "The gate is locked.") + } + } else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 4){ + sendMessage(player, "If you open the gate, the guard will hear you. You need to get rid of him.") + } else sendMessage(player, "The gate is locked.") + return@on true + } + + on(NPCs.SLEEPING_GUARD_6122, NPC, "prod") { player, node -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) < 4) { + return@on true + } + sendChat(node as NPC, "Oi, how dare you wake me up!") + transformNpc(node, NPCs.GOBLIN_GUARD_489, 400) + node.attack(player) + return@on true + } + + on(Scenery.GOBLIN_STOVE_25440, SCENERY, "inspect") { player, _ -> + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> sendDialogueLines(player, "The goblins appear to have been using the lens mould to cook their", "stew!").also { stage++ } + 1 -> sendDialogueLines(player, "You shake out its contents and take it with you.").also { animate(player, 537); stage++ } + 2 -> end().also { + sendChat(player, "Euuuw, that smells awful!") + setVarbit(player, ObservatoryQuest.goblinStoveVarbit ,1) + addItemOrDrop(player, Items.LENS_MOULD_602) + } + } + } + }) + return@on true + } + + on(Scenery.GOBLIN_STOVE_25441, SCENERY, "inspect") { player, _ -> + if (!inInventory(player, Items.LENS_MOULD_602)) addItemOrDrop(player, Items.LENS_MOULD_602) + return@on true + } + + onUseWith(ITEM, Items.LENS_MOULD_602, Items.MOLTEN_GLASS_1775) { player, _, with -> + if (getStatLevel(player, Skills.CRAFTING) < 10) { + sendMessage(player, "You need a crafting level of 10 to do this.") + return@onUseWith true + } + + sendMessage(player, "You pour the molten glass into the mould.") + sendMessage(player, "You clasp it together.") + sendItemDialogue(player, Items.OBSERVATORY_LENS_603, "It has produced a small, convex glass disc.") + if (removeItem(player, with)) { + addItemOrDrop(player, Items.OBSERVATORY_LENS_603) + } + return@onUseWith true + } + + on(intArrayOf(Scenery.STAR_CHART_25578, Scenery.STAR_CHART_25579, Scenery.STAR_CHART_25580, Scenery.STAR_CHART_25581, Scenery.STAR_CHART_25582, Scenery.STAR_CHART_25583), SCENERY, "look-at") { player, _ -> + openInterface(player, ObservatoryQuest.starChartsInterface) + return@on true + } + + on(intArrayOf(Scenery.TELESCOPE_25438, Scenery.TELESCOPE_25439), SCENERY, "view") { player, _ -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 100) { + openDialogue(player, TelescopeDialogue(), NPC(NPCs.OBSERVATORY_PROFESSOR_488)) + } + else if (getQuestStage(player, Quests.OBSERVATORY_QUEST) >= 6) { + if (getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) == null) { + setAttribute(player, ObservatoryQuest.attributeTelescopeStar, (19..30).random()) + } + val randomStar = getAttribute(player, ObservatoryQuest.attributeTelescopeStar, null) + openInterface(player, ObservatoryQuest.telescopeInterface) + player.packetDispatch.sendModelOnInterface(ObservatoryQuestInterfaces.buttonToStarObjectMapping[randomStar]!!, ObservatoryQuest.telescopeInterface, 7, 0) + } else { + sendMessage(player, "The telescope is broken.") + } + return@on true + } + + on(intArrayOf(Scenery.DOOR_25526, Scenery.DOOR_25527), SCENERY, "open") { player, _ -> + sendMessage(player, "The door is locked.") + return@on true + } + + on(Scenery.GRAVE_OF_SCORPIUS_2211, SCENERY, "read") { player, _ -> + sendMessage(player, "Here lies Scorpius: Only those who have seen beyond the stars may seek his counsel.") + return@on true + } + } +} + +class TelescopeDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npcl(FacialExpression.ASKING, "What do you see now?").also { stage++ } + 1 -> playerl("I can see a constellation through the telescope. It looks like Scorpio.").also { stage++ } + 2 -> npcl("Scorpio? Interesting. How very fitting.").also { stage++ } + 3 -> playerl(FacialExpression.ASKING, " What do you mean?").also { stage++ } + 4 -> npcl("Scorpius, the founder of all we know relating to astronomy. There's a book about him in the reception. Perhaps you should check it out, you might learn something.").also { stage++ } + 5 -> playerl("Then perhaps I shall.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt new file mode 100644 index 000000000..dc4f0b74d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/PoisonSpiderBehavior.kt @@ -0,0 +1,20 @@ +package content.region.kandarin.quest.observatoryquest + +import core.api.getAttribute +import core.api.setAttribute +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.GameWorld +import org.rs09.consts.NPCs + +class PoisonSpiderBehavior : NPCBehavior(NPCs.POISON_SPIDER_1009) { + override fun onCreation(self: NPC) { + setAttribute(self, "despawn-time", GameWorld.ticks + 100) + } + + override fun tick(self: NPC): Boolean { + if (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks && !self.inCombat()) + self.clear() + return true + } +} diff --git a/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt b/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt new file mode 100644 index 000000000..6d8fd5815 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/observatoryquest/SpiritOfScorpiusDialogue.kt @@ -0,0 +1,110 @@ +package content.region.kandarin.quest.observatoryquest + +import content.data.Quests +import content.region.kandarin.quest.observatoryquest.ObservatoryQuest.Companion.attributeReceivedMould +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class SpiritOfScorpiusDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return SpiritOfScorpiusDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, SpiritOfScorpiusDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.SPIRIT_OF_SCORPIUS_492) + } +} + +class SpiritOfScorpiusDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SPIRIT_OF_SCORPIUS_492) + + exec { player, npc -> + if (getQuestStage(player, Quests.OBSERVATORY_QUEST) == 100) { + if (!inEquipment(player, Items.GHOSTSPEAK_AMULET_552)) loadLabel(player, "noghostspeak") + else loadLabel(player, "observatoryqueststage100") + } else loadLabel(player, "observatoryquestincomplete") + } + + label("observatoryquestincomplete") + line("They seem to be ignoring you.") + + label("noghostspeak") + line("This powerful spirit seems capable of speaking to you", "even though you are not wearing an Amulet of Ghostspeak.") + exec { player, _ -> + loadLabel(player, "observatoryqueststage100") + } + + label("observatoryqueststage100") + npc("Who treads upon my grave?") + options( + DialogueOption("wisdom", "I seek your wisdom.") { player, _ -> + return@DialogueOption !getAttribute(player, attributeReceivedMould, false) + }, + DialogueOption("anothermould", "I need another unholy symbol mould.", "I need another mould for the unholy symbol.") { player, _ -> + return@DialogueOption getAttribute(player, attributeReceivedMould, false) + }, + DialogueOption("blessing", "I have come to seek your blessing."), + DialogueOption("killyou", "I have come to kill you.") + ) + + label("wisdom") + npc("Indeed, I feel you have beheld the far places in the heavens. My Lord instructs me to help you.") + item(Item(Items.UNHOLY_MOULD_1594), "An unholy mould appears in your inventory.") + exec { player, _ -> + addItemOrDrop(player, Items.UNHOLY_MOULD_1594) + setAttribute(player, attributeReceivedMould, true) + } + npc("Here is a mould to make a token for our Lord; a mould for the unholy symbol of Zamorak. Return to me when you desire my blessing.") + + label("anothermould") + exec { player, _ -> + if (inInventory(player, Items.UNHOLY_MOULD_1594)) { + loadLabel(player, "hasmould") + } + else { + addItemOrDrop(player, Items.UNHOLY_MOULD_1594) + loadLabel(player, "lostmould") + } + } + + label("hasmould") + npc("One you already have, another is not needed. Leave me be.") + + label("lostmould") + npc("A lost object is easy to replace. The loss of the affections of our Lord is impossible to forgive.") + + label("blessing") + exec { player, _ -> + if (inInventory(player, Items.UNPOWERED_SYMBOL_1722)) { + loadLabel(player, "canbless") + } + else loadLabel(player, "cannotbless") + } + + label("canbless") + npc("I see you have the unholy symbol of our Lord. I will bless it for you.") + line("The ghost mutters in a strange voice.", "The unholy symbol throbs with power.") + exec { player, _ -> + removeItem(player, Items.UNPOWERED_SYMBOL_1722) + addItemOrDrop(player, Items.UNHOLY_SYMBOL_1724) + } + npc("The symbol of our Lord has been blessed with power! My master calls.") + + + label("cannotbless") + npc("No blessing will be given to those who have no symbol of our Lord's love.") + + label("killyou") + npc("The might of mortals, to me, is as the dust to the sea.") + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt new file mode 100644 index 000000000..79df6ceac --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCPeksaDialogue.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.api.setQuestStage +import core.game.dialogue.DialogueFile +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import content.data.Quests + +class SCPeksaDialogue(val questStage: Int) : DialogueFile() { + + companion object { + const val ASK_ABOUT_SCORPION = START_DIALOGUE + const val ASK_FOR_HELP = 20 + const val THANK_YOU = 30 + + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + // Word wrap issues + ASK_ABOUT_SCORPION -> npc("Now how could you know about that, I wonder?", "Mind you, I don't have it anymore.").also { stage++ } + ASK_ABOUT_SCORPION + 1 -> npcl(" I gave it as a present to my brother Ivor when I visited our outpost in the west.").also { stage++ } + ASK_ABOUT_SCORPION + 2 -> npcl("Well, actually I hid it in his bed so it would nip him. It was a bit of a surprise gift.").also { stage++ } + + ASK_ABOUT_SCORPION + 3 -> showTopics( + Topic("So where is this outpost?", ASK_FOR_HELP), + Topic("Thanks for the information", THANK_YOU, true) + ) + + ASK_FOR_HELP -> npcl("Its a fair old trek to the west, across the White Wolf Mountains. Then head west, north-west until you see the axes and horned helmets.").also { stage= + THANK_YOU } + + THANK_YOU -> { + playerl("Thanks for the information").also { stage++ } + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_PEKSA_HELP) + } + THANK_YOU + 1 -> npcl ("No problems! Tell Ivor I said hi!").also { stage = END_DIALOGUE } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt new file mode 100644 index 000000000..a15b267c2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCSeerDialogue.kt @@ -0,0 +1,79 @@ +package content.region.kandarin.quest.scorpioncatcher + +import content.region.kandarin.seers.dialogue.SeerDialogue +import core.api.sendDialogue +import core.api.setQuestStage +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import content.data.Quests + +class SCSeerDialogue(val questStage: Int, private val dialogueEntry: Int) : DialogueFile() { + + companion object { + const val FIRST_SCORPION_HELP = 20 + const val FIRST_SCORPION_THORMAC = 30 + const val FIRST_SCORPION_GUIDE = 40 + const val OTHER_SCORPIONS = 50 + + + // There are many ways that this dialogue can be entered + const val ENTRY_HELP = SeerDialogue.SC_QUEST_HELP + const val ENTRY_FRIEND = SeerDialogue.SC_QUEST_FRIEND + const val ENTRY_OTHERS = SeerDialogue.SC_QUEST_OTHER_SCORPIONS + + } + + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> when (dialogueEntry){ + ENTRY_HELP -> npcl("Well you have come to the right place. I am a master of animal detection.").also { stage = FIRST_SCORPION_HELP } + ENTRY_FRIEND -> npcl(FacialExpression.NEUTRAL, "What does the old fellow want?").also { stage = FIRST_SCORPION_THORMAC } + // word wrap doesn't work again + ENTRY_OTHERS -> npc(FacialExpression.NEUTRAL, "Well, I've checked my looking glass. There seems to be", + "a kharid scorpion in a little village to the east,", + "surrounded by lots of uncivilized-looking warriors.", + "Some kind of merchant there seems to have picked it up.").also { stage = OTHER_SCORPIONS } + else -> { + println("Invalid entry to SCSeerDialogue $dialogueEntry") + end() + } + } + + FIRST_SCORPION_THORMAC -> playerl( + FacialExpression.NEUTRAL, + "He's lost his valuable lesser Kharid scorpions." + ).also { stage++ } + FIRST_SCORPION_THORMAC + 1 -> npcl( + FacialExpression.HAPPY, + "Well you have come to the right place. I am a master of animal detection." + ).also { stage = FIRST_SCORPION_GUIDE } + + FIRST_SCORPION_HELP -> npcl("Do you need to locate any particular scorpion? Scorpions are a creature somewhat in abundance.").also { stage++ } + FIRST_SCORPION_HELP + 1 -> playerl("I'm looking for some lesser Kharid scorpions. They belong to Thormac the Sorcerer.").also { + stage = FIRST_SCORPION_GUIDE + } + + FIRST_SCORPION_GUIDE -> npcl(FacialExpression.HAPPY, "Let me look into my looking glass.").also { stage++ } + FIRST_SCORPION_GUIDE + 1 -> sendDialogue(player!!, "The Seer produces a small mirror.").also { stage++ } + FIRST_SCORPION_GUIDE + 2 -> sendDialogue(player!!, "The Seer gazes into the mirror.").also { stage++ } + FIRST_SCORPION_GUIDE + 3 -> sendDialogue(player!!, "The Seer smooths his hair with his hand.").also { stage++ } + FIRST_SCORPION_GUIDE + 4 -> npcl("I can see a scorpion that you seek. It would appear to be near some nasty spiders. I can see two coffins there as well.").also { stage++ } + FIRST_SCORPION_GUIDE + 5 -> npcl("The scorpion seems to be going through some crack in the wall. Its gone into some sort of secret room.").also { stage++ } + FIRST_SCORPION_GUIDE + 6 -> npcl("Well see if you can find the scorpion then, and I'll try and get you some information on the others.").also { + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_DARK_PLACE) + stage = END_DIALOGUE + } + + OTHER_SCORPIONS -> npcl ("That's all I can tell about that scorpion.").also { stage++ } + OTHER_SCORPIONS + 1 -> playerl("Any more scorpions?").also { stage++ } + OTHER_SCORPIONS + 2 -> npcl ("It's good that you should ask. I have information on the last scorpion for you.").also { stage++ } + OTHER_SCORPIONS + 3 -> npcl ("It seems to be in some sort of upstairs room. There seems to be some sort of brown clothing lying on a table.").also { + setQuestStage(player!!, Quests.SCORPION_CATCHER, ScorpionCatcher.QUEST_STATE_OTHER_SCORPIONS) + stage = END_DIALOGUE + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt new file mode 100644 index 000000000..9422c9d06 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt @@ -0,0 +1,135 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import core.ServerConstants +import core.api.* +import core.game.dialogue.Topic +import core.game.node.item.Item +import org.rs09.consts.Items +import content.data.Quests + +class SCThormacDialogue(val questStage: Int) : DialogueFile() { + + companion object { + const val ASK_FOR_HELP = 10 + const val HOW_TO_CATCH = 30 + const val WHY_SHOULD_I_START = 50 + + const val WAITING_FOR_SCORPIONS = 100 + const val GIVE_ANOTHER_CAGE = 110 + + const val GOT_THEM_ALL = 200 + const val FULL_INVENTORY = 300 + } + + + override fun handle(componentID: Int, buttonID: Int) { + when (stage){ + START_DIALOGUE -> if (questStage == 0) { + npcl(FacialExpression.FRIENDLY, "Hello I am Thormac the Sorcerer, " + + "I don't suppose you could be of assistance to me?").also { stage = ASK_FOR_HELP } + } + else { + npcl(FacialExpression.NEUTRAL, "How goes your quest?").also { + stage = if (hasAnItem(player!!, Items.SCORPION_CAGE_463).exists()){ + // Player has all the scorpions caught + GOT_THEM_ALL + } else { + WAITING_FOR_SCORPIONS + } + } + } + + ASK_FOR_HELP -> showTopics( + Topic(FacialExpression.HAPPY,"What do you need assistance with?", ASK_FOR_HELP+1), + Topic(FacialExpression.NEUTRAL, "I'm a little busy.", END_DIALOGUE) + ) + ASK_FOR_HELP+1 -> npcl(FacialExpression.WORRIED, " I've lost my pet scorpions. " + + "They're lesser Kharid scorpions, a very rare breed.").also { stage++ } + ASK_FOR_HELP+2 -> npcl(FacialExpression.WORRIED, "I left their cage door open, now I don't know where they've gone.").also { stage++ } + ASK_FOR_HELP+3 -> npcl(FacialExpression.WORRIED, "There's three of them, and they're quick little beasties. " + + "They're all over " + ServerConstants.SERVER_NAME + ".").also { stage++ } + ASK_FOR_HELP+4 -> showTopics( + Topic(FacialExpression.ASKING, "So how would I go about catching them then?", HOW_TO_CATCH), + Topic(FacialExpression.ASKING, "What's in it for me?", WHY_SHOULD_I_START), + Topic(FacialExpression.NEUTRAL, "I'm not interested then.", END_DIALOGUE) + ) + + WHY_SHOULD_I_START -> npcl(FacialExpression.WORRIED, "Well I suppose I can aid you with my skills as a staff sorcerer. " + + "Most battlestaffs around here are a bit puny. I can beef them up for you a bit.").also { + // Need to recheck the quest stage since it may have been changed in this dialogue + if (getQuestStage(player!!, Quests.SCORPION_CATCHER) == 0) { + stage++ + } else { + stage = END_DIALOGUE + } + } + WHY_SHOULD_I_START+1 -> showTopics( + Topic(FacialExpression.ASKING, "So how would I go about catching them then?", HOW_TO_CATCH), + Topic(FacialExpression.NEUTRAL, "I'm not interested then.", END_DIALOGUE) + ) + + HOW_TO_CATCH -> npcl(FacialExpression.WORRIED, "Well I have a scorpion cage here which you can " + + "use to catch them in.").also { + if (hasSpaceFor(player!!,Item(Items.SCORPION_CAGE_456))) stage++ + else stage = FULL_INVENTORY + } + HOW_TO_CATCH+1 -> { + sendItemDialogue(player!!, Items.SCORPION_CAGE_456, "Thormac gives you a cage.").also { stage++ } + startQuest(player!!, Quests.SCORPION_CATCHER) + addItemOrDrop(player!!, Items.SCORPION_CAGE_456) + } + HOW_TO_CATCH+2 -> npcl(FacialExpression.WORRIED, "If you go up to the village of Seers, to the North of " + + "here, one of them will be able to tell you where the scorpions are now.").also { stage++ } + HOW_TO_CATCH+3 -> showTopics( + Topic(FacialExpression.ASKING, "What's in it for me?", WHY_SHOULD_I_START), + Topic(FacialExpression.NEUTRAL, "Ok, I will do it then.", END_DIALOGUE ) + ) + + + WAITING_FOR_SCORPIONS -> { + if (!hasAnItem(player!!, arrayOf(Items.SCORPION_CAGE_456, Items.SCORPION_CAGE_457, Items.SCORPION_CAGE_458, + Items.SCORPION_CAGE_459, Items.SCORPION_CAGE_460, Items.SCORPION_CAGE_461, + Items.SCORPION_CAGE_462), false).exists()){ + playerl(FacialExpression.SAD, "I've lost my cage.").also { stage = GIVE_ANOTHER_CAGE } + } else { + playerl(FacialExpression.NEUTRAL, "I've not caught all the scorpions yet.").also { stage++ } + } + } + WAITING_FOR_SCORPIONS+1 -> npcl(FacialExpression.WORRIED, "Well remember to go speak to the Seers" + + " North of here, if you need any help.").also { stage = END_DIALOGUE } + + GIVE_ANOTHER_CAGE -> { + player!!.inventory.add(Item(Items.SCORPION_CAGE_456)) + // Clear all attributes keeping track of the scorpions + player!!.removeAttribute("scorpion_catcher:caught_taverly") + player!!.removeAttribute("scorpion_catcher:caught_barb") + player!!.removeAttribute("scorpion_catcher:caught_monk") + npcl(FacialExpression.NEUTRAL, "Ok, here's another cage. You're almost as bad" + + " at losing things as me.").also { stage = END_DIALOGUE } + } + + GOT_THEM_ALL -> { + playerl ("I have retrieved all your scorpions.").also { stage++ } + } + GOT_THEM_ALL+1 ->{ + npcl(FacialExpression.HAPPY, "Aha, my little scorpions home at last!").also { stage++ } + player!!.inventory.remove(Item(Items.SCORPION_CAGE_463)) + + // Don't need to keep track of which scorpions have been caught anymore + player!!.removeAttribute("scorpion_catcher:caught_taverly") + player!!.removeAttribute("scorpion_catcher:caught_barb") + player!!.removeAttribute("scorpion_catcher:caught_monk") + } + GOT_THEM_ALL+2 ->{ + end().also { finishQuest(player!!, Quests.SCORPION_CATCHER) } + } + + + FULL_INVENTORY -> npcl("You don't have space to hold a cage").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt new file mode 100644 index 000000000..864f2ce08 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCWallListener.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.api.getQuestStage +import core.api.sendMessage +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Scenery +import content.data.Quests + +class SCWallListener : InteractionListener { + + override fun defineListeners() { + on(Scenery.OLD_WALL_2117, IntType.SCENERY, "search"){ player, node -> + // You can only go through if you are doing the quest + //https://youtu.be/crc-47rwjvE?feature=shared&t=841 + // Otherwise the crack reverts back to normal + // Doesn't make any sense but that's authentic... + if ((ScorpionCatcher.QUEST_STATE_DARK_PLACE .. 99).contains(getQuestStage(player, Quests.SCORPION_CATCHER))) { + // Check what side the player is on and teleport them to the other + if (player.location == Location(2875, 9799, 0)){ + sendMessage(player, "You've found a secret door") + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + else{ + // We're leaving the room + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + } + else{ + sendMessage(player, "You search the wall but find nothing.") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt new file mode 100644 index 000000000..2c314f0d6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcher.kt @@ -0,0 +1,148 @@ +package content.region.kandarin.quest.scorpioncatcher + +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class ScorpionCatcher : Quest(Quests.SCORPION_CATCHER, 108, 107, 1, 76, 0, 1, 6) { + companion object { + const val QUEST_STATE_NOT_STARTED = 0 + const val QUEST_STATE_TALK_SEERS = 10 + const val QUEST_STATE_DARK_PLACE = 20 + const val QUEST_STATE_OTHER_SCORPIONS = 30 + const val QUEST_STATE_PEKSA_HELP = 40 + const val QUEST_STATE_DONE = 100 + + const val ATTRIBUTE_TAVERLEY = "scorpion_catcher:caught_taverly" + const val ATTRIBUTE_BARB = "scorpion_catcher:caught_barb" + const val ATTRIBUTE_MONK = "scorpion_catcher:caught_monk" + + } + + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + + var ln = 12 + + val caughtTaverly = player!!.getAttribute(ATTRIBUTE_TAVERLEY, false) + val caughtBarb = player.getAttribute(ATTRIBUTE_BARB, false) + val caughtMonk = player.getAttribute(ATTRIBUTE_MONK, false) + + if (stage == QUEST_STATE_NOT_STARTED){ + line(player, "I can start this quest by speaking to !!Thormac?? who is in the", ln++) + line(player, "!!Sorcerer's Tower?? south-west of !!Catherby??", ln++) + ln++ //blank line + line(player, "Requirements:", ln++) + line(player, "Level 31 Prayer", ln, player.skills.staticLevels[Skills.PRAYER] >= 31) + } else { + line(player, "I've spoken to Thormac in the Sorcerer's Tower south-west", ln++, true) + line(player, "of Catherby. He's lost his pet Kharid Scorpions and needs", ln++, true) + line(player, "my help to find them.", ln++, true) + + // 10 -> 20 + if (stage >= QUEST_STATE_DARK_PLACE) { + ln++ + line(player, "I've spoken to a Seer and been given the location of one", ln++, true) + line(player, "of the Kharid Scorpions.", ln++, true) + } else if (stage >= QUEST_STATE_TALK_SEERS) { + ln++ + line(player, "I need to go to the !!Seers' Village?? and talk to the !!Seer??", ln++) + line(player, "about the lost !!Kharid Scorpions??.", ln++) + } + + // 20 -> 20 + 1st Scorpion + if (stage >= QUEST_STATE_DARK_PLACE && caughtTaverly || stage >= QUEST_STATE_OTHER_SCORPIONS) { + ln++ + line(player, "The first Kharid Scorpion is in a secret room near some", ln++, true) + line(player, "nasty spiders with two coffins nearby.", ln++, true) + } else if (stage >= QUEST_STATE_DARK_PLACE) { + ln++ + line(player, "The first !!Kharid Scorpion?? is in a secret room near some", ln++) + line(player, "!!nasty spiders?? with two !!coffins?? nearby.", ln++) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The first !!Kharid Scorpion?? is in a !!dark place between a lake??", ln++) +// line(player, "and a !!holy island??. It will be close when you enter.", ln++) + ln++ + line(player, "I'll need to talk to a !!Seer?? again one I've caught the first", ln++) + line(player, "!!Kharid Scorpion??.", ln++) + } + + // 20 + 1st Scorpion -> 30 + if (stage >= QUEST_STATE_OTHER_SCORPIONS) { + // This line disappears when the you talk to the Seer again. + } else if (stage >= QUEST_STATE_DARK_PLACE && caughtTaverly){ + // Todo check this line + ln++ + line(player, "I should go back to the Seer and ask about the other", ln++) + line(player, "scorpions.", ln++) + } + + // 30 -> 40 + if (stage >= QUEST_STATE_OTHER_SCORPIONS){ + ln++ + val barb_strike = caughtBarb || (stage == QUEST_STATE_PEKSA_HELP) + line(player, "The second !!Kharid Scorpion?? has been in a !!village of??", ln++, barb_strike || stage == QUEST_STATE_DONE) + line(player, "!!uncivilised-looking warriors in the east??. It's been picked up", ln++, barb_strike || stage == QUEST_STATE_DONE) + line(player, "by some sort of !!merchant??.", ln++, barb_strike || stage == QUEST_STATE_DONE) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The second !!Kharid Scorpion?? was once in a !!village two??", ln++, barb_strike || stage == QUEST_STATE_DONE) +// line(player, "!!canoe trips from lumbridge??. A !!shopkeeper?? there picked it up.", ln++, barb_strike || stage == QUEST_STATE_DONE) + if (stage == QUEST_STATE_PEKSA_HELP){ + // todo check this block + ln++ + line(player, "I spoke with !!Peksa?? who said he sent it to his brother", ln++, caughtBarb) + line(player, "at the !!Barbarian outpost.??", ln++, caughtBarb) + } + ln++ + line(player, "The third !!Kharid Scorpion?? is in some sort of !!upstairs room??", ln++, caughtMonk || stage == QUEST_STATE_DONE) + line(player, "with !!brown clothing on a table??.", ln++, caughtMonk || stage == QUEST_STATE_DONE) + // Jan 21, 2010 version has a slightly updated but similar location. +// line(player, "The third !!Kharid Scorpion?? is in an !!upstairs room?? with !!brown??", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "!!clothing on a table?? The clothing is adorned with a golden", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "four-pointed star. You should start looking where monks", ln++, caughtMonk || stage == QUEST_STATE_DONE) +// line(player, "reside.", ln++, caughtMonk || stage == QUEST_STATE_DONE) + } + + // 40 -> 100 + if (stage == QUEST_STATE_DONE) { + // This line disappears when you complete the quest. + } else if (caughtBarb && caughtTaverly && caughtMonk && stage >= QUEST_STATE_OTHER_SCORPIONS){ + ln++ + line(player, "I need to take the !!Kharid Scorpions?? to !!Thormac??.", ln) + } + + // 100 + if (stage == QUEST_STATE_DONE) { + ln++ + line(player, "I've spoken to Thormac and he thanked me for finding his", ln++, true) + line(player, "pet Kharid Scorpions.", ln++, true) + ln++ + ln++ + line(player, "QUEST COMPLETE!", ln) + return + } + + } + } + + override fun finish(player: Player?) { + var ln = 10 + super.finish(player) + player!!.packetDispatch.sendString("You have completed the Scorpion Catcher Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.SCORPION_CAGE_463, 240, 277, 5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "6625 Strength XP", ln) + + player.skills.addExperience(Skills.STRENGTH, 6625.0) + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt new file mode 100644 index 000000000..3b95da47b --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt @@ -0,0 +1,88 @@ +package content.region.kandarin.quest.scorpioncatcher + +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_TAVERLEY +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_BARB +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_MONK +import core.api.* +import core.game.node.item.Item +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.system.config.NPCConfigParser +import core.game.world.GameWorld +import core.tools.Log +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class ScorpionCatcherUseListener : InteractionListener { + override fun defineListeners() { + val scorpToAttr = mapOf( + /* 385 - Barbarian + * 386 - Taverley + * 387 - Monastery + */ + NPCs.KHARID_SCORPION_385 to ATTRIBUTE_BARB, + NPCs.KHARID_SCORPION_386 to ATTRIBUTE_TAVERLEY, + NPCs.KHARID_SCORPION_387 to ATTRIBUTE_MONK + ) + val cageToScorps = mapOf( + /* Taverley(386) Barbarian(385) Monastery(387) + * TBM + * 456 --- + * 457 O-- + * 458 00- + * 459 -0- + * 460 -00 + * 461 --0 + * 462 0-0 + * 463 000 + */ + Items.SCORPION_CAGE_456 to setOf(), + Items.SCORPION_CAGE_457 to setOf(NPCs.KHARID_SCORPION_386), + Items.SCORPION_CAGE_458 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_385), + Items.SCORPION_CAGE_459 to setOf(NPCs.KHARID_SCORPION_385), + Items.SCORPION_CAGE_460 to setOf(NPCs.KHARID_SCORPION_385, NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_461 to setOf(NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_462 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_387), + Items.SCORPION_CAGE_463 to setOf(NPCs.KHARID_SCORPION_386, NPCs.KHARID_SCORPION_385, NPCs.KHARID_SCORPION_387) + ) + + fun catchScorpion(player: Player, item: Node, scorpion: Node): Boolean { + val haveInCage = cageToScorps[item.id] ?: return false + if (scorpion.id in haveInCage) { + sendMessage(player, "You already have this scorpion in this cage.") //TODO check this message + return true + } + val newScorpionSet = haveInCage + setOf(scorpion.id) + var newItem: Int? = null + for ((cage, scorps) in cageToScorps) { + if (scorps == newScorpionSet) { + newItem = cage + } + } + if (newItem == null) { + log(this::class.java, Log.ERR, "Error looking up new scorpion cage item - this isn't possible") + return false + } + val attribute = scorpToAttr[scorpion.id] + if (removeItem(player, Item(item.id, 1)) && addItem(player, newItem)) { + sendMessage(player, "You catch a scorpion!") + setAttribute(player, "/save:$attribute", true) + runTask(player, 2) { + scorpion.asNpc().respawnTick = GameWorld.ticks + scorpion.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return true + } + return false + } + + for (scorp in scorpToAttr.keys) { + for (cage in cageToScorps.keys) { + onUseWith(IntType.NPC, cage, scorp) { player, usedCage, usedScorp -> + return@onUseWith catchScorpion(player, usedCage, usedScorp) + } + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/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..7f4c3d09c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt @@ -0,0 +1,283 @@ +package content.region.kandarin.quest.templeofikov + +import content.data.LightSource +import content.data.Quests +import content.global.skill.agility.AgilityHandler +import content.global.skill.skillcapeperks.SkillcapePerks +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.global.action.PickupHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.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!") + + // Ikov dungeon top stairs to either boots of lightness or the dark room + on(Scenery.STAIRS_35121, SCENERY, "Climb-down") { player, node -> + if (LightSource.hasActiveLightSource(player) || SkillcapePerks.isActive(SkillcapePerks.CONSTANT_GLOW, player)) { // has light source + teleport(player, Location.create(2641, 9763, 0)) + } else { // doesn't have light source + teleport(player, Location.create(2641, 9740, 0)) + sendDialogueLines(player, "Hmm...bit dark down here! I'm not going to venture far!") + } + true + } + + // Ikov dungeon bottom stairs (lit) to top stairs + on(Scenery.STAIRS_96, SCENERY, "Climb-up") { player, node -> + teleport(player, Location.create(2649, 9804, 0)) + } + + // Ikov dungeon bottom stairs (dark) to top stairs + on(Scenery.STAIRS_33232, SCENERY, "Climb-up") { player, node -> + teleport(player, Location.create(2649, 9804, 0)) + } + + // 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..c4c0d1ef0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt @@ -0,0 +1,162 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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/CamelotGuardDialogue.java b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java index d8f856276..5cc97311a 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java @@ -52,6 +52,6 @@ public final class CamelotGuardDialogue extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 812 }; + return new int[] { 6183, 6184 }; } } diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/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..323e82d85 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt @@ -1,44 +1,48 @@ package content.region.kandarin.seers.dialogue -import core.Util import core.game.dialogue.DialoguePlugin import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.diary.AchievementDiary import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import org.json.simple.JSONObject @Initializable -class GeoffreyDialogue(player: Player? = null) : 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()) { + //determine reward level that has been claimed + var gotoStage = 0 + //want the highest value, so this is checked hardest->easiest + if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,2)) { + gotoStage = 102 + } + else if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,1)) { + gotoStage = 101 + } + else if (AchievementDiary.hasClaimedLevelRewards(player,DiaryType.SEERS_VILLAGE,0)) { + gotoStage = 100 + } + //give reward, or proceed to normal dialogue + if (gotoStage != 0) { player("Hello there. Are you Geoff-erm-Flax? I've been told that", "you'll give me some flax.") - // If 1 day has not passed since last flax reward - if (player.getAttribute("diary:seers:flax-timer", 0) > System.currentTimeMillis()) { - stage = 98 - return true - } - // If player cannot receive flax reward - if (!player.inventory.hasSpaceFor(Item(Items.FLAX_1780, 1))) { - stage = 99 - return true - } - // Determine flax reward by seers diary reward status - when (diary.reward) { - -1 -> stage = 999 - 0 -> stage = 100 - 1 -> stage = 101 - 2 -> stage = 102 - } - } else { + //Already claimed flax else no room else give correct reward + stage = if (getStoreFile().getBoolean(player.username.lowercase())) { 98 } + else if (!player.inventory.hasSpaceFor(Item(Items.FLAX_1780, 1))) { 99 } + else { gotoStage } + } + //If the diary has not been completed + else { player("Hello there. You look busy.") stage = 0 } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GeoffreyDialogue(player) } @@ -46,7 +50,7 @@ class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug when (stage) { 999 -> end() 0 -> npc("Yes, I am very busy. Picking GLORIOUS flax.", "The GLORIOUS flax won't pick itself. So I pick it.", "I pick it all day long.").also { stage++ } - 1 -> player("Wow, all that flax must really mount up. What do you do with it all?").also { stage++ } + 1 -> player("Wow, all that flax must really mount up.", "What do you do with it all?").also { stage++ } 2 -> npc("I give it away! I love picking the GLORIOUS flax,", "but, if I let it all mount up, I wouldn't have any", "room for more GLORIOUS flax.").also { stage++ } 3 -> player("So, you're just picking the flax for fun? You must", "really like flax.").also { stage++ } 4 -> npc("'Like' the flax? I don't just 'like' flax. The", "GLORIOUS flax is my calling, my reason to live.", "I just love the feeling of it in my hands!").also { stage++ } @@ -55,7 +59,7 @@ class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 7 -> player("I know this area! It's, erm, Seers' Village. There's", "a pub and, er, a bank.").also { stage++ } 8 -> npc("Pah! You call that local knowledge? Perhaps if you", "were wearing some kind of item from one of the", "seers, I might trust you.").also { stage = 999 } - 98 -> npc("I've already given you your GLORIOUS flax", "for the day. Come back tomorrow.").also { stage = 999 } // TODO find accurate dialogue + 98 -> npc("Don't be greedy. Other people want GLORIOUS flax too.", "You can have some more tomorrow.").also { stage = 999 } // TODO find accurate dialogue, no source found yet, so I'm using the modern RS3 dialogue from wiki source (https://runescape.wiki/w/Transcript:Geoffrey) 99 -> npc("Yes, but your inventory is full. Come back", "when you have some space for GLORIOUS flax.").also { stage = 999 } // TODO find accurate dialogue 100 -> {rewardFlax(30, "Yes. The seers have instructed me to give you an", "allowance of 30 GLORIOUS flax a day. I'm not going", "to argue with them, so here you go.")} // TODO find accurate dialogue 101 -> {rewardFlax(60, "Yes. Stankers has instructed me to give you an", "allowance of 60 GLORIOUS flax a day. I'm not going", "to argue with a dwarf, so here you go.")} // TODO find accurate dialogue @@ -67,7 +71,7 @@ class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug fun rewardFlax(n: Int, vararg messages: String): Unit { npc(*messages) player.inventory.add(Item(Items.FLAX_1780, n)) - player.setAttribute("/save:diary:seers:flax-timer", Util.nextMidnight(System.currentTimeMillis())) + getStoreFile()[player.username.toLowerCase()] = true stage = 999 } @@ -75,4 +79,8 @@ class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return intArrayOf(8590) } -} + fun getStoreFile(): JSONObject { + return ServerStore.getArchive("daily-seers-flax") + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/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..e241c34f2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialogue.kt @@ -0,0 +1,108 @@ +package content.region.kandarin.seers.dialogue + +import content.region.kandarin.seers.diary.SeerDiaryDialogue +import content.region.kandarin.quest.scorpioncatcher.SCSeerDialogue +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher +import core.api.getAttribute +import core.api.getQuestStage +import core.api.isQuestInProgress +import core.api.openDialogue +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs +import core.game.dialogue.Topic +import content.data.Quests + + + +@Initializable +class SeerDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SEER_388) + } + + companion object { + const val OTHER_TOPIC = 10 + const val DIARY = 20 + const val SC_QUEST = 30 + + const val SC_QUEST_HELP = 40 + const val SC_QUEST_FRIEND = 50 + const val SC_QUEST_OTHER_SCORPIONS = 60 + + const val MANY_GREETINGS = 70 + const val POWER = 80 + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.WORRIED, "Uh, what was that dark force? I've never sensed anything like it...").also { stage = START_DIALOGUE } // https://www.youtube.com/watch?v=mYsxit46rGo May 14 2010 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val scorpionCatcherQuestStage = getQuestStage(player, Quests.SCORPION_CATCHER) + when (stage) { + START_DIALOGUE -> + npcl(FacialExpression.NEUTRAL, "Anyway, sorry about that.").also { stage++ } + START_DIALOGUE+1 -> { + if (isQuestInProgress(player, Quests.SCORPION_CATCHER, 1, 99)) { + showTopics( + Topic("Talk about Scorpion Catcher.", SC_QUEST, true), + Topic("Talk about Achievement Diary.", DIARY, true) + ) + } + else { + showTopics( + Topic("Talk about something else.", OTHER_TOPIC, true), + Topic("Talk about achievement diary.", DIARY, true) + ) + } + } + + SC_QUEST -> { + if (scorpionCatcherQuestStage == ScorpionCatcher.QUEST_STATE_TALK_SEERS) { + showTopics( + Topic("I need to locate some scorpions.", SC_QUEST_HELP,), + Topic("Your friend Thormac sent me to speak to you.", SC_QUEST_FRIEND,), + Topic("I seek knowledge and power!", POWER) + ) + } + else if ((scorpionCatcherQuestStage == ScorpionCatcher.QUEST_STATE_DARK_PLACE) and + getAttribute(player!!, ScorpionCatcher.ATTRIBUTE_TAVERLEY, false) + ) { + playerl( + FacialExpression.NEUTRAL, + "Hi, I have retrieved the scorpion from near the spiders." + ).also { stage = SC_QUEST_OTHER_SCORPIONS } + } + else { + npcl(FacialExpression.NEUTRAL, "Good luck finding those scorpions.").also { stage = END_DIALOGUE } + } + } + SC_QUEST_HELP, SC_QUEST_FRIEND, SC_QUEST_OTHER_SCORPIONS -> { + // Use the current stage value as the entry point to Seers + openDialogue(player, SCSeerDialogue(scorpionCatcherQuestStage, stage), npc) + } + + OTHER_TOPIC -> showTopics( + Topic("Many greetings.", MANY_GREETINGS), + Topic("I seek knowledge and power!", POWER) + ) + DIARY -> openDialogue(player, SeerDiaryDialogue(), npc) + + MANY_GREETINGS -> npcl(FacialExpression.NEUTRAL, + "Remember, whenever you set out to do something, something else must be done first.").also { stage = END_DIALOGUE } + + POWER -> npcl(FacialExpression.NEUTRAL, "Knowledge comes from experience, power comes from battleaxes.").also { stage = END_DIALOGUE } + + } + return true + } +} diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java b/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java deleted file mode 100644 index 327e2e4d4..000000000 --- a/Server/src/main/content/region/kandarin/seers/dialogue/SeerDialoguePlugin.java +++ /dev/null @@ -1,154 +0,0 @@ -package content.region.kandarin.seers.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SeerDialoguePlugin dialogue. - * - * @author 'afaroutdude - */ -@Initializable -public class SeerDialoguePlugin extends DialoguePlugin { - - public SeerDialoguePlugin() { - } - - public SeerDialoguePlugin(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[]{388}; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SeerDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - // npc("Uh, what was that dark force? I've never sensed", "anything like it..."); // https://www.youtube.com/watch?v=mYsxit46rGo May 14 2010 - // npc("Anyway, sorry about that."); - options("Talk about something else.", "Talk about achievement diary."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - int level = 0; - - switch (stage) { - case 999: - end(); - break; - case 0: - switch (buttonId) { - case 1: - npc("Many greetings."); - stage = 1; - break; - case 2: - if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, 0)) { - player("I seem to have lost my seers' headband..."); - stage = 80; - break; - } - else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { - player("Can you remind me what my headband does?"); - stage = 90; - break; - } - else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { - player("Hi. I've completed the Easy tasks in my Achievement", "Diary."); - stage = 200; - break; - } - else { - player("Hi! Can you help me out with the Achievement Diary", "tasks?"); - stage = 101; - break; - } - } - break; - case 80: - AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, 0); - npc("Here's your replacement. Please be more careful."); - stage = 999; - break; - case 90: - npc("Your headband marks you as an honourary seer.", "Geoffrey - who works in the field to the", "south - will give you free flax every day."); - stage = 999; - break; - case 100: - npc("I certainly do - we have a set of tasks spanning Seers'", "Village, Catherby, Hemenster and the Sinclair Mansion.", "Just complete the tasks listed in the Achievement Diary", "and they will be ticked off automatically."); - stage = 999; - break; - case 101: - npc("I'm afraid not. It is important that adventurers", "complete the tasks unaided. That way, only the truly", "worthy collect the spoils."); - stage = 999; - break; - case 200: - npc("Well done, adventurer. You are clearly a " + (player.isMale() ? "man" : "woman") + "of", "great wisdom. I have a gift for you."); - stage++; - break; - case 201: - if (!AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, 0)) { - npc("Come back when you have two free inventory slots."); - stage = 999; - } else { - interpreter.sendItemMessage(AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, 0)[0], "The seer hands you a strange-looking headband and a", "rusty lamp."); - stage++; - } - break; - case 202: - npc("You are now an honourary seer and Geoffrey - who", "works in the field to the south - will give you free flax", "every day. Don't call him 'Geoffrey' though: he prefers", "to be known as 'Flax'."); - stage++; - break; - case 203: - player("Flax? What sort of name is that for a person."); - stage++; - break; - case 204: - npc("I know, I know. The poor boy is a simple soul - he just", "really loves picking flax. A little too much, I fear."); - stage=999; - break; - - case 1: - options("Many greetings.", "I seek knowledge and power!"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("Many greetings."); - stage = 10; - break; - case 2: - player("I seek knowledge and power!"); - stage = 20; - break; - } - break; - case 10: - npc("Remember, whenever you set out to do something,", "something else must be done first."); - stage = 999; - break; - case 20: - npc("Knowledge comes from experience, power", "comes from battleaxes."); - stage = 999; - break; - } - return true; - } - -} diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt new file mode 100644 index 000000000..e0eb61789 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt @@ -0,0 +1,87 @@ +package content.region.kandarin.seers.diary + +import core.api.sendItemDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +class SeerDiaryDialogue : DialogueFile() { + + companion object { + + const val LOST_HEAD_BAND = 20 + const val HEAD_BAND_HELP = 30 + const val CLAIM_HEAD_BAND = 40 + const val ASK_FOR_HELP = 50 + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE ->{ + if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.SAD, "I seem to have lost my seers' headband...").also { + stage = LOST_HEAD_BAND + } + } else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.ASKING, "Can you remind me what my headband does?").also { + stage = HEAD_BAND_HELP + } + } else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl( + FacialExpression.HAPPY, + "Hi. I've completed the Easy tasks in my Achievement Diary." + ).also { + stage = CLAIM_HEAD_BAND + } + } else { + playerl(FacialExpression.ASKING, "Do you have an Achievement Diary for me?").also { + stage = ASK_FOR_HELP + } + } + } + + + LOST_HEAD_BAND -> { + if (AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, 0)) + npcl(FacialExpression.ANNOYED, "Here's your replacement. Please be more careful.").also { + stage = END_DIALOGUE + } + else + // This line is just guessed + npcl(FacialExpression.HALF_GUILTY, "It seems your inventory is full").also { stage = END_DIALOGUE } + } + HEAD_BAND_HELP -> npcl(FacialExpression.NEUTRAL, "Your headband marks you as an honourary seer. Geoffrey - who works in the field to the south - will give you free flax every day.").also { + stage = END_DIALOGUE + } + + // This has to be npc otherwise wordwrap goes wrong and extends to 5 lines + ASK_FOR_HELP -> npc(FacialExpression.HAPPY, "I certainly do - we have a set of tasks spanning Seers'", "Village, Catherby, Hemenster and the Sinclair Mansion.", + "Just complete the tasks listed in the Achievement Diary", "and they will be ticked off automatically.").also { stage++ } + ASK_FOR_HELP + 1 -> playerl(FacialExpression.ASKING, "Can you help me out with the Achievement Diary tasks?").also { stage++ } + ASK_FOR_HELP + 2 -> npcl(FacialExpression.SAD, + "I'm afraid not. It is important that adventurers complete the tasks unaided. That way, only the truly worthy collect the spoils.").also { + stage = END_DIALOGUE + } + + CLAIM_HEAD_BAND -> npcl(FacialExpression.HAPPY, "Well done, adventurer. You are clearly a "+(if (player!!.isMale) "man" else "woman")+" of great wisdom. I have a gift for you.").also { stage++ } + CLAIM_HEAD_BAND + 1 -> { + if (!AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, 0)) { + npcl(FacialExpression.NEUTRAL, "Come back when you have two free inventory slots.").also { + stage = END_DIALOGUE + } + } else { + sendItemDialogue(player!!, AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, 0)[0], + "The seer hands you a strange-looking headband and a rusty lamp.").also { stage++ } + } + } + CLAIM_HEAD_BAND + 2 -> npcl(FacialExpression.HAPPY, "You are now an honourary seer and Geoffrey - who works in the field to the south - will give you free flax every day. Don't call him 'Geoffrey' though: he prefers to be known as 'Flax'.").also { stage++ } + CLAIM_HEAD_BAND + 3 -> playerl(FacialExpression.ASKING, "Flax? What sort of name is that for a person?").also { stage++ } + CLAIM_HEAD_BAND + 4 -> npcl(FacialExpression.NEUTRAL, "I know, I know. The poor boy is a simple soul - he just really loves picking flax. A little too much, I fear.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchivementDiary.kt b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt similarity index 76% 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..bd9e90188 100644 --- a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchivementDiary.kt +++ b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt @@ -1,20 +1,23 @@ 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.entity.player.link.prayer.PrayerType import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items import org.rs09.consts.NPCs -import 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,10 +26,12 @@ 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) private val SEERS_COAL_TRUCKS_AREA = ZoneBorders(2690, 3502, 2699, 3508) + private val SEERS_COURTHOUSE_AREA = ZoneBorders(2732, 3467, 2739, 3471) private val RANGING_GUILD_LOCATION = Location(2657, 3439) @@ -46,6 +51,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 +101,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 +130,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 +177,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 +297,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 ) } } @@ -272,10 +307,60 @@ 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 + ) + } + } + + 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 + ) + } + } + } + + override fun onAreaVisited(player: Player) { + if (inBorders(player, SEERS_COURTHOUSE_AREA) && player.prayer.equals(PrayerType.PIETY)) { finishTask( player, DiaryLevel.HARD, - HardTasks.DIAL_FAIRY_RING_MCGRUBORS_WOOD + HardTasks.ENTER_SEERS_COURTHOUSE_WITH_PIETY ) } } 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..8d25adaf1 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,41 @@ 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 +import core.api.* /** * 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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..f43ac6bed 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt @@ -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) @@ -103,7 +103,7 @@ class EWListeners : InteractionListener { } // Player needs to receive a battered book sendItemDialogue(player, Item(Items.BATTERED_BOOK_2886), "You find a book titled 'The Elemental Shield'.") - addItem(player, batteredBook.id) + addItemOrDrop(player, batteredBook.id) return@on true } @@ -120,7 +120,7 @@ class EWListeners : InteractionListener { } sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.") - addItem(player, slashedBook.id) + addItemOrDrop(player, slashedBook.id) if (player.inventory.addIfDoesntHave(batteredKey)) { sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.") } @@ -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 } @@ -237,7 +237,7 @@ class EWListeners : InteractionListener { on(Scenery.CRATE_3400, IntType.SCENERY, "search") { player, _ -> if (!getAttribute(player, "/save:ew1:got_needle", false)) { setAttribute(player, "/save:ew1:got_needle", true) - addItem(player, Items.NEEDLE_1733) + addItemOrDrop(player, Items.NEEDLE_1733) sendMessage(player, "You find a needle.") } else { sendMessage(player, "You search the crate but find nothing.") @@ -249,7 +249,7 @@ class EWListeners : InteractionListener { on(Scenery.CRATE_3394, IntType.SCENERY, "search") { player, _ -> if (!getAttribute(player, "/save:ew1:got_leather", false)) { setAttribute(player, "/save:ew1:got_leather", true) - addItem(player, Items.LEATHER_1741) + addItemOrDrop(player, Items.LEATHER_1741) sendMessage(player, "You find some leather.") } else { sendMessage(player, "You search the crate but find nothing.") @@ -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..097b61613 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++) } } } @@ -115,13 +113,12 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command } override fun defineCommands() { - define("resetew", Privilege.ADMIN) { player, _ -> + define("resetew", Privilege.ADMIN, description = "Resets Elemental Workshop I progress and teleports you to the start.") { player, _ -> setAttribute(player, "/save:ew1:got_needle", false) 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) @@ -132,17 +129,17 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command addItem(player, Items.HAMMER_2347) addItem(player, Items.COAL_453, 4) } - define("readyew", Privilege.ADMIN) { player, _ -> + define("readyew", Privilege.ADMIN, description = "Sets Elemental Workshop I furnace as ready (queststage->95, changes all required attributes/varbits)") { player, _ -> val enabled = 1 setAttribute(player, "/save:ew1:got_needle", true) 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/handlers/TribesmanNPC.java b/Server/src/main/content/region/karamja/handlers/TribesmanNPC.java deleted file mode 100644 index 184357c1d..000000000 --- a/Server/src/main/content/region/karamja/handlers/TribesmanNPC.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.karamja.handlers; - -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.game.system.config.NPCConfigParser; - -/** - * Represents the tribesamn npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class TribesmanNPC extends AbstractNPC { - - /** - * Represents the npc ids. - */ - private static final int[] IDS = new int[] { 191, 2496, 2497 }; - - /** - * Constructs a new {@code TribesmanNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public TribesmanNPC(int id, Location location) { - super(id, location, true); - getDefinition().getHandlers().put(NPCConfigParser.POISONOUS, true); - } - - /** - * Constructs a new {@code TribesmanNPC} {@code Object}. - */ - public TribesmanNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new TribesmanNPC(id, location); - } - - @Override - public int[] getIds() { - return IDS; - } - -} diff --git a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java index 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..a8d2da26e 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,126 @@ 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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..97a16f7d9 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt @@ -8,6 +8,7 @@ import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import content.data.Quests class TribalTotemListeners : InteractionListener { @@ -22,19 +23,21 @@ class TribalTotemListeners : InteractionListener { override fun defineListeners() { on(frontDoor, IntType.SCENERY, "Open"){ player, door -> - if(player.questRepository.getStage("Tribal Totem") >= 35){ + if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) >= 35){ core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) } - sendMessage(player,"The door is locked shut.") + else { + sendMessage(player,"The door is locked shut.") + } return@on true } on(realCrate, IntType.SCENERY, "Investigate"){ player, node -> - if(player.questRepository.getStage("Tribal Totem") in 1..19 && !player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ - 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) + 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, Ardougne.You carefully peel it off and take it.") + addItemOrDrop(player,Items.ADDRESS_LABEL_1858,1) } - else if(player.questRepository.getStage("Tribal Totem") in 1..19 && player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ + else if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) in 1..19 && player.inventory.containsAtLeastOneItem(Items.ADDRESS_LABEL_1858)){ sendDialogue(player,"There was a label on this crate, but it's gone now since you took it!") } return@on true @@ -48,7 +51,7 @@ class TribalTotemListeners : InteractionListener { onUseWith(IntType.SCENERY,label,wizCrate){ player, used, with -> sendDialogue(player,"You carefully place the delivery address label over the existing label, covering it completely.") removeItem(player,label) - player.questRepository.getQuest("Tribal Totem").setStage(player,20) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,20) return@onUseWith true } @@ -56,7 +59,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 } @@ -95,7 +98,8 @@ class TribalTotemListeners : InteractionListener { on(openChest, IntType.SCENERY, "Search"){ player, node -> if(!player.inventory.containsAtLeastOneItem(Items.TOTEM_1857)){ sendDialogue(player,"Inside the chest you find the tribal totem.") - addItem(player,Items.TOTEM_1857) + addItemOrDrop(player,Items.TOTEM_1857) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,35) } else{ sendDialogue(player,"Inside the chest you don't find anything because you already took the totem!") diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt index ea707ce49..c89baea9e 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,14 +19,14 @@ 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++) - line(player,"!!Shrimp & Parrot?? restaurant in Brimhaven.",line++) + line(player,"I can start this quest by speaking to !!Kangai Mau?? in !!the??",line++) + line(player,"!!Shrimp & Parrot?? restaurant in Brimhaven",line++) line += 1 line(player,"To complete this quest I need:",line++) - line(player,"!!Level 21 Theiving??",line++, player?.skills?.getStaticLevel(Skills.THIEVING)!! >= 21) + line(player,"!!Level 21 Thieving??",line++, player?.skills?.getStaticLevel(Skills.THIEVING)!! >= 21) } else if(started && stage != 100){ if(stage >= 10){ 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/TzHaarDialogue.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarDialogue.kt new file mode 100644 index 000000000..bac301a6a --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarDialogue.kt @@ -0,0 +1,49 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.openNpcShop +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class TzHaarDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HALF_GUILTY, "Can I help you JalYt-Ket-${player.username}?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What do you have to trade?", 10, true), + Topic(FacialExpression.HALF_GUILTY, "What did you call me?", 20), + Topic(FacialExpression.HALF_GUILTY, "No I'm fine thanks.", END_DIALOGUE), + ) + 10 -> end().also { openNpcShop(player, npc.id) } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?").also { stage++ } + 21 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?", 22), + Topic(FacialExpression.HALF_GUILTY, "I guess so...", 25), + Topic(FacialExpression.HALF_GUILTY, "No I'm not!", END_DIALOGUE) + ) + + 22 -> npcl(FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?").also { stage++ } + 23 -> playerl(FacialExpression.HALF_GUILTY, "Well yes I suppose I am...").also { stage++ } + 24 -> npcl(FacialExpression.HALF_GUILTY, "Then you JalYt-Ket!").also { stage = END_DIALOGUE } + + 25 -> npcl(FacialExpression.HALF_GUILTY, "Well then, no problems.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TZHAAR_HUR_TEL_2620, NPCs.TZHAAR_HUR_LEK_2622, NPCs.TZHAAR_MEJ_ROH_2623) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java deleted file mode 100644 index 09653c566..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJah.java +++ /dev/null @@ -1,313 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; -import core.game.world.GameWorld; - -/** - * Handles the TzHaarMejJal dialogue. - * @author 'Vexia - * @author Empathy - * @author Logg - */ -@Initializable -public class TzHaarMejJah extends DialoguePlugin { - private static final Item APPEARANCE_FEE = new Item(6529, 8000); // 8000 tokkul, about the same as you get from failing jad - - public TzHaarMejJah() { - - } - - public TzHaarMejJah(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new TzHaarMejJah(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You want help JalYt-Ket-" + player.getUsername() + "?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (GameWorld.getSettings().getJad_practice_enabled()) { - if (player.getAttribute("fc_practice_jad", false)) { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "About my challenge...", "No I'm fine thanks."); - } else { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "I want to challenge Jad directly.", "No I'm fine thanks."); - } - } else { - interpreter.sendOptions("Select an Option", player.getInventory().containItems(6570) ? "I have a fire cape here." : "What is this place?", "What did you call me?", "No I'm fine thanks."); - } - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - if (player.getInventory().containItems(6570)) { - interpreter.open(DialogueInterpreter.getDialogueKey("firecape-exchange"), npc); - break; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What did you call me?"); - stage = 20; - break; - case 3: - if (GameWorld.getSettings().getJad_practice_enabled()) { - if (player.getAttribute("fc_practice_jad", false)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "About my challenge..."); - stage = 64; - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "The challenge is too long.", "I want to challenge Jad directly."); - stage = 50; - } - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm fine thanks."); - stage = 30; - } - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm fine thanks."); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This is the fight caves, TzHaar-Xil made it for practice,", "but many JalYt come here to fight too.", "Just enter the cave and make sure you're prepared."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Are there any rules?", "Ok thanks."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Are there any rules?"); - stage = 14; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok thanks."); - stage = 13; - break; - } - break; - case 13: - end(); - break; - case 14: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Rules? Survival is the only rule in there."); - stage = 15; - break; - case 15: - interpreter.sendOptions("Select an Option", "Do I win anything?", "Sounds good."); - stage = 16; - break; - case 16: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do I win anything?"); - stage = 17; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sounds good."); - stage = 13; - break; - - } - break; - case 17: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You ask a lot of questions.", "Might give you TokKul if you last long enough."); - stage = 18; - break; - case 18: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "..."); - stage = 19; - break; - case 19: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Before you ask, TokKul is like your Coins."); - stage = 500; - break; - case 500: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gold is like you JalYt, soft and easily broken, we use", "hard rock forged in fire like TzHaar!"); - stage = 501; - break; - case 501: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?"); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "What's a 'JalYt-Ket'?", "I guess so...", "No I'm not!"); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?"); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I guess so..."); - stage = 200; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I'm not!"); - stage = 300; - break; - } - break; - case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?"); - stage = 101; - break; - case 101: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well yes I suppose I am..."); - stage = 102; - break; - case 102: - end(); - break; - case 200: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I guess so...."); - stage = 201; - break; - case 201: - end(); - break; - case 300: - end(); - break; - case 30: - end(); - break; - - case 50: - interpreter.sendDialogues(npc, FacialExpression.DISGUSTED_HEAD_SHAKE, "I thought you strong and tough", "but you want skip endurance training?"); - stage = 57; - break; - case 57: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Maybe you not JalYt-Ket afterall."); - stage = 58; - break; - case 58: - interpreter.sendOptions("Select an Option", "I don't have time for it, man.", "No, I'm JalYt-Ket!"); - stage = 51; - break; - case 51: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have time for it, man."); - stage = 52; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm JalYt-Ket! I swear!", "I'll do the training properly."); - stage = 30; - break; - } - break; - case 52: - interpreter.sendDialogues(npc, FacialExpression.DISGUSTED_HEAD_SHAKE, "JalYt, you know you not get reward","if you not do training properly, ok?"); - stage = 56; - break; - case 56: - interpreter.sendOptions("Select an Option", "That's okay, I don't need a reward.", "Oh, nevermind then."); - stage = 53; - break; - case 53: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's okay, I don't need a reward."); - stage = 54; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, nevermind then."); - stage = 30; - break; - } - break; - case 54: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I just wanna fight the big guy."); - stage = 55; - break; - case 55: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Okay JalYt.","TzTok-Jad not show up for just anyone."); - stage = 59; - break; - case 59: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You give 8000 TokKul, TzTok-Jad know you serious.", "You get it back if you victorious."); - stage = 60; - break; - case 60: - interpreter.sendOptions("Select an Option", "That's fair, here's 8000 TokKul.", "I don't have that much on me, but I'll go get it.", "TzTok-Jad must be old and tired to not just accept my challenge."); - stage = 61; - case 61: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's fair, here's 8000 TokKul."); - if (!player.getInventory().containsItem(APPEARANCE_FEE)) { - stage = 62; - break; - } - if (player.getInventory().remove(APPEARANCE_FEE)) { - stage = 69; - } else { - stage = 62; - } - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have that much on me, but I'll go get it."); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "TzTok-Jad must be old and tired", "to not just accept my challenge."); - stage = 63; - break; - } - break; - case 62: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "JalYt, you not have the TokKul.", "You come back when you are serious."); - stage = 30; - break; - case 63: - interpreter.sendDialogues(npc, FacialExpression.ANGRY, "JalYt-Mor, you the old and tired one.", "You the one not want to do proper training."); - stage = 30; - break; - case 64: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "TzTok-Jad is waiting for you.", "Do not make TzTok-Jad wait long."); - stage = 30; - break; - case 69: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Okay JalYt. Enter cave when you are prepared.", "You find TzTok-Jad waiting for JalYt challenger."); - player.setAttribute("fc_practice_jad", true); - stage = 30; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("tzhaar-mej"), 2617 }; - } -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt new file mode 100644 index 000000000..a0b865ca7 --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejJal.kt @@ -0,0 +1,167 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.GameWorld.settings +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handles the TzHaarMejJal dialogue. + * @author 'Vexia + * @author Empathy + * @author Logg + */ +@Initializable +class TzHaarMejJal(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HALF_GUILTY, "You want help JalYt-Ket-${player.username}?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> showTopics( + if (inInventory(player, Items.FIRE_CAPE_6570)) { + Topic(FacialExpression.HALF_GUILTY, "I have a fire cape here.", 100) + } else { + Topic(FacialExpression.HALF_GUILTY, "What is this place?", 10) + }, + Topic(FacialExpression.HALF_GUILTY, "What did you call me?", 20), + IfTopic(FacialExpression.HALF_GUILTY, "About my challenge...", 50, getAttribute(player, "fc_practice_jad", false) && settings!!.jad_practice_enabled), + IfTopic("I want to challenge Jad directly.", 30, !getAttribute(player, "fc_practice_jad", false) && settings!!.jad_practice_enabled, skipPlayer = true), + Topic(FacialExpression.HALF_GUILTY, "No I'm fine thanks.", END_DIALOGUE) + ) + + 10 -> npc(FacialExpression.HALF_GUILTY, + "This is the fight caves, TzHaar-Xil made it for practice,", + "but many JalYt come here to fight too.", + "Just enter the cave and make sure you're prepared." + ).also { stage++ } + + 11 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "Are there any rules?", 12), + Topic(FacialExpression.HALF_GUILTY, "Ok thanks.", END_DIALOGUE) + ) + + 12 -> npcl(FacialExpression.HALF_GUILTY, "Rules? Survival is the only rule in there.").also { stage++ } + 13 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "Do I win anything?", 14), + Topic(FacialExpression.HALF_GUILTY, "Sounds good.", END_DIALOGUE) + ) + + 14 -> npc(FacialExpression.HALF_GUILTY, + "You ask a lot of questions.", + "Might give you TokKul if you last long enough." + ).also { stage++ } + 15 -> playerl(FacialExpression.HALF_GUILTY, "...").also { stage ++ } + 16 -> npcl(FacialExpression.HALF_GUILTY, "Before you ask, TokKul is like your Coins.").also { stage++ } + 17 -> npc(FacialExpression.HALF_GUILTY, + "Gold is like you JalYt, soft and easily broken, we use", + "hard rock forged in fire like TzHaar!" + ).also{ stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.HALF_GUILTY, "Are you not JalYt-Ket?").also { stage++ } + 21 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "What's a 'JalYt-Ket'?", 22), + Topic(FacialExpression.HALF_GUILTY, "I guess so...", END_DIALOGUE), + Topic(FacialExpression.HALF_GUILTY, "No I'm not!", END_DIALOGUE) + ) + + 22 -> npcl(FacialExpression.HALF_GUILTY, "That what you are... you tough and strong no?").also { stage++ } + 23 -> playerl(FacialExpression.HALF_GUILTY, "Well yes I suppose I am...").also { stage = END_DIALOGUE } + + 30 -> player(FacialExpression.HALF_GUILTY, + "The challenge is too long.", + "I want to challenge Jad directly." + ).also { stage++ } + 31 -> npc(FacialExpression.DISGUSTED_HEAD_SHAKE, + "I thought you strong and tough", + "but you want skip endurance training?" + ).also { stage++ } + 32 -> npcl(FacialExpression.NEUTRAL, "Maybe you not JalYt-Ket afterall.").also { stage++ } + 33 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "I don't have time for it, man.", 35), + Topic("No, I'm JalYt-Ket!", 34, skipPlayer = true) + ) + + 34 -> player(FacialExpression.HALF_GUILTY, + "No, I'm JalYt-Ket! I swear!", + "I'll do the training properly." + ).also { stage = END_DIALOGUE } + + 35 -> npc(FacialExpression.DISGUSTED_HEAD_SHAKE, + "JalYt, you know you not get reward", + "if you not do training properly, ok?" + ).also { stage++ } + 36 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "That's okay, I don't need a reward.", 37), + Topic(FacialExpression.HALF_GUILTY, "Oh, nevermind then.", END_DIALOGUE) + ) + + 37 -> playerl(FacialExpression.NEUTRAL, "I just wanna fight the big guy.").also { stage++ } + 38 -> npc(FacialExpression.NEUTRAL, + "Okay JalYt.", + "TzTok-Jad not show up for just anyone." + ).also { stage++ } + 39 -> npc(FacialExpression.NEUTRAL, + "You give 8000 TokKul, TzTok-Jad know you serious.", + "You get it back if you victorious." + ).also { stage++ } + 40 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "That's fair, here's 8000 TokKul.", 41), + Topic(FacialExpression.HALF_GUILTY, "I don't have that much on me, but I'll go get it.", END_DIALOGUE), + Topic("TzTok-Jad must be old and tired to not just accept my challenge.", 42, skipPlayer = true) + ) + + 41 -> if (removeItem(player, Item(Items.TOKKUL_6529, 8000))) { + npc(FacialExpression.NEUTRAL, + "Okay JalYt. Enter cave when you are prepared.", + "You find TzTok-Jad waiting for JalYt challenger." + ) + .also { setAttribute(player, "fc_practice_jad", true) } + .also { stage = END_DIALOGUE } + } else npc(FacialExpression.NEUTRAL, + "JalYt, you not have the TokKul.", + "You come back when you are serious." + ).also { stage = END_DIALOGUE } + + 42 -> player(FacialExpression.HALF_GUILTY, + "TzTok-Jad must be old and tired", + "to not just accept my challenge." + ).also { stage++ } + 43 -> npc(FacialExpression.ANGRY, + "JalYt-Mor, you the old and tired one.", + "You the one not want to do proper training." + ).also { stage = END_DIALOGUE } + + 50 -> npc(FacialExpression.NEUTRAL, + "TzTok-Jad is waiting for you.", + "Do not make TzTok-Jad wait long." + ).also { stage = END_DIALOGUE } + + 100 -> sendDialogueOptions(player, "Sell your fire cape?", "Yes, sell it for 8,000 TokKul.", "No, keep it.").also { stage++ } + 101 -> when (buttonId) { + 1 -> npcl(FacialExpression.OLD_NORMAL, "Hand your cape here, young JalYte.").also { stage++ } + 2 -> end() + } + 102 -> end().also { if (removeItem(player, Items.FIRE_CAPE_6570)) addItem(player, Items.TOKKUL_6529, 8000) } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return TzHaarMejJal(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TZHAAR_MEJ_JAL_2617) + } +} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java deleted file mode 100644 index 2c5993906..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzRekJadNPC.java +++ /dev/null @@ -1,263 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.repository.Repository; -import core.plugin.Plugin; -import core.plugin.ClassScanner; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles everything in regards to jad pet. - * @author Empathy - * - */ -@Initializable -public class TzRekJadNPC extends OptionHandler { - - /** - * The tzhaar mej id. - */ - private static final int TZHAAR_MEJ_ID = 2617; - - /** - * The firecape item. - */ - private static final Item FIRECAPE = new Item(6570); - - /** - * The tokkul item. - */ - private static final Item TOKKUL = new Item(6529); - - /** - * The jad item. - */ - private static final Item JAD_PET = new Item(14828); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(TZHAAR_MEJ_ID).getHandlers().put("option:exchange fire cape", this); - ClassScanner.definePlugins(new TzhaarMejJalDialogue(), new TzRekJadDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "exchange fire cape": - player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey(player.getInventory().containsItem(FIRECAPE) ? "firecape-exchange" : "tzhaar-mej"), node.asNpc()); - break; - } - return true; - } - - /** - * Handles the TzhaarMejJal Dialogue to gamble for jad pet. - * @author Empathy - * - */ - public final class TzhaarMejJalDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzhaarMejJalDialogue} {@code Object}. - */ - public TzhaarMejJalDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzhaarMejJalDialogue} {@code Object}. - * - * @param player the player. - */ - public TzhaarMejJalDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzhaarMejJalDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I have a fire cape here."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Sell your fire cape?", "Yes, sell it for 8,000 TokKul.", "No, keep it.", "Bargain for TzRek-Jad."); - stage = 1; - break; - case 1: - switch(buttonId) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, player.getInventory().containsItems(FIRECAPE) ? "Hand your cape here, young JalYte." : "You not have firecape, JalYt."); - stage = 10; - break; - case 2: - end(); - break; - case 3: - interpreter.sendOptions("Sacrifice your firecape?", "Yes, I know I won't get my cape back.", "No, I like my cape!"); - stage = 20; - break; - } - break; - case 10: - if (player.getInventory().containsItem(FIRECAPE)) { - if (player.getInventory().remove(FIRECAPE)) { - TOKKUL.setAmount(8000); - player.getInventory().add(TOKKUL); - TOKKUL.setAmount(1); - } - } - end(); - break; - case 20: - switch (buttonId) { - case 1: - if (player.hasItem(JAD_PET)) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Best you train one TzRek-Jad only."); - stage = 21; - break; - } - if (player.getFamiliarManager().hasFamiliar()) { - if (player.getFamiliarManager().getFamiliar().getId() == 8650) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Best you train one TzRek-Jad only."); - stage = 21; - break; - } - } - if (player.getInventory().remove(FIRECAPE)) { - int r = RandomFunction.getRandom(200); - if (r == 1) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You lucky. Better train him good else TzTok-Jad find", "you, JalYt."); - if (!player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().summon(JAD_PET, true); - player.sendNotificationMessage("You have a funny feeling like you're being followed."); - } else if (player.getInventory().freeSlots() > 0) { - player.getInventory().add(JAD_PET); - player.sendNotificationMessage("You feel something weird sneaking into your backpack."); - } - Repository.sendNews(player.getUsername() + " now commands a miniature TzTok-Jad!"); - } else { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You not lucky. Maybe next time, JalYt."); - } - } - stage = 21; - break; - case 2: - end(); - break; - } - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("firecape-exchange") }; - } - } - - /** - * Handles the TzRekJad dialogue. - * @author Empathy - * - */ - public final class TzRekJadDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzRekJadDialogue} {@code Object}. - */ - public TzRekJadDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzRekJadDialogue} {@code Object}. - * - * @param player the player. - */ - public TzRekJadDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzRekJadDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int i = RandomFunction.getRandom(1); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, i == 1 ? "Do you miss your people?" : "Are you hungry?"); - stage = (i == 1 ? 0 : 5); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Mej-TzTok-Jad Kot-Kl!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No.. I don't think so."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Jal-Zek Kl?"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, no, I wouldn't hurt you."); - stage = 4; - break; - case 4: - end(); - break; - case 5: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Kl-Kra!"); - stage = 6; - break; - case 6: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ooookay..."); - stage = 4; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8650 }; - } - } - -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java deleted file mode 100644 index b4b23b029..000000000 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java +++ /dev/null @@ -1,198 +0,0 @@ -package content.region.karamja.tzhaar.handlers; - -import core.cache.def.impl.SceneryDefinition; -import core.game.activity.ActivityManager; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used for tzhaar city. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class TzhaarCityPlugin extends OptionHandler { - - /** - * Represents the locations to use. - */ - private static final Location[] LOCATIONS = new Location[] { Location.create(2480, 5175, 0), Location.create(2866, 9571, 0) }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(31284).getHandlers().put("option:enter", this);// karamja - // cave. - SceneryDefinition.forId(9359).getHandlers().put("option:enter", this);// tzhaar - // exit - SceneryDefinition.forId(9356).getHandlers().put("option:enter", this); - SceneryDefinition.forId(9369).getHandlers().put("option:pass", this); - new TzhaarDialogue().init(); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int id = ((Scenery) node).getId(); - switch (option) { - case "enter": - switch (id) { - case 31284: - player.getProperties().setTeleportLocation(LOCATIONS[0]); - break; - case 9359: - player.getProperties().setTeleportLocation(LOCATIONS[1]); - break; - case 9356: - if (player.getFamiliarManager().hasFamiliar()) { - player.getPacketDispatch().sendMessage("You can't enter this with a follower."); - break; - } - ActivityManager.start(player, "fight caves", false); - break; - } - break; - case "pass": - switch (id) { - case 9369: - ActivityManager.start(player, "fight pits", false); - break; - } - break; - } - return true; - } - - /** - * Represents the dialogue plugin used for the tzhaar npcs. - * @author 'Vexia - * @version 1.0 - */ - public static final class TzhaarDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TzhaarDialogue} {@code Object}. - */ - public TzhaarDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TzhaarDialogue} {@code Object}. - * @param player the player. - */ - public TzhaarDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TzhaarDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Can I help you JalYt-Ket-" + player.getUsername() + "?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("What do you have to trade?", "What did you call me?", "No I'm fine thanks."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - player("What did you call me?"); - stage = 20; - break; - case 3: - player("No I'm fine thanks."); - stage = 30; - break; - } - break; - case 10: - break; - case 20: - npc("Are you not JalYt-Ket?"); - stage = 21; - break; - case 21: - options("What's a 'JalYt-Ket'?", "I guess so...", "No I'm not!"); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - player("What's a 'JalYt-Ket'?"); - stage = 100; - break; - case 2: - player("I guess so..."); - stage = 120; - break; - case 3: - player("No I'm not!"); - stage = 130; - break; - } - break; - case 100: - npc("That what you are... you tough and strong no?"); - stage = 101; - break; - case 101: - player("Well yes I suppose I am..."); - stage = 102; - break; - case 102: - npc("Then you JalYt-Ket!"); - stage = 103; - break; - case 103: - end(); - break; - case 120: - npc("Well then, no problems."); - stage = 121; - break; - case 121: - end(); - break; - case 130: - end(); - break; - case 23: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2620, 2622, 2623 }; - } - - } -} diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java index b6054f080..5a4cdfd11 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java @@ -1,6 +1,5 @@ package content.region.karamja.tzhaar.handlers; -import content.global.skill.slayer.SlayerManager; import core.game.event.NPCKillEvent; import core.game.activity.ActivityPlugin; import content.data.BossKillCounter; @@ -10,8 +9,6 @@ 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.diary.DiaryType; -import core.game.node.entity.skill.Skills; -import content.global.skill.slayer.Tasks; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -23,6 +20,7 @@ import core.plugin.Initializable; import core.tools.RandomFunction; import core.game.world.GameWorld; import core.game.world.repository.Repository; +import org.rs09.consts.NPCs; import java.util.ArrayList; import java.util.List; @@ -62,7 +60,7 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { * @param player The player. */ public TzhaarFightCavesPlugin(Player player) { - super("fight caves", true, true, true, ZoneRestriction.CANNON, ZoneRestriction.RANDOM_EVENTS); + super("fight caves", true, true, true, ZoneRestriction.CANNON, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.FOLLOWERS); super.player = player; } @@ -147,7 +145,6 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { public void leave(Player player, int wave) { activeNPCs.clear(); player.getProperties().setTeleportLocation(getSpawnLocation()); - player.getSkills().restore(); boolean practice = player.getAttribute("fc_practice_jad", false); if (wave == 63) { if (!practice) { @@ -163,12 +160,7 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { } player.getPacketDispatch().sendMessage("You were victorious!"); if (!practice) { - BossKillCounter.addtoKillcount(player, 2745); - if (SlayerManager.getInstance(player).getTask() == Tasks.JAD) { - player.getSkills().addExperience(Skills.SLAYER, 25000); - SlayerManager.getInstance(player).clear(); - player.sendMessage("You receive 25,000 slayer experience for defeating TzTok-Jad."); - } + BossKillCounter.addToBossKillCount(player, NPCs.TZTOK_JAD_2745); player.getDialogueInterpreter().sendDialogues(2617, null, "You even defeated TzTok-Jad, I am most impressed!", "Please accept this gift as a reward."); Repository.sendNews(player.getUsername() + " has been victorious in defeating TzTok-Jad for a firecape!"); } else { 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/karamja/tzhaar/handlers/TzhaarListeners.kt b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarListeners.kt new file mode 100644 index 000000000..f785ecd0b --- /dev/null +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarListeners.kt @@ -0,0 +1,36 @@ +package content.region.karamja.tzhaar.handlers + +import core.api.sendNPCDialogueLines +import core.api.teleport +import core.game.activity.ActivityManager +import core.game.dialogue.FacialExpression +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class TzhaarListeners : InteractionListener { + override fun defineListeners() { + on(intArrayOf(31284, 9359, 9356), IntType.SCENERY, "enter") { player, node -> + when (node.id) { + 31284 -> teleport(player, Location.create(2480, 5175, 0)) + 9359 -> teleport(player, Location.create(2866, 9571, 0)) + 9356 -> { + if (player.familiarManager.hasFamiliar()) { + sendNPCDialogueLines(player, NPCs.TZHAAR_MEJ_JAL_2617, FacialExpression.ANGRY, false, "No Kimit-Zil in the cave! This is a fight for YOU,", "not your friends!") + } else ActivityManager.start(player, "fight caves", false) + } + } + return@on true + } + + on(9369, IntType.SCENERY, "pass") { player, _ -> + ActivityManager.start(player, "fight pits", false) + return@on true + } + + on(31292, IntType.SCENERY, "go-through") { _, _ -> + return@on false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/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/apeatoll/dialogue/marim/SolihibDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt new file mode 100644 index 000000000..381e3e856 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt @@ -0,0 +1,42 @@ +package content.region.misc.apeatoll.dialogue.marim + +import content.data.Quests +import core.api.hasRequirement +import core.api.openNpcShop +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class SolihibDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.SOLIHIB_1433, IntType.NPC, "talk-to") { player, node -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + DialogueLabeller.open(player, SolihibDialogueLabellerFile(), node as NPC) + return@on true + } + on(NPCs.SOLIHIB_1433, IntType.NPC, "trade") { player, _ -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + openNpcShop(player, NPCs.SOLIHIB_1433) + return@on true + } + } + + class SolihibDialogueLabellerFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SOLIHIB_1433) + + npc(ChatAnim.FRIENDLY, "Would you like to buy or sell some food?") + options( + DialogueOption("trade", "Yes, please."), + DialogueOption("nowhere", "No, thanks.") + ) + label("trade") + exec { player, _ -> openNpcShop(player, NPCs.SOLIHIB_1433) } + goto("nowhere") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java index beaaccd03..15e7f8803 100644 --- a/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java @@ -8,6 +8,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the dialogue plugin used for a cave monk. @@ -52,7 +53,7 @@ public final class CaveMonk extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Lost City"); + quest = player.getQuestRepository().getQuest(Quests.LOST_CITY); switch (quest.getStage(player)) { case 0: case 10: diff --git a/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt b/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt index 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..97d0d8dbb --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlandebirDialogue.kt @@ -0,0 +1,110 @@ +package content.region.misc.keldagrim.dialogue + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +@Initializable +class BlandebirDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.BLANDEBIR_2321, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, BlandebirDialogueFile(), node as NPC) + return@on true + } + } +} + + +class BlandebirDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.BLANDEBIR_2321) + + player(ChatAnim.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...") + npc(ChatAnim.OLD_DEFAULT, "I might be able to - what do you need to know?") + goto("main_menu") + + label("main_menu") { + options( + DialogueOption("how_brew_ales", "How do I brew ales?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew ales?"), + DialogueOption("how_brew_cider", "How do I brew cider?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew cider?"), + DialogueOption("what_when_matured", "What do I do once my ale has matured?", expression = ChatAnim.HALF_ASKING, spokenText = "What do I do once my ale has matured?"), + DialogueOption("buy_yeast", "Do you have any spare ale yeast?", expression = ChatAnim.HALF_ASKING, spokenText = "Do you have any spare ale yeast?"), + DialogueOption("end", "That's all I need to know, thanks.") + ) + } + + label("how_brew_ales") { + npc(ChatAnim.OLD_DEFAULT, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.") + npc(ChatAnim.OLD_DEFAULT, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.") + npc(ChatAnim.OLD_DEFAULT, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.") + goto("main_menu") + } + + label("how_brew_cider") { + npc(ChatAnim.OLD_DEFAULT, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.") + npc(ChatAnim.OLD_DEFAULT, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.") + goto("main_menu") + } + + label("what_when_matured") { + npc(ChatAnim.OLD_DEFAULT, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.") + goto("main_menu") + } + + label("buy_yeast") { + npc(ChatAnim.OLD_DEFAULT, "Well, as a matter of fact I do, although I wouldn't describe it as spare. This ale yeast I've got is the best money can buy, but if you've got a pot I'll fill it for you for 25GP - very cheap as it happens.") + options( + DialogueOption("confirm_buy", "That's a good deal - please fill my pot with ale yeast for 25GP."), + DialogueOption("end", "No, that's too much I'm afraid.") + ) + } + + label("confirm_buy") { + exec { player, npc -> + if (!inInventory(player, Items.EMPTY_POT_1931, 1)) { + loadLabel(player, "no_pot") + } else if (!inInventory(player, Items.COINS_995, 25)) { + loadLabel(player, "no_coins") + } else { + loadLabel(player, "process_purchase") + } + } + } + + label("no_pot") { + npc(ChatAnim.OLD_DEFAULT, "I'm afraid you'll need a pot for me to put the yeast in.") + goto("end") + } + + label("no_coins") { + line("You can not afford that.") + goto("end") + } + + label("process_purchase") { + exec { player, npc -> + val emptyPots = amountInInventory(player, Items.EMPTY_POT_1931) + val coins = amountInInventory(player, Items.COINS_995) + val canAfford = coins / 25 + val quantity = minOf(emptyPots, canAfford) + + if (removeItem(player, Item(Items.EMPTY_POT_1931, quantity)) && removeItem(player, Item(Items.COINS_995, quantity * 25))) { + addItem(player, Items.ALE_YEAST_5767, quantity) + } + } + goto("main_menu") + } + + label("end") { + } + } +} 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..24a7a333b 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,16 +41,6 @@ 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) @@ -78,7 +70,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 +94,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 +104,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 deleted file mode 100644 index 8e5640807..000000000 --- a/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java +++ /dev/null @@ -1,155 +0,0 @@ -package content.region.misc.tutisland.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the banker tutor dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BankerTutorDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BankerTutorDialogue} {@code Object}. - * @param player the player. - */ - public BankerTutorDialogue(final Player player) { - super(player); - } - - /** - * Constructs a new {@code BankerTutorDialogue} {@code Object}. - */ - public BankerTutorDialogue() { - /** - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BankerTutorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = args[0] instanceof NPC ? (NPC) args[0] : null; - npc("Good day, how may I help you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("How do I use the bank?", "I'd like to access my bank account please.", "I'd like to check my PIN settings."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - options("Using the bank itself.", "Using Bank deposit boxes.", "What's this PIN thing that people keep talking about?", "Goodbye."); - stage = 9; - break; - case 2: - end(); - player.getBank().open(); - break; - case 3: - end(); - player.getBankPinManager().openSettings(); - break; - } - break; - case 9: - switch (buttonId) { - case 1: - player("Using the bank itself. I'm not sure how....?"); - stage = 10; - break; - case 2: - player("Using Bank deposit boxes.... what are they?"); - stage = 20; - break; - case 3: - player("What's this PIN thing that people keep talking about?"); - stage = 30; - break; - case 4: - player("Goodbye."); - stage = 99; - break; - } - break; - case 10: - npc("Speak to any banker and ask to see your bank", "account. If you have set a PIN you will be asked for", "it, then all the belongings you have placed in the bank", "will appear in the window. To withdraw one item, left-"); - stage = 11; - break; - case 11: - npc("click on it once."); - stage = 12; - break; - case 12: - npc("To withdraw many, right-click on the item and select", "from the menu. The same for depositing, left-click on", "the item in your inventory to deposit it in the bank.", "Right-click on it to deposit many of the same items."); - stage = 13; - break; - case 13: - npc("To move things around in your bank: firstly select", "Swap or Insert as your default moving mode, you can", "find these buttons on the bank window itself. Then click", "and drag an item to where you want it to appear."); - stage = 14; - break; - case 14: - npc("You may withdraw 'notes' or 'certificates' when the", "items you are trying to withdraw do not stack in your", "inventory. This will only work for items which are", "tradeable."); - stage = 15; - break; - case 15: - npc("For instance, if you wanted to sell 100 logs to another", "player, they would not fit in one inventory and you", "would need to do multiple trades. Instead, click the", "Note button to do withdraw the logs as 'certs' or 'notes',"); - stage = 16; - break; - case 16: - npc("then withdraw the items you need."); - stage = 99; - break; - case 20: - npc("They look like grey pillars, there's one just over there,", "near the desk. Bank deposit boxes save so much time.", "If you're simply wanting to deposit a single item, 'Use'", "it on the deposit box."); - stage = 21; - break; - case 21: - npc("Otherwise, simply click once on the box and it will give", "you a choice of what to deposit in an interface very", "similar to the bank itself. Very quick for when you're", "simply fishing or mining etc."); - stage = 22; - break; - case 22: - end(); - break; - case 30: - npc("The PIN - Personal Identification Number - can be", "set on your bank account to protect the items there in", "case someone finds out your account password. It", "consists of four numbers that you remember and tell"); - stage = 31; - break; - case 31: - npc("no one."); - stage = 32; - break; - case 32: - npc("So if someone did manage to get your password they", "couldn't steal your items if they were in the bank."); - stage = 33; - break; - case 33: - end(); - break; - case 99: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4907 }; - } - -} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt new file mode 100644 index 000000000..3728d6d9c --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/RatPenDialogue.kt @@ -0,0 +1,15 @@ +package content.region.misc.tutisland.dialogue + +import content.region.misc.tutisland.handlers.sendStageDialog +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller + +/** + * Vannaka's angry dialogue when you try to enter his rat pen before you're supposed to + */ +class RatPenDialogue : DialogueLabeller() { + override fun addConversation() { + npc(ChatAnim.ANGRY, "Oi, get away from there!", "Don't enter my rat pen unless I say so!") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt index 99503423a..f19f67a64 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt @@ -9,25 +9,26 @@ import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles Skippy's skip tutorial dialogue - * @author Ceikry + * @author Ceikry, Player Name */ @Initializable -class SkipTutorialDialogue(player: Player? = null) : 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.") + setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Hey, would you like to skip to the end? Choose wisely! This is the only time you get this choice.")) return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> options("Yes, I'd like to skip the tutorial.", "No thanks.").also { stage++ } + 0 -> options("Yes, I'd like to skip the tutorial.", "No, thanks.").also { stage++ } 1 -> when(buttonId) { 1 -> { diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt index 01006fed9..4e0385178 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt @@ -1,119 +1,108 @@ package content.region.misc.tutisland.dialogue -import core.api.addItem +import core.api.addItemOrDrop import core.api.inInventory import core.api.setAttribute -import core.game.component.Component -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.getAttribute +import core.api.inEquipment +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item -/** - * Handles the survival expert's dialogue - * @author Ceikry - */ -@Initializable -class SurvivalExpertDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return SurvivalExpertDialogue(player) +class SurvivalExpertDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.SURVIVAL_EXPERT_943, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, SurvivalExpertDialogueFile(), node as NPC) + return@on true + } } +} - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 - when(tutStage) - { - 4 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.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", - "all: making a fire." - ) - ) +class SurvivalExpertDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SURVIVAL_EXPERT_943) - 11 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.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." - ) - ) - - 5, 14, 15 -> { - if(!inInventory(player, Items.BRONZE_AXE_1351)) - { - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_AXE_1351, "The Survival Expert gives you a spare bronze axe.") - addItem(player, Items.BRONZE_AXE_1351) + exec { player, _ -> + when (val stage = getAttribute(player, "/save:tutorial:stage", 0)) { + 4 -> loadLabel(player, "hello") + 5, 6, 7, 8, 9, 10, 12, 13, 14 -> { + if (!inInventory(player, Items.BRONZE_AXE_1351) && !inEquipment(player, Items.BRONZE_AXE_1351)) { + loadLabel(player, "spare axe") + } + if (!inInventory(player, Items.TINDERBOX_590)) { + loadLabel(player, "spare tinderbox") + } + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") + } + loadLabel(player, "nowhere") } - if(!inInventory(player, Items.TINDERBOX_590)) - { - player.dialogueInterpreter.sendItemMessage(Items.TINDERBOX_590, "The Survival Expert gives you a spare tinderbox.") - addItem(player, Items.TINDERBOX_590) - } - return false + 11 -> loadLabel(player, "fishing") + else -> loadLabel(player, "nowhere") } } - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(player?.getAttribute("tutorial:stage", 0)) - { - 4 -> when(stage) - { - 0 -> { - Component.setUnclosable( - player, - interpreter.sendDoubleItemMessage( - Items.TINDERBOX_590, - Items.BRONZE_AXE_1351, - "The Surivival Guide gives you a tinderbox and a bronze axe!" - ) - ) - addItem(player, Items.TINDERBOX_590) - addItem(player, Items.BRONZE_AXE_1351) - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 5) - TutorialStage.load(player, 5) - } + label("hello") + npc(ChatAnim.FRIENDLY, "Hello there, newcomer. My name is Brynna. My job is to teach you a few survival tips and tricks. First off we're going to start with the most basic survival skill of all: making a fire.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.TINDERBOX_590) + addItemOrDrop(player, Items.BRONZE_AXE_1351) + } + item(Item(Items.TINDERBOX_590), Item(Items.BRONZE_AXE_1351), "The Survival Guide gives you a tinderbox and a bronze", "axe!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 5) + TutorialStage.load(player, 5) + } + goto("nowhere") + + label("fishing") + npc(ChatAnim.FRIENDLY, "Well done! Next we need to get some food in our bellies. We'll need something to cook. There are shrimp in the pond there, so let's catch and cook some.", unclosable = true) + exec { player, _ -> addItemOrDrop(player, Items.SMALL_FISHING_NET_303) } + item(Item(Items.SMALL_FISHING_NET_303), "The Survival Guide gives you a", "net!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 12) + TutorialStage.load(player, 12) + } + goto("nowhere") + + label("spare axe") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_AXE_1351) } + item(Item(Items.BRONZE_AXE_1351), "The Survival Guide gives you a spare bronze axe.", unclosable = true) + exec { player, _ -> + if (!inInventory(player, Items.TINDERBOX_590)) { + loadLabel(player, "spare tinderbox") } - - 11 -> when(stage){ - 0 -> { - Component.setUnclosable( - player, - interpreter.sendItemMessage(303, "The Survival Guide gives you a net!") - ) - addItem(player, Items.SMALL_FISHING_NET_303) - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 12) - TutorialStage.load(player, 12) - } + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") } } - return true - } + goto("nowhere") //closes the dialogue, letting the hook reopen the tutorial stage dialog as appropriate - override fun getIds(): IntArray { - return intArrayOf(NPCs.SURVIVAL_EXPERT_943) - } + label("spare tinderbox") + exec { player, _ -> addItemOrDrop(player, Items.TINDERBOX_590) } + item(Item(Items.TINDERBOX_590), "The Survival Guide gives you a spare tinderbox.", unclosable = true) + exec { player, _ -> + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") + } + } + goto("nowhere") -} \ No newline at end of file + label("spare net") + exec { player, _ -> addItemOrDrop(player, Items.SMALL_FISHING_NET_303) } + item(Item(Items.SMALL_FISHING_NET_303), "The Survival Guide gives you a spare net.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt index 058eb4db2..1ab335e26 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt @@ -1,95 +1,106 @@ package content.region.misc.tutisland.dialogue -import core.api.* -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.item.Item -/** - * Handles the combat instructor's dialogue - * @author Ceikry - */ -@Initializable -class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return TutorialCombatInstructorDialogue(player) +class CombatInstructorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.COMBAT_INSTRUCTOR_944, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, CombatInstructorDialogueFile(), node as NPC) + return@on true + } + } +} + +class CombatInstructorDialogueFile : DialogueLabeller() { + fun lostWeapon(player: Player, id: Int): Boolean { + return !inInventory(player, id) && !inEquipment(player, id) } - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC + override fun addConversation() { + assignToIds(NPCs.COMBAT_INSTRUCTOR_944) - 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!") - 54 -> { - player.dialogueInterpreter.sendDoubleItemMessage(Items.SHORTBOW_841, Items.BRONZE_ARROW_882, "The Combat Guide gives you some bronze arrows and a shortbow!") - if(!inInventory(player, Items.SHORTBOW_841) && !inEquipment(player, Items.SHORTBOW_841)) - addItem(player, Items.SHORTBOW_841) - if(!inInventory(player, Items.BRONZE_ARROW_882) && !inEquipment(player, Items.BRONZE_ARROW_882)) - addItem(player, Items.BRONZE_ARROW_882, 30) + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 44 -> loadLabel(player, "hello") + 47 -> loadLabel(player, "butter") + 48, 49, 50, 51, 52 -> loadLabel(player, if (lostWeapon(player, Items.BRONZE_SWORD_1277)) "lost sword" else if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) "lost shield" else "nowhere") + 53 -> loadLabel(player, "killed rat") + 54 -> loadLabel(player, if (lostWeapon(player, Items.SHORTBOW_841)) "lost bow" else if (lostWeapon(player, Items.BRONZE_ARROW_882)) "lost arrows" else "nowhere") + else -> loadLabel(player, "nowhere") } } - return true - } - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - 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++ } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 45) - TutorialStage.load(player, 45) - } - } - - 47 -> when(stage){ - 0 -> { - addItemOrDrop(player, Items.BRONZE_SWORD_1277) - addItemOrDrop(player, Items.WOODEN_SHIELD_1171) - sendDoubleItemDialogue(player, Items.BRONZE_SWORD_1277, Items.WOODEN_SHIELD_1171, "The Combat Guide gives you a bronze sword and a wooden shield!") - stage++ - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 48) - TutorialStage.load(player, 48) - } - } - - 53 -> when(stage){ - 0 -> npcl(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++ } - 2 -> { - sendDoubleItemDialogue(player, Items.SHORTBOW_841, Items.BRONZE_ARROW_882, "The Combat Guide gives you some bronze arrows and a shortbow!") - if(!inInventory(player, Items.SHORTBOW_841) && !inEquipment(player, Items.SHORTBOW_841)) - addItem(player, Items.SHORTBOW_841) - if(!inInventory(player, Items.BRONZE_ARROW_882) && !inEquipment(player, Items.BRONZE_ARROW_882)) - addItem(player, Items.BRONZE_ARROW_882, 30) - stage++ - } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 54) - TutorialStage.load(player, 54) - } - } + label("hello") + player(ChatAnim.FRIENDLY, "Hi! My name's ${player?.username}.", unclosable = true) + npc(ChatAnim.ANGRY, "Do I look like I care? To me you're just another newcomer who thinks they're ready to fight.", unclosable = true) + npc(ChatAnim.FRIENDLY, "I'm Vannaka, the greatest swordsman alive.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's get started by teaching you to wield a weapon.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 45) + TutorialStage.load(player, 45) } - return true - } - override fun getIds(): IntArray { - return intArrayOf(NPCs.COMBAT_INSTRUCTOR_944) - } + label("butter") + npc(ChatAnim.FRIENDLY, "Very good, but that little butter knife isn't going to protect you much. Here, take these.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.BRONZE_SWORD_1277) + addItemOrDrop(player, Items.WOODEN_SHIELD_1171) + } + item(Item(Items.BRONZE_SWORD_1277), Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a bronze sword and a", "wooden shield!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 48) + TutorialStage.load(player, 48) + } + goto("nowhere") -} \ No newline at end of file + label("lost sword") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_SWORD_1277) } + item(Item(Items.BRONZE_SWORD_1277), "The Combat Guide gives you a spare sword.", unclosable = true) + exec { player, _ -> if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) loadLabel(player, "lost shield") } + goto("nowhere") + + label("lost shield") + exec { player, _ -> addItemOrDrop(player, Items.WOODEN_SHIELD_1171) } + item(Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a spare shield.", unclosable = true) + goto("nowhere") + + label("killed rat") + player(ChatAnim.FRIENDLY, "I did it! I killed a giant rat!", unclosable = true) + npc(ChatAnim.FRIENDLY, "I saw, ${player?.username}. You seem better at this than I thought. Now that you have grasped basic swordplay, let's move on.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's try some ranged attacking, with this you can kill foes from a distance. Also, foes unable to reach you are as good as dead. You'll be able to attack the rats, without entering the pit.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.SHORTBOW_841) + addItemOrDrop(player, Items.BRONZE_ARROW_882, 30) + } + item(Item(Items.SHORTBOW_841), Item(Items.BRONZE_ARROW_882), "The Combat Guide gives you some bronze arrows and", "a shortbow!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 54) + TutorialStage.load(player, 54) + } + goto("nowhere") + + label("lost bow") + exec { player, _ -> addItemOrDrop(player, Items.SHORTBOW_841) } + item(Item(Items.SHORTBOW_841), "The Combat Guide gives you a spare bow.", unclosable = true) + goto("nowhere") + + label("lost arrows") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_ARROW_882, 10) } + item(Item(Items.BRONZE_ARROW_882), "You receive some spare arrows.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt index 305911ae5..d94bcd922 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt @@ -7,11 +7,12 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable import core.game.world.GameWorld.settings /** * Handles the finance tutor's dialogue - * @author Ceikry + * @author Ceikry, Player Name */ @Initializable class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { @@ -23,8 +24,8 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 58 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, who are you?") - 59 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Move along, now.").also { return false } + 58 -> setUnclosable(player, playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, who are you?")) + 59 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Move along, now.")) } return true } @@ -32,21 +33,22 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(getAttribute(player, "tutorial:stage", 0)){ 58 -> when(stage++){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm the Financial Advisor. I'm here to tell people how to make money.") - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay. How can I make money then?") - 2 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "How you can make money? Quite.") - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well there are three basic ways of making money here: combat, quests, and trading. I will talk you through each of them very quickly.") - 4 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Let's start with combat as it is probably still fresh in your mind. Many enemies, both human and monster will drop items when they die.") - 5 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Now, the next way to earn money quickly is by quests. Many people on " + settings!!.name + " have things they need doing, which they will reward you for.") - 6 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "By getting a high level in skills such as Cooking, Mining, Smithing or Fishing, you can create or catch your own items and sell them for pure profit.") - 7 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Lastly, we have jobs you can get from tutors in Lumbridge. These pay very handsomely early on!").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, that about covers it. Move along now.") + 0 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm the Financial Advisor. I'm here to tell people how to make money.")) + 1 -> setUnclosable(player, playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay. How can I make money then?")) + 2 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "How you can make money? Quite.")) + 3 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well there are three basic ways of making money here: combat, quests, and trading. I will talk you through each of them very quickly.")) + 4 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Let's start with combat as it is probably still fresh in your mind. Many enemies, both human and monster will drop items when they die.")) + 5 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Now, the next way to earn money quickly is by quests. Many people on " + settings!!.name + " have things they need doing, which they will reward you for.")) + 6 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "By getting a high level in skills such as Cooking, Mining, Smithing or Fishing, you can create or catch your own items and sell them for pure profit.")) + 7 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Lastly, we have jobs you can get from tutors in Lumbridge. These pay very handsomely early on!")).also { stage++ } + 8 -> setUnclosable(player, npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, that about covers it. Move along now.")) 9 -> { end() setAttribute(player, "tutorial:stage", 59) TutorialStage.load(player, 59) } } + 59 -> TutorialStage.load(player, 59) } return true } @@ -54,5 +56,4 @@ class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogu override fun getIds(): IntArray { return intArrayOf(NPCs.FINANCIAL_ADVISOR_947) } - } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt index 4bee2197f..68707766d 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt @@ -1,205 +1,229 @@ package content.region.misc.tutisland.dialogue +import content.global.handlers.iface.RulesAndInfo import content.region.misc.tutisland.handlers.* +import core.ServerConstants import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player import core.game.node.entity.player.link.IronmanMode import core.game.node.entity.player.link.TeleportManager import core.game.node.item.Item +import core.game.world.GameWorld import core.game.world.map.Location import core.plugin.Initializable +import core.worker.ManagementEvents import org.rs09.consts.Items import org.rs09.consts.NPCs import proto.management.JoinClanRequest -import core.ServerConstants -import content.global.handlers.iface.RulesAndInfo -import core.game.world.GameWorld -import core.tools.END_DIALOGUE -import core.worker.ManagementEvents -/** - * Handles the magic tutor's dialogue - * @author Ceikry - */ @Initializable -class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - private val STARTER_PACK = arrayOf( - Item(1351, 1), - Item(590, 1), - Item(303, 1), - Item(315, 1), - Item(1925, 1), - Item(1931, 1), - Item(2309, 1), - Item(1265, 1), - Item(1205, 1), - Item(1277, 1), - Item(1171, 1), - Item(841, 1), - Item(882, 25), - Item(556, 25), - Item(558, 15), - Item(555, 6), - Item(557, 4), - Item(559, 2) - ) - private val STARTER_BANK = arrayOf(Item(995, 25)) - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return TutorialMagicTutorDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - when(getAttribute(player, "tutorial:stage", 0)) - { - 67 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.") - 69 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.") - 70 -> if(!inInventory(player, Items.AIR_RUNE_556) && !inInventory(player, Items.MIND_RUNE_558)) - { - player.dialogueInterpreter.sendDoubleItemMessage(Items.AIR_RUNE_556, Items.MIND_RUNE_558, "You receive some spare runes.") - addItem(player, Items.AIR_RUNE_556, 15) - addItem(player, Items.MIND_RUNE_558, 15) - return false +class TutorialMagicTutorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.MAGIC_INSTRUCTOR_946, NPC, "talk-to") { player, _ -> + val stage = getAttribute(player, "tutorial:stage", 0) + if (stage == 70 && inInventory(player, Items.AIR_RUNE_556) && inInventory(player, Items.MIND_RUNE_558)) { + // Player should be killing chickens instead, and could be. Instead of opening the dialogue and doing nothing (which will make you lose the tutorial island dialog), do nothing at all + return@on true } - 71 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Alright, last thing. Are you interested in being an ironman or changing your experience rate?") - else -> return false + openDialogue(player, TutorialMagicTutorDialogueFile(), NPC(NPCs.MAGIC_INSTRUCTOR_946)) + return@on true } - return true } +} - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) - { - 67 -> when(stage++){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spell list.") - 1 -> { - end() - setAttribute(player, "tutorial:stage", 68) - TutorialStage.load(player, 68) +class TutorialMagicTutorDialogueFile : DialogueLabeller() { + override fun addConversation() { + val STARTER_PACK = arrayOf( + Item(Items.BRONZE_AXE_1351), + Item(Items.TINDERBOX_590), + Item(Items.SMALL_FISHING_NET_303), + Item(Items.SHRIMPS_315), + Item(Items.BUCKET_1925), + Item(Items.EMPTY_POT_1931), + Item(Items.BREAD_2309), + Item(Items.BRONZE_PICKAXE_1265), + Item(Items.BRONZE_DAGGER_1205), + Item(Items.BRONZE_SWORD_1277), + Item(Items.WOODEN_SHIELD_1171), + Item(Items.SHORTBOW_841), + Item(Items.BRONZE_ARROW_882, 25), + Item(Items.AIR_RUNE_556, 25), + Item(Items.MIND_RUNE_558, 15), + Item(Items.WATER_RUNE_555, 6), + Item(Items.EARTH_RUNE_557, 4), + Item(Items.BODY_RUNE_559, 2) + ) + val STARTER_BANK = arrayOf(Item(Items.COINS_995, 25)) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 67 -> loadLabel(player, "hello") + 69 -> loadLabel(player, "spelllist") + 70 -> { + if (!inInventory(player, Items.AIR_RUNE_556, 1) || !inInventory(player, Items.MIND_RUNE_558, 1)) { + loadLabel(player, "givemorerunes") + } else { + goto("nowhere") + } + } + 71 -> loadLabel(player, "finishedtutorial") + else -> goto("nowhere") + } + } + + label("hello") + player(ChatAnim.FRIENDLY, "Hello.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spell list.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 68) + TutorialStage.load(player, 68) + } + + label("spelllist") + npc(ChatAnim.FRIENDLY, "Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 70) + addItemOrDrop(player, Items.AIR_RUNE_556, 15) + addItemOrDrop(player, Items.MIND_RUNE_558, 15) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "Terrova gives you 15 air runes and 15 mind runes!", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("givemorerunes") + exec { player, _ -> + addItemOrDrop(player, Items.AIR_RUNE_556, 5) + addItemOrDrop(player, Items.MIND_RUNE_558, 5) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "You receive some spare runes.", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("finishedtutorial") + exec { player, _ -> + if (ServerConstants.XP_RATES || ServerConstants.IRONMAN) { + loadLabel(player, "talk about inauthentic") + } else { + loadLabel(player, "leave") } } - 69 -> when(stage++){ - 0 -> { - sendDoubleItemDialogue(player, Items.AIR_RUNE_556, Items.MIND_RUNE_558, "Terrova gives you 15 air runes and 15 mind runes!") - addItemOrDrop(player, Items.AIR_RUNE_556, 5) - addItemOrDrop(player, Items.MIND_RUNE_558, 5) - } - 1 -> { - end() - setAttribute(player, "tutorial:stage", 70) - TutorialStage.load(player, 70) - } + + label("talk about inauthentic") + npc(ChatAnim.FRIENDLY, "Alright, last thing. Are you interested in our inauthentic ${ServerConstants.SERVER_NAME} features?", unclosable = true) + goto("inauthentic") + + label("inauthentic") + options( + DialogueOption("xprate","Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.XP_RATES + }, + DialogueOption("ironman","Set ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()})", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.IRONMAN + }, + DialogueOption("leave","I'm ready now."), + unclosable = true) + + label("xprate") + options( + DialogueOption("1.0x","1.0x (default)", skipPlayer = true), + DialogueOption("2.5x","2.5x", skipPlayer = true), + DialogueOption("5.0x","5.0x", skipPlayer = true), + title = "Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", + unclosable = true + ) + for (rate in doubleArrayOf(1.0, 2.5, 5.0)) { + label("${rate}x") + exec { player, _ -> player.skills.experienceMultiplier = rate } + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") } + goto("inauthentic") } - 71 -> when(stage){ - 0 -> options("Set Ironman Mode (current: ${player.ironmanManager.mode.name})", "Change XP Rate (current: ${player.skills.experienceMutiplier}x)", "I'm ready now.").also { stage++ } - 1 -> when(buttonId){ - 1 -> options("None","Standard","Hardcore (Permadeath!)","Ultimate","Nevermind.").also { stage = 10 } - 2 -> options("1.0x","2.5x","5.0x","10x").also { stage = 20 } - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, you're all finished here now. I'll give you a reasonable number of starting items when you leave.").also { stage = 30 } + + label("ironman") + options( + DialogueOption("NONE","None (default)", skipPlayer = true), + DialogueOption("STANDARD","Standard", skipPlayer = true), + DialogueOption("ULTIMATE","Ultimate (no bank)", skipPlayer = true), + title = "Change ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()}x)" + ) + for (mode in arrayOf(IronmanMode.NONE, IronmanMode.STANDARD, IronmanMode.ULTIMATE)) { + label(mode.name) + exec { player, _ -> player.ironmanManager.mode = mode } + manual(unclosable = true){ player, _ -> return@manual player.dialogueInterpreter.sendDialogue("You set your ironman mode to: ${mode.name.toLowerCase()}.") } + exec { player, _ -> loadLabel(player, if (player.ironmanManager.mode == IronmanMode.NONE) "inauthentic" else "ironwarning") } + } + + label("ironwarning") + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue(*splitLines("WARNING: You have selected an ironman mode. This is an uncompromising mode that WILL completely restrict your ability to trade. This MAY leave you unable to complete certain content, including quests.")) } + goto("inauthentic") + + label("leave") + npc(ChatAnim.FRIENDLY, "Well, you're all finished here now. I'll give you a reasonable number of starting items when you leave.", unclosable = true) + options( + DialogueOption("leave:yes","Yes, I'm ready.","I'm ready to go now, thank you.", ChatAnim.FRIENDLY), + DialogueOption("nowhere","No, not yet.","I'm not quite ready to go yet, thank you.", ChatAnim.FRIENDLY), + title = "Leave Tutorial Island?", + unclosable = true + ) + + label("leave:yes") + manual { player, _ -> + setAttribute(player, "/save:tutorial:complete", true) + setVarbit(player, 3756, 0) + setVarp(player, 281, 1000, true) + teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) + closeOverlay(player) + + player.inventory.clear() + player.bank.clear() + player.equipment.clear() + player.interfaceManager.restoreTabs() + player.interfaceManager.setViewedTab(3) + player.inventory.add(*STARTER_PACK) + player.bank.add(*STARTER_BANK) + + TutorialStage.removeHintIcon(player) + player.unhook(TutorialKillReceiver) + player.unhook(TutorialFireReceiver) + player.unhook(TutorialResourceReceiver) + player.unhook(TutorialUseWithReceiver) + player.unhook(TutorialInteractionReceiver) + player.unhook(TutorialButtonReceiver) + player.unhook(TutorialDialogPreserver) + + if (GameWorld.settings!!.enable_default_clan) { + player.communication.currentClan = ServerConstants.SERVER_NAME.toLowerCase() + + val clanJoin = JoinClanRequest.newBuilder() + clanJoin.clanName = ServerConstants.SERVER_NAME.toLowerCase() + clanJoin.username = player.name + + ManagementEvents.publish(clanJoin.build()) } - 10 -> { - stage = 0 - if(buttonId < 5) - { - val mode = IronmanMode.values()[buttonId - 1] - player.dialogueInterpreter.sendDialogue("You set your ironman mode to: ${mode.name}.") - player.ironmanManager.mode = mode - if (player.skills.experienceMutiplier == 10.0 && mode != IronmanMode.HARDCORE) player.skills.experienceMutiplier = 5.0 - } - else - { - handle(interfaceId, 0) - } - } - - 20 -> { - val rates = arrayOf(1.0,2.5,5.0,10.0) - val rate = rates[buttonId - 1] - if(rate == 10.0 && player.ironmanManager.mode != IronmanMode.HARDCORE) { - player.dialogueInterpreter.sendDialogue("10.0x is only available to Hardcore Ironmen!") - stage = 0 - return true - } - player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") - player.skills.experienceMutiplier = rate - stage = 0 - } - - 30 -> player.dialogueInterpreter.sendOptions("Leave Tutorial Island?", "Yes, I'm ready.", "No, not yet.").also { stage++ } - 31 -> when(buttonId) - { - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm ready to go now, thank you.").also { stage = 40 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm not quite ready to go yet, thank you.").also { stage = END_DIALOGUE } - } - - 40 -> { - setAttribute(player, "/save:tutorial:complete", true) - setVarbit(player, 1021, 0, 0) - teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) - closeOverlay(player) - - player.inventory.clear() - player.bank.clear() - player.equipment.clear() - player.interfaceManager.restoreTabs() - player.interfaceManager.setViewedTab(3) - player.inventory.add(*STARTER_PACK) - player.bank.add(*STARTER_BANK) - - if(player.ironmanManager.mode == IronmanMode.HARDCORE) - { - setAttribute(player, "/save:permadeath", true) - } - else if(player.skills.experienceMutiplier == 10.0) - { - player.skills.experienceMutiplier = 5.0 - } - - //This overwrites the stuck dialogue after teleporting to Lumbridge for some reason - //Dialogue from 2007 or thereabouts - //Original is five lines, but if the same is done here it will break. Need to find another way of showing all this information. - interpreter.sendDialogue( - "Welcome to Lumbridge! To get more help, simply click on the", - "Lumbridge Guide or one of the Tutors - these can be found by looking", - "for the question mark icon on your mini-map. If you find you are lost", - "at any time, look for a signpost or use the Lumbridge Home Port Spell." - ) - stage = 12 - TutorialStage.removeHintIcon(player) - - player.unhook(TutorialKillReceiver) - player.unhook(TutorialFireReceiver) - player.unhook(TutorialResourceReceiver) - player.unhook(TutorialUseWithReceiver) - player.unhook(TutorialInteractionReceiver) - player.unhook(TutorialButtonReceiver) + // This shows the actual dialog, which is what this manual stage is for. + // Dialog is from 2007 or thereabouts. + // Original is five lines, but if the same is done here it will break. Need to find another way of showing all this information. + player.dialogueInterpreter.sendDialogue( + "Welcome to Lumbridge! To get more help, simply click on the", + "Lumbridge Guide or one of the Tutors - these can be found by looking", + "for the question mark icon on your mini-map. If you find you are lost", + "at any time, look for a signpost or use the Lumbridge Home Port Spell." + ) + if (ServerConstants.RULES_AND_INFO_ENABLED) { RulesAndInfo.openFor(player) - - if (GameWorld.settings!!.enable_default_clan) { - player.communication.currentClan = ServerConstants.SERVER_NAME - - val clanJoin = JoinClanRequest.newBuilder() - clanJoin.clanName = ServerConstants.SERVER_NAME - clanJoin.username = player.name - - ManagementEvents.publish(clanJoin.build()) + // The teleport finishing will release the player, so we need to relock them here + queueScript(player, 4, QueueStrength.SOFT) { _ -> + player.lock() + return@queueScript stopExecuting(player) } } - - 12 -> { - player.setAttribute("close_c_", true) - end() - } + return@manual null } - } - return true - } - override fun getIds(): IntArray { - return intArrayOf(NPCs.MAGIC_INSTRUCTOR_946) + label("nowhere") + exec { player, _ -> sendStageDialog(player) } } - -} \ 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..89d985f29 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt @@ -1,7 +1,7 @@ package content.region.misc.tutisland.dialogue import core.api.* -import core.game.component.Component +import core.game.component.Component.setUnclosable import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -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) } @@ -25,32 +25,17 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 18 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.FacialExpression.FRIENDLY, - "Ahh! Welcome, newcomer. I am the Master Chef, Lev. It", - "is here I will teach you how to cook food truly fit for a", - "king." - ) - ) - - 19,20 -> { - if(!inInventory(player, Items.BREAD_DOUGH_2307)) - { - if(!inInventory(player, Items.BUCKET_OF_WATER_1929)) - { - sendItemDialogue(player, Items.BUCKET_OF_WATER_1929, "The Master Chef gives you another bucket of water.") - addItem(player, Items.BUCKET_OF_WATER_1929) - TutorialStage.load(player, 19) + 18 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Ah! Welcome, newcomer. I am the Master Chef, Lev. It", "is here I will teach you how to cook food truly fit for a", "king.")) + 19, 20 -> { + if (!inInventory(player, Items.BREAD_DOUGH_2307)) { + if (!inInventory(player, Items.BUCKET_OF_WATER_1929)) { + setUnclosable(player, player.dialogueInterpreter.sendItemMessage(Items.BUCKET_OF_WATER_1929, "The Master Chef gives you another bucket of water.")) + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929) return false } - if(!inInventory(player, Items.POT_OF_FLOUR_1933)) - { - sendItemDialogue(player, Items.POT_OF_FLOUR_1933, "The Master Chef gives you another pot of flour.") - addItem(player, Items.POT_OF_FLOUR_1933) - TutorialStage.load(player, 19) + if (!inInventory(player, Items.POT_OF_FLOUR_1933)) { + setUnclosable(player, player.dialogueInterpreter.sendItemMessage(Items.POT_OF_FLOUR_1933, "The Master Chef gives you another pot of flour.")) + addItemOrDrop(player, Items.POT_OF_FLOUR_1933) return false } } @@ -61,54 +46,21 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) - { - 18 -> when(stage) - { - 0 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - player, - core.game.dialogue.FacialExpression.HALF_GUILTY, - "I already know how to cook. Brynna taught me just", - "now." - ) - ).also { stage++ } - 1 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.FacialExpression.LAUGH, - "Hahahahahaha! You call THAT cooking? Some shrimp", - "on an open log fire? Oh, no, no no. I am going to", - "teach you the fine art of cooking bread." - ) - ).also { stage++ } - 2 -> Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.FacialExpression.FRIENDLY, - "And no fine meal is complete without good music, so", - "we'll cover that while you're here too." - ) - ).also { stage++ } + when (getAttribute(player, "tutorial:stage", 0)) { + 18 -> when(stage) { + 0 -> setUnclosable(player, interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I already know how to cook. Brynna taught me just", "now.")).also { stage++ } + 1 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.LAUGH, "Hahahahahaha! You call THAT cooking? Some shrimp", "on an open log fire? Oh, no, no no. I am going to", "teach you the fine art of cooking bread.")).also { stage++ } + 2 -> setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "And no fine meal is complete without good music, so", "we'll cover that while you're here too.")).also { stage++ } 3 -> { - Component.setUnclosable( - player, - interpreter.sendDoubleItemMessage( - Items.BUCKET_OF_WATER_1929, - Items.POT_OF_FLOUR_1933, - "The Cooking Guide gives you a bucket of water and a pot of flour." - ) - ) - addItem(player, Items.BUCKET_OF_WATER_1929) - addItem(player, Items.POT_OF_FLOUR_1933) + setUnclosable(player, interpreter.sendDoubleItemMessage(Items.BUCKET_OF_WATER_1929, Items.POT_OF_FLOUR_1933, "The Cooking Guide gives you a bucket of water and a","pot of flour.")) + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929) + addItemOrDrop(player, Items.POT_OF_FLOUR_1933) stage++ + setAttribute(player, "tutorial:stage", 19) + TutorialStage.load(player, 19) } 4 -> { end() - setAttribute(player, "tutorial:stage", 19) TutorialStage.load(player, 19) } } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt index 736c0d08c..fc1f96c81 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt @@ -1,107 +1,95 @@ package content.region.misc.tutisland.dialogue -import core.api.addItem -import core.api.getAttribute -import core.api.inInventory -import core.api.setAttribute -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import content.region.misc.tutisland.handlers.sendStageDialog +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.node.item.Item -/** - * Handles the mining tutor's dialogue - * @author Ceikry - */ -@Initializable -class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return TutorialMiningInstructorDialogue(player) +class MiningInstructorDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.MINING_INSTRUCTOR_948, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, MiningInstructorDialogueFile(), node as NPC) + return@on true + } + } +} + +class MiningInstructorDialogueFile : DialogueLabeller() { + fun lostPickaxe(player: Player): Boolean { + return !inInventory(player, Items.BRONZE_PICKAXE_1265) && !inEquipment(player, Items.BRONZE_PICKAXE_1265) } - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - when(getAttribute(player, "tutorial:stage", 0)) { - 30 -> npcl(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.") - 35 -> { - if(!inInventory(player, Items.BRONZE_PICKAXE_1265)) { - addItem(player, Items.BRONZE_PICKAXE_1265) - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_PICKAXE_1265, "Dezzick gives you a bronze pickaxe!") - stage = 3 - } - else { - TutorialStage.load(player, 35) - } - } - 40 -> playerl(core.game.dialogue.FacialExpression.ASKING, "How do I make a weapon out of this?") - 41 -> { - if(!inInventory(player, Items.HAMMER_2347)) { - addItem(player, Items.HAMMER_2347) - player.dialogueInterpreter.sendItemMessage(Items.HAMMER_2347, "Dezzick gives you a hammer!") - stage = 3 - } - else - { - end() - TutorialStage.load(player, 41) - } + override fun addConversation() { + assignToIds(NPCs.MINING_INSTRUCTOR_948) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 30 -> loadLabel(player, "hello") + 34 -> loadLabel(player, "prospected") + 35, 36, 37, 38, 39 -> loadLabel(player, if (lostPickaxe(player)) "lost pickaxe" else "nowhere") + 40 -> loadLabel(player, "make wep") + 41, 42, 43, 44, 45, 46 -> loadLabel(player, if (lostPickaxe(player)) "lost pickaxe" else if (!inInventory(player, Items.HAMMER_2347)) "lost hammer" else "nowhere") + else -> loadLabel(player, "nowhere") } } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(getAttribute(player, "tutorial:stage", 0)) { - 30 -> when(stage) { - 0 -> playerl(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++ } - 2 -> { - end() - setAttribute(player, "tutorial:stage", 31) - TutorialStage.load(player, 31) - } - } - - 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++ } - 2 -> { - addItem(player, Items.BRONZE_PICKAXE_1265) - player.dialogueInterpreter.sendItemMessage(Items.BRONZE_PICKAXE_1265, "Dezzick gives you a bronze pickaxe!") - stage++ - } - 3 -> { - end() - setAttribute(player, "tutorial:stage", 35) - TutorialStage.load(player, 35) - } - } - - 40,41 -> when(stage){ - 0 -> npcl(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++ } - 1 -> { - addItem(player, Items.HAMMER_2347) - player.dialogueInterpreter.sendItemMessage(Items.HAMMER_2347, "Drezzick gives you a hammer!") - stage++ - } - 2 -> { - end() - setAttribute(player, "tutorial:stage", 41) - TutorialStage.load(player, 41) - } - } + label("hello") + npc(ChatAnim.FRIENDLY, "Hi there. You must be new around here. So what do I call you? 'Newcomer' seems so impersonal, and if we're going to be working together, I'd rather tell you by name.", unclosable = true) + player(ChatAnim.FRIENDLY, "You can call me ${player?.username}.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Ok then, ${player?.username}. My name is Dezzick and I'm a miner by trade. Let's prospect some of these rocks.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 31) + TutorialStage.load(player, 31) } - return true - } + label("prospected") + player(ChatAnim.FRIENDLY, "I prospected both types of rock! One set contains tin and the other has copper ore inside.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Absolutely right, ${player?.username}. These two ore types can be smelted together to make bronze.", unclosable = true) + npc(ChatAnim.FRIENDLY, "So now you know what ore is in the rocks over there, why don't you have a go at mining some tin and copper? Here, you'll need this to start with.", unclosable = true) + exec { player, _ -> + addItem(player, Items.BRONZE_PICKAXE_1265) + setAttribute(player, "tutorial:stage", 35) + TutorialStage.load(player, 35) + } + item(Item(Items.BRONZE_PICKAXE_1265), "Dezzick gives you a bronze pickaxe!", unclosable = true) + goto("nowhere") - override fun getIds(): IntArray { - return intArrayOf(NPCs.MINING_INSTRUCTOR_948) + label("make wep") + player(ChatAnim.ASKING, "How do I make a weapon out of this?", unclosable = true) + npc(ChatAnim.FRIENDLY, "Okay, I'll show you how to make a dagger out of it. You'll be needing this.", unclosable = true) + exec { player, _ -> + addItem(player, Items.HAMMER_2347) + setAttribute(player, "tutorial:stage", 41) + TutorialStage.load(player, 41) + } + item(Item(Items.HAMMER_2347), "Dezzick gives you a hammer!", unclosable = true) + goto("nowhere") + + label("lost pickaxe") + exec { player, _ -> addItem(player, Items.BRONZE_PICKAXE_1265) } + item(Item(Items.BRONZE_PICKAXE_1265), "Dezzick gives you a spare pickaxe.", unclosable = true) + exec { player, _ -> + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 41 && !inInventory(player, Items.HAMMER_2347)) { + loadLabel(player, "lost hammer") + } + } + goto("nowhere") + + label("lost hammer") + exec { player, _ -> addItem(player, Items.HAMMER_2347) } + item(Item(Items.HAMMER_2347), "Dezzick gives you a spare hammer.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } } } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt index 680a929b0..3fb71bd16 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt @@ -9,14 +9,15 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles the prayer guide's dialogue * @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 +25,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 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Greetings! I'd just like to briefly go over two topics with you: Prayer, and Friend's.")) + 62 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Prayers have all sorts of wonderful benefits! From boosting defence and damage, to protecting you from outside damage, to saving items on death!")) + 65 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "For your friend and ignore lists, it's quite simple really! Use your friend list to keep track of players who you like, and ignore those you don't!")) } return true } @@ -35,8 +36,8 @@ class TutorialPrayerDialogue(player: Player? = null) : 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 -> setUnclosable(player, playerl(FacialExpression.FRIENDLY, "Alright, sounds fun!")) + 1 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Right, so first thing: Prayer. Prayer is trained by offering bones to the gods, and can grant you many boons!")) 2 -> { end() setAttribute(player, "tutorial:stage", 61) @@ -45,8 +46,8 @@ class TutorialPrayerDialogue(player: Player? = null) : 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 -> setUnclosable(player, playerl(FacialExpression.AMAZED, "Very cool!")) + 1 -> setUnclosable(player, npcl(FacialExpression.FRIENDLY, "Next up, let's talk about friends.")) 2 -> { end() setAttribute(player, "tutorial:stage", 63) diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt index 34fc5eecc..716bc2cf4 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt @@ -12,14 +12,15 @@ import core.plugin.Initializable import org.rs09.consts.Components import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** * Handles the quest guide's dialogue * @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) } @@ -27,23 +28,23 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 27 -> Component.setUnclosable( + 27 -> setUnclosable( 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." ) ) - 28 -> Component.setUnclosable( + 28 -> setUnclosable( 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." ) ) @@ -57,7 +58,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di when(getAttribute(player, "tutorial:stage", 0)) { 27 -> { - Component.setUnclosable( + setUnclosable( player, interpreter.sendPlaneMessageWithBlueTitle( "Open the Quest Journal.", @@ -67,40 +68,40 @@ 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) { - 0 -> Component.setUnclosable( + 0 -> setUnclosable( 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", "to begin." ) ).also { stage++ } - 1 -> Component.setUnclosable( + 1 -> setUnclosable( 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." ) ).also { stage++ } - 2 -> Component.setUnclosable( + 2 -> setUnclosable( 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++ } @@ -118,5 +119,4 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di override fun getIds(): IntArray { 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..b3611f89e 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt @@ -1,7 +1,6 @@ package content.region.misc.tutisland.dialogue import core.api.setAttribute -import core.game.component.Component import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -9,90 +8,73 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import org.rs09.consts.NPCs import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable /** - * Handles the RuneSccape guide's dialogue + * Handles the 2009scape guide's dialogue * @author Ceikry + * @author Player Name */ @Initializable -class TutorialRSGuideDialogue(player: Player? = null) : 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) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 - if(tutStage < 2) { - Component.setUnclosable( + if (tutStage < 2) { + setUnclosable(player, player.dialogueInterpreter.sendDialogues(npc,FacialExpression.HALF_GUILTY,"Greetings! Please follow the onscreen instructions!")) + stage = 99 + } else if (tutStage == 2) { + player.lock() + setUnclosable( player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.HALF_GUILTY, - "Greetings! Please follow the onscreen", - "instructions!" - ) - ) - return false - } - - if(tutStage == 2) - { - 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!" ) ) stage = 0 - return true - } - else - { - Component.setUnclosable( - player, - interpreter.sendDialogues( - npc, - core.game.dialogue.FacialExpression.HALF_GUILTY, - "Please follow the onscreen instructions!" - ) - ) - return false + } else { + setUnclosable(player, player.dialogueInterpreter.sendDialogues(npc,FacialExpression.HALF_GUILTY,"Please follow the onscreen instructions!")) + stage = 99 } + return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 0 -> Component.setUnclosable( + 0 -> setUnclosable( player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "You have already learned the first thing needed to", "succeed in this world: talking to other people!" ) ).also { stage++ } - 1 -> Component.setUnclosable( + 1 -> setUnclosable( 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." ) ).also { stage++ } - 2 -> Component.setUnclosable( + 2 -> setUnclosable( 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", @@ -100,21 +82,21 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo ) ).also { stage++ } - 3 -> Component.setUnclosable( + 3 -> setUnclosable( player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "contain helpful tips to help you on your", "journey." ) ).also { stage++ } - 4 -> Component.setUnclosable( + 4 -> setUnclosable( 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,9 +104,15 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo 5 -> { end() + player.unlock() setAttribute(player, "tutorial:stage", 3) TutorialStage.load(player, 3) } + 99 -> { + end() + val tutStage = player?.getAttribute("tutorial:stage", 0) ?: 0 + TutorialStage.load(player, tutStage) + } } return true } diff --git a/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java index 6d7b8a5b1..56845ac8b 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; +import content.data.Quests; /** * Handles the tutorial rat npc. @@ -65,7 +66,7 @@ public class RatTutorialNPC extends AbstractNPC { } final Player p = ((Player) killer); if (killer instanceof Player) { - if (p.getQuestRepository().getQuest("Witch's Potion").isStarted(p)) { + if (p.getQuestRepository().getQuest(Quests.WITCHS_POTION).isStarted(p)) { GroundItemManager.create(new Item(300), getLocation(), p); } } diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt new file mode 100644 index 000000000..082da760b --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialArea.kt @@ -0,0 +1,10 @@ +package content.region.misc.tutisland.handlers + +import core.api.* +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +class TutorialArea : MapArea { + override fun defineAreaBorders() : Array { return arrayOf(12079, 12080, 12335, 12336, 12436, 12592).map { ZoneBorders.forRegion(it) }.toTypedArray() } + override fun getRestrictions() : Array { return arrayOf(ZoneRestriction.RANDOM_EVENTS) } +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt new file mode 100644 index 000000000..78a493dcd --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialDialogs.kt @@ -0,0 +1,554 @@ +package content.region.misc.tutisland.handlers + +import core.api.getAttribute +import core.api.inInventory +import core.game.component.Component.setUnclosable +import core.game.event.DialogueCloseEvent +import core.game.event.EventHook +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.GameWorld.settings +import org.rs09.consts.Items +import kotlin.also + +fun sendStageDialog(player: Player, stage: Int) { + val message = when (stage) { + 0 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Getting started", + "To start the tutorial use your left mouse button to click on the", + "" + settings!!.name + " Guide in this room. He is indicated by a flashing", + "yellow arrow above his head. If you can't see him, use your", + "keyboard's arrow keys to rotate the view." + ) + 1 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Game options", + "Please click on the flashing spanner icon found at the bottom", + "right of your screen. This will display your game options." + ) + 2 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Game Options", + "In the interface, you can now see a variety of options such as", + "screen brightness, sound and music volume and whether you", + "want to accept aid from other player's or not. Don't worry", + "about these too much for now; they will become easier as you", + "explore the game. Talk to the " + settings!!.name + " Guide to continue." + ) + 3 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Interacting with scenery", + "You can interact with many items of scenery by simply clicking", + "on them. Right clicking will also give more options. Feel free to", + "try it with the things in this room, then click on the door", + "indicated with the yellow arrow to go though to the next", + "instructor." + ) + 4 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Moving around", + "Follow the path to find the next instructor. Clicking on the", + "ground will walk you to that point. You can also navigate by", + "clicking on the minimap in the top-right corner of your screen.", + "Talk to Survival Expert by the pond to continue the tutorial." + ) + 5 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Viewing the items that you were given.", + "Click on the flashing backpack icon to the right-hand side of", + "the main window to view your inventory. Your inventory is a list", + "of everything you have in your backpack.", + "" + ) + 6 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cut down a tree", + "You can click on the backpack icon at any time to view the", + "items that you currently have in your inventory. You will see", + "that you now have an axe in your inventory. Use this to get", + "some logs by clicking on one of the trees in the area." + ) + 7 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to cut down the tree. Sit back", + "for a moment while " + (if (player.appearance.isMale) "he" else "she") + " does all the hard work.", + "" + ) + 8 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Making a fire", + "Well done! You managed to cut some logs from the tree! Next,", + "use the tinderbox in your inventory to light the logs.", + "First click on the tinderbox to 'use' it.", + "Then click on the logs in your inventory to light them." + ) + 9 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to light the fire.", + "This should only take a few seconds.", + "" + ) + 10 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You gained some experience.", + "", + "Click on the flashing bar graph icon near the inventory button", + "to see your skill state.", + "" + ) + 11 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your skill stats", + "Here you will see how good your skills are. As you move your", + "mouse over any of the icons in this tab, the small yellow popup", + "box will show you the exact amount of experience you have", + "and how much is needed to get to the next level. Speak to the survival guide." + ) + 12 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Catch some shrimp", + "Click on the bubbling fishing spot, indicated by the flashing", + "arrow. Remember, you can check your inventory by clicking the", + "backpack icon.", + "" + ) + 13 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "This should only take a few seconds.", + "As you gain Fishing experience you'll find that there are many", + "types of fish and many ways to catch them." + ) + 14 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking your shrimp", + "Now you have caught some shrimp, let's cook it. First light a", + "fire: chop down a tree and then use the tinderbox on the logs.", + "If you've lost your axe or tinderbox Brynna will give you", + "another." + ).also { + if (!inInventory(player, Items.RAW_SHRIMPS_317, 1)) { + setUnclosable( + player, + player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking your shrimp", + "Now right click on the shrimp and select the use option. Next,", + "left click on the fire you just lit. If while doing this you look in", + "the top left of the screen, you will see the instruction that", + "you're giving your character." + ) + ) + } + } + 15 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Burning your shrimp", + "You have just burnt your first shrimp. This is normal. As you", + "get more experience in Cooking you will burn stuff less often.", + "Let's try cooking without burning it this time. First catch some", + "more shrimp, then use them on a fire." + ) + 16 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Well done, you've just cooked your first " + settings!!.name + " meal.", + "If you'd like a recap on anything you've learnt so far, speak to", + "the Survival Expert. You can now move on to the next", + "instructor. Click on the gate shown and follow the path.", + "Remember, you can move the camera with the arrow keys." + ) + 17 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Find your next instructor", + "Follow the path until you get to the door with the yellow arrow", + "above it. Click on the door to open it. Notice the mini map in the", + "top right; this shows a top down view of the area around you.", + "This can also be used for navigation." + ) + 18 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Find your next instructor", + "Talk to the chef indicated. He will teach you the more advanced", + "aspects of Cooking such as combining ingredients. He will also", + "teach you about your Music Player.", + "" + ) + 19 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Making dough", + "This is the base for many of the meals. To make dough we must", + "mix flour and water. First, right click the bucket of water and", + "select use, then left click on the pot of flour.", + "" + ) + 20 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Cooking dough", + "Now you have made dough, you can cook it. To cook the dough,", + "use it with the range shown by the arrow. If you lose your", + "dough, talk to Lev - he will give you more ingredients.", + "" + ) + 21 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Cooking dough", + "Well done! Your first loaf of bread. As you gain experience in", + "Cooking, you will be able to make other things like pies, cakes", + "and even kebabs. Now you've got the hang of cooking, let's", + "move on. Click on the flashing icon in the bottom right to see", + "the flashing icon in the bottom right to see the Music Player." + ) + 22 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "The Music Player", + "From this interface you can control the music that is played.", + "As you explore the world and complete quests, more of the", + "tunes will become unlocked. Once you've examined this menu,", + "use the next door to continue. If you need a recap on anything", + "you've learnt so far, speak to the Master Chef." + ) + 23 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Emotes", + "", + "Now how about showing some feelings? You will see a flashing", + "icon in the shape of a person. Click on that to access your", + "emotes." + ) + 24 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Emotes", + "For those situations where words don't quite describe how you feel try", + "an emote. Go ahead try one out! You might notice that some of the", + "emotes are grey and cannot be used now. Don't worry! As you", + "progress further into the game you'll gain access to all sorts of things." + ) + 25 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Running", + "", + "It's only a short distance to the next guide.", + "Why not try running there? To do this, click on the run icon", + "next to the minimap." + ) + 26 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Run to the next guide", + "Now that you have the run button turned on, follow the path", + "until you come to the end. You may notice that the number on", + "the button goes down. This is your run energy. If your run", + "energy reaches zero, you'll stop running. Click on the door to", + "pass through it." + ) + 27 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Talk with the Quest Guide.", + "", + "He will tell you all about quests.", + "", + "" + ) + 28 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your Quest Journal", + "", + "This is your Quest Journal, a list of all the quests in the game.", + "Talk to the Quest Guide again for an explanation.", + "" + ) + 29 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Moving on", + "It's time to enter some caves. Click on the ladder to go down to", + "the next area.", + "" + ) + 30 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining and Smithing", + "", + "Next let's get you a weapon, or more to the point, you can", + "make your first weapon yourself. Don't panic, the Mining", + "Instructor will help you. Talk to him and he'll tell you all about it." + ) + 31 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Prospecting", + "To prospect a mineable rock, just right click it and select the", + "'prospect rock' option. This will tell you the type of ore you can", + "mine from it. Try it now on one of the rocks indicated.", + "" + ) + 32 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to prospect the rock. This", + "should only take a few seconds.", + "" + ) + 33 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "It's tin.", + "", + "So now you know there's tin in the grey rocks, try prospecting the", + "brown ones next.", + "" + ) + 34 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "It's copper.", + "", + "Talk to the Mining Instructor to find out about these types of", + "ore and how you can mine them.", + "He'll even give you the required tools.", + ) + 35 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining", + "", + "It's quite simple really. All you need to do is right click on the", + "rock and select 'mine' You can only mine when you have a", + "pickaxe. So give it a try: first mine one tin ore.", + ) + 36 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Please wait.", + "", + "Your character is now attempting to mine the rock.", + "This should only take a few seconds.", + "" + ) + 37 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Mining", + "", + "Now you have some tin ore you just need some copper ore,", + "then you'll have all you need to create a bronze bar. As you", + "did before right click on the copper rock and select 'mine'." + ) + 38 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smelting", + "You should now have both some copper and tin ore. So let's", + "smelt them to make a bronze bar. To do this, right click on", + "either tin or copper ore and select use then left click on the", + "furnace. Try it now." + ) + 39, 40 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You've made a bronze bar!", + "", + "Speak to the Mining Instructor and he'll show you how to make", + "it into a weapon.", + "" + ) + 41 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smithing a dagger", + "To smith you'll need a hammer - like the one you were given by", + "Dezzick - access to an anvil like the one with the arrow over it", + "and enough metal bars to make what you are trying to smith.", + "To start the process, use the bar on one of the anvils." + ) + 42 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Smithing a dagger.", + "Now you have the Smithing menu open, you will see a list of all", + "the things you can make. Only the dagger can be made at your", + "skill level; this is shown by the white text under it. You'll need", + "to select the dagger to continue." + ) + 43 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You've finished in this area.", + "", + "So let's move on. Go through the gates shown by the arrow.", + "Remember, you may need to move the camera to see your", + "surroundings. Speak to the guide for a recap at any time.", + ) + 44 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Combat", + "", + "In this area you will find out about combat with swords and", + "bows. Speak to the guide and he will tell you all about it.", + "" + ) + 45 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Wielding weapons", + "", + "You now have access to a new interface. Click on the flashing", + "icon of a man, the one to the right of your backpack icon.", + "" + ) + 46 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your worn inventory.", + "From here you can see what items you have equipped. Let's", + "get one of those slots filled, go back to your inventory and", + "right click your dagger, select wield from the menu.", + "" + ) + 47 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "You're now holding your dagger.", + "Clothes, armour, weapons and many other items are equipped", + "like this. You can unequip items by clicking on the item in the", + "worn equipment. You can close this window by clicking on the", + "small 'x' in the top-right hand corner. Speak to the Combat", + "Instructor." + ) + 48 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Unequipping items.", + "In your worn inventory panel, right click on the dagger and", + "select the remove option from the drop down list. After you've", + "unequipped the dagger, wield the sword and shield. As you", + "pass the mouse over an item you will see its name appear at", + "the top left of the screen." + ) + 49 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Combat interface.", + "", + "Click on the flashing crossed swords icon to see the combat", + "interface.", + "" + ) + 50 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "This is your combat interface.", + "From this interface you can select the type of attack your", + "character will use. Different monsters have different", + "weaknesses. If you hover your mouse over the buttons, you", + "will see the type of XP you will receive when using each type of", + "attack. Now you have the tools needed for battle why not slay", + "some rats. Click on the gates indicated to continue." + ) + 51 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Attacking", + "To attack the rat, click it and select the attack option. You", + "will then walk over to it and start hitting it.", + "", + "" + ) + 52 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Sit back and watch.", + "While you are fighting you will see a bar over your head. The", + "bar shows how much health you have left. Your opponent will", + "have one too. You will continue to attack the rat until it's dead", + "or you do something else." + ) + 53 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Well done, you've made your first kill!", + "", + "Pass through the gate and talk to the Combat Instructor; he", + "will give you your next task.", + "" + ) + 54 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Rat ranging", + "Now you have a bow and some arrows. Before you can use", + "them you'll need to equip them. Once equipped with the", + "ranging gear try killing another rat. Remember: to attack, right", + "click on the monster and select attack." + ) + 55 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Moving on.", + "You have completed the tasks here. To move on, click on the", + "ladder shown. If you need to go over any of what you learnt", + "here, just talk to the Combat Instructor and he'll tell you what", + "he can." + ) + 56 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Banking.", + "Follow the path and you will come to the front of a building.", + "This is the 'Bank of " + settings!!.name + "' where you can store all your", + "most valued items. To open your bank box just right click on an", + "open booth indicated and select 'use'." + ) + 57 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your bank box.", + "You can store stuff here for safekeeping. If you die, anything", + "in your bank will be saved. To deposit something, right click it", + "and select 'Deposit-1'. Once you've had a good look, close the", + "window and move on through the door indicated." + ) + 58 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Financial advice", + "", + "The guide here will tell you all about making cash. Just click on", + "him to hear what he's got to say.", + "" + ) + 59 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Continue through the next door.", + "", + "" + ) + 60 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Prayer", + "Follow the path to the chapel and enter it.", + "Once inside talk to the monk. He'll tell you all about the Prayer", + "skill.", + "" + ) + 61 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your Prayer List", + "", + "Click on the flashing icon to open the Prayer List.", + "", + "" + ) + 62 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Your Prayer List", + "", + "Talk with Brother Brace and he'll tell you all about prayers.", + "" + ) + 63 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Friends list", + "You should now see another new icon. Click on the flashing", + "smiling face to open your Friend List.", + "" + ) + 64 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your Friends List.", + "", + "This will be explained by Brother Brace shortly, but first click", + "on the other flashing face in the interface.", + "" + ) + 65 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "This is your Ignore List.", + "The two lists, Friends and Ignore - can be very helpful for", + "keeping track of when your friends are online or for blocking", + "messages from people you simply don't like. Speak with", + "Brother Brace and he will tell you more." + ) + 66 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "Your final instructor!", + "You're almost finished on tutorial island. Pass through the", + "door to find the path leading to your final instructor.", + "" + ) + 67 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Your final instructor!", + "Just follow the path to the Wizard's house, where you will be", + "shown how to cast spells. Just talk with the mage indicated to", + "find out more.", + "" + ) + 68 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Open up your final tab.", + "", + "Open up the Magic Spellbook tab by clicking on the flashing", + "icon next to the Prayer List tab you just learned about.", + "" + ) + 69 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "This is your spell list.", + "", + "Ask the mage about it.", + "" + ) + 70 -> player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + "Cast Wind Strike at a chicken.", + "Now you have the runes you should see the Wind Strike icon at the", + "top-left of your spellbook, second in from the left. Walk over", + "to the caged chickens, click the Wind Strike icon and then", + "select one of the chickens to cast it on. It may take several", + "tries." + ) + 71 -> player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "You have almost completed the tutorial!", + "", + "All you need to do now is teleport to the mainland. Just speak", + "with Terrova and he'll tell you how to do that.", + "" + ) + else -> null + } + setUnclosable(player, message ?: return) +} + +fun sendStageDialog(entity: Entity) { + val player = entity as Player + val stage = getAttribute(player, "tutorial:stage", 0) + sendStageDialog(player, stage) +} + +// Because we don't have proper interface stacking, the above unclosable dialogs can still be closed, if a new dialog +// opens up over it and that dialog *is* closable. This is the case for npc dialogs and the smelting interface. Fake the +// authentic behavior by making sure we reopen our unclosable dialog when this happens. +object TutorialDialogPreserver : EventHook { + override fun process(entity: Entity, event: DialogueCloseEvent) { + sendStageDialog(entity) + } +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt index e9319bcc2..c65191bb1 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt @@ -7,13 +7,30 @@ import core.game.node.entity.player.Player import content.global.skill.fishing.FishingSpot import content.global.skill.gather.mining.MiningNode import content.global.skill.gather.woodcutting.WoodcuttingNode +import core.api.animate +import core.api.delayScript +import core.api.forceWalk +import core.api.inInventory +import core.api.lock +import core.api.lockInteractions +import core.api.playAudio +import core.api.queueScript +import core.api.replaceSlot +import core.api.stopExecuting import core.game.event.* +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds /** * Event receivers for tutorial island - * @author Ceikry + * @author Ceikry, Player Name */ object TutorialButtonReceiver : EventHook { @@ -65,7 +82,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) } @@ -77,7 +94,7 @@ object TutorialButtonReceiver : EventHook } //Open equipment tab SD:548,42 HD:746,45 - 45 -> if((event.iface == 548 && event.buttonId == 42) || (event.iface == 746 && event.buttonId == 45)){ + 45, 46 -> if((event.iface == 548 && event.buttonId == 42) || (event.iface == 746 && event.buttonId == 45)){ setAttribute(entity, "tutorial:stage", 46) TutorialStage.load(entity, 46) } @@ -136,18 +153,6 @@ object TutorialInteractionReceiver : EventHook TutorialStage.load(entity, 13) } - //Prospect rock - Tin - 31 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(2.toByte()) == true && event.option == "prospect"){ - setAttribute(entity, "tutorial:stage", 32) - TutorialStage.load(entity, 32) - } - - //Prospect rock- Copper - 33 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(1.toByte()) == true && event.option == "prospect"){ - setAttribute(entity, "tutorial:stage", 34) - TutorialStage.load(entity, 34) - } - //Mine rock - Tin 35 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(2.toByte()) == true && event.option == "mine"){ setAttribute(entity, "tutorial:stage", 36) @@ -211,30 +216,12 @@ object TutorialResourceReceiver : EventHook TutorialStage.load(entity, 14) } - //Cook a shrimp - 14,15 -> if(event.itemId == Items.BURNT_SHRIMP_7954) - { - setAttribute(entity, "tutorial:stage", 15) - TutorialStage.load(entity, 15) - } - else if(event.itemId == Items.SHRIMPS_315) - { - setAttribute(entity, "tutorial:stage", 16) - TutorialStage.load(entity, 16) - } - //Make some bread dough 19 -> if(event.itemId == Items.BREAD_DOUGH_2307) { setAttribute(entity, "tutorial:stage", 20) TutorialStage.load(entity, 20) } - //Bake some bread - 20 -> if(event.itemId == Items.BREAD_2309 || event.itemId == Items.BURNT_BREAD_2311) { - setAttribute(entity, "tutorial:stage", 21) - TutorialStage.load(entity, 21) - } - //Mine some tin ore 36 -> if(event.itemId == Items.TIN_ORE_438){ setAttribute(entity, "tutorial:stage", 37) diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt deleted file mode 100644 index c4139d6f5..000000000 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialFurnaceListener.kt +++ /dev/null @@ -1,50 +0,0 @@ -package content.region.misc.tutisland.handlers - -import core.api.* -import core.game.event.ResourceProducedEvent -import core.game.node.entity.skill.Skills -import content.global.skill.smithing.smelting.Bar -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import org.rs09.consts.Scenery -import core.game.interaction.IntType -import core.game.interaction.InteractionListener - -/** - * Listener for tutorial island furnace - * @author Byte - */ -class TutorialFurnaceListener : InteractionListener { - - companion object { - private val ANIMATION = Animation(833) - - private val ORES = intArrayOf( - Items.TIN_ORE_438, - Items.COPPER_ORE_436 - ) - } - - override fun defineListeners() { - onUseWith(IntType.SCENERY, ORES, Scenery.FURNACE_3044) { player, _, _ -> - if (!inInventory(player, Items.TIN_ORE_438) || !inInventory(player, Items.COPPER_ORE_436)) { - return@onUseWith true - } - - animate(player, ANIMATION) - submitIndividualPulse(player, object: Pulse(2) { - override fun pulse(): Boolean { - removeItem(player, Items.TIN_ORE_438) - removeItem(player, Items.COPPER_ORE_436) - addItem(player, Items.BRONZE_BAR_2349) - rewardXP(player, Skills.SMITHING, Bar.BRONZE.experience) - player.dispatch(ResourceProducedEvent(Items.BRONZE_BAR_2349, 1, player, Items.COPPER_ORE_436)) - return true - } - }) - - return@onUseWith true - } - } -} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt index c7dd26f50..5a143acfb 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt @@ -1,13 +1,25 @@ package content.region.misc.tutisland.handlers +import content.global.skill.smithing.smelting.Bar +import content.region.misc.tutisland.dialogue.RatPenDialogue import core.api.* +import core.game.event.ResourceProducedEvent import core.game.node.scenery.Scenery import core.game.system.task.Pulse import core.game.world.map.Location import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Animator +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item import core.game.world.repository.Repository +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.Sounds /** * Handles tutorial-specific node interactions @@ -16,10 +28,13 @@ import core.game.world.repository.Repository class TutorialListeners : InteractionListener { val GUIDE_HOUSE_DOOR = 3014 val COOKS_DOOR = 3017 + val RANGE = 3039 val COOKS_EXIT = 3018 val QUEST_ENTER = 3019 val QUEST_LADDER = 3029 val QUEST_EXIT_LADDER = 3028 + val TIN_ROCK = 3043 + val COPPER_ROCK = 3042 val COMBAT_EXIT = 3030 val BANK_EXIT = 3024 val FINANCE_EXIT = 3025 @@ -27,6 +42,7 @@ class TutorialListeners : InteractionListener { val FIRST_GATE = intArrayOf(3015,3016) val COMBAT_GATES = intArrayOf(3020,3021) val RAT_GATES = intArrayOf(3022, 3023) + val FURNACE = 3044 override fun defineListeners() { on(GUIDE_HOUSE_DOOR, IntType.SCENERY, "open"){ player, door -> @@ -60,6 +76,36 @@ class TutorialListeners : InteractionListener { return@on true } + fun cookBread(player: Player, dough: Item): Boolean { + if (getAttribute(player, "tutorial:stage", 0) < 20) { + return true + } + // Need to reinvent the wheel of cooking. Yes, I do. On tutorial island, we don't want the default stuff like asking the player what dough they want to make. + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 0) { + val RANGE_ANIMATION = Animation(883, Animator.Priority.HIGH) + lock(player, RANGE_ANIMATION.duration) + lockInteractions(player, RANGE_ANIMATION.duration) + animate(player, RANGE_ANIMATION) + playAudio(player, Sounds.FRY_2577) + return@queueScript delayScript(player, RANGE_ANIMATION.duration) + } else { + replaceSlot(player, dough.slot, Item(Items.BREAD_2309), dough) + setAttribute(player, "tutorial:stage", 21) + TutorialStage.load(player, 21) + return@queueScript stopExecuting(player) + } + } + return true + } + on(RANGE, IntType.SCENERY, "use") { player, _ -> + val dough = player.inventory.get(Item(Items.BREAD_DOUGH_2307)) ?: return@on true + return@on cookBread(player, dough) + } + onUseWith(IntType.SCENERY, Items.BREAD_DOUGH_2307, RANGE) { player, dough, _ -> + return@onUseWith cookBread(player, dough as Item) + } + on(COOKS_EXIT, IntType.SCENERY, "open"){ player, door -> if(getAttribute(player, "tutorial:stage", 0) != 22) return@on true @@ -105,23 +151,41 @@ class TutorialListeners : InteractionListener { return@on true } - on(COMBAT_GATES, IntType.SCENERY, "open"){ player, gate -> - if(getAttribute(player, "tutorial:stage", 0) != 43) + on(TIN_ROCK, IntType.SCENERY, "prospect") { player, _ -> + if (getAttribute(player, "tutorial:stage", 0) != 31) { return@on true + } + setAttribute(player, "tutorial:stage", 32) + TutorialStage.load(player, 32) + return@on true + } + on(COPPER_ROCK, IntType.SCENERY, "prospect") { player, _ -> + if (getAttribute(player, "tutorial:stage", 0) != 33) { + return@on true + } + setAttribute(player, "tutorial:stage", 34) + TutorialStage.load(player, 34) + return@on true + } - setAttribute(player, "tutorial:stage", 44) - TutorialStage.load(player, 44) + on(COMBAT_GATES, IntType.SCENERY, "open"){ player, gate -> + if (getAttribute(player, "tutorial:stage", 0) < 43) { + return@on true + } + if (getAttribute(player, "tutorial:stage", 0) == 43) { + setAttribute(player, "tutorial:stage", 44) + TutorialStage.load(player, 44) + } core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate as Scenery) } on(RAT_GATES, IntType.SCENERY, "open") { player, gate -> val stage = getAttribute(player, "tutorial:stage", 0) - if(stage !in 50..53){ - player.dialogueInterpreter.sendDialogues(NPCs.COMBAT_INSTRUCTOR_944, core.game.dialogue.FacialExpression.ANGRY, "Oi, get away from there!","Don't enter my rat pen unless I say so!") + if (stage !in 50..53) { + openDialogue(player, RatPenDialogue(), NPC(NPCs.COMBAT_INSTRUCTOR_944)) return@on true } - - if(stage == 50) { + if (stage == 50) { setAttribute(player, "tutorial:stage", 51) TutorialStage.load(player, 51) } @@ -130,11 +194,14 @@ class TutorialListeners : InteractionListener { } on(COMBAT_EXIT, IntType.SCENERY, "climb-up") { player, ladder -> - if(getAttribute(player, "tutorial:stage", 0) != 55) + val stage = getAttribute(player, "tutorial:stage", 0) + if (stage < 55) { return@on true - - setAttribute(player, "tutorial:stage", 56) - TutorialStage.load(player, 56) + } + if (stage == 55) { + setAttribute(player, "tutorial:stage", 56) + TutorialStage.load(player, 56) + } core.game.global.action.ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-up") } @@ -165,5 +232,28 @@ class TutorialListeners : InteractionListener { core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) } + fun smeltBronzeBar(player: Player): Boolean { + if (getAttribute(player, "tutorial:stage", 0) < 38) { + return true + } + if (!inInventory(player, Items.COPPER_ORE_436) || !inInventory(player, Items.TIN_ORE_438)) { + return true + } + animate(player, 833) + queueScript(player, 2, QueueStrength.WEAK) { + if (removeItem(player, Items.COPPER_ORE_436) && removeItem(player, Items.TIN_ORE_438)) { + addItem(player, Items.BRONZE_BAR_2349) + rewardXP(player, Skills.SMITHING, Bar.BRONZE.experience) + player.dispatch(ResourceProducedEvent(Items.BRONZE_BAR_2349, 1, player, Items.COPPER_ORE_436)) + TutorialStage.load(player, 39) + } + return@queueScript stopExecuting(player) + } + return true + } + on(FURNACE, IntType.SCENERY, "use") { player, _ -> smeltBronzeBar(player) } + for (item in arrayOf(Items.COPPER_ORE_436, Items.TIN_ORE_438)) { + onUseWith(IntType.SCENERY, item, FURNACE) { player, _, _ -> smeltBronzeBar(player) } + } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt index 8ec2666db..50eeddb00 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt @@ -12,12 +12,13 @@ import core.game.world.map.Location import org.rs09.consts.Components import core.api.Event import core.game.world.GameWorld.Pulser -import core.game.world.GameWorld.settings import core.game.world.repository.Repository +import org.rs09.consts.NPCs /** * Loads stage-relevant tutorial data * @author Ceikry + * @author Player Name */ object TutorialStage { /** @@ -25,535 +26,228 @@ object TutorialStage { * @param player the player to perform the actions on * @param stage the stage to load */ - fun load(player: Player, stage: Int, login: Boolean = false){ - if(login) - { + fun load(player: Player, stage: Int, login: Boolean = false) { + if (login) { player.hook(Event.ButtonClicked, TutorialButtonReceiver) player.hook(Event.Interacted, TutorialInteractionReceiver) player.hook(Event.ResourceProduced, TutorialResourceReceiver) player.hook(Event.UsedWith, TutorialUseWithReceiver) player.hook(Event.FireLit, TutorialFireReceiver) player.hook(Event.NPCKilled, TutorialKillReceiver) + player.hook(Event.DialogueClosed, TutorialDialogPreserver) openOverlay(player, Components.TUTORIAL_PROGRESS_371) player.packetDispatch.sendInterfaceConfig(371, 4, true) } - updateProgressBar(player) - when(stage) - { + when(stage) { 0 -> { lock(player, 10) teleport(player, Location.create(3094, 3107, 0)) hideTabs(player, login) CharacterDesign.open(player) + // We have two dialogs in this stage. This is awkward, but not a problem. + // The first dialog is impossible to close in any way, so we can send it here manually. + // The second dialog could be lost by e.g. talking to an npc, so this dialog gets implemented in + // TutorialDialogs.kt, which has the hook for restoring it if it does get lost. + Component.setUnclosable( + player, + player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Getting started", + "Please take a moment to design your character.", + "" + ) + ).also { + runTask(player, 10) { + sendStageDialog(player, stage) + } + } } - 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", - "Please click on the flashing spanner icon found at the bottom", - "right of your screen. This will display your player controls.", - "" - ) - ) + sendStageDialog(player, stage) } - 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." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.RUNESCAPE_GUIDE_945)!!) + sendStageDialog(player, stage) } - 3 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Location.create(3098,3107,0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Interacting with scenery", - "You can interact with many items of scenery by simply clicking", - "on them. Right clicking will also give more options. Feel free to", - "try it with the things in this room, then click on the door", - "indicated with the yellow arrow to go though to the next", - "instructor." - ) - ) + registerHintIcon(player, Location.create(3098, 3107, 0), 125) + sendStageDialog(player, stage) } - 4 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(943)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Moving around", - "Follow the path to find the next instructor. Clicking on the", - "ground will walk you to that point. Talk to the Survival Expert by", - "the pond to continue the tutorial. Remember you can rotate", - "the view by pressing the arrow keys." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) } - 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", - "the main window to view your inventory. Your inventory is a list", - "of everything you have in your backpack.", - "" - ) - ) + sendStageDialog(player, stage) } - 6 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cut down a tree", - "You can click on the backpack icon at any time to view the", - "items that you currently have in your inventory. You will see", - "that you now have an axe in your inventory. Use this to get", - "some logs by clicking on one of the trees in the area." - ) - ) + setVarbit(player, 3756, 4) + sendStageDialog(player, stage) } - 7 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to cut down the tree. Sit back", - "for a moment while " + (if (player.appearance.isMale) "he" else "she") + " does all the hard work.", - "" - ) - ) + sendStageDialog(player, stage) } - 8 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Making a fire", - "Well done! You managed to cut some logs from the tree! Next,", - "use the tinderbox in your inventory to light the logs.", - "First click on the tinderbox to 'use' it.", - "Then click on the logs in your inventory to light them." - ) - ) + sendStageDialog(player, stage) } - 9 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to light a fire.", - "This should only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) } - 10 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.STATS_320)) - setVarbit(player, 1021, 0, 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." - ) - ) + setVarbit(player, 3756, 2) + sendStageDialog(player, stage) } - 11 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - 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." - ) - ) + setVarbit(player, 3756, 2) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) } - 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.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.FISHING_SPOT_952)!!) + sendStageDialog(player, stage) } - 13 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "This should only take a few seconds.", - "As you gain Fishing experience you'll find that there are many types", - "of fish and many ways to catch them." - ) - ) + sendStageDialog(player, stage) } - 14 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cooking your shrimp", - "Now you have caught some shrimp, let's cook it. First light a fire: chop", - "down a tree and then use the tinderbox on the logs. If you've lost", - "your axe or tinderbox Brynna will give you another.", - "Once you have a fire, \"use\" your shrimp on the fire." - ) - ) + sendStageDialog(player, stage) } - 15 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Burning your shrimp", - "You have just burnt your first shrimp. This is normal. As you get", - "more experience in Cooking you will burn stuff less often. Let's try", - "cooking without burning it this time.", - "" - ) - ) + sendStageDialog(player, stage) } - 16 -> { hideTabs(player, login) + removeHintIcon(player) registerHintIcon(player, Location.create(3089, 3091, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Well done, you've just cooked your first " + settings!!.name + " meal.", - "", - "You can now move on to the next", - "instructor. Click on the gate shown and follow the path.", - "Remember, you can move the camera with the arrow keys." - ) - ) + sendStageDialog(player, stage) } - 17 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Location.create(3078, 3084, 0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Find your next instructor.", - "Follow the path until you get to the door with the yellow arrow", - "above it. Click on the door to open it. Notice the mini-map in", - "the top right, this shows a top down view of the area around", - "you. This can also be used for navigation." - ) - ) + registerHintIcon(player, Location.create(3079, 3084, 0), 125) + sendStageDialog(player, stage) } - 18 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(942)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Find your next instructor", - "Talk to the chef indicated. He will teach you the more advanced", - "aspects of Cooking such as combining ingredients. He will also", - "teach you about your music player menu as well.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MASTER_CHEF_942)!!) + sendStageDialog(player, stage) } - 19 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Making dough", - "This is the base for many of the meals. To make dough we must mix", - "flour and water. First right click the bucket of water and select use.", - "then left click on the pot of flour.", - "" - ) - ) + sendStageDialog(player, stage) } - 20 -> { hideTabs(player, login) registerHintIcon(player, Location.create(3076, 3081, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Cooking dough", - "Now you have made dough you can cook it. To cook the dough use", - "it with the range shown by the arrow. If you lose your dough talk to", - "Lev he will give you more ingredients.", - "" - ) - ) + sendStageDialog(player, stage) } - 21 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.MUSIC_V3_187)) - setVarbit(player, 1021, 0, 14) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "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", - "the flashing icon in the bottom right to see the Music Player." - ) - ) + setVarbit(player, 3756, 14) + sendStageDialog(player, stage) } - 22 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - registerHintIcon(player, Location.create(3072, 3090, 0), 125) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "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." - ) - ) + setVarbit(player, 3756, 0) + registerHintIcon(player, Location.create(3073, 3090, 0), 125) + sendStageDialog(player, stage) } - 23 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 13) + setVarbit(player, 3756, 13) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.EMOTES_464)) stopWalk(player) player.locks.lockMovement(100000) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Emotes", - "", - "Now how about showing some feelings? You will see a flashing icon in", - "the shape of a person. Click on that to access your emotes.", - "" - ) - ) + sendStageDialog(player, stage) } - 24 -> { hideTabs(player, login) - setVarbit(player, 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", - "an emote. Go ahead try one out! You might notice that some of the", - "emotes are grey and cannot be used now. Don't worry! As you", - "progress further into the game you'll gain access to all sorts of things." - ) - ) + sendStageDialog(player, stage) } - 25 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Running", - "", - "It's only a short distance to the next guide.", - "Why not try running there? To do this click on the run icon in", - "your settings tab." - ) - ) + sendStageDialog(player, stage) } - 26 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(949)!!) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) player.locks.unlockMovement() - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Run to the next guide", - "Now that you have the run button turned on, follow the path", - "until you come to the end. You may notice that the number on", - "the button goes down. This is your run energy. If your run", - "energy reaches zero, you'll stop running. Click on the door to", - "pass through it." - ) - ) + sendStageDialog(player, stage) } - 27 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(949)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Talk with the Quest Guide.", - "", - "He will tell you all about quests.", - "", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) + sendStageDialog(player, stage) } - 28 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your quest Journal", - "", - "This is your quest Journal, a list of all the quests in the game. Talk", - "to the Quest Guide again for an explanation.", - "" - ) - ) + setVarbit(player, 3756, 0) + sendStageDialog(player, stage) } - 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.", - "" - ) - ) + sendStageDialog(player, stage) } - 30 -> { hideTabs(player, login) removeHintIcon(player) - setVarbit(player, 1021, 0, 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." - ) - ) + setVarbit(player, 3756, 0) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 31 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3076, 9504, 0), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Prospecting", - "To prospect a mineable rock, just right click it and select the prospect", - "rock option. This will tell you the type of ore you can mine from it.", - "Try it now on one of the rocks indicated.", - "" - ) - ) + sendStageDialog(player, stage) } - 32 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to prospect the rock. This should", - "Only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) Pulser.submit(object : Pulse(3) { override fun pulse(): Boolean { setAttribute(player, "tutorial:stage", 33) @@ -562,619 +256,263 @@ object TutorialStage { } }) } - 33 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3086, 9501, 0), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "It's tin.", - "", - "So now you know there's tin in the grey rocks, try prospecting the", - "brown ones next.", - "" - ) - ) + sendStageDialog(player, stage) } - 34 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(948)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "It's copper.", - "Talk to the Mining Instructor to find out about these types of", - "ore and how you can mine them.", - "He'll even give you the required tools.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 35 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3076, 9504), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Mining", - "It's quite simple really. All you need to do is right click on the rock", - "and select 'mine'. You can only mine when you have a pickaxe. So", - "give it a try: first mine one tin ore.", - "" - ) - ) + sendStageDialog(player, stage) } - 36 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Please wait.", - "", - "Your character is now attempting to mine the rock.", - "This should only take a few seconds.", - "" - ) - ) + sendStageDialog(player, stage) } - 37 -> { hideTabs(player, login) registerHintIcon(player, Location.create(3086, 9501), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Mining", - "Now you have some tin ore you just need some copper ore, then", - "you'll have all you need to create a bronze bar. As you did before", - "right click on the copper rock and select 'mine'.", - "" - ) - ) + sendStageDialog(player, stage) } - 38 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3079, 9496), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smelting", - "You should now have both some copper and tin ore. So let's smelt", - "them to make a bronze bar. To do this right click on either tin or", - "copper ore and select use then left click on the furnance. Try it now.", - "" - ) - ) + sendStageDialog(player, stage) + } + 39 -> { + sendStageDialog(player, stage) } - - //Yes I know I skipped one. Blegh. - 40 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(948)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You've made a bronze bar!", - "", - "Speak to the Mining Instructor and he'll show you how to make", - "it into a weapon.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) } - 41 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3083, 9499), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smithing a dagger", - "To smith you'll need a hammer - like the one you were given by", - "Dezzick - access to an anvil like the one with the arrow over it", - "and enough metal bars to make what you are trying to smith.", - "To start the process, use the bar on one of the anvils." - ) - ) + sendStageDialog(player, stage) } - 42 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Smithing a dagger.", - "Now you have the Smithing menu open, you will see a list of all", - "the things you can make. Only the dagger can be made at this time; ", - "this is shown by the white text under it. You'll need", - "to select the dagger to continue." - ) - ) + sendStageDialog(player, stage) } - 43 -> { hideTabs(player, login) - registerHintIcon(player, Location.create(3094, 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.", - "" - ) - ) + registerHintIcon(player, Location.create(3095, 9502), 75) + sendStageDialog(player, stage) } - 44 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(944)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Combat.", - "In this area you will find out about combat, both melee and", - "ranged. Speak to the guide and he'll tell you about it.", - "", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 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) { + // this part needs sendStageDialog because you could just be logging in here + sendStageDialog(player, stage) + }.also { + // for this part, you are locked into the interface so we don't need sendStageDialog here + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) + 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) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your worn inventory.", - "From here you can see what items you have equipped. Let's", - "get one of those slots filled, go back to your inventory and", - "right click your dagger, select wield from the menu.", - "" - ) - ) + setVarbit(player, 3756, 0) + sendStageDialog(player, stage) } - 47 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "You're now holding your dagger.", - "Clothes, armor, 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." - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 48 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Unequipping items.", - "In your worn inventory panel, right click on the dagger and", - "select the remove option from the drop down list. After you've", - "unequipped the dagger, wield the sword and shield. As you", - "pass the mouse over an item you will see its name appear at", - "the top left of the screen." - ) - ) + sendStageDialog(player, stage) } - 49 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 1) + setVarbit(player, 3756, 1) var wepInter = player.getExtension(WeaponInterface::class.java) - if(wepInter == null) - { + if (wepInter == null) { wepInter = WeaponInterface(player) player.addExtension(WeaponInterface::class.java, wepInter) } player.interfaceManager.openTab(wepInter) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Combat interface.", - "", - "Click on the flashing crossed swords icon to see the combat", - "interface.", - "" - ) - ) + sendStageDialog(player, stage) } - 50 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - registerHintIcon(player, Location.create(3110,9518,0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "This is your combat interface.", - "From this interface you can select the type of attack your", - "character will use. Different monsters have different", - "weaknesses. If you hover your mouse over the buttons, you", - "will see the type of XP you will receive when using each type of", - "attack. Now you have the tools needed for battle why not slay", - "some rats. Click on the gates indicated to continue." - ) - ) + setVarbit(player, 3756, 0) + registerHintIcon(player, Location.create(3111,9518,0), 75) + sendStageDialog(player, stage) } - 51 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Attacking", - "To attack the rat, click it and select the attack option. You", - "will then walk over to it and start hitting it.", - "", - "" - ) - ) + //FIXME: add a hint arrow over the rat closest to you that is not in combat with somebody else. https://www.youtube.com/watch?v=FGQ2BZrJIug. The below should work but doesn't. + registerHintIcon(player, Repository.findNPC(NPCs.GIANT_RAT_86)!!) + sendStageDialog(player, stage) } - 52 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Sit back and watch", - "While you are fighting you will see a bar over your head. The", - "bar shows how much health you have left. Your opponent will", - "have one too. You will continue to attack the rat until it's dead", - "or you do something else." - ) - ) + //FIXME: add a hint arrow over the rat you're in combat with (also in the ranging part btw). https://www.youtube.com/watch?v=FGQ2BZrJIug + sendStageDialog(player, stage) } - 53 -> { hideTabs(player, login) removeHintIcon(player) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Well done, you've made your first kill!", - "Pass through the gate and talk to the Combat Instructor; he", - "will give you your next task.", - "", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) } - 54 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Rat ranging.", - "Now you have a bow and some arrows. Before you can use", - "them you'll need to equip them. Once equipped with the", - "ranging gear try killing another rat. Remember, to attack: right", - "click on the monster and select attack." - ) - ) + sendStageDialog(player, stage) } - 55 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3111,9526), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Moving on.", - "You have completed the tasks here, to move on click on the", - "ladder shown.", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 56 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3122,3124), 50) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Banking.", - "Follow the path and you will come to the front of a building.", - "This is the 'Bank of " + settings!!.name + "' where you can store all your", - "most valued items. To open your bank box just right click on an", - "open booth indicated and select use." - ) - ) + sendStageDialog(player, stage) } - 57 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3125, 3124), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your bank box.", - "You can store stuff here for safekeeping. If you die, anything", - "in your bank will be saved. To deposit something, right click it", - "and select 'store'. One you've had a good look, close the", - "window and move on through the door indicated." - ) - ) + sendStageDialog(player, stage) } - 58 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(947)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Financial advice.", - "The guide here will tell you about making cash. Just click on", - "him to hear what he's got to say.", - "", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.FINANCIAL_ADVISOR_947)!!) + sendStageDialog(player, stage) } - 59 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3130, 3124, 0), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Continue through the next door.", - "", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 60 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(954)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Prayer", - "Follow the path to the chapel and enter it.", - "Once inside talk to the monk. He'll tell you all about the prayer", - "skill.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 61 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.PRAYER_271)) - setVarbit(player, 1021, 0, 6) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your Prayer menu", - "Click on the flashing icon to open the Prayer menu.", - "", - "", - "" - ) - ) + setVarbit(player, 3756, 6) + sendStageDialog(player, stage) } - 62 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(954)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Your Prayer menu.", - "Talk with Brother Brace and he'll tell you all about prayer.", - "", - "" - ) - ) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 63 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.FRIENDS2_550)) - setVarbit(player, 1021, 0, 9) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "Friends list.", - "You should now see another new icon. Click on the flashing", - "smiling face to open your friend list.", - "" - ) - ) + setVarbit(player, 3756, 9) + sendStageDialog(player, stage) } - 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.", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 65 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) - registerHintIcon(player, Repository.findNPC(945)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "This is your ignore list.", - "The two lists, friends and ignore, can be very helpful for", - "keeping track of when your friends are online or for blocking", - "messages from people you simply don't like. Speak with", - "Brother Brace and he will tell you more." - ) - ) + setVarbit(player, 3756, 0) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) } - 66 -> { hideTabs(player, login) removeHintIcon(player) registerHintIcon(player, Location.create(3122,3102), 75) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your final instructor!", - "You're almost finished on tutorial island. Pass through the", - "door to find the path leading to your last instructor.", - "", - "" - ) - ) + sendStageDialog(player, stage) } - 67 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Repository.findNPC(946)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your final instructor!", - "Just follow the path to the wizards house, where you will be", - "shown how to cast spells. Just talk with the mage indicated to", - "find out more.", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } - 68 -> { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(player.spellBookManager.spellBook)) - setVarbit(player, 1021, 0, 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.", - "", - "" - ) - ) + setVarbit(player, 3756, 7) + sendStageDialog(player, stage) } - 69 -> { hideTabs(player, login) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "", - "This is your spell list.", - "", - "Ask the mage about it.", - "" - ) - ) + removeHintIcon(player) + setVarbit(player, 3756, 0) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } - 70 -> { hideTabs(player, login) - registerHintIcon(player, Repository.findNPC(41)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Cast Wind Strike at a chicken.", - "Now you have the runes you should see the Wind Strike icon", - "at the top-left of your spellbook, second from the left. Walk over to", - "the caged chickens, click the Wind Strike icon and then ", - "select one of the chickens to cast it on. It may take", - "several tries." - ) - ) + removeHintIcon(player) + //FIXME: as with the rats, the below should work, but doesn't + registerHintIcon(player, Repository.findNPC(NPCs.CHICKEN_41)!!) + sendStageDialog(player, stage) } - 71 -> { removeHintIcon(player) player.interfaceManager.restoreTabs() - registerHintIcon(player, Repository.findNPC(946)!!) - Component.setUnclosable( - player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "You have almost completed the tutorial!", - "All you need to do now is teleport to the mainland. Just speak with", - "Terrova and he'll tell you how to do that.", - "You have almost completed the tutorial!", - "" - ) - ) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) } } } @JvmStatic - public fun hideTabs(player: Player, login: Boolean) + fun hideTabs(player: Player, login: Boolean) { val stage = getAttribute(player, "tutorial:stage", 0) if(login && player.interfaceManager.tabs.isNotEmpty()) @@ -1194,10 +532,9 @@ object TutorialStage { player.interfaceManager.openTab(Component(Components.QUESTJOURNAL_V2_274)) if(stage > 45) player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) - if(stage > 49){ + if(stage > 46){ var wepInter = player.getExtension(WeaponInterface::class.java) - if(wepInter == null) - { + if (wepInter == null) { wepInter = WeaponInterface(player) player.addExtension(WeaponInterface::class.java, wepInter) } @@ -1219,7 +556,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 +578,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..c20c65f72 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. */ @@ -159,8 +178,8 @@ public final class CharacterDesign { case 40: player.getSettings().toggleMouseButton(); break; - case 92://hair style - case 93: + case 92://hair style left arrow + case 93://hair style right arrow changeLook(player, 0, buttonId == 93); break; case 97: @@ -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); } @@ -321,18 +348,23 @@ public final class CharacterDesign { private static int getValue(Player player, String key, int index, int currentIndex, boolean increment) { int[] array = player.getAttribute("male", player.getAppearance().isMale()) ? MALE_LOOK_IDS[index] : FEMALE_LOOK_IDS[index]; int val = 0; - if (increment && currentIndex + 1 > array.length -1) { - val = (int) array[0]; - currentIndex = 0; - } else if (!increment && currentIndex -1 < 0) { - val = (int) array[array.length -1]; - currentIndex = array.length -1; - } else if (increment) { - val = (int) array[currentIndex + 1]; - currentIndex++; + if (player.isArtificial() && index <= 1) { + currentIndex = RandomFunction.random(array.length -1); + val = array[currentIndex]; } else { - val = (int) array[currentIndex - 1]; - currentIndex--; + if (increment && currentIndex + 1 > array.length -1) { + val = (int) array[0]; + currentIndex = 0; + } else if (!increment && currentIndex -1 < 0) { + val = (int) array[array.length -1]; + currentIndex = array.length -1; + } else if (increment) { + val = (int) array[currentIndex + 1]; + currentIndex++; + } else { + val = (int) array[currentIndex - 1]; + currentIndex--; + } } player.setAttribute(key + ":" + index, currentIndex); return val; 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..0608d0fea 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,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 gift of peace dialogue plugin. @@ -12,11 +15,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 +48,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..1135cefd5 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,15 +50,14 @@ 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; } player.getSavedData().getGlobalData().getStrongHoldRewards()[1] = true; - interpreter.sendDialogue("...congratualtions adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Slap Head emote!"); + interpreter.sendDialogue("...congratulations adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Slap Head emote!"); stage = 1; - player.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..c6de2b356 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..3361fedce --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.kt @@ -0,0 +1,144 @@ +package content.region.misthalin.barbvillage.stronghold.playersafety + +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ProfessorHenryDialogue(player : Player? = null) : DialoguePlugin(player){ + + companion object{ + const val HAND_IN_TEST = 10 + const val MEETING = 100 + const val GET_TEST = 200 + const val iFace = 277 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage){ + START_DIALOGUE -> { + if (player.savedData.globalData.testStage == 2 && inInventory(player, Items.TEST_PAPER_12626)){ + // The player has the test and has completed it but need it marked + player("Hello, Professor.").also { stage = HAND_IN_TEST} + } + else if (player.savedData.globalData.testStage >= 3){ + // The player has already had their test marked and taken + npcl(FacialExpression.HAPPY, "Good job ${player.username}, you completed the test!").also { stage = END_DIALOGUE } + return true + } + else{ + player(FacialExpression.NEUTRAL, "Hello.").also { stage = MEETING } + } + } + MEETING -> npcl(FacialExpression.ANNOYED, "Hello what?").also { stage++ } + MEETING + 1 -> playerl(FacialExpression.HALF_GUILTY, "Uh...hello there?").also { stage++ } + MEETING + 2 -> npcl(FacialExpression.HALF_GUILTY, "Hello, 'Professor'. Manners cost nothing, you know. " + + "When you're in my classroom, I ask that you use the proper address for my station.").also { stage++ } + MEETING + 3 -> playerl(FacialExpression.HALF_GUILTY, "Your station?").also { stage++ } + MEETING + 4 -> npcl(FacialExpression.HALF_GUILTY, "Yes. It means 'position', so to speak.").also { stage++ } + MEETING + 5 -> playerl(FacialExpression.HALF_GUILTY, "Oh, okay.").also { stage++ } + MEETING + 6 -> npcl(FacialExpression.HALF_GUILTY, "Now, what can I do for you, exactly?").also { stage++ } + MEETING + 7 -> playerl(FacialExpression.HALF_GUILTY, "What is this place?").also { stage++ } + MEETING + 8 -> npcl(FacialExpression.HALF_GUILTY, "This is the Misthalin Training Centre of Excellence. " + + "It is where bold adventurers, such as yourself, can come to learn of the dangers of " + + "the wide world and gain some valuable experience at the same time.").also { stage++ } + MEETING + 9 -> playerl(FacialExpression.HALF_GUILTY, "What can I do here?").also { stage++ } + MEETING + 10 -> npcl(FacialExpression.HALF_GUILTY, "Here you can take part in the Player Safety test: " + + "a set of valuable lessons to learn about staying safe " + + "in ${ServerConstants.SERVER_NAME}.").also { stage++ } + MEETING + 11 -> npcl(FacialExpression.HALF_GUILTY, "I can give you a test paper to take and, once completed, " + + "you can bring it back to me for marking. Would you like to take the test? " + + "It will not cost you anything.").also { stage++ } + MEETING + 12 -> + showTopics( + Topic("Yes, please.", GET_TEST), + Topic("Not right now, thanks.", END_DIALOGUE) + ) + + GET_TEST -> { + if (player.inventory.isFull) { + npcl(FacialExpression.HALF_GUILTY, "It seems your inventory is full.").also { stage = END_DIALOGUE } + } else if (amountInInventory(player, Items.TEST_PAPER_12626) > 0) { + npcl( + FacialExpression.HALF_GUILTY, + "You already have a test, please fill it out and return it to me." + ).also { stage = END_DIALOGUE } + } else { + player.savedData.globalData.testStage = 1 + addItem(player, Items.TEST_PAPER_12626) + npcl( FacialExpression.HALF_GUILTY, "Right then. Here is the test paper. " + + "When you have completed all the questions, bring it back to me for marking." + ).also { stage ++ } + } + } + GET_TEST + 1 -> playerl(FacialExpression.HALF_GUILTY, "Okay, thanks.").also { stage = END_DIALOGUE } + + HAND_IN_TEST -> npcl(FacialExpression.HAPPY, + "Ah, ${player.username}. How's the test going?").also { stage ++ } + HAND_IN_TEST + 1 -> playerl(FacialExpression.NEUTRAL, "I think I've finished.").also { stage++ } + HAND_IN_TEST + 2 -> npcl(FacialExpression.HAPPY, "Excellent! Let me just mark the paper for you then.").also { stage++ } + HAND_IN_TEST + 3 -> npcl(FacialExpression.HAPPY, "Hmm. Uh-huh, yes I see. Good! Yes, that's right.").also{ stage++ } + HAND_IN_TEST + 4-> npcl(FacialExpression.HAPPY, "Excellent! Allow me to reward you for your work. " + + "I have these two old lamps that you may find useful.").also { stage++ } + // This needs to be npc for word wrap + HAND_IN_TEST + 5 -> npc("Also, there is an old jail block connected to the cells", + "below the training centre, which have been overrun with", + "vicious creatures. If you search around the jail cells", + "downstairs, you should find it easily enough.").also { stage++ } + HAND_IN_TEST + 6 -> npcl(FacialExpression.HAPPY, "Now, your rewards.").also { stage++ } + HAND_IN_TEST + 7 ->{ + // Check for at least 1 slot + // Player will get 2 lamps but we will take the test from them + if (freeSlots(player) >= 1){ + showReward().also { stage = END_DIALOGUE } + } + else { + npcl(FacialExpression.SAD, "You do not have space in your inventory for the rewards").also { stage = END_DIALOGUE } + } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PROFESSOR_HENRY_7143) + } + + private fun showReward(){ + // Show the poster tunnel + setVarp(player, 1203, 1 shl 29, true) + player.savedData.globalData.testStage = 3 + + if (removeItem(player, Items.TEST_PAPER_12626)) { + addItem(player, Items.ANTIQUE_LAMP_4447, 2) + } + player.emoteManager.unlock(Emotes.SAFETY_FIRST) + + openInterface(player, iFace) + + // Clear the other lines + for (i in 9.. 18){ + player.packetDispatch.sendString("", iFace, i) + } + + player.packetDispatch.sendString("You have completed the Player Safety test!", iFace, 4) + player.packetDispatch.sendString(player.getQuestRepository().points.toString() + "", iFace, 7) + + player.packetDispatch.sendString("2 Experience lamps", iFace, 9) + player.packetDispatch.sendString("Access to the Stronghold of", iFace, 10) + player.packetDispatch.sendString("Player Safety Dungeon", iFace, 11) + player.packetDispatch.sendString("The Safety First' emote", iFace, 12) + + sendItemZoomOnInterface(player, iFace, 5, Items.TEST_PAPER_12626) + + } +} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java deleted file mode 100644 index 940125f7c..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/SafetyMapZone.java +++ /dev/null @@ -1,15 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -import core.game.world.map.zone.MapZone; - -public class SafetyMapZone extends MapZone { - - public SafetyMapZone() { - super("strong hold of player safety", true); - } - - @Override - public void configure() { - - } -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyListener.kt b/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/StrongHoldOfPlayerSafetyListener.kt new file mode 100644 index 000000000..68fd59034 --- /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 = "/save: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..e8310e992 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ChemicalsBook.kt @@ -0,0 +1,80 @@ +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.api.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..a79a331bd --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt @@ -0,0 +1,945 @@ +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 -> + if (removeItem(player, node)) { + impact(player, 25) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false + } + + on(Items.NITROGLYCERIN_703, ITEM, "drop") { player, node -> + if (removeItem(player, node)) { + impact(player, 35) + sendChat(player, "Ow! The nitroglycerin exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false + } + + on(Items.MIXED_CHEMICALS_705, ITEM, "drop") { player, node -> + if (removeItem(player, node)) { + impact(player, 45) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false + } + + on(Items.MIXED_CHEMICALS_706, ITEM, "drop") { player, node -> + if (removeItem(player, node)) { + impact(player, 55) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false + } + + on(Items.CHEMICAL_COMPOUND_707, ITEM, "drop") { player, node -> + if (removeItem(player, node)) { + impact(player, 65) + sendChat(player, "Ow! The liquid exploded!") + sendMessage(player, "You were injured by the burning liquid.") + return@on true + } + return@on false + } + + // Scenery not tied to quest + + 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/CaveGoblinMinerNPC.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt index 6813ad0b9..4d6feec03 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt @@ -29,7 +29,7 @@ class CaveGoblinMinerNPC(id: Int = 0, location: Location? = null) : AbstractNPC( mining = (id > 2074) if(properties.combatPulse.isAttacking && mining){ - transform(id - 6) + transformWithHpCarryover(id - 6) this.isWalks = true this.walkRadius = 4 this.isNeverWalks = false 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..de266e188 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 @@ -12,6 +13,7 @@ import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Items +import core.api.* private val BOOK = Item(Items.GOBLIN_SYMBOL_BOOK_5009) @Initializable @@ -37,9 +39,10 @@ class LostTribeOptionHandler : OptionHandler(){ node ?: return false when(node.id){ 5008 -> player.interfaceManager.open(Component(50)) - 5009 -> player.interfaceManager.open(Component(183)) + 5009 -> player.interfaceManager.open(Component(183)).also { storeBookInHouse(player, node) } 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 +50,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/DraynorManorChairNPC.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt new file mode 100644 index 000000000..423e57dec --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt @@ -0,0 +1,141 @@ +package content.region.misthalin.draynor.handlers + +import core.api.hasLineOfSight +import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.impl.PulseType +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.path.Pathfinder +import core.plugin.Initializable + +/** + * Represents the Draynor Manor Chair NPC, these are the chairs that follow the player + * around the manor. + * + * @author Broseki + */ + +private const val DRAYNOR_MANOR_CHAIR_NPC_ID = 3293 +private const val FOLLOWING_DISTANCE = 5 +private const val STOP_FOLLOWING_DISTANCE = 30 + +@Initializable +class DraynorManorChairNPC(id: Int = DRAYNOR_MANOR_CHAIR_NPC_ID, location: Location? = null) : + AbstractNPC(id, location) { + + // The player this NPC is currently following, null if nobody is being followed + private var following: Player? = null + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return DraynorManorChairNPC(id, location) + } + + override fun handleTickActions() { + super.handleTickActions() + val closestPlayer = findClosestPlayer() + // If there is a player nearby, follow them + if (closestPlayer != null && following != closestPlayer) { + stopFollowing() + following = closestPlayer + follow(closestPlayer) + following?.let { face(it) } + } else { + // If we didn't find a player within `FOLLOWING_DISTANCE` + // but we are following a player, check that they are still + // within `STOP_FOLLOWING_DISTANCE`, if they are gone, stop + // trying to follow them. + following?.let { player -> + if (findDistanceToPlayer(player) > STOP_FOLLOWING_DISTANCE + || !player.isActive + || player.isInvisible) { + stopFollowing() + } else { + if (!pulseManager.hasPulseRunning()) { + follow(player) + } + face(player) + } + } + } + } + + /** + * Stops following `following` + */ + fun stopFollowing() { + following = null + resetWalk() + pulseManager.clear(PulseType.STANDARD) + } + + /** + * Finds the closest player to the current entity within `FOLLOWING_DISTANCE` + * that is currently in the chair's line of sight. + * + * @return The Player object representing the closest player, or null if there are no players nearby. + */ + fun findClosestPlayer(): Player? { + val players = RegionManager.getLocalPlayers(this, FOLLOWING_DISTANCE) + if (players.isEmpty()) { + return null + } + + var closestPlayer: Player? = null + var closestDistance = Double.MAX_VALUE + + for (player in players) { + // Make sure the chair does not try to start + // following a player in another room + if (!hasLineOfSight(this, player)) { + continue + } + + // If the player is invisible, don't follow them + if (player.isInvisible) { + continue + } + + val distance = findDistanceToPlayer(player) + if (distance < closestDistance) { + closestDistance = distance + closestPlayer = player + } + } + + return closestPlayer + } + + /** + * Calculates the distance between the current entity and the specified player. + * + * @param player The player whose distance from the current entity is to be calculated. + * @return The distance between the current entity and the specified player as a Double value. + */ + fun findDistanceToPlayer(player: Player): Double { + return this.location.getDistance(player.location) + } + + /** + * Triggers the current entity to follow the specified player using a movement pulse. + * + * @param player The player that the entity should follow. + */ + fun follow(player: Player) { + pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) { + override fun pulse(): Boolean { + return false + } + }), PulseType.STANDARD) + } + + override fun getIds(): IntArray { + return intArrayOf(DRAYNOR_MANOR_CHAIR_NPC_ID) + } + + override fun shouldPreventStacking(mover: Entity?): Boolean { + return mover is DraynorManorChairNPC + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt new file mode 100644 index 000000000..529b497a6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt @@ -0,0 +1,57 @@ +package content.region.misthalin.draynor.handlers + +import core.game.node.entity.Entity +import core.game.world.map.Location +import core.game.world.map.RegionManager.getLocalNpcs +import core.game.world.map.path.Pathfinder +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneBuilder +import core.plugin.Initializable +import core.plugin.Plugin + +/** + * Represents the interior of Draynor Manor. + * + * @author Broseki + */ +@Initializable +class DraynorManorHouseZone : MapZone("Draynor Manor House", true), Plugin { + + override fun newInstance(arg: Any?): Plugin { + ZoneBuilder.configure(this) + return this + } + + override fun move(e: Entity, loc: Location?, destination: Location): Boolean { + for (n in getLocalNpcs(e, 5)) { + if (n.isInvisible() || n === e) { + continue + } + if (n.shouldPreventStacking(e)) { + val s1 = e.size() + val s2 = n.size() + val x = destination.x + val y = destination.y + val l = n.getLocation() + if (Pathfinder.isStandingIn(x, y, s1, s1, l.x, l.y, s2, s2)) { + return false + } + } + } + return true + } + + override fun enter(entity: Entity?): Boolean { + return super.enter(entity) + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any? { + return null + } + + override fun configure() { + register(ZoneBorders(3097, 3373, 3119, 3364)) + register(ZoneBorders(3090, 3363, 3126, 3354)) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/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 deleted file mode 100644 index c39e162aa..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java +++ /dev/null @@ -1,569 +0,0 @@ -package content.region.misthalin.lumbridge.dialogue; - -import core.cache.def.impl.ItemDefinition; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import content.data.RepairItem; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import content.global.handlers.item.equipment.BarrowsEquipmentRegister; - -/** - * Represents the dialogue plugin used for the bob npc who repairs items. - * @author 'Vexia - * @version 1.0 - */ -public final class BobDialogue extends DialoguePlugin { - - /** - * Represents the item id being repaired. - */ - private int itemId = 0; - - /** - * Represents the item being repaired. - */ - private Item item; - - /** - * Represents the item repairing. - */ - private static RepairItem repairitem = null; - - /** - * The achievement diary. - */ - private final int level = 1; - - /** - * Constructs a new {@code BobDialogue} {@code Object}. - */ - public BobDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BobDialogue} {@code Object}. - * @param player the player. - */ - public BobDialogue(Player player) { - super(player); - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 754: - options("Yes, please.", "No, thank you."); - stage = 755; - break; - case 755: - switch (buttonId) { - case 1: - player("Yes, please."); - stage = 757; - break; - case 2: - player("No, thank you."); - stage = 756; - break; - } - break; - case 756: - end(); - break; - case 757: - end(); - if (repairitem != null) { - if (!player.getInventory().contains(995, repairitem.getCost())) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough to pay him."); - break; - } - if (!player.getInventory().contains(itemId, 1)) { - end(); - return true; - } - player.getInventory().remove(new Item(995, repairitem.getCost())); - if (player.getInventory().remove(new Item(itemId, 1))) { - player.getInventory().add(repairitem.getProduct()); - } - String cost = "free"; - if (repairitem.getCost() != 0) { - cost = repairitem.getCost() + "gp"; - } - player.getPacketDispatch().sendMessage("Bob fixes your " + ItemDefinition.forId(itemId).getName().toLowerCase().replace("broken", "").trim() + " for " + cost + "."); - } - if (repairitem == null) { - String cost = "free"; - String type = BarrowsEquipment.formatedName(itemId); - String single = BarrowsEquipment.getSingleName(type); - String equipment = BarrowsEquipment.getEquipmentType(type); - String newString = type.toLowerCase().replace(single, "").trim().replace("'s", ""); - StringBuilder newewString = new StringBuilder(); - newewString.append(newString).append(" " + equipment); - final BarrowsEquipment.BarrowsFullEquipment fullequip = BarrowsEquipment.BarrowsFullEquipment.forName(newewString.toString()); - if (BarrowsEquipment.getFormatedCost(equipment, item) != 0) { - cost = String.valueOf(BarrowsEquipment.getFormatedCost(equipment, item) + "gp"); - } - if (!player.getInventory().contains(995, BarrowsEquipment.getFormatedCost(equipment, item))) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough to pay him."); - break; - } - if (fullequip == null || fullequip.getFull() == null) { - player.getPacketDispatch().sendMessage("Report this to an administrator!"); - return true; - } - if (!player.getInventory().contains(itemId, 1)) { - end(); - return true; - } - player.getInventory().remove(new Item(995, BarrowsEquipment.getFormatedCost(equipment, item))); - if (player.getInventory().remove(new Item(itemId, 1))) { - player.getInventory().add(fullequip.getFull()); - } - player.getPacketDispatch().sendMessage("Bob fixes your " + equipment + " for " + cost + "."); - } - break; - case 678: - end(); - break; - case 0: - switch (buttonId) { - case 1: - player("Give me a quest!"); - stage = -5; - break; - case 2: - player("Have you anything to sell?"); - stage = 10; - break; - case 3: - player("Can you repair my items for me?"); - stage = 20; - break; - case 4: - player("I'd like to talk about Achievement Diaries."); - stage = 30; - break; - } - break; - case -5: - interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Get yer own!"); - stage = -4; - break; - case -4: - end(); - break; - case 10: - npc("Yes! I buy and sell axes! Take your pick (or axe)!"); - stage = 11; - break; - case 11: - end(); - npc.openShop(player); - break; - case 20: - npc("Of course I'll repair it, though the materials may cost", "you. Just hand me the item and I'll have a look."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { - player("I've done all the medium tasks in my Lumbridge", "Achievement Diary."); - stage = 150; - break; - } - if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { - player("I've seemed to have lost my explorer's ring..."); - stage = 160; - break; - } - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage = 31; - break; - case 31: - switch (buttonId) { - case 1: - player("What is the Achievement Diary?"); - stage = 110; - break; - case 2: - player("What are the rewards?"); - stage = 120; - break; - case 3: - player("How do I claim the rewards?"); - stage = 130; - break; - case 4: - player("See you later!"); - stage = 140; - break; - } - break; - case 110: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Keldagrim. In Lumbridge and Draynor i can help you", "discover some very useful things indeed."); - stage++; - break; - case 111: - npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts."); - stage++; - break; - case 112: - npc("You can access your Achievement Diary by going to", "the Quest Journal. When you've opened the Quest", "Journal click on the green star icon on the top right", "hand corner. This will open the diary."); - stage = 30; - break; - case 120: - npc("Ah, well there are different rewards for each", "Achievement Diary. For completing the Lumbridge and", "Draynor diary you are presented with an explorer's", "ring."); - stage++; - break; - case 121: - npc("This ring will become increasingly useful with each", "section of the diary that you complete."); - stage = 30; - break; - case 130: - npc("You need to complete the tasks so that they're all ticked", "off, then you can claim your reward. Most of them are", "straightforward although you might find some required", "quests to be started, if not finished."); - stage++; - break; - case 131: - npc("To claim the explorer's ring speak to Explorer Jack", " in Lumbridge, Ned in Draynor Village or myself."); - stage = 30; - break; - case 140: - end(); - break; - case 150: - npc("Yes I see that, you'll be wanting your", "reward then I assume?"); - stage++; - break; - case 151: - player("Yes please."); - stage++; - break; - case 152: - AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level); - npc("This ring is a representation of the adventures you", "went on to complete your tasks."); - stage ++; - break; - case 153: - player("Wow, thanks!"); - stage = 30; - break; - case 160: - AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level); - npc("You better be more careful this time."); - stage = -1; - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BobDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - boolean repair = false; - boolean wrong = false; - if (npc.getId() == 3797 && args.length == 1) { - player("Can you repair my items for me?"); - stage = 20; - return true; - } - if (args.length == 1) { - options("Give me a quest!", "Have you anything to sell?", "Can you repair my items for me?", "Talk about Achievement Diaries"); - stage = 0; - return true; - } - if (args[1] != null) { - repair = (boolean) args[1]; - } - if (args[2] != null) { - wrong = (boolean) args[2]; - } - if (args[3] != null) { - repairitem = RepairItem.forId((int) args[3]); - itemId = (int) args[3]; - } - if (args[4] != null) { - item = (Item) args[4]; - } - if (repair && !wrong) { - String cost = "free"; - if (repairitem != null) { - if (repairitem.getCost() != 0) { - cost = String.valueOf(repairitem.getCost() + "gp"); - } - } else { - String type = BarrowsEquipment.formatedName(itemId); - String single = BarrowsEquipment.getSingleName(type); - String equipment = BarrowsEquipment.getEquipmentType(type); - String newString = type.toLowerCase().replace(single, "").trim().replace("'s", ""); - StringBuilder newewString = new StringBuilder(); - newewString.append(newString).append(" " + equipment); - if (BarrowsEquipment.getFormatedCost(equipment, item) != 0) { - cost = String.valueOf(BarrowsEquipment.getFormatedCost(equipment, item) + "gp"); - } - } - npc("Quite badly damaged, but easy to repair. Would you", "like me to repair it for " + cost + "?"); - stage = 754; - return true; - } - if (repair == true && wrong == true) { - npc("Sorry friend, but I can't do anything with that."); - stage = 678; - return true; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 519, 3797 }; - } - - /** - * Barrows equipment information. - * @author 'Vexia - */ - public static class BarrowsEquipment { - - /** - * Represents the base names. - */ - private String[] base = new String[] { "dharok", "verac", "ahrim", "torag", "guthan" }; - - /** - * The weapon names. - */ - private static final String[] weapon_names = new String[] { "flail", "greataxe", "spear", "x-bow", "hammer", "hammers", "staff" }; - - /** - * The weapon body names. - */ - private static final String[] body_names = new String[] { "top", "platebody", "body" }; - - /** - * The helm names. - */ - private static final String[] helm_names = new String[] { "hood", "helm", "coif" }; - - /** - * The leg names. - */ - private static final String[] leg_names = new String[] { "skirt", "legs", "plateskirt", "platelegs" }; - - /** - * The prices. - */ - private static final Object[][] prices = new Object[][] { { "weapon", 100 }, { "body", 90 }, { "legs", 80 }, { "helm", 60 } }; - - /** - * Represents the items. - */ - private static final Object[][] ITEMS = { { 4856, "Ahrim's hood" }, { 4857, "Ahrim's hood" }, { 4858, "Ahrim's hood" }, { 4859, "Ahrim's hood" }, { 4860, "Ahrim's hood" }, { 4862, "Ahrim's staff" }, { 4863, "Ahrim's staff" }, { 4864, "Ahrim's staff" }, { 4865, "Ahrim's staff" }, { 4866, "Ahrim's staff" }, { 4868, "Ahrim's top" }, { 4869, "Ahrim's top" }, { 4870, "Ahrim's top" }, { 4871, "Ahrim's top" }, { 4872, "Ahrim's top" }, { 4874, "Ahrim's skirt" }, { 4875, "Ahrim's skirt" }, { 4876, "Ahrim's skirt" }, { 4877, "Ahrim's skirt" }, { 4878, "Ahrim's skirt" }, { 4880, "Dharok's helm" }, { 4881, "Dharok's helm" }, { 4882, "Dharok's helm" }, { 4883, "Dharok's helm" }, { 4884, "Dharok's helm" }, { 4886, "Dharok's greataxe" }, { 4887, "Dharok's greataxe" }, { 4888, "Dharok's greataxe" }, { 4889, "Dharok's greataxe" }, { 4890, "Dharok's greataxe" }, { 4892, "Dharok's platebody" }, { 4893, "Dharok's platebody" }, { 4894, "Dharok's platebody" }, { 4895, "Dharok's platebody" }, { 4896, "Dharok's platebody" }, { 4898, "Dharok's platelegs" }, { 4899, "Dharok's platelegs" }, { 4900, "Dharok's platelegs" }, { 4901, "Dharok's platelegs" }, { 4902, "Dharok's platelegs" }, { 4904, "Guthan's helm" }, { 4905, "Guthan's helm" }, { 4906, "Guthan's helm" }, { 4907, "Guthan's helm" }, { 4908, "Guthan's helm" }, { 4910, "Guthan's spear" }, { 4911, "Guthan's spear" }, { 4912, "Guthan's spear" }, { 4913, "Guthan's spear" }, { 4914, "Guthan's spear" }, { 4916, "Guthan's body" }, { 4917, "Guthan's body" }, { 4918, "Guthan's body" }, { 4919, "Guthan's body" }, { 4920, "Guthan's body" }, { 4922, "Guthan's skirt" }, { 4923, "Guthan's skirt" }, { 4924, "Guthan's skirt" }, { 4925, "Guthan's skirt" }, { 4926, "Guthan's skirt" }, { 4928, "Karil's coif" }, { 4929, "Karil's coif" }, { 4930, "Karil's coif" }, { 4931, "Karil's coif" }, { 4932, "Karil's coif" }, { 4934, "Karil's x-bow" }, { 4935, "Karil's x-bow" }, { 4936, "Karil's x-bow" }, { 4937, "Karil's x-bow" }, { 4938, "Karil's x-bow" }, { 4940, "Karil's top" }, { 4941, "Karil's top" }, { 4942, "Karil's top" }, { 4943, "Karil's top" }, { 4944, "Karil's top" }, { 4946, "Karil's skirt" }, { 4947, "Karil's skirt" }, { 4948, "Karil's skirt" }, { 4949, "Karil's skirt" }, { 4950, "Karil's skirt" }, { 4952, "Torag's helm" }, { 4953, "Torag's helm" }, { 4954, "Torag's helm" }, { 4955, "Torag's helm" }, { 4956, "Torag's helm" }, { 4958, "Torag's hammers" }, { 4959, "Torag's hammers" }, { 4960, "Torag's hammers" }, { 4961, "Torag's hammers" }, { 4962, "Torag's hammers" }, { 4964, "Torag's body" }, { 4965, "Torag's body" }, { 4966, "Torag's body" }, { 4967, "Torag's body" }, { 4968, "Torag's body" }, { 4970, "Torag's legs" }, { 4971, "Torag's legs" }, { 4972, "Torag's legs" }, { 4973, "Torag's legs" }, { 4974, "Torag's legs" }, { 4976, "Verac's helm" }, { 4977, "Verac's helm" }, { 4978, "Verac's helm" }, { 4979, "Verac's helm" }, { 4980, "Verac's helm" }, { 4982, "Verac's flail" }, { 4983, "Verac's flail" }, { 4984, "Verac's flail" }, { 4985, "Verac's flail" }, { 4986, "Verac's flail" }, { 4988, "Verac's top" }, { 4989, "Verac's top" }, { 4990, "Verac's top" }, { 4991, "Verac's top" }, { 4992, "Verac's top" }, { 4994, "Verac's skirt" }, { 4995, "Verac's skirt" }, { 4996, "Verac's skirt" }, { 4997, "Verac's skirt" }, { 4998, "Verac's skirt" } }; - - /** - * Gets the cost. - * @param name the name. - * @return the price. - */ - public static int getFormatedCost(String name, Item item) { - int ticks = BarrowsEquipmentRegister.TICKS; - int[] degrades = new int[] { 100, 75, 50, 25, 0 }; - for (int i = 0; i < prices.length; i++) { - String check = (String) prices[i][0]; - if (check.equals(name)) { - int degrade = 0; - for (int d : degrades) { - if (item.getName().contains(String.valueOf(d))) { - degrade = d; - break; - } - } - degrade -= 25 - (25 * ((double)item.getCharge() / (double)ticks)); - int max = (int) prices[i][1] * 1000; - return (int) (max - (max * (degrade * 0.01))); - } - } - return 0; - } - - /** - * Gets the cost of the item type. - * @param name the name. - * @return the return type. - */ - public static int getCost(String name) { - for (int i = 0; i < prices.length; i++) { - String check = (String) prices[i][0]; - if (check.equals(name)) { - return (int) prices[i][1]; - } - } - return 0; - } - - /** - * Represents if an item is a barrows item. - * @param id the id. - * @return {@code True} if so. - */ - public static boolean isBarrowsItem(int id) { - for (int i = 0; i < ITEMS.length; i++) { - if ((int) ITEMS[i][0] == id) { - return true; - } - } - return false; - } - - /** - * Gets the formatted name. - * @param id the id. - * @return the name. - */ - public static String formatedName(int id) { - for (int i = 0; i < ITEMS.length; i++) { - if ((int) ITEMS[i][0] == id) { - return (String) ITEMS[i][1]; - } - } - return null; - } - - /** - * Gets the equipment type. - * @param name the name. - * @return the type. - */ - public static String getEquipmentType(String name) { - name = name.toLowerCase().replace("verac's", "").replace("karil's", "").replace("dharok's", "").replace("torag's", "").replace("guthan's", "").replace("ahrim's", "").trim(); - for (int i = 0; i < weapon_names.length; i++) { - if (weapon_names[i].contains(name)) { - return "weapon"; - } - } - for (int k = 0; k < body_names.length; k++) { - if (body_names[k].contains(name)) { - return "body"; - } - } - for (int z = 0; z < leg_names.length; z++) { - if (leg_names[z].contains(name)) { - return "legs"; - } - } - for (int q = 0; q < helm_names.length; q++) { - if (helm_names[q].contains(name)) { - return "helm"; - } - } - return null; - } - - /** - * Method used t get its single name. - * @param name the name. - * @return the name. - */ - public static String getSingleName(String name) { - name = name.toLowerCase().replace("verac's", "").replace("karil's", "").replace("dharok's", "").replace("torag's", "").replace("guthan's", "").replace("ahrim's", "").trim(); - for (int i = 0; i < weapon_names.length; i++) { - if (weapon_names[i].contains(name)) { - return weapon_names[i]; - } - } - for (int k = 0; k < body_names.length; k++) { - if (body_names[k].contains(name)) { - return body_names[k]; - } - } - for (int z = 0; z < leg_names.length; z++) { - if (leg_names[z].contains(name)) { - return leg_names[z]; - } - } - for (int q = 0; q < helm_names.length; q++) { - if (helm_names[q].contains(name)) { - return helm_names[q]; - } - } - return null; - } - - /** - * Gets the bases. - * @return the base. - */ - public String[] getBase() { - return base; - } - - /** - * Represents the multiple full barrows equipment items. - * @author 'Vexia - * @version 1.0 - */ - public enum BarrowsFullEquipment { - VERAC_LEGS(new Item(4759, 1)), VERAC_TOP(new Item(4757, 1)), VERAC_WEAPON(new Item(4755, 1)), VERAC_HELM(new Item(4753, 1)), TORAG_LEGS(new Item(4751, 1)), TORAG_BODY(new Item(4749, 1)), TORAG_HELM(new Item(4745, 1)), TORAG_WEAPON(new Item(4747, 1)), KARIL_HELM(new Item(4732, 1)), KARIL_WEAPON(new Item(4734, 1)), KARIL_BODY(new Item(4736, 1)), KARIL_LEGS(new Item(4738, 1)), GUTHAN_HELM(new Item(4724, 1)), GUTHAN_BODY(new Item(4728, 1)), GUTHAN_LEGS(new Item(4730, 1)), GUTHAN_WEAPON(new Item(4726, 1)), DHAROK_HELM(new Item(4716, 1)), DHAROK_BODY(new Item(4720, 1)), DHAROK_LEGS(new Item(4722, 1)), DHAROK_WEAPON(new Item(4718, 1)), AHRIM_HELM(new Item(4708, 1)), AHRIM_BODY(new Item(4712, 1)), AHRIM_LEGS(new Item(4714, 1)), AHRIM_WEAPON(new Item(4710, 1)); - - /** - * Constructs a new {@code BarrowsEquipment} {@code Object}. - * @param full the full item. - */ - BarrowsFullEquipment(Item full) { - this.full = full; - } - - /** - * Represents the full item. - */ - private final Item full; - - /** - * for name - * @param name thename. - * @return the equipment. - */ - public static BarrowsFullEquipment forName(String name) { - if (name.equals("guthan body body")) { - name = "guthan body"; - } else if (name.equals("torag body body")) { - name = "torag body"; - } else if (name.equals("verac body")) { - name = "verac top"; - } - for (BarrowsFullEquipment barrow : BarrowsFullEquipment.values()) { - if (barrow.name().toLowerCase().replace("_", " ").trim().equalsIgnoreCase(name)) { - return barrow; - } - } - return null; - } - - /** - * Gets the full. - * @return The full. - */ - public Item getFull() { - return full; - } - } - - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt new file mode 100644 index 000000000..c759ef0b2 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt @@ -0,0 +1,266 @@ +package content.region.misthalin.lumbridge.dialogue + +import content.data.RepairItem +import content.global.handlers.item.equipment.BarrowsEquipment +import core.cache.def.impl.ItemDefinition +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import core.plugin.Initializable + +/** + * Represents the dialogue plugin used for the bob npc who repairs items. + * @author 'Vexia + * @author Damighty - Kotlin conversion + * @version 1.0 + */ +@Initializable +class BobDialogue(player: Player? = null) : DialoguePlugin(player) { + + /** + * Represents the item id being repaired. + */ + private var itemId = 0 + + /** + * Represents the item being repaired. + */ + private lateinit var item: Item + + /** + * Represents the item repairing. + */ + private var repairItem: RepairItem? = null + + /** + * The achievement diary. + */ + private val level = 1 + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 754 -> { + options("Yes, please.", "No, thank you.") + stage = 755 + } + 755 -> when (buttonId) { + 1 -> { + player("Yes, please.") + stage = 757 + } + 2 -> { + player("No, thank you.") + stage = 756 + } + } + 756 -> end() + 757 -> { + end() + val repairItemDef = RepairItem.forId(itemId) + + if (repairItemDef != null) { + // Standard repairable items + if (!player.inventory.contains(995, repairItemDef.cost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairItemDef.cost)) + player.inventory.add(repairItemDef.product) + val costText = if (repairItemDef.cost > 0) "${repairItemDef.cost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${item.name.lowercase().replace("broken", "").trim()} for $costText.") + } + } else { + // Barrows items + val barrowsDef = BarrowsEquipment.getDefinition(itemId) ?: return true + val repairCost = BarrowsEquipment.getRepairCost(item) + + if (!player.inventory.contains(995, repairCost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairCost)) + player.inventory.add(Item(barrowsDef.repairedId)) + val costText = if (repairCost > 0) "${repairCost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${barrowsDef.itemName.lowercase()} for $costText.") + } + } + return true + } + 678 -> end() + 0 -> when (buttonId) { + 1 -> { + player("Give me a quest!") + stage = -5 + } + 2 -> { + player("Have you anything to sell?") + stage = 10 + } + 3 -> { + player("Can you repair my items for me?") + stage = 20 + } + 4 -> { + player("I'd like to talk about Achievement Diaries.") + stage = 30 + } + } + -5 -> { + interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Get yer own!") + stage = -4 + } + -4 -> end() + 10 -> { + npc("Yes! I buy and sell axes! Take your pick (or axe)!") + stage = 11 + } + 11 -> { + end() + npc.openShop(player) + } + 20 -> { + npc("Of course I'll repair it, though the materials may cost", "you. Just hand me the item and I'll have a look.") + stage = 21 + } + 21 -> end() + 30 -> { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { + player("I've done all the medium tasks in my Lumbridge", "Achievement Diary.") + stage = 150 + } else if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { + player("I've seemed to have lost my explorer's ring...") + stage = 160 + } else { + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later.") + stage = 31 + } + } + 31 -> when (buttonId) { + 1 -> { + player("What is the Achievement Diary?") + stage = 110 + } + 2 -> { + player("What are the rewards?") + stage = 120 + } + 3 -> { + player("How do I claim the rewards?") + stage = 130 + } + 4 -> { + player("See you later!") + stage = 140 + } + } + 110 -> { + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Gielinor. In Lumbridge and Draynor I can help you", "discover some very useful things indeed.") + stage++ + } + 111 -> { + npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts.") + stage++ + } + 112 -> { + npc("You can access your Achievement Diary by going to", "the Quest Journal. When you've opened the Quest", "Journal click on the green star icon on the top right", "hand corner. This will open the diary.") + stage = 30 + } + 120 -> { + npc("Ah, well there are different rewards for each", "Achievement Diary. For completing the Lumbridge and", "Draynor diary you are presented with an explorer's", "ring.") + stage++ + } + 121 -> { + npc("This ring will become increasingly useful with each", "section of the diary that you complete.") + stage = 30 + } + 130 -> { + npc("You need to complete the tasks so that they're all ticked", "off, then you can claim your reward. Most of them are", "straightforward although you might find some required", "quests to be started, if not finished.") + stage++ + } + 131 -> { + npc("To claim the explorer's ring speak to Explorer Jack", " in Lumbridge, Ned in Draynor Village or myself.") + stage = 30 + } + 140 -> end() + 150 -> { + npc("Yes I see that, you'll be wanting your", "reward then I assume?") + stage++ + } + 151 -> { + player("Yes please.") + stage++ + } + 152 -> { + AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level) + npc("This ring is a representation of the adventures you", "went on to complete your tasks.") + stage++ + } + 153 -> { + player("Wow, thanks!") + stage = 30 + } + 160 -> { + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level) + npc("You better be more careful this time.") + stage = -1 + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return BobDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + var repair = false + var wrong = false + + if (npc.id == 3797 && args.size == 1) { + player("Can you repair my items for me?") + stage = 20 + return true + } + + if (args.size == 1) { + options("Give me a quest!", "Have you anything to sell?", "Can you repair my items for me?", "Talk about Achievement Diaries") + stage = 0 + return true + } + + if (args.size > 1) repair = args[1] as Boolean + if (args.size > 2) wrong = args[2] as Boolean + if (args.size > 3) { + repairItem = RepairItem.forId(args[3] as Int) + itemId = args[3] as Int + } + if (args.size > 4) item = args[4] as Item + + if (repair && !wrong) { + val cost = RepairItem.forId(itemId)?.cost ?: BarrowsEquipment.getRepairCost(item) + val costText = if (cost > 0) "${cost}gp" else "free" + npc("Quite badly damaged, but easy to repair. Would you", "like me to repair it for $costText?") + stage = 754 + return true + } + + if (repair && wrong) { + npc("Sorry friend, but I can't do anything with that.") + stage = 678 + return true + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(519, 3797) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/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..965dcb157 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,13 @@ 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 +import core.game.node.entity.player.link.SpellBookManager class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { @@ -309,6 +312,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 +348,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) { @@ -391,14 +402,12 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onSpellCast(player: Player, event: SpellCastEvent) { - when (event.spellId) { - Modern.LUMBRIDGE_TELEPORT -> { - finishTask( - player, - DiaryLevel.MEDIUM, - MediumTasks.CAST_LUMBRIDGE_TELEPORT - ) - } + if (event.spellBook == SpellBookManager.SpellBook.MODERN && event.spellId == Modern.LUMBRIDGE_TELEPORT) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CAST_LUMBRIDGE_TELEPORT + ) } } @@ -413,4 +422,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/BobRepairItem.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.java deleted file mode 100644 index 12025ef46..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.misthalin.lumbridge.handlers; - -import content.region.misthalin.lumbridge.dialogue.BobDialogue; -import content.data.RepairItem; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; -import core.plugin.Initializable; -import core.plugin.ClassScanner; - -/** - * Represents the plugin used to handle an item being used on bob. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BobRepairItem extends UseWithHandler { - - /** - * Constructs a new {@code BobRepairItem} {@code Object}. - */ - public BobRepairItem() { - super(494, 468, 496, 470, 498, 472, 500, 502, 474, 504, 476, 506, 478, 6741, 4856, 4857, 4858, 4859, 4860, 4862, 4863, 4864, 4865, 4866, 4868, 4869, 4870, 4871, 4872, 4874, 4875, 4876, 4877, 4878, 4880, 4881, 4882, 4883, 4884, 4886, 4887, 4888, 4889, 4890, 4892, 4893, 4894, 4895, 4896, 4898, 4899, 4900, 4901, 4902, 4904, 4905, 4906, 4907, 4908, 4910, 4911, 4912, 4913, 4914, 4916, 4917, 4918, 4919, 4920, 4922, 4923, 4924, 4925, 4926, 4928, 4929, 4930, 4931, 4932, 4934, 4935, 4936, 4937, 4938, 4940, 4941, 4942, 4943, 4944, 4946, 4947, 4948, 4949, 4950, 4952, 4953, 4954, 4955, 4956, 4958, 4959, 4960, 4961, 4962, 4964, 4965, 4966, 4967, 4968, 4970, 4971, 4972, 4973, 4974, 4976, 4977, 4978, 4979, 4980, 4982, 4983, 4984, 4985, 4986, 4988, 4989, 4990, 4991, 4992, 4994, 4995, 4996, 4997, 4998); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(519, NPC_TYPE, this); - addHandler(3797, NPC_TYPE, this); - ClassScanner.definePlugin(new BobDialogue()); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final RepairItem repair = RepairItem.forId(event.getUsedItem().getId()); - if (repair == null && !BobDialogue.BarrowsEquipment.isBarrowsItem(event.getUsedItem().getId())) { - player.getDialogueInterpreter().open(519, ((NPC) event.getUsedWith()), true, true, null); - return true; - } - player.getDialogueInterpreter().open(519, ((NPC) event.getUsedWith()), true, false, event.getUsedItem().getId(), event.getUsedItem()); - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt new file mode 100644 index 000000000..d939865f1 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/BobRepairItem.kt @@ -0,0 +1,47 @@ +package content.region.misthalin.lumbridge.handlers + +import content.global.handlers.item.equipment.BarrowsEquipment +import content.data.RepairItem +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +import core.game.node.entity.npc.NPC +import core.plugin.Plugin +import core.plugin.Initializable + +private val ALL_REPAIRABLE_ITEM_IDS = (RepairItem.repairableItemIds + BarrowsEquipment.getAllRepairableBarrowsIds()).toIntArray() + +/** + * Handles items being used on Bob for repair services. + * @author 'Vexia + * @author Damighty - Kotlin conversion + */ +@Initializable +class BobRepairItem : UseWithHandler(*ALL_REPAIRABLE_ITEM_IDS) { + + override fun newInstance(arg: Any?): Plugin { + addHandler(519, NPC_TYPE, this) + addHandler(3797, NPC_TYPE, this) + return this + } + + override fun handle(event: NodeUsageEvent): Boolean { + val player = event.player + val item = event.usedItem + val npc = event.usedWith as NPC + + val isBarrowsItem = BarrowsEquipment.isBarrowsItem(item.id) + + if (isBarrowsItem) { + if (BarrowsEquipment.isFullyRepaired(item.id)) { + player.dialogueInterpreter.open(519, npc, true, true, item.id) + return true + } + } else if (RepairItem.forId(item.id) == null) { + player.dialogueInterpreter.open(519, npc, true, true) + return true + } + + player.dialogueInterpreter.open(519, npc, true, false, item.id, item) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/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..e82bcce41 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 @@ -17,7 +15,7 @@ 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) + setVarbit(player, 1850, 5) setAttribute(player, "culino-tier", player.questRepository.points / 18) //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.) @@ -92,6 +90,7 @@ class CulinomancerShop : LoginListener { 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..17942de67 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java @@ -184,7 +184,7 @@ public class TormentedDemonNPC extends AbstractNPC { super.finalizeDeath(killer); reTransform(); fireShield = true; - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); } @Override @@ -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/handlers/WallBeastBehavior.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/WallBeastBehavior.kt new file mode 100644 index 000000000..309fc9e0e --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/WallBeastBehavior.kt @@ -0,0 +1,145 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import core.game.interaction.MovementPulse +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.repository.Repository +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * NPCs.WALL_BEAST_7823 starts out as NPCs.HOLE_IN_THE_WALL_2058 first. + * On death, it will respawn a new hole in the wall. + * + * h2QAZzGds9o + * + * npc anim 1807 + * {3161,9547,0,0,3}-{3164,9556,0,0,4}-{3162,9574,0,0,3}-{3198,9554,0,0,7}-{3198,9572,0,0,1}-{3215,9560,0,0,1}-{3216,9588,0,0,1}- + */ +class WallBeastBehavior : NPCBehavior(NPCs.HOLE_IN_THE_WALL_2058, NPCs.WALL_BEAST_7823) { + override fun onCreation(self: NPC) { + // This NPC should never move. + self.walkRadius = 0 + self.isNeverWalks = true + } + + override fun onDeathFinished(self: NPC, entity: Entity) { + self.reTransform() + // I don't think this is needed since the respawn will be immediate. + } +} + +class WallBeastNTrigger : MapArea { + + companion object { + /** Finds the nearest NPC of npcId to location. */ + fun findNearestNPC(location: Location, npcId: Int): NPC? { + // maybe replace contentAPI's version of this + return Repository.npcs + .filter { it.id == npcId } + .toMutableList() + .sortedWith { a, b -> + return@sortedWith (Location.getDistance(a.location, location) - Location.getDistance(b.location, location)).toInt() + } + .firstOrNull() + } + + fun isHelmetInEquipment(entity: Player): Boolean { + return inEquipment(entity, Items.SPINY_HELMET_4551) || + inEquipment(entity, Items.SLAYER_HELMET_13263) || + inEquipment(entity, Items.SPIKED_HELMET_13105) || + inEquipment(entity, Items.SLAYER_HELMET_E_14636) || + inEquipment(entity, Items.SLAYER_HELMET_CHARGED_14637) + } + } + override fun defineAreaBorders(): Array { + // The goals here are in order + return arrayOf( + ZoneBorders(3161,9546,3161,9546), + ZoneBorders(3164,9555,3164,9555), + ZoneBorders(3162,9573,3162,9573), + ZoneBorders(3198,9553,3198,9553), + ZoneBorders(3198,9571,3198,9571), + ZoneBorders(3215,9559,3215,9559), + ZoneBorders(3216,9587,3216,9587), + ) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + val nearbyWallbeast = findNearestNPC(entity.location, NPCs.HOLE_IN_THE_WALL_2058) // wallbeasts are always 1 square north. + // println(nearbyWallbeast) + if (nearbyWallbeast != null) { + lock(entity, 2) + stopWalk(entity) + // Stop walk doesn't stop the player from walking on. + // You have to clear the movement pulse that is continually feeding the walk location. + val movementPulse = entity.getPulseManager().current; + if (movementPulse is MovementPulse) { + movementPulse.stop(); + } + face(entity, nearbyWallbeast.location) + queueScript(nearbyWallbeast, 2, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + if (isHelmetInEquipment(entity)) { + animate(nearbyWallbeast, 1805) + } else { + animate(nearbyWallbeast, 1806) + } + return@queueScript delayScript(nearbyWallbeast, 1) + } + 1 -> { + if (isHelmetInEquipment(entity)) { + // You can only attack the wallbeast if you have a helmet + transformNpc(nearbyWallbeast, NPCs.WALL_BEAST_7823, 300) + return@queueScript stopExecuting(nearbyWallbeast) + } else { + animate(nearbyWallbeast, 1807) + return@queueScript delayScript(nearbyWallbeast, 4) + } + } + 2 -> { + animate(nearbyWallbeast, 1808) + return@queueScript stopExecuting(nearbyWallbeast) + } + else -> return@queueScript stopExecuting(nearbyWallbeast) + } + } + queueScript(entity, 2, QueueStrength.STRONG) { stage: Int -> + when (stage) { + 0 -> { + if (isHelmetInEquipment(entity)) { + return@queueScript stopExecuting(entity) + } + lock(entity, 5) + return@queueScript delayScript(entity, 1) + } + + 1 -> { + animate(entity, 1810) // 734 + return@queueScript delayScript(entity, 4) + } + + 2 -> { + // I don't know how much they are supposed to hit... osrs says up to 18... + impact(entity, (13 .. 18).random() , HitsplatType.NORMAL) + animate(entity, 1811) // 734 + return@queueScript stopExecuting(entity) + } + + else -> return@queueScript stopExecuting(entity) + } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt index 790b6aec2..54d38f426 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt @@ -4,6 +4,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable +import content.data.Quests /** * The Quest Journal and Configuration for the Cook's Assistant Quest. @@ -11,7 +12,7 @@ import core.plugin.Initializable */ @Initializable -class CooksAssistant : Quest("Cook's Assistant",15, 14, 1, 29, 0, 1, 2){ +class CooksAssistant : Quest(Quests.COOKS_ASSISTANT,15, 14, 1, 29, 0, 1, 2){ val MILK = 1927 val FLOUR = 1933 val EGG = 1944 @@ -21,7 +22,7 @@ class CooksAssistant : Quest("Cook's Assistant",15, 14, 1, 29, 0, 1, 2){ super.drawJournal(player, stage) var line = 12 - var stage = player?.questRepository?.getStage("Cook's Assistant")!! + var stage = player?.questRepository?.getStage(Quests.COOKS_ASSISTANT)!! if(stage < 10){ //If the quest has not been started diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/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 deleted file mode 100644 index cefb58d12..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt +++ /dev/null @@ -1,46 +0,0 @@ -package content.region.misthalin.lumbridge.quest.lostcity - -import core.game.node.scenery.Scenery -import content.data.skill.SkillingTool -import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse -import core.game.world.map.Location -import org.rs09.consts.NPCs -import org.rs09.consts.Scenery as Sceneries -import core.game.interaction.InteractionListener -import core.api.questStage -import core.api.sendMessage -import core.game.interaction.IntType - -class DramenTreeListener : InteractionListener { - - override fun defineListeners() { - - on(Sceneries.DRAMEN_TREE_1292, IntType.SCENERY, "chop down"){ player, node -> - val questStage = questStage(player,"Lost City") - if (SkillingTool.getHatchet(player) == null) { - sendMessage(player,"You do not have an axe which you have the level to use.") - return@on true - } - if (questStage < 20) { - return@on true - } - if (questStage == 20) { - if (player.getAttribute("treeSpawned", false)) { - return@on true - } - val spirit = TreeSpiritNPC(NPCs.TREE_SPIRIT_655, Location(2862, 9734, 0)) - spirit.target = player - spirit.init() - spirit.attack(player) - player.setAttribute("treeSpawned", true) - spirit.sendChat("You must defeat me before touching the tree!") - return@on true - } - - player.pulseManager.run(WoodcuttingSkillPulse(player, node as Scenery)) - return@on true - } - - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt index f83e0248a..89f48fe42 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt @@ -1,21 +1,29 @@ package content.region.misthalin.lumbridge.quest.lostcity +import core.api.Event +import core.api.LoginListener +import core.api.clearScripts +import core.api.getQuestStage +import core.game.event.EventHook +import core.game.event.InteractionEvent +import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.game.node.item.Item +import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items +import content.data.Quests +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery /** * LostCity class for the Lost City quest - * @author lila - * @author Vexia - * @author Aero + * @author lila, Vexia, Aero, Player Name */ @Initializable -class LostCity : Quest("Lost City", 83, 82, 3, 147, 0, 1, 6) { - +class LostCity : Quest(Quests.LOST_CITY, 83, 82, 3, 147, 0, 1, 6), LoginListener { class SkillRequirement(val skill: Int?, val level: Int?) val requirements = arrayListOf() @@ -66,9 +74,45 @@ class LostCity : Quest("Lost City", 83, 82, 3, 147, 0, 1, 6) { line(player, BLUE + "and be able to defeat a " + RED + "Level 101 Spirit without weapons", line++) } + private val DramenTreeHook = object : EventHook { + override fun process(entity: Entity, event: InteractionEvent) { + if (event.target.id == Scenery.DRAMEN_TREE_1292 && event.option == "chop down") { + val player = entity as Player + val questStage = getQuestStage(player, Quests.LOST_CITY) + if (questStage == 20) { + if (player.getAttribute("treeSpawned", false)) { + return + } + val spirit = TreeSpiritNPC(NPCs.TREE_SPIRIT_655, Location(2862, 9734, 0)) + spirit.target = player + spirit.init() + spirit.attack(player) + player.setAttribute("treeSpawned", true) + spirit.sendChat("You must defeat me before touching the tree!") + } + } + } + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + if (stage <= 20) { + player.hook(Event.Interacted, DramenTreeHook) + } + if (stage == 21) { + player.unhook(DramenTreeHook) + } + } + + override fun login(player: Player) { + if (getQuestStage(player, Quests.LOST_CITY) <= 20) { + player.hook(Event.Interacted, DramenTreeHook) + } + } + override fun newInstance(`object`: Any?): Quest { requirements.add(SkillRequirement(Skills.WOODCUTTING, 36)) requirements.add(SkillRequirement(Skills.CRAFTING, 31)) return this } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt index 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..072c108a8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthix.kt @@ -0,0 +1,133 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.plugin.Initializable +import org.rs09.consts.Items + +/** + * Tears of Guthix Quest + * + * Most of the attributes are for the minigame. + * In order to reset, setQuestStage + * + * if (VARPBIT[451] > 1) return 2; if (VARPBIT[451] == 0) return 0; return 1; }; if (arg0 == 88) + */ +@Initializable +class TearsOfGuthix : Quest(Quests.TEARS_OF_GUTHIX, 120, 119, 1, 449, 451, 0, 1, 2) { + + companion object { + const val attributePreviousDate = "/save:quest:tearsofguthix-previousdateofaccess" // The date in milliseconds in which TOG was played. + const val attributePreviousXPAmount = "/save:quest:tearsofguthix-previousxpamount" // The last snapshot of XP user had. + const val attributePreviousQuestPoints = "/save:quest:tearsofguthix-previousquestpoints" // The last snapshot of quest points user had. + + fun isHandsFree(player: Player): Boolean { + return getItemFromEquipment(player, EquipmentSlot.WEAPON) == null + && getItemFromEquipment(player, EquipmentSlot.SHIELD) == null + } + + fun daysLeft(player: Player): Int { + val currentTime = System.currentTimeMillis() + val previousTime = getAttribute(player, attributePreviousDate, 0) + + val numberOfDaysLeft = (currentTime - previousTime) / 86400000L + return 6 - numberOfDaysLeft.toInt() + } + + fun xpLeft(player: Player): Int { + val currentXP = player.skills.totalXp + val previousXP = getAttribute(player, attributePreviousXPAmount, 0) + return 100000 - (currentXP - previousXP) + } + + fun questPointsLeft(player: Player): Int { + val currentQuestPoints = getQuestPoints(player) + val previousQuestPoints = getAttribute(player, attributePreviousQuestPoints, 0) + return 1 - (currentQuestPoints - previousQuestPoints) + } + + fun hasRequirements(player: Player): Boolean { + return arrayOf( + getQuestPoints(player) >= 43, + hasLevelStat(player, Skills.FIREMAKING, 49), + hasLevelStat(player, Skills.CRAFTING, 20), + hasLevelStat(player, Skills.MINING, 20), + ).all { it } + } + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.TEARS_OF_GUTHIX) > 0 + + if (!started) { + line(player, "I can start this quest by speaking to !!Juna the serpent?? who", line++, false) + line(player, "lives deep in the !!Lumbridge Swamp Caves??.", line++, false) + line(player, "I will need to have:", line++, false) + line(player, "Level 49 firemaking", line++, hasLevelStat(player, Skills.FIREMAKING, 49)) + line(player, "!!Level 20 crafting??", line++, hasLevelStat(player, Skills.CRAFTING, 20)) + line(player, "!!Level 20 mining??", line++, hasLevelStat(player, Skills.MINING, 20)) + line(player, "!!43 quest points??", line++, getQuestPoints(player) >= 43) + line(player, "!!Level 49 crafting would be an advantage??", line++, hasLevelStat(player, Skills.CRAFTING, 49)) + line(player, "!!Level 49 smithing would be an advantage??", line++, hasLevelStat(player, Skills.SMITHING, 49)) + } else if (stage < 100) { + line(player, "I met Juna the serpent in a deep chasm beneath the", line++, true) + line(player, "Lumbridge Swamp Caves.", line++, true) + line(player, "I told her a story and she said she would let me into the", line++, false) + line(player, "Tears of Guthix cave if I brought her a !!bowl?? made from the", line++, false) + line(player, "stone in !!the cave on the South side of the chasm??.", line++, false) + } else { + line(player, "I met Juna the serpent in a deep chasm beneath the", line++, true) + line(player, "Lumbridge Swamp Caves. I made a bowl out of magical", line++, true) + line(player, "stone in order to catch the Tears of Guthix.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line++) + line++ + line(player, "Now Juna will let me into the cave to collect the Tears if I", line++, false) + line(player, "!!tell her stories?? of my adventures.", line++, false) + // TOG Minigame + if (daysLeft(player) > 0 && xpLeft(player) > 0 && questPointsLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix !!in " + daysLeft(player) + " days??, as", line++, false) + line(player, "long as I gain either !!1 Quest Point?? or !!" + xpLeft(player) + " total XP??", line++, false) + } else if (xpLeft(player) > 0 && questPointsLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix, as long as I", line++, false) + line(player, "gain either !!1 Quest Point?? or !!" + xpLeft(player) + " total XP??", line++, false) + } else if (daysLeft(player) > 0) { + line(player, "I will be able to collect the Tears of Guthix !!in " + daysLeft(player) + " days??.", line++, false) + } else { + line(player, "I have had enough adventures to tell Juna more stories,", line++, false) + line(player, "and a week has passed since I last collected the Tears. I", line++, false) + line(player, "can visit Juna again now.", line++, false) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Tears of Guthix quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.STONE_BOWL_4704,230,277,5) + + drawReward(player, "1 quest point", ln++) + drawReward(player, "1,000 Crafting XP", ln++) + drawReward(player, "Access to the Tears of Guthix", ln++) + drawReward(player, "cave", ln++) + + rewardXP(player, Skills.CRAFTING, 1000.0) + } + + override fun reset(player: Player) { + removeAttribute(player, attributePreviousDate) + removeAttribute(player, attributePreviousXPAmount) + removeAttribute(player, attributePreviousQuestPoints) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt new file mode 100644 index 000000000..bd7c48800 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixListeners.kt @@ -0,0 +1,143 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.impl.ForceMovement +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class TearsOfGuthixListeners : InteractionListener { + + companion object { + fun crossTheChasm(player: Player, with: NPC) { + // Unless you have time to animate this, this fucking thing is waaay too complicated. + // THIS IS JUST AESTHETICS + // You have to do the following: + // 1 - Get the light creature to your location. + // 2 - Animate both you and the light creature to float up. + // 3 - Walk both YOU AND THE LIGHT CREATURE to the other side. + // 4 - Float both you and the light creature to the ground. + // + // 2046 - Magically float into the air + // 2047 - Magically float back to the ground + // 2048 - Keep floating in the air + + // Instead you will just get fucking thrown to that other side. + val lightCreature = with as NPC + sendMessage(player, "The light-creature is attracted to your beam and comes towards you...") + LightCreatureBehavior.moveLightCreature(lightCreature, player.location) + // Could also do player.appearance.setAnimations(Animation(913)) which is the group animation for floating. + if (player.location.y > 9516) { + forceMove(player, player.location, Location.create(3229, 9504, 2), 0, 400, null, 2048) + } else { + forceMove(player, player.location, Location.create(3228, 9527, 2), 0, 400, null, 2048) + } + } + } + + override fun defineListeners() { + // Similar to RockClimbShortcut.kt + on(Scenery.ROCKS_6673, SCENERY, "climb") { player, _ -> + if (player.location.x > 3240) { + ForceMovement.run(player, player.location, Location.create(player.location).transform(-2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } else { + ForceMovement.run(player, player.location, Location.create(player.location).transform(2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } + return@on true + } + // Similar to RockClimbShortcut.kt + on(Scenery.ROCKS_6672, SCENERY, "climb") { player, _ -> + if (player.location.x > 3239) { + sendMessage(player, "You could climb down here, but it is too uneven to climb up.") + } else { + ForceMovement.run(player, player.location, Location.create(player.location).transform(2, 0, 0), Animation(1148), Animation(1148), Direction.WEST, 13).endAnimation = Animation.RESET + } + return@on true + } + + // Please note: part of this is already done in craftBullseyeLantern() except for the swapping out which is here. + onUseWith(ITEM, Items.BULLSEYE_LANTERN_4548, Items.SAPPHIRE_1607) { player, used, with -> + sendMessage(player, "You swap the lantern's lens for a sapphire.") + if(removeItem(player, with) && removeItem(player, used)) { + addItemOrDrop(player, Items.SAPPHIRE_LANTERN_4701) + addItemOrDrop(player, Items.LANTERN_LENS_4542) + } + return@onUseWith true + } + onUseWith(ITEM, Items.SAPPHIRE_LANTERN_4701, Items.LANTERN_LENS_4542) { player, used, with -> + sendMessage(player, "You swap the lantern's sapphire for a lens.") + if(removeItem(player, with) && removeItem(player, used)) { + addItemOrDrop(player, Items.BULLSEYE_LANTERN_4548) + addItemOrDrop(player, Items.SAPPHIRE_1607) + } + return@onUseWith true + } + onUseWith(ITEM, Items.BULLSEYE_LANTERN_4549, Items.SAPPHIRE_1607) { player, used, with -> + sendMessage(player, "The lantern is too hot to do that while it is lit.") + return@onUseWith true + } + onUseWith(ITEM, Items.SAPPHIRE_LANTERN_4702, Items.LANTERN_LENS_4542) { player, used, with -> + sendMessage(player, "The lantern is too hot to do that while it is lit.") + return@onUseWith true + } + + // MAGIC_STONE are set in MiningNode, but I can't change the messages without screwing up + // When examining ore: sendMessage(player, "This rock contains a magical kind of stone.") + // When mining: sendMessage(player, "You manage to mine some stone.") + // If you have stone in your inventory: sendMessage(player, "You have already mined some stone. You don't need any more.") + + // Note: The construction MAGIC_STONE is MAGIC_STONE_8788 NOT MAGIC_STONE_4703 WHICH IS FOR TEARS OF GUTHIX + onUseWith(ITEM, Items.MAGIC_STONE_4703, Items.CHISEL_1755) { player, used, with -> + sendMessage(player, "You make a stone bowl.") + if(removeItem(player, used)) { + addItemOrDrop(player, Items.STONE_BOWL_4704) + } + return@onUseWith true + } + + onUseWith(NPC, Items.SAPPHIRE_LANTERN_4702, NPCs.LIGHT_CREATURE_2021) { player, used, with -> + if (hasRequirement(player, Quests.WHILE_GUTHIX_SLEEPS)) { + // Options when you have WGS - B6KHH7AQc2Q + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> interpreter!!.sendOptions("Select an Option", "Across the Chasm.", "Into the Chasm.").also { stage++ } + 1 -> when(buttonID){ + 1 -> { + crossTheChasm(player, with as NPC) + end() + } + 2 -> { + // This was old. + player.lock(2) + player.teleport(Location.create(2538, 5881, 0)) + end() + } + } + } + } + }) + } else { + crossTheChasm(player, with as NPC) + } + return@onUseWith true + } + + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC, intArrayOf(NPCs.LIGHT_CREATURE_2021),"use"){ player, node -> + return@setDest player.location + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt new file mode 100644 index 000000000..ab27cbd71 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt @@ -0,0 +1,402 @@ +package content.region.misthalin.lumbridge.quest.tearsofguthix + +import content.data.Quests +import core.api.* +import core.game.component.Component +import core.game.event.EventHook +import core.game.event.TickEvent +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +/** + * // ::setvarbit 454 (varp 449 7-11) 0-10 where its just time bar length + * // ::setvarbit 455 (varp 449 12-20) X where x is number of points he gets + * + * anim 2040 - Hold bowl + * anim 2041 - Walk with ToG Bowl + * anim 2042 - Run with ToG Bowl + * anim 2043 - Fill ToG bowl + * anim 2044 - Finish filling from ToG + * anim 2045 - Drink from bowl + * + */ +class TearsOfGuthixMinigame : InteractionListener, EventHook, MapArea { + companion object { + + const val varbitTimeBar = 454 + const val varbitPoints = 455 + const val attributeTicksRemaining = "minigame:tearsofguthix-ticksremaining" + const val attributeTearsCollected = "minigame:tearsofguthix-tearscollected" + const val attributeIsCollecting = "minigame:tearsofguthix-iscollecting" + + // In order specified by RS + private val rewardArray = arrayOf( + Skills.COOKING, + Skills.CRAFTING, + Skills.FIREMAKING, + Skills.FISHING, + Skills.MAGIC, + Skills.MINING, + Skills.PRAYER, + Skills.RANGE, + Skills.RUNECRAFTING, + Skills.SMITHING, + Skills.WOODCUTTING, + Skills.AGILITY, + Skills.HERBLORE, + Skills.FLETCHING, + Skills.THIEVING, + Skills.SLAYER, + Skills.ATTACK, + Skills.DEFENCE, + Skills.STRENGTH, + Skills.HITPOINTS, + Skills.FARMING, + Skills.CONSTRUCTION, + Skills.HUNTER, + Skills.SUMMONING, // This isn't but lmao. + ) + // In order specified by RS + val rewardText = arrayOf( + "You have a brief urge to cook some food.", + "Your fingers feel nimble and suited to delicate work.", + "You have a brief urge to set light to something.", + "You gain a deep understanding of the creatures of the sea.", + "You feel the power of the runes surging through you. ", + "You gain a deep understanding of the stones of the earth.", + "You suddenly feel very close to the gods.", + "Your aim improves.", + "You gain a deep understanding of runes.", + "You gain a deep understanding of all types of metal.", + "You gain a deep understanding of the trees in the wood.", + "You feel very nimble.", + "You gain a deep understanding of all kinds of strange plants.", + "You gain a deep understanding of wooden sticks.", + "You feel your respect for others' property slipping away.", + "You gain a deep understanding of many strange creatures.", + "You feel a brief surge of aggression.", + "You feel more able to defend yourself.", + "Your muscles bulge.", + "You feel very healthy.", + "You gain a deep understanding of the cycles of nature.", + "You feel homesick.", + "You briefly experience the joy of the hunt.", + "You feel at one with nature.", + ) + + /** Calculates the XP to reward. */ + fun rewardTears(player: Player) { + val lowestSkill = rewardArray.reduce{ acc, curr -> + // If you don't have construction, you cannot earn xp on it. + if (curr == Skills.CONSTRUCTION && !hasHouse(player)) { + acc + } + // If you don't have Druidic Ritual completed, you cannot earn xp on it. + else if (curr == Skills.HERBLORE && !isQuestComplete(player, Quests.DRUIDIC_RITUAL)) { + acc + } + // If you don't have Rune Mysteries, you cannot earn xp on it. + else if (curr == Skills.RUNECRAFTING && !isQuestComplete(player, Quests.RUNE_MYSTERIES)) { + acc + } + // If you don't have Wolf Whistle, you cannot earn xp on it. + else if (curr == Skills.SUMMONING && !isQuestComplete(player, Quests.WOLF_WHISTLE)) { + acc + } + else if (player.skills.getExperience(acc) <= player.skills.getExperience(curr)) { + acc + } else { + curr + } + } + + var perTearXP = 60.0 // Caps at level 30, giving 60 per XP. + if (getStatLevel(player, lowestSkill) < 30) { + perTearXP = (getStatLevel(player, lowestSkill) - 1) * 1.724137 // From 50/29 + perTearXP += 10 + } + + sendMessage(player, rewardText[rewardArray.indexOf(lowestSkill)]) + + val tearsCollected = getAttribute(player, attributeTearsCollected, 0) + rewardXP(player, lowestSkill, perTearXP * tearsCollected) + } + + /** Opens interface, walks the player to the center and start game. */ + fun startGame(player: Player) { + lock(player, 15) + // Opens the Tears of Guthix Interface in the tab. + player.interfaceManager.openSingleTab(Component(Components.TOG_WATER_BOWL_4)) + // Sets up the interface varbits. + setAttribute(player, attributeTicksRemaining, getQuestPoints(player) + 15) // 15 to offset the stupid walking. + setAttribute(player, attributeTearsCollected, 0) + setVarbit(player, varbitTimeBar, 10) + setVarbit(player,varbitPoints, 0) + + // Forces the player to hold a bowl and animate accordingly. + replaceSlot(player, EquipmentSlot.WEAPON.ordinal, Item(Items.STONE_BOWL_4704), null, Container.EQUIPMENT) + // Change the player's SET ANIMATIONS to the bowl holding set. Found using ::ranim + player.appearance.setAnimations(Animation(357)) // THIS WAS TRIAL AND ERROR AND WAS FUCKING HARD TO FIND + player.appearance.sync() + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + val distance = player.location.getDistance(Location(3251, 9516, 2)).toInt() + 1// Per tick? + forceMove(player, player.location, Location(3251, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 1 -> { + face(player, Location(3252, 9516, 2)) + val junaScenery = getScenery(Location(3252, 9516, 2)) + if (junaScenery != null) { + animateScenery(junaScenery, 2055) + } + return@queueScript delayScript(player, 2) + } + 2 -> { + val distance = player.location.getDistance(Location(3253, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 3 -> { + face(player, Location(3253, 9517, 2)) + return@queueScript delayScript(player, 2) + } + 4 -> { + val distance = player.location.getDistance(Location(3253, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 5 -> { + face(player, Location(3257, 9517, 2)) + return@queueScript delayScript(player, 2) + } + 6 -> { + val distance = player.location.getDistance(Location(3257, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3257, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 7 -> { + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + fun endGame(player: Player) { + lock(player, 22) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "Your time in the cave is up.") + val distance = player.location.getDistance(Location(3253, 9517, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9517, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 1 -> { + face(player, Location(3253, 9516, 2)) + return@queueScript delayScript(player, 2) + } + 2 -> { + val distance = player.location.getDistance(Location(3253, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3253, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 3 -> { + face(player, Location(3251, 9516, 2)) + val junaScenery = getScenery(Location(3252, 9516, 2)) + if (junaScenery != null) { + animateScenery(junaScenery, 2055) + } + return@queueScript delayScript(player, 2) + } + 4 -> { + val distance = player.location.getDistance(Location(3251, 9516, 2)).toInt() + 1 // Per tick? + forceMove(player, player.location, Location(3251, 9516, 2), 0, distance * 15, null, 2041) + return@queueScript delayScript(player, distance) + } + 5 -> { + sendMessage(player, "You drink the liquid...") + animate(player, 2045) + return@queueScript delayScript(player, 3) + } + 6 -> { + rewardTears(player) + setAttribute(player, TearsOfGuthix.attributePreviousDate, System.currentTimeMillis()) + setAttribute(player, TearsOfGuthix.attributePreviousXPAmount, player.skills.totalXp) + setAttribute(player, TearsOfGuthix.attributePreviousQuestPoints, getQuestPoints(player)) + removeAttribute(player, attributeTearsCollected) + if (player.interfaceManager.singleTab?.id == 4) { + player.interfaceManager.closeSingleTab() + } + player.interfaceManager.restoreTabs() + removeItem(player, Items.STONE_BOWL_4704, Container.EQUIPMENT) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + } + + override fun defineListeners() { + + on(Scenery.WEEPING_WALL_6660, SCENERY, "collect-from") { player, node -> + animate(player, 2043) + val index = TearsOfGuthixGlobalTick.allWalls.indexOf(node.location) + setAttribute(player, attributeIsCollecting, index) + return@on true + } + + } + + // Timer step per tick while you are in the minigame. + override fun process(entity: Entity, event: TickEvent) { + if (entity is Player) { + if (getAttribute(entity, attributeTicksRemaining, -1) > 0) { + setAttribute(entity, attributeTicksRemaining, getAttribute(entity, attributeTicksRemaining, 0) - 1) + setVarbit(entity, varbitTimeBar, (getAttribute(entity, attributeTicksRemaining, 0) * 10 / getQuestPoints(entity)), false) + if (getAttribute(entity, attributeIsCollecting, 0) != 0) { + val currentArrayIndex = getAttribute(entity, attributeIsCollecting, 0) + val currentTearState = TearsOfGuthixGlobalTick.globalWallState[currentArrayIndex] + if (currentTearState == 1) { + setAttribute(entity, attributeTearsCollected, getAttribute(entity, attributeTearsCollected, 0) + 1) + } else if (currentTearState == 2 && getAttribute(entity, attributeTearsCollected, 0) > 0){ + setAttribute(entity, attributeTearsCollected, getAttribute(entity, attributeTearsCollected, 0) - 1) + } + setVarbit(entity, varbitPoints, getAttribute(entity, attributeTearsCollected, 0)) + } + } else if (getAttribute(entity, attributeTicksRemaining, -1) == 0) { + removeAttribute(entity, attributeTicksRemaining) + endGame(entity) + } + } + } + + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(3253, 9513, 3262, 9522, 2)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + if (getAttribute(entity, attributeTicksRemaining, 0) <= 0) { + removeItem(entity, Items.STONE_BOWL_4704, Container.EQUIPMENT) + teleport(entity, Location(3251, 9516, 2)) + } else { + entity.hook(Event.Tick, this) + } + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + entity.unhook(this) + if (logout) { + removeItem(entity, Items.STONE_BOWL_4704, Container.EQUIPMENT) + removeAttribute(entity, attributeTearsCollected) + removeAttribute(entity, attributeTicksRemaining) + teleport(entity, Location(3251, 9516, 2)) + } + } + } + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + if (entity is Player) { + entity.hook(Event.Tick, this) + setAttribute(entity, attributeIsCollecting, 0) // If you move, you ain't collecting + } + } +} + +/** + * Global Tick class to randomize the walls consistently for everyone. + */ +class TearsOfGuthixGlobalTick : TickListener { + + companion object { + var ticks = 0 + var globalWallState = intArrayOf(0, 0, 2, 1, 2, 1, 0, 0, 2, 1) + val allWalls = arrayOf( + // Blank + Location(0, 0, 0), + // Left Walls + Location(3258, 9520, 2), + Location(3261, 9516, 2), + Location(3261, 9518, 2), + Location(3257, 9514, 2), + Location(3259, 9514, 2), + // Right Walls + Location(3257, 9520, 2), + Location(3259, 9520, 2), + Location(3261, 9517, 2), + Location(3258, 9514, 2), + ) + } + + override fun tick() { + // Do this every 10 ticks. + if (ticks++ > 10) { ticks = 0 } else { return } + // Shuffle the walls + val wallStates = intArrayOf(0, 0, 0, 1, 1, 1, 2, 2, 2) // 0 is absent, 1 is blue, 2 is green + wallStates.shuffle() + globalWallState = intArrayOf(0) + wallStates + + /* + * Explanation: The walls are layered sceneries, which makes it rabidly fucked to change them. + * What I did was to add the tears scenery first (essentially overriding the tears scenery), + * then add the WEEPING_WALL_6660 right after it so that the interactions are still there. + * this is how a layer is like: + * 1 - WEEPING_WALL_6660 - No model, but holds the option "collect-from" + * 2 - BLUE/GREEN/ABSENT - Model of the blue/green/absent waterfall. + * 3 - WEEPING_WALL_6664 - The actual model, but not interactive. + * 6661 - 6664 is left side, 6665 to 6668 is right side + */ + wallStates.forEachIndexed { index, state -> + val scenery = getScenery(allWalls[index + 1])!! + val newSceneryId = if (state == 2) { + if (index + 1 <= 5) { + Scenery.GREEN_TEARS_6662 + } else { + Scenery.GREEN_TEARS_6666 + } + } else if (state == 1) { + if (index + 1 <= 5) { + Scenery.BLUE_TEARS_6661 + } else { + Scenery.BLUE_TEARS_6665 + } + } else { + if (index + 1 <= 5) { + Scenery.ABSENCE_OF_TEARS_6663 + } else { + Scenery.ABSENCE_OF_TEARS_6667 + } + } + addScenery(core.game.node.scenery.Scenery( + newSceneryId, + scenery.location, + 4, + scenery.rotation + )) + addScenery(core.game.node.scenery.Scenery(Scenery.WEEPING_WALL_6660, scenery.location, 0, scenery.rotation)) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java index c301fc637..acf0ca8c2 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import core.game.node.entity.player.Player; import core.game.dialogue.DialoguePlugin; +import content.data.Quests; /** @@ -42,7 +43,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - int questStage = player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player); + int questStage = player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player); if (questStage == 10) { npc("Have you got rid of the ghost yet?"); stage = 520; @@ -72,7 +73,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getQuestRepository().isComplete("The Restless Ghost")) { + if (player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { interpreter.sendOptions("What would you like to say?", "Can you change my gravestone now?", "Who's Saradomin?", "Nice place you've got here."); stage = 1; } else { @@ -128,7 +129,7 @@ public final class FatherAereckDialogue extends DialoguePlugin { end(); break; case 510: - player.getQuestRepository().getQuest(RestlessGhost.NAME).start(player); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).start(player); player.getQuestRepository().syncronizeTab(player); npc("Thank you. The problem is, there is a ghost in the", "church graveyard. I would like you to get rid of it."); stage = 511; diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java index abedfb9a0..219c0c562 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,7 @@ package content.region.misthalin.lumbridge.quest.therestlessghost; +import content.data.Quests; +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; @@ -40,7 +42,7 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - npc("Go away! I'm meditating!"); + npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); stage = 0; return true; } @@ -49,14 +51,14 @@ 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) { - options("Well, that's friendly.", "I've come to respossess your house."); + if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 0) { + options("Well, that's friendly.", "I've come to repossess your house."); stage = 1; - } else if (player.getQuestRepository().getQuest(RestlessGhost.NAME).getStage(player) == 10) { - options("Well, that's friendly.", "I've come to respossess your house.", "Father Aereck sent me to talk to you."); + } else if (player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).getStage(player) == 10) { + options("Well, that's friendly.", "Father Aereck sent me to talk to you.", "I've come to repossess your house."); stage = 500; - } else if (player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney") || player.getQuestRepository().isComplete(RestlessGhost.NAME)) { - options("Well, that's friendly.", "I've come to respossess your house.", "I've lost the Amulet of Ghostspeak."); + } else if (player.getGameAttributes().getAttributes().containsKey("restless-ghost:urhney") || player.getQuestRepository().isComplete(Quests.THE_RESTLESS_GHOST)) { + options("Well, that's friendly.", "I've lost the Amulet of Ghostspeak.", "I've come to repossess your house."); stage = 514; } break; @@ -67,17 +69,17 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 10; break; case 2: - player("I've come to repossess your house."); - stage = 20; - break; - case 3: player("Father Aereck sent me to talk to you."); stage = 501; break; + case 3: + player("I've come to repossess your house."); + stage = 20; + break; } break; case 501: - npc("I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); + npc(FacialExpression.ANGRY, "I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); stage = 502; break; case 502: @@ -85,23 +87,23 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 503; break; case 503: - npc("Oh, the silly fool."); + npc(FacialExpression.ANGRY, "Oh, the silly fool."); stage = 504; break; case 504: - npc("I leave town for just five months, and ALREADY he", "can't manage."); + npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); stage = 505; break; case 505: - npc("(sigh)"); + npc(FacialExpression.SAD, "(sigh)"); stage = 506; break; case 506: - npc("Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); + npc(FacialExpression.ANGRY, "Well, I can't go back and exorcise it. I vowed not to", "leave this place. Until I had done a full two years of", "prayer and meditation."); stage = 507; break; case 507: - npc("Tell you what I can do though; take this amulet."); + npc(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); stage = 508; break; case 508: @@ -112,20 +114,20 @@ 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; case 509: - npc("It is an Amulet of Ghostspeak."); + npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak."); stage = 510; break; case 510: - npc("So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted."); + npc(FacialExpression.NEUTRAL, "So called, because when you wear it you can speak to", "ghosts. A lot of ghosts are doomed to be ghosts because", "they have left some important task uncompleted."); stage = 511; break; case 511: - npc("Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any gurantees mind you,", "but it is the best I can do right now."); + npc(FacialExpression.NEUTRAL, "Maybe if you know what this task is, you can get rid of", "the ghost. I'm not making any guarantees mind you,", "but it is the best I can do right now."); stage = 512; break; case 512: @@ -142,12 +144,12 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 10; break; case 2: - player("I've come to repossess your house."); - stage = 20; + player(FacialExpression.NEUTRAL, "I've lost the Amulet of Ghostspeak."); + stage = 515; break; case 3: - player("I've lost the Amulet of Ghostpeak."); - stage = 515; + player("I've come to repossess your house."); + stage = 20; break; } break; @@ -170,14 +172,14 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 518; break; case 517: - npc("What are you talking about? I can see you've got it", "in your bank!"); + npc(FacialExpression.ANGRY, "You come here wasting my time... Has it even", "occurred to you to look in your bank? Now GO", "AWAY!"); stage = 518; break; case 518: end(); break; case 519: - npc("How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare."); + npc(FacialExpression.ANGRY, "How careless can you get? Those things aren't easy to", "come by you know! It's a good job I've got a spare."); stage = 520; break; case 520: @@ -187,7 +189,7 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 521; break; case 521: - npc("Be more careful this time."); + npc(FacialExpression.ANGRY, "Be more careful this time."); stage = 522; break; case 522: @@ -251,7 +253,7 @@ public final class FatherUhrneyDialogue extends DialoguePlugin { stage = 102; break; case 102: - player("Sorry. I mus thave got the wrong address. All the", "houses look the same around here."); + player("Sorry. I must have got the wrong address. All the", "houses look the same around here."); stage = 103; break; case 103: 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..b19d00484 --- /dev/null +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt @@ -0,0 +1,29 @@ +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 +import core.game.node.entity.player.link.diary.DiaryType + +// 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)) + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 9) + } + 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..e9a4ea493 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: @@ -383,7 +420,7 @@ public final class GertrudeDialogue extends DialoguePlugin { * @return the item. */ public Item getKitten() { - return new Item(RandomFunction.random(1555, 1560)); + return new Item(RandomFunction.random(1555, 1561)); } @Override 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/GrandExchangeClerk.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeClerk.java index b4e712799..6c00a6bae 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeClerk.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeClerk.java @@ -52,9 +52,10 @@ public final class GrandExchangeClerk extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { + String salutation = player.isMale() ? "sir" : "madam"; switch (stage) { case 0: - npc("Good day to you, sir, How can I help?"); + npc("Good day to you, " + salutation + ", how can I help?"); stage = 1; break; case 1: @@ -80,13 +81,13 @@ public final class GrandExchangeClerk extends DialoguePlugin { stage = 40; break; case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine actually."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine, actually."); stage = 50; break; } break; case 10: - npc("Only too happy to help you, sir."); + npc("Only too happy to help you, " + salutation + "."); stage = 11; break; case 11: @@ -94,7 +95,7 @@ public final class GrandExchangeClerk extends DialoguePlugin { StockMarket.openFor(player); break; case 20: - npc("As you wish, sir."); + npc("As you wish, " + salutation + "."); stage = 21; break; case 21: @@ -110,7 +111,7 @@ public final class GrandExchangeClerk extends DialoguePlugin { GrandExchangeRecords.getInstance(player).openHistoryLog(player); break; case 40: - npc("It would be my pleasure, sir."); + npc("It would be my pleasure, " + salutation + "."); stage = 41; break; case 41: @@ -118,7 +119,7 @@ public final class GrandExchangeClerk extends DialoguePlugin { ExchangeItemSets.openFor(player); break; case 50: - npc("If you say so, sir."); + npc("If you say so, " + salutation + "."); stage = 51; break; case 51: 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/RomilyWeaklaxDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java index ed93e45e1..26eac9af2 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java @@ -247,7 +247,7 @@ public class RomilyWeaklaxDialogue extends DialoguePlugin { @Override public boolean handle(NodeUsageEvent event) { if (!event.getPlayer().getAchievementDiaryManager().getDiary(DiaryType.VARROCK).isComplete(2,5)) { - event.getPlayer().getDialogueInterpreter().open(3205, event.getUsedItem()); + event.getPlayer().getDialogueInterpreter().open(3205, event.getUsedWith().asNpc(), event.getUsedItem()); } return true; } 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..e61b9d5dc --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt @@ -0,0 +1,204 @@ +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.api.storeBookInHouse +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, node -> + setAttribute(player, "bookInterfaceCallback", ::display) + setAttribute(player, "bookInterfaceCurrentPage", 0) + display(player, 0, 0) + storeBookInHouse(player, node) + 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..949786650 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt @@ -0,0 +1,380 @@ +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.storeBookInHouse +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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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/ShooAwayStrayDogPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/ShooAwayStrayDogPlugin.java deleted file mode 100644 index d21dee850..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/ShooAwayStrayDogPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package content.region.misthalin.varrock.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.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the option plugin used to shoo away a dog. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ShooAwayStrayDogPlugin extends OptionHandler { - - /** - * Represents the animatio to use. - */ - private static final Animation ANIMATION = new Animation(2110); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(5917).getHandlers().put("option:shoo-away", this); - NPCDefinition.setOptionHandler("shoo-away", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.sendChat("Thbbbbt!"); - player.animate(ANIMATION); - NPC dog = (NPC) node; - dog.sendChat("Whine!"); - dog.moveStep(); - dog.getPulseManager().clear(); - return true; - } - -} 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..22243ca0b 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt @@ -17,6 +17,7 @@ import org.json.simple.JSONObject import org.rs09.consts.Items import core.ServerStore import core.ServerStore.Companion.getInt +import content.data.Quests /** * Represents the plugin used for buying a battle staff from zeke. @@ -70,7 +71,7 @@ class ZaffPlugin : OptionHandler() { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC - quest = player.questRepository.getQuest("What Lies Below") + quest = player.questRepository.getQuest(Quests.WHAT_LIES_BELOW) interpreter.sendDialogues( npc, core.game.dialogue.FacialExpression.HALF_GUILTY, @@ -387,23 +388,25 @@ 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 + if(amt == 0){ + return@sendInputDialogue + } val coinage = amt * 7000 if(!inInventory(player, Items.COINS_995, coinage)){ sendDialogue(player, "You can't afford that many.") return@sendInputDialogue } - - if(amt == 0){ + if(!hasSpaceFor(player, Item(Items.BATTLESTAFF_1392, amt)) && amountInInventory(player, Items.COINS_995) > coinage){ + sendDialogue(player, "You don't have enough inventory space.") return@sendInputDialogue } - if(removeItem(player, Item(Items.COINS_995, coinage), Container.INVENTORY)){ addItem(player, Items.BATTLESTAFF_1392, amt) - ZaffPlugin.getStoreFile()[player.username.toLowerCase()] = amt + ammount + getStoreFile()[player.username.toLowerCase()] = amt + ammount } } } 2 -> { @@ -430,7 +433,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..56e81b3bb 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,24 @@ 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, + 1282 /*this is actually unused*/, + 5133 /*source: ::varbits 1282, after trial and error confirmed that I need ::setvarp 1282 0 QUEST_STAGE*/, + 0, 1, 90) { //not started -- in progress -- completed override fun newInstance(`object`: Any?): Quest { return this } @@ -22,52 +27,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,15 +152,7 @@ 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) player.questRepository.syncronizeTab(player) } - - override fun getConfig(player: Player?, stage: Int): IntArray { - if(stage == 100) return intArrayOf(1282, 90) - 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..fc857c7e6 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,12 @@ 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 +import core.game.node.entity.player.link.diary.DiaryType @Initializable @@ -19,19 +20,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 +134,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,19 +167,20 @@ 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) } } 6000 -> npc("Not to worry, here they are").also { stage = 1000 addItem(player, getAttribute(player, "family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778)) + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 9) } 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..2b8f1aef9 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,14 +120,8 @@ 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) } - - /*override fun getConfig(player: Player?, stage: Int): IntArray { - if(stage == 100) return intArrayOf(1282, 90) - if(stage > 0) return intArrayOf(1282, 1) - else return intArrayOf(1282, 0) - }*/ } 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..867aa08d3 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt @@ -0,0 +1,107 @@ +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 +import core.api.storeBookInHouse + +/** + * 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, node -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + storeBookInHouse(player, node) + 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..41b7314a2 --- /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, Items.SILVER_SICKLEB_2963, 1)) { + npc(FacialExpression.HALF_GUILTY, "I don't have a spare lying around, sorry friend.", "Hopefully you'll find something else that can protect you", "against ghasts!") + 31 + } else { + 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.") + addItemOrDrop(player, Items.SILVER_SICKLEB_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..369aa550f --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/handlers/CanafisWerewolfNPC.kt @@ -0,0 +1,64 @@ +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.skill.Skills +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 -> { + val humanMaxLp = self.getSkills().getMaximumLifepoints() + transformNpcWithHpCarryover(self, WEREWOLF_NPCS[self.id - 6026], 200) + self.getSkills().heal(self.getSkills().getMaximumLifepoints() - humanMaxLp) + 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..cb80df540 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,14 +68,16 @@ 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) + if (removeItem(player, i, Container.INVENTORY)) { + addItem(player, Items.ROTTEN_FOOD_2959) + } sendMessage(player, "You feel something attacking your backpack, and smell a terrible stench.") break } } + 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..254bbd4a0 100644 --- a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt @@ -13,6 +13,9 @@ import org.rs09.consts.Scenery import core.game.interaction.InteractionListener import core.game.interaction.IntType import kotlin.random.Random +import content.data.Quests +import core.game.interaction.InterfaceListener +import org.rs09.consts.Components /** * File to be used for anything Morytania related. @@ -39,12 +42,15 @@ class MorytaniaListeners : InteractionListener { on(SWAMP_GATES, IntType.SCENERY, "open"){ player, node -> if(player.location.y == 3457){ core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + sendMessage(player, "You skip gladly out of murky Mort Myre.") + removeTimer(player) GlobalScope.launch { findLocalNPC(player, NPCs.ULIZIUS_1054)?.sendChat("Oh my! You're still alive!", 2) } } else { - if (player.questRepository.hasStarted("Nature Spirit")) { - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + if (player.questRepository.hasStarted(Quests.NATURE_SPIRIT)) { + setAttribute(player, "swampgate", node) + openInterface(player, Components.CWS_WARNING_20_580) } else { sendNPCDialogue( player, @@ -96,4 +102,24 @@ class MorytaniaListeners : InteractionListener { return@on true } } +} + +class warningInterface : InterfaceListener { + override fun defineInterfaceListeners() { + on(Components.CWS_WARNING_20_580) { player, _, _, buttonID, _, _ -> + when(buttonID) { + 17 -> { + val gate = getAttribute(player, "swampgate", null) as? core.game.node.scenery.Scenery + if (gate != null) { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate) + sendMessage(player, "You walk into the gloomy atmosphere of Mort Myre.") + } + closeInterface(player) + } + 18 -> closeInterface(player) + } + removeAttribute(player, "swampgate") + return@on true + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java b/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java deleted file mode 100644 index 69e663f47..000000000 --- a/Server/src/main/content/region/morytania/handlers/MoyrtniaSwampPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package content.region.morytania.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 mortynia swamp plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MoyrtniaSwampPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(3506).getHandlers().put("option:open", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().sendDialogue("There's a message attached to this gate, it reads:~", "~ Mort Myre is a dangerous Ghast infested swamp. ~", "~ Do not enter if you value your life. ~", "~ All persons wishing to enter must see Drezel. ~"); - return true; - } - -} diff --git a/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt b/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt new file mode 100644 index 000000000..995294730 --- /dev/null +++ b/Server/src/main/content/region/morytania/handlers/SwampDecayHandler.kt @@ -0,0 +1,148 @@ +package content.region.morytania.handlers + +import core.api.* +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 +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items +import kotlin.random.Random + + +/** + * The area for swamp decay + */ +class SwampDecayArea : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf( + ZoneBorders(3392, 3328, 3519, 3455) + ) + } + + /** + * Sources: + * General mechanic: https://runescape.wiki/w/Swamp_decay + * Historic damage: https://runescape.wiki/w/Mort_Myre_Swamp?oldid=1961846 + * Precise area: https://oldschool.runescape.wiki/w/Swamp_decay + * Video: https://www.youtube.com/watch?v=gkw959x8Q6s + * Video: https://www.youtube.com/watch?v=N86RywmEyEU + */ + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + getOrStartTimer(entity) + } + super.areaEnter(entity) + } + +} + +/** + * The timer for swamp decay + */ +class SwampDecayTimer : PersistTimer (200, "swampdecay", isSoft = true, flags = arrayOf(TimerFlag.ClearOnDeath)) { + companion object { + // Silver sickle, Rod of Ivandis, or Ivandis Flail in equip or inv will protect the player. + private val PROTECTIVE_ITEMS = intArrayOf( + Items.SILVER_SICKLEB_2963, + Items.ROD_OF_IVANDIS10_7639, + Items.ROD_OF_IVANDIS9_7640, + Items.ROD_OF_IVANDIS8_7641, + Items.ROD_OF_IVANDIS7_7642, + Items.ROD_OF_IVANDIS6_7643, + Items.ROD_OF_IVANDIS5_7644, + Items.ROD_OF_IVANDIS4_7645, + Items.ROD_OF_IVANDIS3_7646, + Items.ROD_OF_IVANDIS2_7647, + Items.ROD_OF_IVANDIS1_7648, + Items.IVANDIS_FLAIL_30_13117, + Items.IVANDIS_FLAIL_29_13118, + Items.IVANDIS_FLAIL28_13119, + Items.IVANDIS_FLAIL_27_13120, + Items.IVANDIS_FLAIL_26_13121, + Items.IVANDIS_FLAIL_25_13122, + Items.IVANDIS_FLAIL_24_13123, + Items.IVANDIS_FLAIL_23_13124, + Items.IVANDIS_FLAIL_22_13125, + Items.IVANDIS_FLAIL_21_13126, + Items.IVANDIS_FLAIL_20_13127, + Items.IVANDIS_FLAIL_19_13128, + Items.IVANDIS_FLAIL_18_13129, + Items.IVANDIS_FLAIL_17_13130, + Items.IVANDIS_FLAIL_16_13131, + Items.IVANDIS_FLAIL_15_13132, + Items.IVANDIS_FLAIL_14_13133, + Items.IVANDIS_FLAIL_13_13134, + Items.IVANDIS_FLAIL_12_13135, + Items.IVANDIS_FLAIL_11_13136, + Items.IVANDIS_FLAIL_10_13137, + Items.IVANDIS_FLAIL_9_13138, + Items.IVANDIS_FLAIL_8_13139, + Items.IVANDIS_FLAIL_7_13140, + Items.IVANDIS_FLAIL_6_13141, + Items.IVANDIS_FLAIL_5_13142, + Items.IVANDIS_FLAIL_4_13143, + Items.IVANDIS_FLAIL_3_13144, + Items.IVANDIS_FLAIL_2_13145, + Items.IVANDIS_FLAIL_1_13146 + ) + } + + // returning false stops the timer. returning true keeps it running. + override fun run(entity: Entity): Boolean { + if (entity !is Player) return false + + // player is outside the zone + if (entity.location.x !in 3392..3519 || entity.location.y !in 3328..3455) { + sendMessage(entity, "The swamp decay effect is now over.") + return false + } + + // the nature spirit camp zone is safe + if (entity.location.x in 3435..3445 && entity.location.y in 3331..3442) { + sendMessage(entity, "The aura of Filliman's camp protects you from the swamp.") + return true + } + + // protected + if (hasProtection(entity)) { + return true + } + + // decayed (2-4 random damage) + sendMessage(entity, "The swamp decays you!") + impact(entity, Random.nextInt(2, 5)) + visualize(entity, -1, 255) + return true + } + + override fun onRegister(entity: Entity) { + if (entity !is Player) return + } + + override fun getTimer(vararg args: Any): RSTimer { + val t = SwampDecayTimer() + t.runInterval = args.getOrNull(0) as? Int ?: 200 + return t + } + + // always remember your protection + private fun hasProtection(player: Player): Boolean { + for (itemId in PROTECTIVE_ITEMS) { + if (inEquipmentOrInventory(player, itemId)) { + val prot = when(itemId) { + 2963 -> "blessed silver sickle" + in 7639..7648 -> "rod of Ivandis" + in 13117..13146 -> "Ivandis flail" + else -> "item" // fallback + } + sendMessage(player, "The power of the $prot prevents the swamp decay.") + visualize(player, -1, 259) + return true + } + } + return false + } +} \ No newline at end of file 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..40f395cf4 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 { /** @@ -80,13 +82,13 @@ public final class GhostDiscipleDialogue extends DialoguePlugin { stage++; break; case 1: - options("What is the Ectofunuts?", "Where do I get ectoplasm from?", "How do I grind bones?", "How do I receive Ectotokens?", "Thanks for your time."); + options("What is the Ectofuntus?", "Where do I get ectoplasm from?", "How do I grind bones?", "How do I receive Ectotokens?", "Thanks for your time."); stage++; break; case 2: switch (buttonId) { case 1: - player("What is the Ectofunuts?"); + player("What is the Ectofuntus?"); stage = 10; break; case 2: diff --git a/Server/src/main/content/region/morytania/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/dialogue/MetarialusDialogue.kt b/Server/src/main/content/region/morytania/phas/dialogue/MetarialusDialogue.kt new file mode 100644 index 000000000..cefb117d4 --- /dev/null +++ b/Server/src/main/content/region/morytania/phas/dialogue/MetarialusDialogue.kt @@ -0,0 +1,127 @@ +package content.region.morytania.phas.dialogue + +import core.api.* +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +@Initializable +class MetarialusDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.METARIALUS_2322, IntType.NPC, "talk-to") { player, node -> + if (inEquipment(player, Items.GHOSTSPEAK_AMULET_552) || inEquipment(player, Items.GHOSTSPEAK_AMULET_4250)) { + DialogueLabeller.open(player, MetarialusDialogueFile(), node as NPC) + return@on true + } else { + DialogueLabeller.open(player, MetarialusDialogueNoGhostSpeakFile(), node as NPC) + return@on true + } + } + } +} + + +class MetarialusDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.METARIALUS_2322) + + player(ChatAnim.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...") + npc(ChatAnim.NEUTRAL, "I might be able to - what do you need to know?") + goto("main_menu") + + label("main_menu") { + options( + DialogueOption("how_brew_ales", "How do I brew ales?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew ales?"), + DialogueOption("how_brew_cider", "How do I brew cider?", expression = ChatAnim.HALF_ASKING, spokenText = "How do I brew cider?"), + DialogueOption("what_when_matured", "What do I do once my ale has matured?", expression = ChatAnim.HALF_ASKING, spokenText = "What do I do once my ale has matured?"), + DialogueOption("buy_yeast", "Do you have any spare ale yeast?", expression = ChatAnim.HALF_ASKING, spokenText = "Do you have any spare ale yeast?"), + DialogueOption("end", "That's all I need to know, thanks.") + ) + } + + label("how_brew_ales") { + npc(ChatAnim.NEUTRAL, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.") + npc(ChatAnim.NEUTRAL, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.") + npc(ChatAnim.NEUTRAL, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.") + goto("main_menu") + } + + label("how_brew_cider") { + npc(ChatAnim.NEUTRAL, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.") + npc(ChatAnim.NEUTRAL, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.") + goto("main_menu") + } + + label("what_when_matured") { + npc(ChatAnim.NEUTRAL, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.") + goto("main_menu") + } + + label("buy_yeast") { + npc(ChatAnim.NEUTRAL, "Well, as a matter of fact I do, although I wouldn't describe", + "it as spare. This ale yeast I've got is the best money can", + "buy, but if you've got a pot I'll fill it for you for", + "5 ecto-tokens - very cheap as it happens.") + options( + DialogueOption("confirm_buy", "That's a good deal - please fill my pot with ale yeast for 5 ecto-tokens."), + DialogueOption("end", "No, that's too much I'm afraid.") + ) + } + + label("confirm_buy") { + exec { player, npc -> + if (!inInventory(player, Items.EMPTY_POT_1931, 1)) { + loadLabel(player, "no_pot") + } else if (!inInventory(player, Items.ECTO_TOKEN_4278, 5)) { + loadLabel(player, "no_tokens") + } else { + loadLabel(player, "process_purchase") + } + } + } + + label("no_pot") { + npc(ChatAnim.NEUTRAL, "I'm afraid you'll need a pot for me to put the yeast in.") + goto("end") + } + + label("no_tokens") { + line("You can not afford that.") + goto("end") + } + + label("process_purchase") { + exec { player, npc -> + val emptyPots = amountInInventory(player, Items.EMPTY_POT_1931) + val tokens = amountInInventory(player, Items.ECTO_TOKEN_4278) + val canAfford = tokens / 5 + val quantity = minOf(emptyPots, canAfford) + + if (removeItem(player, Item(Items.EMPTY_POT_1931, quantity)) && removeItem(player, Item(Items.ECTO_TOKEN_4278, quantity * 5))) { + addItem(player, Items.ALE_YEAST_5767, quantity) + } + } + goto("main_menu") + } + + label("end") { + } + } +} + + +class MetarialusDialogueNoGhostSpeakFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.METARIALUS_2322) + + npc(ChatAnim.WORRIED, "Wooo Woo ooo wooowoo!") // Placeholder taken from restless ghost, can't find source for his no-ghostspeak text + } +} \ No newline at end of file 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..70131a4ba 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."); 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..7a07c5a44 --- /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 + } + } +} 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..6c7bd9830 --- /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..b580aa4f3 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt @@ -17,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 @@ -131,8 +132,7 @@ class NSListeners : InteractionListener { } on(SPELLCARD, IntType.ITEM, "cast"){ player, node -> - if(NSUtils.castBloom(player)){ - removeItem(player, node.asItem(), Container.INVENTORY) + if (NSUtils.castBloom(player) && removeItem(player, node.asItem(), Container.INVENTORY)) { addItem(player, Items.A_USED_SPELL_2969) } return@on true @@ -140,7 +140,7 @@ class NSListeners : InteractionListener { on(intArrayOf(DRUID_POUCH, DRUID_POUCH_EMPTY), IntType.ITEM, "fill"){ player, node -> - if(player.questRepository.getStage("Nature Spirit") >= 75) { + if(player.questRepository.getStage(Quests.NATURE_SPIRIT) >= 75) { if (amountInInventory(player, PEAR) >= 3) { if (node.id != Items.DRUID_POUCH_2958) { removeItem(player, node, Container.INVENTORY) @@ -181,6 +181,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 +252,7 @@ class CompleteSpellPulse(val player: Player) : Pulse(2){ override fun pulse(): Boolean { when(counter++){ 0 -> repeat(6) { spawnProjectile(locations[it], dest, 268, 0, 1000, 0, 40, 20) } - 1 -> player.questRepository.getQuest("Nature Spirit").setStage(player, 60) + 1 -> player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, 60) 2 -> player.teleport(player.location.transform(0,0,1)) 3 -> openDialogue(player, NPCs.NATURE_SPIRIT_1051, findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC).also { unlock(player); return true } } diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt index e9332bd8e..7559ff085 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt @@ -6,7 +6,6 @@ import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.quest.Quest import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Graphics @@ -14,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++ } - 75 -> sendDialogue("The spirit of Filliman Tarlock gives you","another bloom spell.").also { addItem(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + 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 { addItemOrDrop(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.") - addItem(player, Items.DRUIDIC_SPELL_2968) + npcl(FacialExpression.NEUTRAL, "Sure, but look after this one.") + addItemOrDrop(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..1a59aa8df 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) { @@ -57,7 +58,7 @@ object NSUtils { if(pouchAmt == 1) shouldAddEmptyPouch = true if(pouchAmt > 0 && removeItem(player, Items.DRUID_POUCH_2958, Container.INVENTORY)){ if(shouldAddEmptyPouch){ - addItem(player, Items.DRUID_POUCH_2957) + addItemOrDrop(player, Items.DRUID_POUCH_2957) } spawnProjectile(player, attacker, 268) submitWorldPulse(object : Pulse(){ @@ -122,7 +123,7 @@ object NSUtils { */ 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..2d111e7cd 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; +import content.data.Quests; /** * Handles Islwyn's dialogue for Roving Elves. @@ -29,7 +30,7 @@ public class IslwynDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); switch (stage) { case 500: end(); @@ -171,7 +172,7 @@ public class IslwynDialogue extends DialoguePlugin { stage = 21; break; case 21: - interpreter.sendDoubleItemMessage(RovingElves.CRYSTAL_BOW_FULL, RovingElves.CRYSTAL_SHIELD_FULL, "Islwyn shows you a crystal bow and a crystal shield."); + interpreter.sendDoubleItemMessage(RovingElves.CRYSTAL_BOW_FULL.getId(), RovingElves.CRYSTAL_SHIELD_FULL.getId(), "Islwyn shows you a crystal bow and a crystal shield."); stage = 22; break; case 22: @@ -355,8 +356,8 @@ public class IslwynDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); - final Quest waterfall = player.getQuestRepository().getQuest("Waterfall"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); + final Quest waterfall = player.getQuestRepository().getQuest(Quests.WATERFALL_QUEST); if (quest.getStage(player) == 0 && waterfall.isCompleted(player)) { interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); stage = 0; diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java index 842f3cc9b..82685174f 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java @@ -13,6 +13,7 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Plugin; import core.plugin.ClassScanner; +import content.data.Quests; /** * The level 84 Moss Giant in Glarial's tomb. @@ -47,7 +48,7 @@ public final class MossGiantGuardianNPC extends AbstractNPC { super.finalizeDeath(killer); if (killer instanceof Player) { final Player player = (Player) killer; - final Quest quest = player.getQuestRepository().getQuest("Roving Elves"); + final Quest quest = player.getQuestRepository().getQuest(Quests.ROVING_ELVES); if (quest.getStage(player) == 15 && !player.getInventory().contains(RovingElves.CONSECRATION_SEED.getId(), 1)) { player.getPacketDispatch().sendMessages("A small grey seed drops on the ground."); GroundItemManager.create(new Item(RovingElves.CONSECRATION_SEED.getId()), getLocation(), player); diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java index 52546a877..91d68130e 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java @@ -6,6 +6,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import content.data.Quests; /** * The Roving Elves quest. @@ -43,7 +44,7 @@ public class RovingElves extends Quest { * Constructs a new {@Code RovingElves} {@Code Object} */ public RovingElves() { - super("Roving Elves", 105, 104, 1, 402, 0, 1, 6); + super(Quests.ROVING_ELVES, 105, 104, 1, 402, 0, 1, 6); } @Override diff --git a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java index 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..53bd862e5 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. @@ -118,7 +118,7 @@ public class BorkNPC extends AbstractNPC { @Override public void finalizeDeath(Entity killer){ super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); } @Override @@ -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..9e4f6f758 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. @@ -82,7 +82,7 @@ public class ChaosElementalNPC extends AbstractNPC { @Override public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + BossKillCounter.addToBossKillCount((Player) killer, this.getId()); } /** @@ -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..0b71e76e6 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.addToBossKillCount((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/KingBlackDragonArea.kt b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonArea.kt new file mode 100644 index 000000000..7940a2468 --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonArea.kt @@ -0,0 +1,14 @@ +package content.region.wilderness.handlers + +import core.api.* +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +class KingBlackDragonArea : MapArea { + override fun defineAreaBorders() : Array { + return arrayOf(ZoneBorders(2256, 4680, 2287, 4711, 0, true)) + } + override fun getRestrictions() : Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java index d7dd2c7b5..75f8a94de 100644 --- a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java @@ -14,12 +14,15 @@ import core.game.node.entity.impl.Animator.Priority; 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.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; 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 +31,218 @@ 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.addToBossKillCount((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) { + 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) { + if (style != CombatStyle.MELEE && victim.getImpactHandler().getDisabledTicks() <= GameWorld.getTicks()) { + fireType.getTask().exec(victim, entity); + } + 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/WildernessAreaZone.java b/Server/src/main/content/region/wilderness/handlers/WildernessAreaZone.java deleted file mode 100644 index acf3727c6..000000000 --- a/Server/src/main/content/region/wilderness/handlers/WildernessAreaZone.java +++ /dev/null @@ -1,575 +0,0 @@ -package content.region.wilderness.handlers; - -import core.game.dialogue.DialogueAction; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.global.action.DoorActionHandler; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import content.global.skill.gather.SkillingResource; -import content.data.skill.SkillingTool; -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.Entity; -import core.game.node.entity.combat.BattleState; -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.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.ZoneBuilder; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles the Wilderness Area Zone. - * @author Empathy - * @author Vexia - * - */ -@Initializable -public class WildernessAreaZone extends MapZone implements Plugin { - - /** - * The list of players in the arena. - */ - private final List players = new ArrayList<>(20); - - /** - * Constructs a new {@code WildernessAreaZone} {@code Object} - */ - public WildernessAreaZone() { - super("Wilderness Area Zone", true); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - ClassScanner.definePlugins(new MandrithDialoguePlugin(), new PilesDialoguePlugin(), new PilesItemHandler(), new RuniteGolemNPC()); - return this; - } - - @Override - public boolean interact(Entity e, final Node node, Option option) { - if (e instanceof Player) { - Player player = e.asPlayer(); - switch (node.getId()) { - case 42044: - switch (option.getName()) { - case "Open": - if (player.getLocation().equals(Location.create(3184, 3945, 0))) { - if (player.getInventory().contains(995, 7500)) { - player.getDialogueInterpreter().sendOptions("Pay 7500 coins?", "Yes", "No"); - player.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - switch (buttonId) { - case 2: - if (player.getInventory().remove(new Item(995, 7500))) { - DoorActionHandler.handleDoor(player, node.asScenery()); - player.sendMessage("You pay 7500 coins and enter the resource arena."); - } - break; - } - } - - }); - return true; - } - player.sendMessage("You do not have enough coins to enter the Arena."); - return true; - } - DoorActionHandler.handleDoor(player, node.asScenery()); - return true; - case "Players-inside": - if (player.getLocation().getY() < 3495) { - player.sendMessage("All you see is the barren wasteland of the Wilderness."); - return true; - } - String message = "You peek inside the gate and see no adventurers inside the arena."; - if (players.size() > 0) { - message = players.size() == 1 ? "You peek inside the gate and see 1 adventurer inside the arena." : "You peek inside the gate and see " + players.size() + " inside the arena."; - } - player.getDialogueInterpreter().sendDialogue(message); - break; - } - break; - case 8666: - switch (option.getName()) { - case "Mine": - player.getPulseManager().run(new RuniteGolemMinePulse(player, node.asNpc())); - return true; - case "Prospect": - player.sendMessages("You examine the rock for ores...", "This rock contains runite ore."); - return true; - } - break; - } - } - return super.interact(e, node, option); - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public void configure() { - register(new ZoneBorders(3174, 3924, 3196, 3945)); - } - - @Override - public boolean enter(Entity e) { - if (e instanceof Player) { - Player p = e.asPlayer(); - players.add(p); - } - return super.enter(e); - } - - @SuppressWarnings("deprecation") - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - Player p = e.asPlayer(); - players.remove(p); - if (logout && p.getLocation().getY() <= 3944) { - p.setLocation(Location.create(3184, 3945, 0)); - return true; - } - } - return super.leave(e, logout); - } - - /** - * The mining of a runite golem. - * @author Vexia - * - */ - public class RuniteGolemMinePulse extends SkillPulse { - - /** - * The skilling tool used. - */ - private SkillingTool tool; - - /** - * The ticks. - */ - private int ticks; - - /** - * Constructs a new @{Code RuniteGolemMinePulse} object. - * @param player The player. - * @param node The node. - */ - public RuniteGolemMinePulse(Player player, NPC node) { - super(player, node); - } - - @Override - public void start() { - resource = SkillingResource.RUNITE_ORE_0; - if (node.getId() != 8666) { - player.getPacketDispatch().sendMessage("This rock contains no ore."); - return; - } - if (resource == null) { - return; - } - super.start(); - } - - - @Override - public boolean checkRequirements() { - if (node.getId() != 8666) { - return false; - } - if (player.getSkills().getLevel(resource.getSkillId()) < resource.getLevel()) { - player.getPacketDispatch().sendMessage("You need a " + Skills.SKILL_NAME[resource.getSkillId()] + " level of " + resource.getLevel() + " to mine this rock."); - return false; - } - if ((tool = SkillingTool.getPickaxe(player)) == null) { - player.getPacketDispatch().sendMessage("You do not have a pickaxe to use."); - return false; - } - if (player.getInventory().freeSlots() < 1) { - player.getDialogueInterpreter().sendDialogue("Your inventory is too full to hold any more runite ore."); - return false; - } - return true; - } - - @Override - public void animate() { - if (tool != null) { - player.animate(tool.getAnimation()); - } - } - - @Override - public boolean reward() { - if (++ticks % 4 != 0) { - return false; - } - if (!checkReward()) { - return false; - } - node.transform(8667); - node.setAttribute("reward-tick", GameWorld.getTicks() + resource.getRespawnDuration()); - final Item item = new Item(resource.getReward()); - player.getInventory().add(item); - player.getSkills().addExperience(resource.getSkillId(), resource.getExperience(), true); - return true; - } - - /** - * Checks if the player gets rewarded. - * @return {@code True} if so. - */ - private boolean checkReward() { - int skill = Skills.MINING; - int level = 1 + player.getSkills().getLevel(skill) + player.getFamiliarManager().getBoost(skill); - double hostRatio = Math.random() * (100.0 * resource.getRate()); - double clientRatio = Math.random() * ((level - resource.getLevel()) * (1.0 + tool.getRatio())); - return hostRatio < clientRatio; - } - } - - /** - * Handles the runite golem npc. - * @author Vexia - * - */ - public class RuniteGolemNPC extends AbstractNPC { - - /** - * The respawn time. - */ - private int respawn = -1; - - /** - * Constructs a new @{Code RuniteGolemNPC} object. - * @param id The npc id. - * @param location The location. - */ - public RuniteGolemNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new @{Code RuniteGolemNPC} object. - */ - public RuniteGolemNPC() { - super(-1, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new RuniteGolemNPC(id, location); - } - - @Override - public void init() { - super.init(); - setAggressive(false); - } - - @Override - public void sendImpact(BattleState state) { - super.sendImpact(state); - if (state.getEstimatedHit() > 16) { - state.setEstimatedHit(16); - } - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (isAggressive()) { - setAggressive(false); - } - if (respawn != -1 && respawn < GameWorld.getTicks() && getId() > 8664) { - transform(8664); - unlock(); - } - if (getId() == 8667) { - int rewardTick = getAttribute("reward-tick", -1); - if (rewardTick != -1 && rewardTick < GameWorld.getTicks()) { - transform(8666); - } - } - } - - @Override - public void finalizeDeath(Entity killer) { - respawn = GameWorld.getTicks() + 500; - fullRestore(); - getProperties().setTeleportLocation(getLocation()); - setRespawnTick(0); - setInvisible(false); - transform(8666); - lock(); - faceLocation(null); - face(null); - } - - @Override - public int[] getIds() { - return new int[] {8664}; - } - - - } - - /** - * Handles the item on piles interaction. - * @author Vexia - * - */ - public static class PilesItemHandler extends UseWithHandler { - - /** - * The allowed items for piles. - */ - private static final int[] ALLOWED = new int[] {14937, 14939, 449, 447, 440, 444, 453, 451, 1515, 1513, 2349, 9467,2351,2355,2353,2357,2359,2361,2363}; - - /** - * Constructs a new @{Code PilesItemHandler} object. - */ - public PilesItemHandler() { - super(); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(8663, NPC_TYPE, this); - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - for (int itemId : ALLOWED) { - if (event.getUsedItem().getId() == itemId) { - final int amount = player.getInventory().getAmount(itemId); - final int price = 50 * amount; - if (!player.getInventory().contains(995, price)) { - player.getDialogueInterpreter().sendDialogues(8663, FacialExpression.OLD_NORMAL, "I'll need 50 coins per item."); - return true; - } - player.getDialogueInterpreter().sendOptions("Banknote " + amount + " x " + event.getUsedItem().getName() + "?", "Yes - " + price + " gp", "Cancel"); - player.getDialogueInterpreter().addAction((player1, buttonId) -> { - if (player1.getInventory().remove(new Item(995, price)) && player1.getInventory().remove(new Item(event.getUsedItem().getId(), amount))) { - player1.getInventory().add(new Item(event.getUsedItem().getNoteChange(), amount)); - player1.getDialogueInterpreter().sendItemMessage(event.getUsedItem().getId(), "Piles converts your items to banknotes."); - } - }); - return true; - } - } - player.getDialogueInterpreter().sendDialogues(8663, FacialExpression.OLD_NORMAL, "Sorry, I wasn't expecting anyone to want to convert", "that sort of item, so I haven't any banknotes for it."); - return true; - } - - @Override - public boolean isDynamic() { - return true; - } - } - - /** - * Handles the Piles dialogue. - * @author Vexia - * - */ - public class PilesDialoguePlugin extends DialoguePlugin { - - /** - * Constructs a new @{Code PilesDialoguePlugin} object. - */ - public PilesDialoguePlugin() { - /* - * empty. - */ - } - - /** - * Constructs a new @{Code PilesDialoguePlugin} object. - * @param player The player. - */ - public PilesDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PilesDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc("Hello. I can convert items to banknotes, for 50 coins", "per item. Just hand me the items you'd like me to", "convert."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Who are you?", "Thanks."); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 10; - break; - case 2: - player("Thanks."); - stage = 2; - break; - } - break; - case 2: - end(); - break; - case 10: - npc("I'm Piles. I lived in Draynor Village when I was", "young, where I saw three men working in the market,", "converting items to banknotes."); - stage++; - break; - case 11: - npc("Their names were Niles, Miles and Giles. I'm trying to", "be like them, so I've changed my name and started this", "business here."); - stage++; - break; - case 12: - player("Thanks."); - stage = 2; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {8663}; - } - - } - - /** - * Handles the Mandrith dialogue. - * @author Empathy - * - */ - public class MandrithDialoguePlugin extends DialoguePlugin { - - /** - * - * Constructs a new @{Code MandrithDialoguePlugin} object. - */ - public MandrithDialoguePlugin() { - /* - * empty - */ - } - - /** - * - * Constructs a new @{Code MandrithDialoguePlugin} object. - * @param player - */ - public MandrithDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MandrithDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - player("Who are you and what is this place?" ); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - npc("My name is Mandrith." ); - stage = 2; - break; - case 2: - npc("I collect valuable resources and pawn off access to them", "to foolish adventurers, like yourself" ); - stage = 3; - break; - case 3: - npc("You should take a look inside my arena. There's an", "abundance of valuable resources inside." ); - stage = 4; - break; - case 4: - player("And I can take whatever I want?" ); - stage = 5; - break; - case 5: - npc("It's all yours. All I ask is that you pay the upfront fee." ); - stage = 6; - break; - case 6: - player("Will others be able to kill me inside?" ); - stage = 7; - break; - case 7: - npc("Yes. These walls will only hold them back for so long." ); - stage = 8; - break; - case 8: - player("You'll stop them though, right?" ); - stage = 9; - break; - case 9: - npc("Haha! For the right price, I won't deny anyone access", "to my arena. Even if their intention is to kill you." ); - stage = 10; - break; - case 10: - player("Right..." ); - stage = 11; - break; - case 11: - npc("My arena holds many treasure that I've acquired at", "great expense, adventurer. Their bounty can come at a", "price." ); - stage = 12; - break; - case 12: - npc("One day, adventurer, I will boast ownership of a much", "larger, much more dangerous arena than this. Take", "advantage of this offer while it lasts." ); - stage = 13; - break; - case 13: - end(); - break; - } - return false; - } - - @Override - public int[] getIds() { - return new int[] { 8662 }; - } - } -} 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..0c509175f 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,27 @@ 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 (entity.getId() == 6998) { + applyPoison(victim, entity, 40); + } else if (WildernessZone.getWilderness(entity) >= 50) { + applyPoison(victim, entity, 30); } super.impact(entity, victim, state); } @@ -84,9 +89,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..fed2f91ec 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.tools.SystemLogger +import core.api.* +import core.game.node.entity.combat.InteractionType 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 @@ -29,29 +28,210 @@ class RevenantController : TickListener, Commands { private var expectedRevAmount: Int = ServerConstants.REVENANT_POPULATION private var groupPatrolQueue = ArrayList() + // Movement tracking for stuck detection + private val movementStartTick = HashMap() + private val lastKnownLocation = HashMap() + private val failedAttempts = HashMap() + // Stuck detector config + private const val STUCK_CHECK_INTERVAL = 10 // ticks + private const val MAX_FAILED_ATTEMPTS = 5 + @JvmStatic fun registerRevenant(revenantNPC: RevenantNPC) { trackedRevenants.add(revenantNPC) taskTimeRemaining[revenantNPC] = 0 currentTask[revenantNPC] = RevenantTask.NONE + movementStartTick[revenantNPC] = GameWorld.ticks + lastKnownLocation[revenantNPC] = revenantNPC.location + failedAttempts[revenantNPC] = 0 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) + movementStartTick.remove(revenantNPC) + lastKnownLocation.remove(revenantNPC) + failedAttempts.remove(revenantNPC) + groupPatrolQueue.remove(revenantNPC) + if (removeRender) + Repository.RENDERABLE_NPCS.remove(revenantNPC) } + private fun route(vararg coords: Pair): Array = + Array(coords.size) { Location.create(coords[it].first, coords[it].second) } + + // All routes that revs attempt to path through val routes = listOf( - arrayOf(Location.create(3070, 3651), Location.create(3083, 3640), Location.create(3106, 3645), Location.create(3133, 3647), Location.create(3149, 3642), Location.create(3160, 3654), Location.create(3171, 3665, 0), Location.create(3189, 3663, 0), Location.create(3202, 3675, 0), Location.create(3217, 3660, 0), Location.create(3235, 3661, 0), Location.create(3235, 3661, 0), Location.create(3279, 3650, 0), Location.create(3269, 3636, 0), Location.create(3253, 3632, 0), Location.create(3236, 3638, 0), Location.create(3220, 3637, 0), Location.create(3203, 3634, 0), Location.create(3187, 3631, 0), Location.create(3166, 3633, 0), Location.create(3160, 3616, 0), Location.create(3148, 3604, 0), Location.create(3134, 3596, 0), Location.create(3118, 3590, 0), Location.create(3104, 3597, 0)), - arrayOf(Location.create(3077, 3565, 0), Location.create(3093, 3559, 0), Location.create(3110, 3566, 0), Location.create(3127, 3574, 0), Location.create(3146, 3571, 0), Location.create(3164, 3575, 0), Location.create(3183, 3573, 0), Location.create(3197, 3587, 0), Location.create(3215, 3584, 0), Location.create(3233, 3576, 0), Location.create(3251, 3573, 0), Location.create(3269, 3577, 0), Location.create(3287, 3569, 0), Location.create(3305, 3568, 0), Location.create(3321, 3576, 0), Location.create(3338, 3584, 0), Location.create(3352, 3573, 0), Location.create(3354, 3554, 0), Location.create(3342, 3541, 0), Location.create(3324, 3536, 0), Location.create(3306, 3543, 0), Location.create(3290, 3544, 0), Location.create(3272, 3545, 0), Location.create(3255, 3546, 0), Location.create(3239, 3539, 0), Location.create(3222, 3543, 0), Location.create(3206, 3548, 0), Location.create(3189, 3549, 0), Location.create(3173, 3552, 0), Location.create(3157, 3549, 0), Location.create(3140, 3548, 0), Location.create(3122, 3548, 0), Location.create(3110, 3555, 0)), - arrayOf(Location.create(3318, 3691, 0), Location.create(3307, 3700, 0), Location.create(3290, 3696, 0), Location.create(3277, 3706, 0), Location.create(3260, 3706, 0), Location.create(3250, 3707, 0), Location.create(3245, 3723, 0), Location.create(3254, 3735, 0), Location.create(3251, 3754, 0), Location.create(3243, 3768, 0), Location.create(3253, 3780, 0), Location.create(3238, 3783, 0), Location.create(3224, 3793, 0), Location.create(3206, 3786, 0), Location.create(3192, 3780, 0), Location.create(3170, 3787, 0), Location.create(3156, 3800, 0), Location.create(3148, 3814, 0), Location.create(3148, 3814, 0), Location.create(3127, 3840, 0), Location.create(3124, 3856, 0), Location.create(3124, 3872, 0), Location.create(3116, 3892, 0)), - arrayOf(Location.create(2949, 3890, 0), Location.create(2965, 3899, 0), Location.create(2984, 3900, 0), Location.create(2998, 3895, 0), Location.create(3016, 3898, 0), Location.create(3032, 3893, 0), Location.create(3048, 3897, 0), Location.create(3068, 3894, 0), Location.create(3084, 3898, 0), Location.create(3101, 3895, 0), Location.create(3118, 3897, 0), Location.create(3136, 3893, 0), Location.create(3154, 3900, 0), Location.create(3172, 3895, 0), Location.create(3189, 3892, 0), Location.create(3206, 3897, 0), Location.create(3222, 3890, 0), Location.create(3240, 3897, 0), Location.create(3259, 3892, 0), Location.create(3278, 3895, 0), Location.create(3296, 3892, 0), Location.create(3313, 3899, 0), Location.create(3331, 3888, 0), Location.create(3345, 3880, 0)), - arrayOf(Location.create(3308, 3941, 0), Location.create(3301, 3925, 0), Location.create(3287, 3915, 0), Location.create(3276, 3922, 0), Location.create(3266, 3938, 0), Location.create(3267, 3952, 0), Location.create(3250, 3949, 0), Location.create(3235, 3944, 0), Location.create(3219, 3944, 0), Location.create(3206, 3938, 0), Location.create(3194, 3929, 0), Location.create(3182, 3921, 0), Location.create(3174, 3936, 0), Location.create(3180, 3952, 0), Location.create(3167, 3960, 0), Location.create(3155, 3959, 0), Location.create(3141, 3953, 0), Location.create(3126, 3954, 0), Location.create(3110, 3961, 0), Location.create(3093, 3962, 0), Location.create(3078, 3953, 0), Location.create(3066, 3942, 0), Location.create(3059, 3929, 0), Location.create(3049, 3916, 0), Location.create(3033, 3924, 0), Location.create(3020, 3921, 0), Location.create(3010, 3913, 0), Location.create(2993, 3906, 0), Location.create(2977, 3911, 0), Location.create(2970, 3928, 0)) + route( + 3070 to 3651, 3083 to 3640, 3106 to 3645, 3133 to 3647, 3149 to 3642, + 3160 to 3654, 3171 to 3665, 3189 to 3663, 3202 to 3675, 3217 to 3660, + 3235 to 3661, 3235 to 3661, 3279 to 3650, 3269 to 3636, 3253 to 3632, + 3236 to 3638, 3220 to 3637, 3203 to 3634, 3187 to 3631, 3166 to 3633, + 3160 to 3616, 3148 to 3604, 3134 to 3596, 3118 to 3590, 3104 to 3597, + ), + route( + 3077 to 3565, 3093 to 3559, 3110 to 3566, 3127 to 3574, 3146 to 3571, + 3164 to 3575, 3183 to 3573, 3197 to 3587, 3215 to 3584, 3233 to 3576, + 3251 to 3573, 3269 to 3577, 3287 to 3569, 3305 to 3568, 3321 to 3576, + 3338 to 3584, 3352 to 3573, 3354 to 3554, 3342 to 3541, 3324 to 3536, + 3306 to 3543, 3290 to 3544, 3272 to 3545, 3255 to 3546, 3239 to 3539, + 3222 to 3543, 3206 to 3548, 3189 to 3549, 3173 to 3552, 3157 to 3549, + 3140 to 3548, 3122 to 3548, 3110 to 3555, + ), + route( + 3318 to 3691, 3307 to 3700, 3290 to 3696, 3277 to 3706, 3260 to 3706, + 3250 to 3707, 3245 to 3723, 3254 to 3735, 3251 to 3754, 3243 to 3768, + 3253 to 3780, 3238 to 3783, 3224 to 3793, 3206 to 3786, 3192 to 3780, + 3170 to 3787, 3156 to 3800, 3148 to 3814, 3148 to 3814, 3127 to 3840, + 3124 to 3856, 3124 to 3872, 3116 to 3892, + ), + route( + 2949 to 3890, 2965 to 3899, 2984 to 3900, 2998 to 3895, 3016 to 3898, + 3032 to 3893, 3048 to 3897, 3068 to 3894, 3084 to 3898, 3101 to 3895, + 3118 to 3897, 3136 to 3893, 3154 to 3900, 3172 to 3895, 3189 to 3892, + 3206 to 3897, 3222 to 3890, 3240 to 3897, 3259 to 3892, 3278 to 3895, + 3296 to 3892, 3313 to 3899, 3331 to 3888, 3345 to 3880, + ), + route( + 3308 to 3941, 3301 to 3925, 3287 to 3915, 3276 to 3922, 3266 to 3938, + 3267 to 3952, 3250 to 3949, 3235 to 3944, 3219 to 3944, 3206 to 3938, + 3194 to 3929, 3182 to 3921, 3174 to 3936, 3180 to 3952, 3167 to 3960, + 3155 to 3959, 3141 to 3953, 3126 to 3954, 3110 to 3961, 3093 to 3962, + 3078 to 3953, 3066 to 3942, 3059 to 3929, 3049 to 3916, 3033 to 3924, + 3020 to 3921, 3010 to 3913, 2993 to 3906, 2977 to 3911, 2970 to 3928, + ), + route( + 3308 to 3941, 3301 to 3925, 3297 to 3914, 3286 to 3910, 3259 to 3912, + 3267 to 3952, 3251 to 3921, 3236 to 3918, 3222 to 3922, 3209 to 3920, + 3190 to 3933, 3177 to 3939, 3175 to 3951, 3167 to 3960, 3155 to 3959, + 3135 to 3951, 3134 to 3921, 3126 to 3914, 3108 to 3907, 3087 to 3910, + 3077 to 3925, 3076 to 3937, 3066 to 3942, 3059 to 3929, 3049 to 3916, + 3033 to 3924, 3020 to 3921, 3010 to 3913, 2993 to 3906, 2977 to 3911, + 2970 to 3928, + ), ) - val spawnLocations = listOf(Location.create(3075, 3553, 0), Location.create(3077, 3563, 0), Location.create(3077, 3578, 0), Location.create(3093, 3581, 0), Location.create(3103, 3570, 0), Location.create(3101, 3564, 0), Location.create(3030, 3596, 0), Location.create(3015, 3598, 0), Location.create(3000, 3593, 0), Location.create(2986, 3588, 0), Location.create(2969, 3701, 0), Location.create(2982, 3689, 0), Location.create(2967, 3689, 0), Location.create(2953, 3711, 0), Location.create(2966, 3759, 0), Location.create(2989, 3759, 0), Location.create(2986, 3741, 0), Location.create(2961, 3763, 0), Location.create(2969, 3808, 0), Location.create(3004, 3816, 0)) + // Routes that Dark Beasts are too fat for + private val largeNpcIncompatibleRoutes = setOf(4) + + val spawnLocations = listOf( + 3075 to 3553, 3077 to 3563, 3077 to 3578, 3093 to 3581, 3103 to 3570, + 3101 to 3564, 3030 to 3596, 3015 to 3598, 3000 to 3593, 2986 to 3588, + 2969 to 3701, 2982 to 3689, 2967 to 3689, 2953 to 3713, 2966 to 3759, + 2989 to 3759, 2986 to 3741, 2961 to 3763, 2969 to 3808, 3004 to 3816, + ).map { (x, y) -> Location.create(x, y) } + + /** + * Revenant stuck detector + * Makes sure Revenants are either moving, intentionally waiting to act, or are in legitimate combat + * If they aren't, they're passed to the stuck handler + */ + private fun checkIfStuck(revenantNPC: RevenantNPC): Boolean { + val lastTick = movementStartTick[revenantNPC] ?: 0 + val lastLoc = lastKnownLocation[revenantNPC] ?: revenantNPC.location + + // Period between checks - configure above + if (lastTick == 0 || (GameWorld.ticks - lastTick) < STUCK_CHECK_INTERVAL) { + return false + } + + // Check if positioned exactly at a patrol route start location (likely intentionally inactive) + val routeStartLocations = routes.map { it[0] } + val isAtRouteStart = routeStartLocations.any { it.equals(revenantNPC.location) } + + // Check if in legitimate combat + val combatPulse = revenantNPC.properties.combatPulse + val victim = combatPulse.getVictim() + val isInLegitimateCombat = if (victim != null) { + val interactionType = combatPulse.canInteract() + interactionType == InteractionType.STILL_INTERACT // The merits of MOVE_INTERACT regarding establishing a legitimate combat state are covered by the distanceMoved check a few lines from now + } else { + false + } + + if (isAtRouteStart || isInLegitimateCombat) { //If the Revenant has a good reason to not be moving, skip the movement check + movementStartTick[revenantNPC] = GameWorld.ticks + lastKnownLocation[revenantNPC] = revenantNPC.location + failedAttempts[revenantNPC] = 0 + return false + } + + // Check if the revenant has moved + val distanceMoved = revenantNPC.location.getDistance(lastLoc) + val isStuck = distanceMoved <= 2 + + // Reset the timer to check if they are stuck, since we are checking now + movementStartTick[revenantNPC] = GameWorld.ticks + + // Clear the Revenant's demerits towards being timed out if it is not stuck + if (!isStuck) { + lastKnownLocation[revenantNPC] = revenantNPC.location + failedAttempts[revenantNPC] = 0 + } + + return isStuck + } + + /** + * Revenant stuck handler + * Tries to help the Revenant escape after giving it a demerit + * If the Revenant gets too many demerits, it will be timed out instead + * NOTE: times out Revenants in unloaded regions as they have no land to path to and thus cannot move + */ + private fun handleStuckRevenant(revenantNPC: RevenantNPC) { + val attempts = failedAttempts[revenantNPC] ?: 0 + failedAttempts[revenantNPC] = attempts + 1 + + if (attempts + 1 > MAX_FAILED_ATTEMPTS) { + poofClear(revenantNPC) + revenantNPC.setAttribute("done", true) + return + } + + // The stuck Revenant will attempt to unstick itself + val escapeLocation = getNextLocation(revenantNPC) + revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, escapeLocation, Pathfinder.SMART) { + override fun pulse(): Boolean { + return true + } + }) + } + + /** + * Finds a random location to try to move to that is within the Wilderness + */ + fun getNextLocation(revenantNPC: RevenantNPC): Location { + val rawNextX = RandomFunction.random(-revenantNPC.walkRadius, revenantNPC.walkRadius) + val nextX = maxOf(rawNextX, 2951) + val rawNextY = RandomFunction.random(-revenantNPC.walkRadius, revenantNPC.walkRadius) + val nextY = maxOf(rawNextY, 3523) + return revenantNPC.location.transform(nextX, nextY, 0) + } + + /** + * Determines how far Revenants can deviate from their path + */ + private fun getPathVariance(revenantNPC: RevenantNPC, isGroup: Boolean): Int { + return when { + isGroup -> 4 + revenantNPC.size() >= 3 -> 2 // Dark Beasts are fat, so keep them close to their safe routes + else -> 10 + } + } + + /** + * Gets a random route for patrols + * Will not assign Dark Beasts to routes they are too fat for + */ + private fun getSuitableRoute(revenantNPC: RevenantNPC): Array { + val availableRoutes = if (revenantNPC.size() >= 3) { + routes.filterIndexed { index, _ -> index !in largeNpcIncompatibleRoutes } + } else { + routes + } + return availableRoutes.random() + } } override fun tick() { @@ -87,20 +267,19 @@ class RevenantController : TickListener, Commands { } override fun defineCommands() { - define("setrevcap", Privilege.ADMIN) {player, strings -> + define("setrevcap", Privilege.ADMIN, "::setrevcap amount", "Sets revenant spawn cap to amount") {player, strings -> val amt = strings[1].toInt() expectedRevAmount = amt } - define("listrevs", Privilege.ADMIN) {player, strings -> + define("listrevs", Privilege.ADMIN, description = "Logs the IDs and locations of all tracked revenants.") {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) {_, _ -> + define("clearrevs", Privilege.ADMIN, description = "Despawns all revenants.") {_, _ -> for (rev in trackedRevenants.toTypedArray()) rev.clear() } } @@ -110,45 +289,35 @@ 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)) { + if (checkIfStuck(revenantNPC)) { + handleStuckRevenant(revenantNPC) + return + } + + if (!canMoveBasic(revenantNPC)) return + + 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 } }) } - - override fun assign(revenantNPC: RevenantNPC) { - taskTimeRemaining[revenantNPC] = RandomFunction.random(MAX_ROAM_TICKS) - } - - fun canMove(revenantNPC: RevenantNPC) : Boolean { - return !revenantNPC.walkingQueue.isMoving + private fun canMoveBasic(revenantNPC: RevenantNPC): Boolean { + return !revenantNPC.walkingQueue.isMoving + && !revenantNPC.pulseManager.hasPulseRunning() && !revenantNPC.properties.combatPulse.isAttacking && !revenantNPC.properties.combatPulse.isInCombat } - fun getNextLocation(revenantNPC: RevenantNPC) : Location { - val nextX = RandomFunction.random(-revenantNPC.walkRadius, revenantNPC.walkRadius) - val nextY = RandomFunction.random(-revenantNPC.walkRadius, revenantNPC.walkRadius) - return revenantNPC.location.transform(nextX, nextY, 0) + override fun assign(revenantNPC: RevenantNPC) { + taskTimeRemaining[revenantNPC] = RandomFunction.random(MAX_ROAM_TICKS) } }, PATROLLING_ROUTE { @@ -158,7 +327,7 @@ class RevenantController : TickListener, Commands { if (canGroup(revenantNPC)) { addToPatrolGroup(revenantNPC) } else { - revenantNPC.setAttribute("route", routes.random()) + revenantNPC.setAttribute("route", getSuitableRoute(revenantNPC)) revenantNPC.setAttribute("routeidx", -1) } } @@ -168,7 +337,9 @@ class RevenantController : TickListener, Commands { groupPatrolQueue.add(revenantNPC) if (groupPatrolQueue.size == 3) { - val groupRoute = routes.random() + // Gets a route suitable for the largest NPC in the group + val largestNPC = groupPatrolQueue.maxByOrNull { it.size() } ?: revenantNPC + val groupRoute = getSuitableRoute(largestNPC) for (rev in groupPatrolQueue) { rev.setAttribute("route", groupRoute) rev.setAttribute("routeidx", -1) @@ -185,7 +356,12 @@ class RevenantController : TickListener, Commands { val route = revenantNPC.getAttribute>("route", null) val routeIdx = revenantNPC.getAttribute("routeidx", -1) - if (!canMove(revenantNPC)) return + if (checkIfStuck(revenantNPC)) { + handleStuckRevenant(revenantNPC) + return + } + + if (!canMoveAdvanced(revenantNPC)) return if (isGroup && route == null) { //if this is a grouped rev and we are waiting on more revs still taskTimeRemaining[revenantNPC] = 50 //just to make sure it doesn't time out roaming... @@ -208,13 +384,13 @@ class RevenantController : TickListener, Commands { revenantNPC.setAttribute("done", true) return } - val pathVariance = if (isGroup) 4 else 10 + val pathVariance = getPathVariance(revenantNPC, isGroup) val nextLoc = route[routeIdx].transform( RandomFunction.random(-pathVariance, pathVariance), 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 } @@ -222,9 +398,8 @@ class RevenantController : TickListener, Commands { revenantNPC.setAttribute("routeidx", routeIdx + 1) } } - - fun canMove(revenantNPC: RevenantNPC) : Boolean { - return !revenantNPC.walkingQueue.isMoving + private fun canMoveAdvanced(revenantNPC: RevenantNPC) : Boolean { + return !revenantNPC.walkingQueue.isMoving && !revenantNPC.pulseManager.hasPulseRunning() && !revenantNPC.properties.combatPulse.isAttacking && !revenantNPC.properties.combatPulse.isInCombat @@ -237,4 +412,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..cf46e2823 100644 --- a/Server/src/main/core/Server.kt +++ b/Server/src/main/core/Server.kt @@ -1,20 +1,25 @@ 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.net.websocket.GameWebSocketServer +import core.net.websocket.WebSocketTls +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 +46,11 @@ object Server { @JvmStatic var reactor: NioReactor? = null + @JvmStatic + var webSocketServer: GameWebSocketServer? = 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 +61,37 @@ 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() + if (ServerConstants.WEBSOCKET_ENABLED) { + val websocketPort = if (ServerConstants.WEBSOCKET_PORT > 0) { + ServerConstants.WEBSOCKET_PORT + } else { + 53594 + GameWorld.settings?.worldId!! + } + webSocketServer = GameWebSocketServer(websocketPort, 1) + WebSocketTls.configure(webSocketServer!!) + webSocketServer!!.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 +100,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 +113,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 +138,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..8c5f493a4 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -2,264 +2,392 @@ 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 = false - @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_GLOBAL_CHAT = false + + @JvmField + var MAX_PATHFIND_DISTANCE = 25 + + @JvmField + var XP_RATES = false + + @JvmField + var IRONMAN = 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 = false + + @JvmField + var BETTER_DFS = false + + @JvmField + var NEW_PLAYER_ANNOUNCEMENT = false + + @JvmField + var INAUTHENTIC_CANDLELIGHT_RANDOM = false + + @JvmField + var HOLIDAY_EVENT_RANDOMS = false + + @JvmField + var FORCE_HALLOWEEN_EVENTS = false + + @JvmField + var FORCE_CHRISTMAS_EVENTS = false + + @JvmField + var FORCE_EASTER_EVENTS = false + + @JvmField + var APRIL_FOOLS_EVENT = false + + @JvmField + var FORCE_APRIL_FOOLS = false + + @JvmField + var RUNECRAFTING_FORMULA_REVISION = 530 + + @JvmField + var ENHANCED_DEEP_WILDERNESS = false + + @JvmField + var WILDERNESS_EXCLUSIVE_LOOT = false + + @JvmField + var SHOOTING_STAR_RING = false + + @JvmField + var RING_OF_WEALTH_TELEPORT = false + + @JvmField + var SECOND_BANK = false + + @JvmField + var PLAYER_COMMANDS = false + + @JvmField + var BOOSTED_TRAWLER_REWARDS = false + + @JvmField + var STARTUP_MOMENT = Calendar.getInstance() + + @JvmField + var CONNECTIVITY_CHECK_URL = "https://google.com,https://2009scape.org" + + @JvmField + var CONNECTIVITY_TIMEOUT = 500 + + @JvmField + var WEBSOCKET_ENABLED = false + + @JvmField + var WEBSOCKET_PORT = 0 + + @JvmField + var WEBSOCKET_TLS_ENABLED = false + + @JvmField + var WEBSOCKET_TLS_KEYSTORE_PATH = "" + + @JvmField + var WEBSOCKET_TLS_KEYSTORE_PASSWORD = "" + } } 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/Container.kt b/Server/src/main/core/api/Container.kt index 590d94a61..bdcfd4900 100644 --- a/Server/src/main/core/api/Container.kt +++ b/Server/src/main/core/api/Container.kt @@ -3,5 +3,6 @@ package core.api enum class Container { INVENTORY, BANK, - EQUIPMENT + EQUIPMENT, + BoB } \ No newline at end of file diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index d9242a991..7423518a7 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -1,36 +1,68 @@ 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.construction.decoration.pohstorage.StorableFamily +import content.global.skill.construction.decoration.pohstorage.StorageInterface +import content.global.skill.slayer.SlayerEquipmentFlags +import content.global.skill.slayer.SlayerFlags +import content.global.skill.slayer.SlayerManager +import content.global.skill.slayer.Tasks +import content.global.skill.summoning.familiar.BurdenBeast +import core.ServerConstants +import core.api.utils.PlayerStatsCounter +import core.api.utils.Vector +import core.cache.def.impl.AnimationDefinition import core.cache.def.impl.ItemDefinition import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.VarbitDefinition +import core.game.activity.Cutscene import core.game.component.Component +import core.game.consumable.* import core.game.container.impl.EquipmentContainer +import core.game.dialogue.DialogueFile +import core.game.dialogue.SkillDialogueHandler +import core.game.diary.DiaryLevel +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.diary.DiaryType 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 +71,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 +120,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 +134,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 +162,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 +240,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,17 +266,58 @@ 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 { + // Search bank/equip/inv and return if item is found there. + 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) } } } + + // Check all costume storage containers and return if item is found there. + for (id in ids) { + if (StorableFamily.values().any { family -> + player.getPOHStorageState() + .getContainer(family) + .contains(id) + }) { + // Note that this proxy container is a fake container just used to get the correct return type. + return ContainerisedItem(player.POHStorageProxyContainer, id) + } + } + + // Item is not found. return ContainerisedItem(null, -1) } @@ -194,7 +328,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 } @@ -226,9 +360,33 @@ fun removeItem(player: Player, item: T, container: Container = Container.INV Container.INVENTORY -> player.inventory.remove(it) Container.BANK -> player.bank.remove(it) || player.bankSecondary.remove(it) Container.EQUIPMENT -> player.equipment.remove(it) + Container.BoB -> { + if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast) + (player.familiarManager.familiar as BurdenBeast).container.remove(it) + else + false + } } } +/** + * Remove items in a players inventory + * Checks that the player has enough first + * @param player + * @param items the items to remove + */ +fun removeItemsIfPlayerHasEnough(player: Player, vararg items: Item): Boolean { + for (item in items) { + if (amountInInventory(player, item.id) >= 1) continue + return false + } + for (item in items) { + if (removeItem(player, item)) continue + return false + } + return true +} + /** * Add an item to the given player's given container * @param player the player whose container to modify @@ -242,6 +400,12 @@ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Con Container.INVENTORY -> player.inventory Container.BANK -> player.bank Container.EQUIPMENT -> player.equipment + Container.BoB -> { + if(player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast) + (player.familiarManager.familiar as BurdenBeast).container + else + return false + } } return cont.add(Item(id, amount)) @@ -252,6 +416,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. */ @@ -260,24 +425,74 @@ fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null Container.INVENTORY -> player.inventory Container.EQUIPMENT -> player.equipment Container.BANK -> player.bank + Container.BoB -> (player.familiarManager.familiar as BurdenBeast).container + } + + if (item.id == 65535 || item.amount <= 0) { + return cont.replace(null, slot) } if (currentItem == null) { return cont.replace(item, slot) } - if (cont.remove(currentItem)) - return cont.replace(item, slot) + if (cont.remove(currentItem, slot, true)) { + return cont.replace(item, slot) ?: currentItem + } + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential slot-replacement-based dupe attempt, slot: $slot, item: $item") val other = when (container) { Container.INVENTORY -> Container.EQUIPMENT else -> Container.INVENTORY } - if (removeItem(player, currentItem, other)) - return cont.replace(item, slot) + if (removeItem(player, currentItem, other)) { + return cont.replace(item, slot) ?: currentItem + } 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 +501,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 +550,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 +595,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 +612,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 +679,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 +731,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 +747,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 +874,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 +920,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 +936,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 +994,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 +1050,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 +1109,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 +1175,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 @@ -890,6 +1342,25 @@ fun transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int) { }) } +/** + * Transforms an NPC for the given number of ticks with hp carryover + * @param npc the NPC object to transform + * @param transformTo the ID of the NPC to turn into + * @param restoreTicks the number of ticks until the NPC returns to normal + */ +fun transformNpcWithHpCarryover(npc: NPC, transformTo: Int, restoreTicks: Int) { + npc.transformWithHpCarryover(transformTo) + queueScript(npc, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> return@queueScript delayScript(npc, restoreTicks) + else -> { + npc.reTransform() + return@queueScript stopExecuting(npc) + } + } + } +} + /** * Produces a Location object using the given x,y,z values */ @@ -918,61 +1389,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 +1470,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 +1556,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 +1572,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 +1585,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 +1599,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 +1640,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) } /** @@ -1192,6 +1736,14 @@ fun removeAll(player: Player, item: T, container: Container = Container.INVE player.bank.remove(Item(it, amountInPrimary)) && player.bankSecondary.remove(Item(it, amountInSecondary)) } Container.INVENTORY -> player.inventory.remove(Item(it, amountInInventory(player, it))) + Container.BoB -> { + if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar.isBurdenBeast){ + val cont = (player.familiarManager.familiar as BurdenBeast).container + cont.remove(Item(it, cont.getAmount(it))) + } + else + false + } } } @@ -1206,6 +1758,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 +1785,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 +1817,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 +1882,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 +1919,11 @@ fun sendItemDialogue(player: Player, item: Any, message: String) { * @param item2 the ID of the second item to show * @param message the text to display */ -fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: String) { - player.dialogueInterpreter.sendDoubleItemMessage(item1, item2, message) +fun sendDoubleItemDialogue(player: Player, item1: Any, item2: Any, message: String) { + when (item1) { + is Item -> player.dialogueInterpreter.sendDoubleItemMessage(item1, item2 as Item, *splitLines(message)) + is Int -> player.dialogueInterpreter.sendDoubleItemMessage(item1, item2 as Int, *splitLines(message)) + } } /** @@ -1359,6 +1962,7 @@ fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: } player.setAttribute("runscript", handler) + player.setAttribute("input-type", type) } /** @@ -1393,7 +1997,7 @@ fun runTask(entity: Entity, delay: Int = 0, repeatTimes: Int = 1, task: () -> Un entity.pulseManager.run(object : Pulse(delay) { override fun pulse(): Boolean { task.invoke() - return --cycles == 0 + return --cycles <= 0 } }) } @@ -1403,37 +2007,58 @@ 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 quests' stage for a given player is (inclusively) between the provided start and end stage + */ +fun isQuestInProgress(player: Player, quest: Quests, startStage: Int, endStage: Int): Boolean { + return player.questRepository.getStage(quest) in startStage..endStage +} + +fun isQuestStarted(player: Player, quest: Quests): Boolean { + return getQuestStage(player, quest) > 0 +} + /** * Check if a quest is complete */ -fun isQuestComplete(player: Player, quest: String): Boolean { +fun isQuestComplete(player: Player, quest: Quests): Boolean { return player.questRepository.getStage(quest) == 100 } +/** + * 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 +2069,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 +2132,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 @@ -1552,6 +2264,10 @@ fun dumpContainer(player: Player, container: core.game.container.Container): Int var dumpedCount = 0 run beginDepositing@{ + if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + return@beginDepositing + } + container.toArray().filterNotNull().forEach { item -> if (!bank.hasSpaceFor(item)) { sendMessage(player, "You have no more space in your bank.") @@ -1567,10 +2283,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 +2399,7 @@ 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) + return inEquipmentOrInventory(player, Items.SEAL_OF_PASSAGE_9083) } /** @@ -1697,6 +2415,68 @@ fun Player.getCutscene(): Cutscene? { return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE, null) } +/** + * Stores a book in the POH storage + * If this is the first time reading the book, store it in POH bookcase if eligible + * @param player the player + * @param itemID the ID of the book that was interacted with + * @return True if the item was identified and processed (whether read or not) + * False if the node was not a book or did not have a bitIndex + */ +fun storeBookInHouse(player: Player, itemId: Int): Boolean { + // find the corresponding Storable object + val storable = StorageInterface.findStorableByDisplayId(itemId) ?: return false + + // check if the storable is tracked + if (storable.bitIndex == -1) { + return true + } + + // retrieve bitfields where the values of already-read books are stored, and if the book has been read previously + val bits = StorageInterface.BookcaseBitfields.get(player) + val isRead = StorageInterface.BookcaseBitfields.isBitSet(bits, storable.bitIndex) + + // special check for games book. It isn't normally available and is only obtainable from the bookcase. You can't read it without first having it, and if you lose it you can't go back to somewhere else to get a new copy. Thus, it is always spawned in. + if(itemId == Items.GAME_BOOK_7681) { + val updatedBits = StorageInterface.BookcaseBitfields.setBit(bits, storable.bitIndex) + val container = StorageInterface.getStorageContainer(player, StorableFamily.BOOKCASE) + if (!isRead) { + StorageInterface.BookcaseBitfields.set(player, updatedBits) + container.addItem(itemId) + } else { + container.addItem(itemId) + } + + return true + } + + // if book is not read, store it in house + if (!isRead) { + // update bitfields + val updatedBits = StorageInterface.BookcaseBitfields.setBit(bits, storable.bitIndex) + StorageInterface.BookcaseBitfields.set(player, updatedBits) + + // add to the POH container + val container = StorageInterface.getStorageContainer(player, StorableFamily.BOOKCASE) + container.addItem(itemId) + } + return true +} + +/** + * Stores a book in the POH storage + * If this is the first time reading the book, store it in POH bookcase if eligible + * Used inside interaction listeners ("read") on nodes (books) + * @param player the player + * @param node the node (book) that was interacted with + * @return True if the item was identified and processed (whether read or not) + * False if the node was not a book or did not have a bitIndex + */ +fun storeBookInHouse(player: Player, node: Node): Boolean { + val bookId = (node as? Item)?.id ?: return false + return storeBookInHouse(player, bookId) +} + fun Player.getCutsceneStage(): Int { return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE_STAGE, 0) } @@ -1706,15 +2486,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 +2504,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 +2528,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 +2586,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.") } @@ -1800,7 +2599,11 @@ fun getSlayerTip(player: Player): Array { * @return the task flags as Int. */ fun getSlayerTaskFlags(player: Player): Int { - return SlayerManager.getInstance(player).flags.taskFlags + return getSlayerFlags(player).taskFlags +} + +fun getSlayerFlags(player: Player): SlayerFlags { + return SlayerManager.getInstance(player).flags } /** @@ -2185,52 +2988,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 +3021,368 @@ 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_TICKS + ticks. + * @param entity the entity whose clock we are updating + * @param clock the clock we are updating. Please use [core.game.interaction.Clocks] for this argument. + * @param ticks the number of ticks to delay by + * @param delayScript whether to delay the script by the number of ticks passed in. + * @return always returns false so this can be used as a script return value. +**/ +fun delayClock(entity: Entity, clock: Int, ticks: Int, delayScript: Boolean = false) : Boolean { + entity.clocks[clock] = getWorldTicks() + ticks + if (delayScript) { + return delayScript(entity, 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 + } + if(entity.isPoisonImmune()) { + return + } + val existingTimer = getTimer(entity) + + if (existingTimer != null) { + if (existingTimer.severity > severity) { + return + } else { + 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)) +} + +/** + * Finish an achievement diary task for the player. + * @param player the player who just finished the task. + * @param diary the diary the task belongs to. + * @param level the level of the task. + * @param task The task that was just finished. + */ +fun finishTask(player: Player, diary: DiaryType, level: DiaryLevel, task: Int) { + player.achievementDiaryManager.finishTask(player, diary, getDiaryLevelIndex(diary, level), task) +} + +/** + * Resolves a [DiaryLevel] to an index for a specific diary. + * @param diary The diary the task belongs to. + * @param level The task level in the diary. + * @throws IllegalArgumentException if the level is not present in the diary. + * @return the index of the level that can be used with [core.game.node.entity.player.link.diary.AchievementDiaryManager.finishTask]. + */ +fun getDiaryLevelIndex(diary: DiaryType, level: DiaryLevel): Int { + val levelName = level.name.lowercase().replaceFirstChar { c -> c.uppercase() } + val levelIndex = diary.levelNames.indexOf(levelName) + + if (levelIndex < 0) { + throw IllegalArgumentException("'$levelName' was not found in diary '$diary'.") + } + + return levelIndex +} + +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..a572dc4bb 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 @@ -21,6 +23,7 @@ object Event { @JvmStatic val AttributeSet = AttributeSetEvent::class.java @JvmStatic val AttributeRemoved = AttributeRemoveEvent::class.java @JvmStatic val SpellCast = SpellCastEvent::class.java + @JvmStatic val SpellbookChanged = SpellbookChangeEvent::class.java @JvmStatic val ItemAlchemized = ItemAlchemizationEvent::class.java @JvmStatic val ItemEquipped = ItemEquipEvent::class.java @JvmStatic val ItemUnequipped = ItemUnequipEvent::class.java @@ -33,4 +36,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..70eb2b0fd 100644 --- a/Server/src/main/core/api/God.kt +++ b/Server/src/main/core/api/God.kt @@ -1,10 +1,104 @@ 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.DAMAGED_BOOK_3839, + Items.HOLY_BOOK_3840, + Items.HOLY_SYMBOL_1718, + Items.MONKS_ROBE_542, + Items.MONKS_ROBE_544, + 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_ROBE_LEGS_10464, + Items.SARADOMIN_ROBE_TOP_10458, + Items.SARADOMIN_STAFF_2415, + Items.SARADOMIN_STOLE_10470, + Items.SARADOMIN_SWORD_11730, + ), + ZAMORAK( + Items.DAMAGED_BOOK_3841, + Items.UNHOLY_BOOK_3842, + Items.UNHOLY_SYMBOL_1724, + 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_ROBE_LEGS_10468, + Items.ZAMORAK_ROBE_TOP_10460, + Items.ZAMORAK_ROBE_TOP_10786, + Items.ZAMORAK_STAFF_2417, + Items.ZAMORAK_STOLE_10474, + Items.ZAMORAK_ROBE_1033, + Items.ZAMORAK_ROBE_1035, + ), + GUTHIX( + Items.DAMAGED_BOOK_3843, + Items.BOOK_OF_BALANCE_3844, + 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_2673, + Items.GUTHIX_KITESHIELD_2675, + Items.GUTHIX_MITRE_10454, + Items.GUTHIX_MJOLNIR_6760, + Items.GUTHIX_PLATEBODY_10780, + Items.GUTHIX_PLATEBODY_2669, + Items.GUTHIX_PLATELEGS_2671, + 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, + ) +} 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/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..478c9b90a --- /dev/null +++ b/Server/src/main/core/api/utils/Permadeath.kt @@ -0,0 +1,136 @@ +package core.api.utils + +import content.global.skill.construction.HouseLocation +import content.minigame.blastfurnace.BFPlayerState +import content.minigame.blastfurnace.BlastFurnace +import core.ServerConstants +import core.api.isUsingSecondaryBankAccount +import core.api.teleport +import core.api.toggleBankAccount +import core.game.node.entity.player.Player +import core.game.node.entity.player.VarpManager +import core.game.node.entity.player.info.login.PlayerSaver +import core.game.node.entity.player.link.IronmanMode +import core.game.node.entity.player.link.SavedData +import core.game.node.entity.player.link.SpellBookManager +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.quest.QuestRepository +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.world.map.Location +import java.util.ArrayList + +fun permadeath(target: Player) { + teleport(target, Location.create(3094, 3107, 0)) + + // Core + target.inventory.clear() + target.bank.clear() + target.bankSecondary.clear() + for (i in target.bankPrimary.tabStartSlot.indices) { + target.bankPrimary.tabStartSlot[i] = 0 + } + for (i in target.bankSecondary.tabStartSlot.indices) { + target.bankSecondary.tabStartSlot[i] = 0 + } + if (isUsingSecondaryBankAccount(target)) { + toggleBankAccount(target) + } + target.equipment.clear() + target.varpManager = VarpManager(target) + target.varpMap.clear() + target.saveVarp.clear() + target.timers.clearTimers() + + // Skills + target.skills = Skills(target) + + // Settings can be kept + + // Quests + target.questRepository = QuestRepository(target) + + // Appearance doesn't matter because you're going to tutorial island anyway + + // Spellbook + target.spellBookManager.setSpellBook(SpellBookManager.SpellBook.MODERN) + + // Saved data + target.savedData = SavedData(target) + + // Autocast + target.properties.autocastSpell = null + + // Player monitor is a no-op + + // Music player + target.musicPlayer.clearUnlocked() + + // Familiar manager + if (target.familiarManager.hasFamiliar()) { + target.familiarManager.dismiss() + } + val petKeys = target.familiarManager.petDetails.keys.toList() + for (key in petKeys) { + target.familiarManager.removeDetails(key) + } + + // Bank pin data + target.bankPinManager.doCancelPin() + + // House data + target.houseManager.createNewHouseAt(HouseLocation.NOWHERE) + target.getPOHStorageState().clear() + + // Achievements + for (type in DiaryType.values()) { + val diary = target.achievementDiaryManager.getDiary(type) + for (level in 0 until diary.levelStarted.size) { + for (task in 0 until diary.taskCompleted[level].size) { + diary.resetTask(target, level, task) + } + } + } + + // Xp rate + target.skills.experienceMultiplier = 1.0 + + // Ironman data + target.ironmanManager.mode = IronmanMode.NONE + + // Emote data + target.emoteManager.emotes.clear() + + // Stat manager is a no-op + + // Attributes + target.clearAttributes() + + // Pouches + for (pouch in target.pouchManager.pouches.values) { + pouch.container.clear() + pouch.currentCap = pouch.capacity + pouch.charges = pouch.maxCharges + pouch.remakeContainer() + } + + // Destroy any dropped items to prevent droptrading to yourself after death + val droppedItems = ArrayList(); + for (item in GroundItemManager.getItems()) { + if (item.dropperUid == target.details.uid) { + droppedItems.add(item) + } + } + for (item in droppedItems) { + GroundItemManager.destroy(item) + } + + // grep -R savePlayer: jobs, treasure trails, brawling gloves, slayer manager, barcrawl, ge history will simply not get saved if we don't run the hooks + // Only the Blast Furnace needs to be reset explicitly + BlastFurnace.playerStates[target.details.uid] = BFPlayerState(target) + + // Sayonara + PlayerSaver(target).save() + target.clear() +} diff --git a/Server/src/main/core/api/utils/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..39505c241 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,29 +27,63 @@ 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?, times: Int = 1): ArrayList { + val weightedItemsToDrop = ArrayList() + + repeat(times) { + // Guaranteed items + for (item in guaranteedItems) { + if (isSpecialSlot(item.id)) { + val rolls = RandomFunction.random(item.minAmt, item.maxAmt) + repeat(rolls) { + weightedItemsToDrop.add(item) + } + } else { + // Add just one instance with amount set by getItem() + weightedItemsToDrop.add(item) + } + } + + // Weighted roll (one per table roll) + if (isNotEmpty()) { + var rngWeight = RandomFunction.randomDouble(totalWeight) + for (item in this) { + rngWeight -= item.weight + if (rngWeight <= 0) { + if (isSpecialSlot(item.id)) { + val rolls = RandomFunction.random(item.minAmt, item.maxAmt) + repeat(rolls) { + weightedItemsToDrop.add(item) + } + } else { + weightedItemsToDrop.add(item) + } + break + } } } } - return convertWeightedItems(items, receiver) + return convertWeightedItems(weightedItemsToDrop, receiver) + } + + private fun isSpecialSlot(id: Int): Boolean { + return id == SLOT_HDT || id == SLOT_USDT // extend this if a drop table is not dropping multiple times } fun convertWeightedItems(weightedItems: ArrayList, receiver: Entity?): ArrayList { @@ -60,7 +96,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() @@ -127,6 +182,22 @@ open class WeightBasedTable : ArrayList() { return this } + fun excluding(vararg ids: Int): WeightBasedTable { + val idSet = ids.toHashSet() + val filtered = WeightBasedTable() + for (item in this) { + if (item.id !in idSet) { + filtered.add(item) + } + } + for (item in guaranteedItems) { + if (item.id !in idSet) { + filtered.add(item) + } + } + return filtered + } + companion object { @JvmStatic fun create(vararg items: WeightedItem): WeightBasedTable { @@ -142,6 +213,7 @@ open class WeightBasedTable : ArrayList() { val SLOT_CLUE_EASY = Items.TOOLKIT_1 val SLOT_CLUE_MEDIUM = Items.ROTTEN_POTATO_5733 val SLOT_CLUE_HARD = Items.GRANITE_LOBSTER_POUCH_12070 + val CLUE_SCROLL_SLOTS = intArrayOf(SLOT_CLUE_EASY, SLOT_CLUE_MEDIUM, SLOT_CLUE_HARD) val SLOT_CELEDT = Items.NULL_799 val SLOT_USDT = Items.SACRED_CLAY_POUCH_CLASS_1_14422 val SLOT_HDT = Items.SACRED_CLAY_POUCH_CLASS_2_14424 @@ -158,4 +230,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..3459d58b9 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; @@ -142,7 +145,7 @@ public final class Cache { buffer.putInt(0); continue; } - buffer.putInt(cacheFileManagers[index].getInformation().getInformationContainer().getCrc()); + buffer.putInt(cacheFileManagers[index].getInformation().informationContainer.getCrc()); buffer.putInt(cacheFileManagers[index].getInformation().getRevision()); } return buffer.array(); diff --git a/Server/src/main/core/cache/CacheFile.java b/Server/src/main/core/cache/CacheFile.java index a03642895..bd21a8a0d 100644 --- a/Server/src/main/core/cache/CacheFile.java +++ b/Server/src/main/core/cache/CacheFile.java @@ -66,7 +66,7 @@ public final class CacheFile { return null; } if (xteaKeys != null && (xteaKeys[0] != 0 || xteaKeys[1] != 0 || xteaKeys[2] != 0 || xteaKeys[3] != 0)) { - packedData = XTEACryption.decrypt(xteaKeys, ByteBuffer.wrap(packedData), 5, packedData.length).array(); + packedData = XTEACryption.Companion.decrypt(xteaKeys, ByteBuffer.wrap(packedData), 5, packedData.length).array(); } return ContainersInformation.unpackCacheContainer(packedData); } diff --git a/Server/src/main/core/cache/StoreFile.java b/Server/src/main/core/cache/StoreFile.java deleted file mode 100644 index 50e1728d0..000000000 --- a/Server/src/main/core/cache/StoreFile.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.cache; - -import java.nio.ByteBuffer; - -/** - * Represents a file used in the server store. - * @author Emperor - */ -public final class StoreFile { - - /** - * If the data can change during server runtime. - */ - private boolean dynamic; - - /** - * The file data. - */ - private byte[] data; - - /** - * Constructs a new {@code StoreFile} {@code Object}. - */ - public StoreFile() { - /* - * empty. - */ - } - - /** - * Puts the data on the buffer. - * @param buffer The buffer. - */ - public void put(ByteBuffer buffer) { - byte[] data = new byte[buffer.remaining()]; - buffer.get(data); - this.data = data; - } - - /** - * Creates a byte buffer containing the file data. - * @return The buffer. - */ - public ByteBuffer data() { - return ByteBuffer.wrap(data); - } - - /** - * Sets the data. - * @param data The data. - */ - public void setData(byte[] data) { - this.data = data; - } - - /** - * Gets the dynamic. - * @return The dynamic. - */ - public boolean isDynamic() { - return dynamic; - } - - /** - * Sets the dynamic. - * @param dynamic The dynamic to set. - */ - public void setDynamic(boolean dynamic) { - this.dynamic = dynamic; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/cache/crypto/ISAACCipher.java b/Server/src/main/core/cache/crypto/ISAACCipher.java deleted file mode 100644 index 16cd85b18..000000000 --- a/Server/src/main/core/cache/crypto/ISAACCipher.java +++ /dev/null @@ -1,271 +0,0 @@ -package core.cache.crypto; - -/** - *

An implementation of an ISAAC cipher. See - * http://en.wikipedia.org/wiki/ISAAC_(cipher) for more information.

- *

This implementation is based on the one written by Bob Jenkins, which is - * available at - * http://www.burtleburtle.net/bob/java/rand/Rand.java.

- * @author Graham Edgecombe - */ -public class ISAACCipher { - - /** - * The golden ratio. - */ - public static final int RATIO = 0x9e3779b9; - - /** - * The log of the size of the results and memory arrays. - */ - public static final int SIZE_LOG = 8; - - /** - * The size of the results and memory arrays. - */ - public static final int SIZE = 1 << SIZE_LOG; - - /** - * For pseudorandom lookup. - */ - public static final int MASK = (SIZE - 1) << 2; - - /** - * The count through the results. - */ - private int count = 0; - - /** - * The results. - */ - private int results[] = new int[SIZE]; - - /** - * The internal memory state. - */ - private int memory[] = new int[SIZE]; - - /** - * The accumulator. - */ - private int a; - - /** - * The last result. - */ - private int b; - - /** - * The counter. - */ - private int c; - - /** - * Creates the ISAAC cipher. - * @param seed The seed. - */ - public ISAACCipher(int[] seed) { - for (int i = 0; i < seed.length; i++) { - results[i] = seed[i]; - } - init(true); - } - - /** - * Gets the next value. - * @return The next value. - */ - public int getNextValue() { - if (count-- == 0) { - isaac(); - count = SIZE - 1; - } - return 0;//results[count]; - } - - /** - * Generates 256 results. - */ - public void isaac() { - int i, j, x, y; - b += ++c; - for (i = 0, j = SIZE / 2; i < SIZE / 2;) { - x = memory[i]; - a ^= a << 13; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a >>> 6; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a << 2; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a >>> 16; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - } - for (j = 0; j < SIZE / 2;) { - x = memory[i]; - a ^= a << 13; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a >>> 6; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a << 2; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - - x = memory[i]; - a ^= a >>> 16; - a += memory[j++]; - memory[i] = y = memory[(x & MASK) >> 2] + a + b; - results[i++] = b = memory[((y >> SIZE_LOG) & MASK) >> 2] + x; - } - } - - /** - * Initialises the ISAAC. - * @param flag Flag indicating if we should perform a second pass. - */ - public void init(boolean flag) { - int i; - int a, b, c, d, e, f, g, h; - a = b = c = d = e = f = g = h = RATIO; - for (i = 0; i < 4; ++i) { - a ^= b << 11; - d += a; - b += c; - b ^= c >>> 2; - e += b; - c += d; - c ^= d << 8; - f += c; - d += e; - d ^= e >>> 16; - g += d; - e += f; - e ^= f << 10; - h += e; - f += g; - f ^= g >>> 4; - a += f; - g += h; - g ^= h << 8; - b += g; - h += a; - h ^= a >>> 9; - c += h; - a += b; - } - for (i = 0; i < SIZE; i += 8) { - if (flag) { - a += results[i]; - b += results[i + 1]; - c += results[i + 2]; - d += results[i + 3]; - e += results[i + 4]; - f += results[i + 5]; - g += results[i + 6]; - h += results[i + 7]; - } - a ^= b << 11; - d += a; - b += c; - b ^= c >>> 2; - e += b; - c += d; - c ^= d << 8; - f += c; - d += e; - d ^= e >>> 16; - g += d; - e += f; - e ^= f << 10; - h += e; - f += g; - f ^= g >>> 4; - a += f; - g += h; - g ^= h << 8; - b += g; - h += a; - h ^= a >>> 9; - c += h; - a += b; - memory[i] = a; - memory[i + 1] = b; - memory[i + 2] = c; - memory[i + 3] = d; - memory[i + 4] = e; - memory[i + 5] = f; - memory[i + 6] = g; - memory[i + 7] = h; - } - if (flag) { - for (i = 0; i < SIZE; i += 8) { - a += memory[i]; - b += memory[i + 1]; - c += memory[i + 2]; - d += memory[i + 3]; - e += memory[i + 4]; - f += memory[i + 5]; - g += memory[i + 6]; - h += memory[i + 7]; - a ^= b << 11; - d += a; - b += c; - b ^= c >>> 2; - e += b; - c += d; - c ^= d << 8; - f += c; - d += e; - d ^= e >>> 16; - g += d; - e += f; - e ^= f << 10; - h += e; - f += g; - f ^= g >>> 4; - a += f; - g += h; - g ^= h << 8; - b += g; - h += a; - h ^= a >>> 9; - c += h; - a += b; - memory[i] = a; - memory[i + 1] = b; - memory[i + 2] = c; - memory[i + 3] = d; - memory[i + 4] = e; - memory[i + 5] = f; - memory[i + 6] = g; - memory[i + 7] = h; - } - } - isaac(); - count = SIZE; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/cache/crypto/ISAACCipher.kt b/Server/src/main/core/cache/crypto/ISAACCipher.kt new file mode 100644 index 000000000..d3fab467b --- /dev/null +++ b/Server/src/main/core/cache/crypto/ISAACCipher.kt @@ -0,0 +1,213 @@ +package core.cache.crypto + +/** + * An implementation of an ISAAC cipher. + * See [http://en.wikipedia.org/wiki/ISAAC_(cipher)](http://en.wikipedia.org/wiki/ISAAC_(cipher)) for more information. + * Based on the one written by Bob Jenkins, which is available at + * [http://www.burtleburtle.net/bob/java/rand/Rand.java](http://www.burtleburtle.net/bob/java/rand/Rand.java). + */ +class ISAACCipher(seed: IntArray) { + companion object { + /** Golden ratio in 32 bit unsigned int. */ + const val RATIO = -0x61c88647 + /** The base 2 log of the size of the results and memory arrays. */ + const val SIZE_LOG = 8 + /** The size of the results and memory arrays. */ + const val SIZE = 1 shl SIZE_LOG + /** For pseudorandom lookup. */ + const val MASK = SIZE - 1 shl 2 + } + /** The count through the results. */ + private var count = 0 + /** The results. */ + private val results = IntArray(SIZE) + /** The internal memory state. */ + private val memory = IntArray(SIZE) + /** The accumulator. */ + private var a = 0 + /** The last result. */ + private var b = 0 + /** The counter. */ + private var c = 0 + + /** + * Creates the ISAAC cipher. + * @param seed The seed. + */ + init { + for (i in seed.indices) { + results[i] = seed[i] + } + init(true) + } + + val nextValue: Int + /** + * Gets the next value. + * @return The next value. + */ + get() { + if (count-- == 0) { + isaac() + count = SIZE - 1 + } + return 0 //results[count]; + } + + /** + * Generates 256 results. + */ + fun isaac() { + var i: Int + var j: Int + var x: Int + var y: Int + b += ++c + i = 0 + j = SIZE / 2 + while (i < SIZE / 2) { + x = memory[i] + a = a xor (a shl 13) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a ushr 6) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a shl 2) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a ushr 16) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + } + j = 0 + while (j < SIZE / 2) { + x = memory[i] + a = a xor (a shl 13) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a ushr 6) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a shl 2) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + x = memory[i] + a = a xor (a ushr 16) + a += memory[j++] + y = memory[x and MASK shr 2] + a + b + memory[i] = y + b = memory[y shr SIZE_LOG and MASK shr 2] + x + results[i++] = b + } + } + + /** + * Initialises the ISAAC. + * @param flag Flag indicating if we should perform a second pass. + */ + fun init(flag: Boolean) { + var a = RATIO + var b = a; var c = b; var d = c; var e = d; var f = e; var g = f; var h = g + var i = 0 + while (i < 4) { + a = a xor (b shl 11); d += a; b += c + b = b xor (c ushr 2); e += b; c += d + c = c xor (d shl 8); f += c; d += e + d = d xor (e ushr 16); g += d; e += f + e = e xor (f shl 10); h += e; f += g + f = f xor (g ushr 4); a += f; g += h + g = g xor (h shl 8); b += g; h += a + h = h xor (a ushr 9); c += h; a += b + ++i + } + i = 0 + while (i < SIZE) { + if (flag) { + a += results[i] + b += results[i + 1] + c += results[i + 2] + d += results[i + 3] + e += results[i + 4] + f += results[i + 5] + g += results[i + 6] + h += results[i + 7] + } + a = a xor (b shl 11); d += a; b += c + b = b xor (c ushr 2); e += b; c += d + c = c xor (d shl 8); f += c; d += e + d = d xor (e ushr 16); g += d; e += f + e = e xor (f shl 10); h += e; f += g + f = f xor (g ushr 4); a += f; g += h + g = g xor (h shl 8); b += g; h += a + h = h xor (a ushr 9); c += h; a += b + memory[i] = a + memory[i + 1] = b + memory[i + 2] = c + memory[i + 3] = d + memory[i + 4] = e + memory[i + 5] = f + memory[i + 6] = g + memory[i + 7] = h + i += 8 + } + if (flag) { + i = 0 + while (i < SIZE) { + a += memory[i] + b += memory[i + 1] + c += memory[i + 2] + d += memory[i + 3] + e += memory[i + 4] + f += memory[i + 5] + g += memory[i + 6] + h += memory[i + 7] + a = a xor (b shl 11); d += a; b += c + b = b xor (c ushr 2); e += b; c += d + c = c xor (d shl 8); f += c; d += e + d = d xor (e ushr 16); g += d; e += f + e = e xor (f shl 10); h += e; f += g + f = f xor (g ushr 4); a += f; g += h + g = g xor (h shl 8); b += g; h += a + h = h xor (a ushr 9); c += h; a += b + memory[i] = a + memory[i + 1] = b + memory[i + 2] = c + memory[i + 3] = d + memory[i + 4] = e + memory[i + 5] = f + memory[i + 6] = g + memory[i + 7] = h + i += 8 + } + } + isaac() + count = SIZE + } +} \ No newline at end of file diff --git a/Server/src/main/core/cache/crypto/ISAACPair.java b/Server/src/main/core/cache/crypto/ISAACPair.java deleted file mode 100644 index 83c4654b9..000000000 --- a/Server/src/main/core/cache/crypto/ISAACPair.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.cache.crypto; - -/** - * Represents a ISAAC key pair, for both input and output. - * @author `Discardedx2 - */ -public final class ISAACPair { - - /** - * The input cipher. - */ - private ISAACCipher input; - - /** - * The output cipher. - */ - private ISAACCipher output; - - /** - * Constructs a new {@code ISAACPair} {@code Object}. - * @param input The input cipher. - * @param output The output cipher. - */ - public ISAACPair(ISAACCipher input, ISAACCipher output) { - this.input = input; - this.output = output; - } - - /** - * Gets the input cipher. - * @return The input cipher. - */ - public ISAACCipher getInput() { - return input; - } - - /** - * Gets the output cipher. - * @return The output cipher. - */ - public ISAACCipher getOutput() { - return output; - } - -} diff --git a/Server/src/main/core/cache/crypto/ISAACPair.kt b/Server/src/main/core/cache/crypto/ISAACPair.kt new file mode 100644 index 000000000..9056c7a9d --- /dev/null +++ b/Server/src/main/core/cache/crypto/ISAACPair.kt @@ -0,0 +1,9 @@ +package core.cache.crypto + +/** + * Represents a ISAAC key pair, for both input and output. + */ +class ISAACPair( + val input: ISAACCipher, + val output: ISAACCipher +) diff --git a/Server/src/main/core/cache/crypto/XTEACryption.java b/Server/src/main/core/cache/crypto/XTEACryption.java deleted file mode 100644 index 7ed799e58..000000000 --- a/Server/src/main/core/cache/crypto/XTEACryption.java +++ /dev/null @@ -1,124 +0,0 @@ -package core.cache.crypto; - -import java.nio.ByteBuffer; - -/** - * Holds XTEA cryption methods. - * @author ? - * @author Emperor - */ -public final class XTEACryption { - - /** - * The delta value - */ - private static final int DELTA = -1640531527; - - /** - * The sum. - */ - private static final int SUM = -957401312; - - /** - * The amount of "cryption cycles". - */ - private static final int NUM_ROUNDS = 32; - - /** - * Constructs a new {@code XTEACryption}. - */ - private XTEACryption() { - /* - * empty. - */ - } - - /** - * Decrypts the contents of the buffer. - * @param keys The cryption keys. - * @param buffer The buffer. - */ - public static ByteBuffer decrypt(int[] keys, ByteBuffer buffer) { - return decrypt(keys, buffer, buffer.position(), buffer.limit()); - } - - /** - * Decrypts the buffer data. - * @param keys The keys. - * @param buffer The buffer to decrypt. - * @param offset The offset of the data to decrypt. - * @param length The length. - * @return The decrypted data. - */ - public static ByteBuffer decrypt(int[] keys, ByteBuffer buffer, int offset, int length) { - int numBlocks = (length - offset) / 8; - int[] block = new int[2]; - for (int i = 0; i < numBlocks; i++) { - int index = i * 8 + offset; - block[0] = buffer.getInt(index); - block[1] = buffer.getInt(index + 4); - decipher(keys, block); - buffer.putInt(index, block[0]); - buffer.putInt(index + 4, block[1]); - } - return buffer; - } - - /** - * Deciphers the values. - * @param keys The cryption key. - * @param block The values to decipher. - */ - private static void decipher(int[] keys, int[] block) { - long sum = SUM; - for (int i = 0; i < NUM_ROUNDS; i++) { - block[1] -= (keys[(int) ((sum & 0x1933) >>> 11)] + sum ^ block[0] + (block[0] << 4 ^ block[0] >>> 5)); - sum -= DELTA; - block[0] -= ((block[1] << 4 ^ block[1] >>> 5) + block[1] ^ keys[(int) (sum & 0x3)] + sum); - } - } - - /** - * Encrypts the contents of the byte buffer. - * @param keys The cryption keys. - * @param buffer The buffer to encrypt. - */ - public static void encrypt(int[] keys, ByteBuffer buffer) { - encrypt(keys, buffer, buffer.position(), buffer.limit()); - } - - /** - * Encrypts the buffer data. - * @param keys The keys. - * @param buffer The buffer to encrypt. - * @param offset The offset of the data to encrypt. - * @param length The length. - * @return The encrypted data. - */ - public static void encrypt(int[] keys, ByteBuffer buffer, int offset, int length) { - int numBlocks = (length - offset) / 8; - int[] block = new int[2]; - for (int i = 0; i < numBlocks; i++) { - int index = i * 8 + offset; - block[0] = buffer.getInt(index); - block[1] = buffer.getInt(index + 4); - encipher(keys, block); - buffer.putInt(index, block[0]); - buffer.putInt(index + 4, block[1]); - } - } - - /** - * Enciphers the values of the block. - * @param keys The cryption keys. - * @param block The block to encipher. - */ - private static void encipher(int[] keys, int[] block) { - long sum = 0; - for (int i = 0; i < NUM_ROUNDS; i++) { - block[0] += ((block[1] << 4 ^ block[1] >>> 5) + block[1] ^ keys[(int) (sum & 0x3)] + sum); - sum += DELTA; - block[1] += (keys[(int) ((sum & 0x1933) >>> 11)] + sum ^ block[0] + (block[0] << 4 ^ block[0] >>> 5)); - } - } -} \ No newline at end of file diff --git a/Server/src/main/core/cache/crypto/XTEACryption.kt b/Server/src/main/core/cache/crypto/XTEACryption.kt new file mode 100644 index 000000000..f87477008 --- /dev/null +++ b/Server/src/main/core/cache/crypto/XTEACryption.kt @@ -0,0 +1,97 @@ +package core.cache.crypto + +import java.nio.ByteBuffer + +/** Holds XTEA(eXtended Tiny Encryption Algorithm) encryption/decryption methods. Usually used for RS Map decryption. */ +class XTEACryption { + companion object { + // Purely Static Class + // XTEA consts https://scispace.com/pdf/xtea-cryptography-implementation-in-android-chatting-app-3pg6ownewx.pdf + /** Fibonacci hashing. Math.pow(2,32) * Math.sqrt(5)-1)/2 => 32 bits(int) multiply by reciprocal of golden ratio. */ + private const val DELTA = -1640531527 // Formula cast to unsigned int. + /** DELTA << 5. Essentially multiply DELTA by NUM_ROUNDS or number of rounds. */ + private const val SUM = -957401312 // 0xC6EF3720 + /** The amount of "cryption cycles". */ + private const val NUM_ROUNDS = 32 + /** Constant to manipulate the key index in decipher/encrypt. This was a magic number randomly selected by RS. */ + private const val KEY_SCRAMBLE = 0x1933 + + /** + * Decrypts the buffer data. + * @param keys The keys. + * @param buffer The buffer to decrypt. + * @param offset The offset of the data to decrypt. + * @param length The length. + * @return The decrypted data. + */ + fun decrypt(keys: IntArray, buffer: ByteBuffer, offset: Int, length: Int): ByteBuffer { + val numBlocks = (length - offset) / 8 + val block = IntArray(2) + for (i in 0 until numBlocks) { + val index = i * 8 + offset + block[0] = buffer.getInt(index) + block[1] = buffer.getInt(index + 4) + decipher(keys, block) + buffer.putInt(index, block[0]) + buffer.putInt(index + 4, block[1]) + } + return buffer + } + + /** + * Deciphers the values of the block using the Tiny Encryption Algorithm (TEA). + * This algorithm relies on a Feistel network, using bitwise operations and + * modular subtraction to remove confusion(block[0] line) and diffusion(block[1] line). + * + * @param keys 128-bit key (4x32-bit int). + * @param block 64-bit data block (2x32-bit int) to be decrypted. + */ + private fun decipher(keys: IntArray, block: IntArray) { + var sum = + SUM.toLong() // Running decreasing sum to ensure different round constants - should become 0 at the end. + for (i in 0 until NUM_ROUNDS) { + block[1] -= (keys[(sum and KEY_SCRAMBLE.toLong() ushr 11).toInt()] + sum xor (block[0] + (block[0] shl 4 xor (block[0] ushr 5))).toLong()).toInt() + sum -= DELTA.toLong() + block[0] -= (((block[1] shl 4 xor (block[1] ushr 5)) + block[1]).toLong() xor keys[(sum and 0x3L).toInt()] + sum).toInt() + } + } + /** === Encryption functions below are not used unless you are modifying the cache. === */ + /** + * Encrypts the buffer data. + * @param keys The keys. + * @param buffer The buffer to encrypt. + * @param offset The offset of the data to encrypt. + * @param length The length. + * @return The encrypted data. + */ + fun encrypt(keys: IntArray, buffer: ByteBuffer, offset: Int, length: Int) { + val numBlocks = (length - offset) / 8 + val block = IntArray(2) + for (i in 0 until numBlocks) { + val index = i * 8 + offset + block[0] = buffer.getInt(index) + block[1] = buffer.getInt(index + 4) + encipher(keys, block) + buffer.putInt(index, block[0]) + buffer.putInt(index + 4, block[1]) + } + } + + /** + * Enciphers the values of the block using the Tiny Encryption Algorithm (TEA). + * This algorithm relies on a Feistel network, using bitwise operations and + * modular addition to provide confusion(block[0] line) and diffusion(block[1] line). + * + * @param keys 128-bit key (4x32-bit int). + * @param block 64-bit data block (2x32-bit int) to be encrypted. + */ + private fun encipher(keys: IntArray, block: IntArray) { + var sum: Long = 0 // Running sum to ensure different round constants - should match SUM at the end. + for (i in 0 until NUM_ROUNDS) { + block[0] += (((block[1] shl 4 xor (block[1] ushr 5)) + block[1]).toLong() xor keys[(sum and 0x3L).toInt()] + sum).toInt() + sum += DELTA.toLong() + block[1] += (keys[(sum and KEY_SCRAMBLE.toLong() ushr 11).toInt()] + sum xor (block[0] + (block[0] shl 4 xor (block[0] ushr 5))).toLong()).toInt() + } + } + } +} \ No newline at end of file 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..b7c17e5ec 100644 --- a/Server/src/main/core/cache/def/impl/ItemDefinition.java +++ b/Server/src/main/core/cache/def/impl/ItemDefinition.java @@ -5,33 +5,28 @@ 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 +54,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 +182,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 +254,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 +266,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 +313,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 +445,7 @@ public class ItemDefinition extends Definition { def.clientScriptData.put(key, value); } } else { - System.out.println("Unhandled item definition opcode - opcode: " + opcode); + break; } } @@ -519,11 +488,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 +511,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 +541,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 +550,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 +604,19 @@ public class ItemDefinition extends Definition { * @return {@code True} if so. */ public static boolean canEnterEntrana(Player player) { - Container[] container = new Container[] { player.getInventory(), player.getEquipment() }; - for (Container c : container) { + Container[] containers = new Container[] { player.getInventory(), player.getEquipment() }; + if (player.getFamiliarManager().hasFamiliar() && !player.getFamiliarManager().hasPet()) { + return false; + } + + for (Container c : containers) { for (Item i : c.toArray()) { if (i == null) { continue; } + if (GameWorld.getSettings().getSkillcape_perks() && i.getId() == Items.RUNECRAFT_CAPE_9765 || i.getId() == Items.RUNECRAFT_CAPET_9766) { + continue; + } if (!i.getDefinition().isAllowedOnEntrana()) { return false; } @@ -672,6 +648,7 @@ public class ItemDefinition extends Definition { Items.BOOK_OF_BALANCE_3844, Items.DAMAGED_BOOK_3843, Items.WIZARD_BOOTS_2579, + Items.COMBAT_BRACELET_11126, Items.COMBAT_BRACELET1_11124, Items.COMBAT_BRACELET2_11122, Items.COMBAT_BRACELET3_11120, @@ -679,7 +656,36 @@ 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.ZAMORAK_ROBE_1033, + Items.ZAMORAK_ROBE_1035, + Items.PRIEST_GOWN_426, + Items.PRIEST_GOWN_428, + Items.DRUIDS_ROBE_538, + Items.DRUIDS_ROBE_540, + Items.FLOWERS_2460, + Items.FLOWERS_2462, + Items.FLOWERS_2464, + Items.FLOWERS_2466, + Items.FLOWERS_2468, + Items.FLOWERS_2470, + Items.FLOWERS_2472, + Items.FLOWERS_2474, + Items.FLOWERS_2476, + Items.FIXED_DEVICE_6082, + Items.GHOSTLY_BOOTS_6106, + Items.GHOSTLY_ROBE_6107, + Items.GHOSTLY_ROBE_6108, + Items.GHOSTLY_HOOD_6109, + Items.GHOSTLY_GLOVES_6110, + Items.GHOSTLY_CLOAK_6111 )); private static final HashSet entranaBannedItems = new HashSet(Arrays.asList( /**Items.BUTTERFLY_NET_10010, easing the restriction until barehanded implementation**/ @@ -688,15 +694,10 @@ public class ItemDefinition extends Definition { Items.CANNON_BASE_6, Items.CANNON_STAND_8, Items.CANNON_FURNACE_12, - Items.COOKING_GAUNTLETS_775, - Items.CHAOS_GAUNTLETS_777, - Items.GOLDSMITH_GAUNTLETS_776, - Items.KARAMJA_GLOVES_1_11136, - Items.KARAMJA_GLOVES_2_11138, - Items.KARAMJA_GLOVES_3_11140, - Items.VYREWATCH_TOP_9634, - Items.VYREWATCH_LEGS_9636, - Items.VYREWATCH_SHOES_9638 + Items.ZAMORAK_STOLE_10474, + Items.EXPLORERS_RING_1_13560, + Items.EXPLORERS_RING_2_13561, + Items.EXPLORERS_RING_3_13562 )); @@ -711,11 +712,11 @@ public class ItemDefinition extends Definition { if (entranaBannedItems.contains(getId())) { return false; } - if (equipSlot(getId()) == EquipmentSlot.AMMO) { + if (equipSlot(getId()) == EquipmentSlot.AMMO || equipSlot(getId()) == EquipmentSlot.NECK || equipSlot(getId()) == EquipmentSlot.RING) { return true; } - if (getName().toLowerCase().startsWith("ring") || getName().toLowerCase().startsWith("amulet")) { - return true; + if (hasAction("summon")) { + return false; } int[] bonuses = getConfiguration(ItemConfigParser.BONUS); return bonuses == null || Arrays.stream(bonuses).allMatch(x -> x == 0); @@ -734,7 +735,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 +816,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 +900,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 +1404,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 +1541,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); @@ -1515,30 +1556,6 @@ public class ItemDefinition extends Definition { */ private static final String[] BONUS_NAMES = { "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Summoning: ", "Strength: ", "Prayer: " }; - /** - * Updates the equipment stats interface. - * @param player The player to update for. - */ - public static void statsUpdate(Player player) { - if (!player.getAttribute("equip_stats_open", false)) { - return; - } - PacketRepository.send(WeightUpdate.class, player.getPacketDispatch().getContext()); - int index = 0; - int[] bonuses = player.getProperties().getBonuses(); - for (int i = 36; i < 50; i++) { - if (i == 47) { - continue; - } - int bonus = bonuses[index]; - String bonusValue = bonus > -1 ? ("+" + bonus) : Integer.toString(bonus); - player.getPacketDispatch().sendString(BONUS_NAMES[index++] + bonusValue, 667, i); - } - player.getPacketDispatch().sendString("Attack bonus", 667, 34); - DecimalFormat dec = new DecimalFormat("#.#"); - player.getPacketDispatch().sendString(dec.format(player.getSettings().getWeight())+" kg", 667, 32); - } - /** * Checks if it has a plugin. * @return {@code True} if so. @@ -1609,7 +1626,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/cache/misc/Container.java b/Server/src/main/core/cache/misc/Container.java deleted file mode 100644 index 9760845ff..000000000 --- a/Server/src/main/core/cache/misc/Container.java +++ /dev/null @@ -1,109 +0,0 @@ -package core.cache.misc; - -/** - * A container. - * @author Dragonkk - */ -public class Container { - - /** - * The version. - */ - private int version; - - /** - * The CRC. - */ - private int crc; - - /** - * The name hash. - */ - private int nameHash; - - /** - * If updated. - */ - private boolean updated; - - /** - * Construct a new container. - */ - public Container() { - nameHash = -1; - version = -1; - crc = -1; - } - - /** - * Set the version. - * @param version - */ - public void setVersion(int version) { - this.version = version; - } - - /** - * Update the version. - */ - public void updateVersion() { - version++; - updated = true; - } - - /** - * Get the version. - * @return The version. - */ - public int getVersion() { - return version; - } - - /** - * Get the next version. - * @return The next version. - */ - public int getNextVersion() { - return updated ? version : version + 1; - } - - /** - * Set the CRC. - * @param crc The cRC. - */ - public void setCrc(int crc) { - this.crc = crc; - } - - /** - * Get the CRC. - * @return The CRC. - */ - public int getCrc() { - return crc; - } - - /** - * Set the name hash. - * @param nameHash The name hash. - */ - public void setNameHash(int nameHash) { - this.nameHash = nameHash; - } - - /** - * Get the name hash. - * @return The name hash. - */ - public int getNameHash() { - return nameHash; - } - - /** - * If is updated. - * @return If is updated. - */ - public boolean isUpdated() { - return updated; - } -} diff --git a/Server/src/main/core/cache/misc/Container.kt b/Server/src/main/core/cache/misc/Container.kt new file mode 100644 index 000000000..3e1b481d6 --- /dev/null +++ b/Server/src/main/core/cache/misc/Container.kt @@ -0,0 +1,7 @@ +package core.cache.misc + +data class Container( + var version: Int = -1, + var crc: Int = -1, + var nameHash: Int = -1 +) \ No newline at end of file diff --git a/Server/src/main/core/cache/misc/ContainersInformation.java b/Server/src/main/core/cache/misc/ContainersInformation.java deleted file mode 100644 index f786c1d2f..000000000 --- a/Server/src/main/core/cache/misc/ContainersInformation.java +++ /dev/null @@ -1,228 +0,0 @@ -package core.cache.misc; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.zip.CRC32; - -import core.cache.bzip2.BZip2Decompressor; -import core.cache.gzip.GZipDecompressor; - -/** - * A class holding the containers information. - * @author Dragonkk - */ -public final class ContainersInformation { - - /** - * The information container. - */ - private Container informationContainer; - - /** - * The protocol. - */ - private int protocol; - - /** - * The revision. - */ - private int revision; - - /** - * The container indexes. - */ - private int[] containersIndexes; - - /** - * The containers. - */ - private FilesContainer[] containers; - - /** - * If files have to be named. - */ - private boolean filesNamed; - - /** - * If it has to be whirpool. - */ - private boolean whirpool; - - /** - * The data. - */ - private final byte[] data; - - /** - * Construct a new containers information. - * @param informationContainerPackedData The information container data - * packed. - */ - public ContainersInformation(byte[] informationContainerPackedData) { - this.data = Arrays.copyOf(informationContainerPackedData, informationContainerPackedData.length); - informationContainer = new Container(); - informationContainer.setVersion((informationContainerPackedData[informationContainerPackedData.length - 2] << 8 & 0xff00) + (informationContainerPackedData[-1 + informationContainerPackedData.length] & 0xff)); - CRC32 crc32 = new CRC32(); - crc32.update(informationContainerPackedData); - informationContainer.setCrc((int) crc32.getValue()); - decodeContainersInformation(unpackCacheContainer(informationContainerPackedData)); - } - - /** - * Unpacks a container. - * @param packedData The packed container data. - * @return The unpacked data. - */ - public static final byte[] unpackCacheContainer(byte[] packedData) { - ByteBuffer buffer = ByteBuffer.wrap(packedData); - int compression = buffer.get() & 0xFF; - int containerSize = buffer.getInt(); - if (containerSize < 0 || containerSize > 5000000) { - return null; - // throw new RuntimeException(); - } - if (compression == 0) { - byte unpacked[] = new byte[containerSize]; - buffer.get(unpacked, 0, containerSize); - return unpacked; - } - int decompressedSize = buffer.getInt(); - if (decompressedSize < 0 || decompressedSize > 20000000) { - return null; - // throw new RuntimeException(); - } - byte decompressedData[] = new byte[decompressedSize]; - if (compression == 1) { - BZip2Decompressor.decompress(decompressedData, packedData, containerSize, 9); - } else { - GZipDecompressor.decompress(buffer, decompressedData); - } - return decompressedData; - } - - /** - * Get the container indexes. - * @return The container indexes. - */ - public int[] getContainersIndexes() { - return containersIndexes; - } - - /** - * Get the containers. - * @return The containers. - */ - public FilesContainer[] getContainers() { - return containers; - } - - /** - * Get the information container. - * @return The information container. - */ - public Container getInformationContainer() { - return informationContainer; - } - - /** - * Get the revision. - * @return The revision. - */ - public int getRevision() { - return revision; - } - - /** - * Decode the containers information. - * @param data The data. - */ - public void decodeContainersInformation(byte[] data) { - ByteBuffer buffer = ByteBuffer.wrap(data); - protocol = buffer.get() & 0xFF; - if (protocol != 5 && protocol != 6) { - throw new RuntimeException(); - } - revision = protocol < 6 ? 0 : buffer.getInt(); - int nameHash = buffer.get() & 0xFF; - filesNamed = (0x1 & nameHash) != 0; - whirpool = (0x2 & nameHash) != 0; - containersIndexes = new int[buffer.getShort() & 0xFFFF]; - int lastIndex = -1; - for (int index = 0; index < containersIndexes.length; index++) { - containersIndexes[index] = (buffer.getShort() & 0xFFFF) + (index == 0 ? 0 : containersIndexes[index - 1]); - if (containersIndexes[index] > lastIndex) { - lastIndex = containersIndexes[index]; - } - } - containers = new FilesContainer[lastIndex + 1]; - for (int index = 0; index < containersIndexes.length; index++) { - containers[containersIndexes[index]] = new FilesContainer(); - } - if (filesNamed) { - for (int index = 0; index < containersIndexes.length; index++) { - containers[containersIndexes[index]].setNameHash(buffer.getInt()); - } - } - byte[][] filesHashes = null; - if (whirpool) { - filesHashes = new byte[containers.length][]; - for (int index = 0; index < containersIndexes.length; index++) { - filesHashes[containersIndexes[index]] = new byte[64]; - buffer.get(filesHashes[containersIndexes[index]], 0, 64); - } - } - for (int index = 0; index < containersIndexes.length; index++) { - containers[containersIndexes[index]].setCrc(buffer.getInt()); - } - for (int index = 0; index < containersIndexes.length; index++) { - containers[containersIndexes[index]].setVersion(buffer.getInt()); - } - for (int index = 0; index < containersIndexes.length; index++) { - containers[containersIndexes[index]].setFilesIndexes(new int[buffer.getShort() & 0xFFFF]); - } - for (int index = 0; index < containersIndexes.length; index++) { - int lastFileIndex = -1; - for (int fileIndex = 0; fileIndex < containers[containersIndexes[index]].getFilesIndexes().length; fileIndex++) { - containers[containersIndexes[index]].getFilesIndexes()[fileIndex] = (buffer.getShort() & 0xFFFF) + (fileIndex == 0 ? 0 : containers[containersIndexes[index]].getFilesIndexes()[fileIndex - 1]); - if (containers[containersIndexes[index]].getFilesIndexes()[fileIndex] > lastFileIndex) { - lastFileIndex = containers[containersIndexes[index]].getFilesIndexes()[fileIndex]; - } - } - containers[containersIndexes[index]].setFiles(new Container[lastFileIndex + 1]); - for (int fileIndex = 0; fileIndex < containers[containersIndexes[index]].getFilesIndexes().length; fileIndex++) { - containers[containersIndexes[index]].getFiles()[containers[containersIndexes[index]].getFilesIndexes()[fileIndex]] = new Container(); - } - } - if (whirpool) { - for (int index = 0; index < containersIndexes.length; index++) { - for (int fileIndex = 0; fileIndex < containers[containersIndexes[index]].getFilesIndexes().length; fileIndex++) { - containers[containersIndexes[index]].getFiles()[containers[containersIndexes[index]].getFilesIndexes()[fileIndex]].setVersion(filesHashes[containersIndexes[index]][containers[containersIndexes[index]].getFilesIndexes()[fileIndex]]); - } - } - } - if (filesNamed) { - for (int index = 0; index < containersIndexes.length; index++) { - for (int fileIndex = 0; fileIndex < containers[containersIndexes[index]].getFilesIndexes().length; fileIndex++) { - containers[containersIndexes[index]].getFiles()[containers[containersIndexes[index]].getFilesIndexes()[fileIndex]].setNameHash(buffer.getInt()); - } - } - } - } - - /** - * If is whirpool. - * @return If is whirpool {@code true}. - */ - public boolean isWhirpool() { - return whirpool; - } - - /** - * Gets the data. - * @return The data. - */ - public byte[] getData() { - return data; - } - -} diff --git a/Server/src/main/core/cache/misc/ContainersInformation.kt b/Server/src/main/core/cache/misc/ContainersInformation.kt new file mode 100644 index 000000000..831eb954b --- /dev/null +++ b/Server/src/main/core/cache/misc/ContainersInformation.kt @@ -0,0 +1,145 @@ +package core.cache.misc + +import core.cache.bzip2.BZip2Decompressor +import core.cache.gzip.GZipDecompressor +import java.nio.ByteBuffer +import java.util.* +import java.util.zip.CRC32 + +class ContainersInformation(informationContainerPackedData: ByteArray) { + @JvmField + val informationContainer: Container + private var protocol = 0 + var revision = 0 + var containersIndexes: IntArray = intArrayOf() + var containers: Array = arrayOf() + private var filesNamed = false // If files has to be named + private var whirpool = false // Flag for whirpool + val data: ByteArray + + init { + data = Arrays.copyOf(informationContainerPackedData, informationContainerPackedData.size) + informationContainer = Container() + informationContainer.version = + (informationContainerPackedData[informationContainerPackedData.size - 2].toInt() shl 8 and 0xff00) + (informationContainerPackedData[-1 + informationContainerPackedData.size].toInt() and 0xff) + val crc32 = CRC32() + crc32.update(informationContainerPackedData) + informationContainer.crc = crc32.value.toInt() + decodeContainersInformation(unpackCacheContainer(informationContainerPackedData)) + } + + fun decodeContainersInformation(data: ByteArray?) { + val buffer = ByteBuffer.wrap(data) + protocol = buffer.get().toInt() and 0xFF + if (protocol != 5 && protocol != 6) { + throw RuntimeException() + } + revision = if (protocol < 6) 0 else buffer.getInt() + val nameHash = buffer.get().toInt() and 0xFF + filesNamed = 0x1 and nameHash != 0 + whirpool = 0x2 and nameHash != 0 + containersIndexes = IntArray(buffer.getShort().toInt() and 0xFFFF) + var lastIndex = -1 + for (index in containersIndexes.indices) { + val containerIndex = + (buffer.getShort().toInt() and 0xFFFF) + if (index == 0) 0 else containersIndexes[index - 1] + containersIndexes[index] = containerIndex + if (containerIndex > lastIndex) { + lastIndex = containerIndex + } + } + containers = arrayOfNulls(lastIndex + 1) + for (containerIdx in containersIndexes) { + containers[containerIdx] = FilesContainer() + } + if (filesNamed) { + for (containerIdx in containersIndexes) { + containers[containerIdx]!!.nameHash = buffer.getInt() + } + } + var filesHashes: Array? = null + if (whirpool) { + filesHashes = arrayOfNulls(containers.size) + for (containerIdx in containersIndexes) { + filesHashes[containerIdx] = ByteArray(64) + buffer[filesHashes[containerIdx], 0, 64] + } + } + for (containerIdx in containersIndexes) { + containers[containerIdx]!!.crc = buffer.getInt() + } + for (containerIdx in containersIndexes) { + containers[containerIdx]!!.version = buffer.getInt() + } + for (containerIdx in containersIndexes) { + containers[containerIdx]!!.filesIndexes = IntArray(buffer.getShort().toInt() and 0xFFFF) + } + for (containerIdx in containersIndexes) { + val container = containers[containerIdx] + val filesIndexes = container!!.filesIndexes + var lastFileIndex = -1 + for (fileIndex in filesIndexes!!.indices) { + val fileIdx = + (buffer.getShort().toInt() and 0xFFFF) + if (fileIndex == 0) 0 else filesIndexes[fileIndex - 1] + filesIndexes[fileIndex] = fileIdx + if (fileIdx > lastFileIndex) { + lastFileIndex = fileIdx + } + } + val files = arrayOfNulls(lastFileIndex + 1) + container.files = files + for (fileIdx in filesIndexes) { + files[fileIdx] = Container() + } + } + if (whirpool) { + for (containerIdx in containersIndexes) { + val container = containers[containerIdx] + val containerHashes = filesHashes!![containerIdx] + val files = container!!.files + for (fileIdx in container.filesIndexes!!) { + files!![fileIdx]!!.version = containerHashes!![fileIdx].toInt() + } + } + } + if (filesNamed) { + for (containerIdx in containersIndexes) { + val container = containers[containerIdx] + val files = container!!.files + for (fileIdx in container.filesIndexes!!) { + files!![fileIdx]!!.nameHash = buffer.getInt() + } + } + } + } + + companion object { + @JvmStatic + fun unpackCacheContainer(packedData: ByteArray?): ByteArray? { + val buffer = ByteBuffer.wrap(packedData) + val compression = buffer.get().toInt() and 0xFF + val containerSize = buffer.getInt() + if (containerSize < 0 || containerSize > 5000000) { + return null + // throw new RuntimeException(); + } + if (compression == 0) { + val unpacked = ByteArray(containerSize) + buffer[unpacked, 0, containerSize] + return unpacked + } + val decompressedSize = buffer.getInt() + if (decompressedSize < 0 || decompressedSize > 20000000) { + return null + // throw new RuntimeException(); + } + val decompressedData = ByteArray(decompressedSize) + if (compression == 1) { + BZip2Decompressor.decompress(decompressedData, packedData, containerSize, 9) + } else { + GZipDecompressor.decompress(buffer, decompressedData) + } + return decompressedData + } + } +} diff --git a/Server/src/main/core/cache/misc/FilesContainer.java b/Server/src/main/core/cache/misc/FilesContainer.java deleted file mode 100644 index 5f14e448f..000000000 --- a/Server/src/main/core/cache/misc/FilesContainer.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.cache.misc; - -/** - * A class holding the file containers. - * @author Dragonkk - */ -public final class FilesContainer extends Container { - - /** - * The file indexes. - */ - private int[] filesIndexes; - - /** - * The files. - */ - private Container[] files; - - /** - * Construct a new files container. - */ - public FilesContainer() { - - } - - /** - * Set the files. - * @param containers The files. - */ - public void setFiles(Container[] containers) { - this.files = containers; - } - - /** - * Get the files. - * @return The files. - */ - public Container[] getFiles() { - return files; - } - - /** - * Set the file indexes. - * @param containersIndexes The file indexes. - */ - public void setFilesIndexes(int[] containersIndexes) { - this.filesIndexes = containersIndexes; - } - - /** - * Get the file indexes. - * @return The file indexes. - */ - public int[] getFilesIndexes() { - return filesIndexes; - } -} diff --git a/Server/src/main/core/cache/misc/FilesContainer.kt b/Server/src/main/core/cache/misc/FilesContainer.kt new file mode 100644 index 000000000..192ca3389 --- /dev/null +++ b/Server/src/main/core/cache/misc/FilesContainer.kt @@ -0,0 +1,9 @@ +package core.cache.misc + +data class FilesContainer( + var version: Int = -1, + var crc: Int = -1, + var nameHash: Int = -1, + var filesIndexes: IntArray? = null, + var files: Array? = null +) \ No newline at end of file diff --git a/Server/src/main/core/cache/misc/buffer/BufferInputStream.java b/Server/src/main/core/cache/misc/buffer/BufferInputStream.java deleted file mode 100644 index a801c870d..000000000 --- a/Server/src/main/core/cache/misc/buffer/BufferInputStream.java +++ /dev/null @@ -1,39 +0,0 @@ -package core.cache.misc.buffer; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -/** - * Handles the reading of data from a byte buffer. - * @author Emperor - */ -public final class BufferInputStream extends InputStream { - - /** - * The buffer to write on. - */ - private final ByteBuffer buffer; - - /** - * The buffer input stream. - * @param buffer The buffer. - */ - public BufferInputStream(ByteBuffer buffer) throws IOException { - this.buffer = buffer; - } - - @Override - public int read() throws IOException { - return buffer.get(); - } - - /** - * Gets the buffer. - * @return The buffer. - */ - public ByteBuffer getBuffer() { - return buffer; - } - -} diff --git a/Server/src/main/core/cache/misc/buffer/BufferOutputStream.java b/Server/src/main/core/cache/misc/buffer/BufferOutputStream.java deleted file mode 100644 index 88ea2ac69..000000000 --- a/Server/src/main/core/cache/misc/buffer/BufferOutputStream.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.cache.misc.buffer; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -/** - * Handles the writing of data on a byte buffer. - * @author Emperor - */ -public final class BufferOutputStream extends OutputStream { - - /** - * The buffer to write on. - */ - private final ByteBuffer buffer; - - /** - * Constructs a new {@code BufferOutputStream} {@code Object}. - * @param buffer The buffer to write on. - * @throws IOException When an I/O exception occurs. - * @throws SecurityException If a security manager exists and its - * checkPermission method denies enabling subclassing. - */ - public BufferOutputStream(ByteBuffer buffer) throws IOException, SecurityException { - super(); - this.buffer = buffer; - } - - @Override - public void write(int b) throws IOException { - buffer.put((byte) b); - } - - @Override - public void flush() { - /* - * empty. - */ - } - - @Override - public void close() { - /* - * empty. - */ - } - - /** - * Gets the buffer. - * @return The buffer. - */ - public ByteBuffer getBuffer() { - return buffer; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.java b/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.java deleted file mode 100644 index fb04ae272..000000000 --- a/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.java +++ /dev/null @@ -1,182 +0,0 @@ -package core.cache.misc.buffer; - -import java.io.ObjectInputStream; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -/** - * Holds utility methods for reading/writing a byte buffer. - * @author Emperor - */ -public final class ByteBufferUtils { - - /** - * Gets a string from the byte buffer. - * @param buffer The byte buffer. - * @return The string. - */ - public static String getString(ByteBuffer buffer) { - StringBuilder sb = new StringBuilder(); - byte b; - while ((b = buffer.get()) != 0) { - sb.append((char) b); - } - return sb.toString(); - } - - /** - * Puts a string on the byte buffer. - * @param s The string to put. - * @param buffer The byte buffer. - */ - public static void putString(String s, ByteBuffer buffer) { - buffer.put(s.getBytes(StandardCharsets.UTF_8)).put((byte) 0); - } - - /** - * Gets a string from the byte buffer. - * @param s The string. - * @param buffer The byte buffer. - * @return The string. - */ - public static ByteBuffer putGJ2String(String s, ByteBuffer buffer) { - byte[] packed = new byte[256]; - int length = packGJString2(0, packed, s); - return buffer.put((byte) 0).put(packed, 0, length).put((byte) 0); - } - - /** - * Decodes the XTEA encryption. - * @param keys The keys. - * @param start The start index. - * @param end The end index. - * @param buffer The byte buffer. - */ - public static void decodeXTEA(int[] keys, int start, int end, ByteBuffer buffer) { - int l = buffer.position(); - buffer.position(start); - int length = (end - start) / 8; - for (int i = 0; i < length; i++) { - int firstInt = buffer.getInt(); - int secondInt = buffer.getInt(); - int sum = 0xc6ef3720; - int delta = 0x9e3779b9; - for (int j = 32; j-- > 0;) { - secondInt -= keys[(sum & 0x1c84) >>> 11] + sum ^ (firstInt >>> 5 ^ firstInt << 4) + firstInt; - sum -= delta; - firstInt -= (secondInt >>> 5 ^ secondInt << 4) + secondInt ^ keys[sum & 3] + sum; - } - buffer.position(buffer.position() - 8); - buffer.putInt(firstInt); - buffer.putInt(secondInt); - } - buffer.position(l); - } - - /** - * Converts a String to an Integer? - * @param position The position. - * @param buffer The buffer used. - * @param string The String to convert. - * @return The Integer. - */ - public static int packGJString2(int position, byte[] buffer, String string) { - int length = string.length(); - int offset = position; - for (int i = 0; length > i; i++) { - int character = string.charAt(i); - if (character > 127) { - if (character > 2047) { - buffer[offset++] = (byte) ((character | 919275) >> 12); - buffer[offset++] = (byte) (128 | ((character >> 6) & 63)); - buffer[offset++] = (byte) (128 | (character & 63)); - } else { - buffer[offset++] = (byte) ((character | 12309) >> 6); - buffer[offset++] = (byte) (128 | (character & 63)); - } - } else - buffer[offset++] = (byte) character; - } - return offset - position; - } - - /** - * Gets a tri-byte from the buffer. - * @param buffer The buffer. - * @return The value. - */ - public static int getMedium(ByteBuffer buffer) { - return ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF); - } - - /** - * Gets a smart from the buffer. - * @param buffer The buffer. - * @return The value. - */ - public static int getSmart(ByteBuffer buffer) { - int peek = buffer.get() & 0xFF; - if (peek <= Byte.MAX_VALUE) { - return peek; - } - return ((peek << 8) | (buffer.get() & 0xFF)) - 32768; - } - - /** - * Gets a smart from the buffer. - * @param buffer The buffer. - * @return The value. - */ - public static int getBigSmart(ByteBuffer buffer) { - int value = 0; - int current = getSmart(buffer); - while (current == 32767) { - current = getSmart(buffer); - value += 32767; - } - value += current; - return value; - } - - /* *//** - * Writes an object on the buffer. - * @param buffer The buffer to write on. - * @param o The object. - */ - /* - * public static void putObject(ByteBuffer buffer, Object o) { ByteBuffer b; - * try (ObjectOutputStream out = new ObjectOutputStream(new - * BufferOutputStream(b = ByteBuffer.allocate(99999)))) { - * out.writeObject(o); b.flip(); } catch (Throwable e) { - * e.printStackTrace(); b = (ByteBuffer) ByteBuffer.allocate(0).flip(); } - * buffer.putInt(b.remaining()); if (b.remaining() > 0) { buffer.put(b); } } - */ - - /** - * Gets an object from the byte buffer. - * @param buffer The buffer. - * @return The object. - */ - public static Object getObject(ByteBuffer buffer) { - int length = buffer.getInt(); - if (length > 0) { - byte[] bytes = new byte[length]; - buffer.get(bytes); - try (ObjectInputStream str = new ObjectInputStream(new BufferInputStream(ByteBuffer.wrap(bytes)))) { - return (Object) str.readObject(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - return null; - } - - /** - * Constructs a new {@code ByteBufferUtils} {@code Object}. - */ - private ByteBufferUtils() { - /* - * empty. - */ - } -} \ No newline at end of file diff --git a/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.kt b/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.kt new file mode 100644 index 000000000..73d72df70 --- /dev/null +++ b/Server/src/main/core/cache/misc/buffer/ByteBufferUtils.kt @@ -0,0 +1,142 @@ +package core.cache.misc.buffer + +import java.nio.ByteBuffer +import java.nio.charset.StandardCharsets + +object ByteBufferUtils { + /** + * Gets a string from the byte buffer. + * @param buffer The byte buffer. + * @return The string. + */ + @JvmStatic + fun getString(buffer: ByteBuffer): String { + val sb = StringBuilder() + var b: Byte + while (buffer.get().also { b = it }.toInt() != 0) { + sb.append(Char(b.toUShort())) + } + return sb.toString() + } + + /** + * Puts a string on the byte buffer. + * @param s The string to put. + * @param buffer The byte buffer. + */ + @JvmStatic + fun putString(s: String, buffer: ByteBuffer) { + buffer.put(s.toByteArray(StandardCharsets.UTF_8)).put(0.toByte()) + } + + /** + * Gets a string from the byte buffer. + * @param s The string. + * @param buffer The byte buffer. + * @return The string. + */ + fun putGJ2String(s: String, buffer: ByteBuffer): ByteBuffer { + val packed = ByteArray(256) + val length = packGJString2(0, packed, s) + return buffer.put(0.toByte()).put(packed, 0, length).put(0.toByte()) + } + + /** + * Decodes the XTEA encryption. + * @param keys The keys. + * @param start The start index. + * @param end The end index. + * @param buffer The byte buffer. + */ + fun decodeXTEA(keys: IntArray, start: Int, end: Int, buffer: ByteBuffer) { + val l = buffer.position() + buffer.position(start) + val length = (end - start) / 8 + for (i in 0 until length) { + var firstInt = buffer.getInt() + var secondInt = buffer.getInt() + var sum = -0x3910c8e0 + val delta = -0x61c88647 + var j = 32 + while (j-- > 0) { + secondInt -= keys[sum and 0x1c84 ushr 11] + sum xor (firstInt ushr 5 xor (firstInt shl 4)) + firstInt + sum -= delta + firstInt -= (secondInt ushr 5 xor (secondInt shl 4)) + secondInt xor keys[sum and 3] + sum + } + buffer.position(buffer.position() - 8) + buffer.putInt(firstInt) + buffer.putInt(secondInt) + } + buffer.position(l) + } + + /** + * Converts a String to an Integer? + * @param position The position. + * @param buffer The buffer used. + * @param string The String to convert. + * @return The Integer. + */ + @JvmStatic + fun packGJString2(position: Int, buffer: ByteArray, string: String): Int { + val length = string.length + var offset = position + var i = 0 + while (length > i) { + val character = string[i].code + if (character > 127) { + if (character > 2047) { + buffer[offset++] = (character or 919275 shr 12).toByte() + buffer[offset++] = (128 or (character shr 6 and 63)).toByte() + buffer[offset++] = (128 or (character and 63)).toByte() + } else { + buffer[offset++] = (character or 12309 shr 6).toByte() + buffer[offset++] = (128 or (character and 63)).toByte() + } + } else buffer[offset++] = character.toByte() + i++ + } + return offset - position + } + + /** + * Gets a tri-byte from the buffer. + * @param buffer The buffer. + * @return The value. + */ + @JvmStatic + fun getMedium(buffer: ByteBuffer): Int { + return (buffer.get().toInt() and 0xFF shl 16) + (buffer.get().toInt() and 0xFF shl 8) + (buffer.get() + .toInt() and 0xFF) + } + + /** + * Gets a smart from the buffer. + * @param buffer The buffer. + * @return The value. + */ + @JvmStatic + fun getSmart(buffer: ByteBuffer): Int { + val peek = buffer.get().toInt() and 0xFF + return if (peek <= Byte.MAX_VALUE) { + peek + } else (peek shl 8 or (buffer.get().toInt() and 0xFF)) - 32768 + } + + /** + * Gets a smart from the buffer. + * @param buffer The buffer. + * @return The value. + */ + @JvmStatic + fun getBigSmart(buffer: ByteBuffer): Int { + var value = 0 + var current = getSmart(buffer) + while (current == 32767) { + current = getSmart(buffer) + value += 32767 + } + value += current + return value + } +} \ 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..a75dcb4b5 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 @@ -131,6 +166,18 @@ abstract class Cutscene(val player: Player) { player.dialogueInterpreter.addAction {_,_ -> onContinue.invoke()} } + /** + * Sends a non-NPC dialogue to the player, which updates the cutscene stage by default when continued + * @param message the message to send + * @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default. + */ + fun dialogueLinesUpdate(vararg message: String, onContinue: () -> Unit = {incrementStage()}) + { + logCutscene("Sending standard dialogue lines update.") + sendDialogueLines(player, *message) + player.dialogueInterpreter.addAction {_,_ -> onContinue.invoke()} + } + /** * Sends a player dialogue, which updates the cutscene stage by default when continued * @param expression the FacialExpression to use @@ -221,13 +268,19 @@ abstract class Cutscene(val player: Player) { logCutscene("Added NPC $id at location LOCAL[$regionX,$regionY,$plane] GLOBAL[${npc.location.x},${npc.location.y},$plane]") } - fun start() + fun start(){ + start(true) + } + + fun start(hideMiniMap: Boolean) { logCutscene("Starting cutscene for ${player.username}.") region = RegionManager.forId(player.location.regionId) base = RegionManager.forId(player.location.regionId).baseLocation setup() - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + if (hideMiniMap) { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + } runStage(player.getCutsceneStage()) setAttribute(player, ATTRIBUTE_CUTSCENE, this) setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, 0) @@ -237,31 +290,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 +326,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 +412,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 +483,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/AIPBuilder.java b/Server/src/main/core/game/bots/AIPBuilder.java index e79c95447..fb619f730 100644 --- a/Server/src/main/core/game/bots/AIPBuilder.java +++ b/Server/src/main/core/game/bots/AIPBuilder.java @@ -91,7 +91,7 @@ public final class AIPBuilder { //Varrock Ge bank RandomAfkPlayer(new Location(3189, 3439)); - //Home Bank + // Lunar Island Bank RandomAfkPlayer(new Location(2099, 3918)); } diff --git a/Server/src/main/core/game/bots/AIPlayer.java b/Server/src/main/core/game/bots/AIPlayer.java index 288a1a9bb..e54fe732a 100644 --- a/Server/src/main/core/game/bots/AIPlayer.java +++ b/Server/src/main/core/game/bots/AIPlayer.java @@ -109,7 +109,7 @@ public class AIPlayer extends Player { * Generates bot stats/equipment/etc based on OSRScopyLine */ public void updateRandomValues() { - this.getAppearance().setGender(RandomFunction.random(5) == 1 ? Gender.FEMALE : Gender.MALE); + this.getAppearance().setGender(RandomFunction.random(3) == 1 ? Gender.FEMALE : Gender.MALE); int setTo = RandomFunction.random(0,10); CharacterDesign.randomize(this,true); this.setDirection(Direction.values()[new Random().nextInt(Direction.values().length)]); //Random facing dir @@ -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..c2ac69649 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,8 +502,10 @@ 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.") + Repository.sendGrandExchangeNews( + SERVER_GE_NAME + " just offered " + itemAmt + " " + + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE." + ) } bot.bank.remove(Item(id, itemAmt)) bot.bank.refresh() @@ -522,7 +535,10 @@ class ScriptAPI(private val bot: Player) { } val canSell = GrandExchange.addBotOffer(actualId, itemAmt) if (canSell && saleIsBigNews(actualId, itemAmt)) { - Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") + Repository.sendGrandExchangeNews( + SERVER_GE_NAME + " just offered " + itemAmt + " " + + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE." + ) } bot.bank.remove(item) bot.bank.refresh() @@ -558,7 +574,10 @@ 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 -> Repository.sendGrandExchangeNews( + SERVER_GE_NAME + " just offered " + itemAmt + " " + + ItemDefinition.forId(actualId).name.lowercase() + " on the GE." + ) } } bot.bank.remove(item) @@ -567,7 +586,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 +625,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 +646,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 +676,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 +708,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 +728,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..decb90a85 100644 --- a/Server/src/main/core/game/bots/SkillingBotAssembler.kt +++ b/Server/src/main/core/game/bots/SkillingBotAssembler.kt @@ -2,6 +2,7 @@ package core.game.bots import core.game.node.item.Item import core.game.world.map.Location +import org.rs09.consts.Items class SkillingBotAssembler { fun produce(type: Wealth, loc: Location): AIPlayer { @@ -21,8 +22,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) @@ -38,7 +40,161 @@ class SkillingBotAssembler { RICH } - 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 + val POORSETS = arrayOf( + listOf(Items.BLACK_ROBE_581, Items.BLACK_SKIRT_1015, Items.STAFF_OF_AIR_1381), + listOf(Items.BLACK_ROBE_581, Items.BLACK_SKIRT_1015), + listOf(Items.BLACK_ROBE_581, Items.BLACK_SKIRT_1015, Items.WIZARD_HAT_1017), + listOf(), // No equipment + listOf(Items.WIZARD_HAT_579), + listOf(Items.WIZARD_HAT_579, Items.STAFF_OF_AIR_1381), + listOf(Items.WIZARD_HAT_579, Items.STAFF_1379), + listOf(Items.WIZARD_HAT_579, Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011), + listOf(Items.WIZARD_HAT_579, Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.TIARA_5525), + listOf(Items.WIZARD_HAT_579, Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.TEAM_1_CAPE_4315), + listOf(Items.WIZARD_HAT_579, Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.STAFF_OF_AIR_1381), + listOf(Items.WIZARD_HAT_579, Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.STAFF_OF_WATER_1383), + listOf(Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011), + listOf(Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.TIARA_5525), + listOf(Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.TEAM_1_CAPE_4315), + listOf(Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.STAFF_OF_AIR_1381), + listOf(Items.WIZARD_ROBE_577, Items.BLUE_SKIRT_1011, Items.STAFF_OF_WATER_1383), + listOf(), // No equipment + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_PLATEBODY_1119, Items.STEEL_KITESHIELD_1193, Items.STEEL_LONGSWORD_1295, Items.TEAM_29_CAPE_4371), + listOf(Items.STEEL_PLATESKIRT_1083, Items.STEEL_PLATEBODY_1119, Items.STEEL_KITESHIELD_1193, Items.STEEL_LONGSWORD_1295, Items.TEAM_30_CAPE_4373), + listOf(Items.STEEL_PLATESKIRT_1083, Items.STEEL_PLATEBODY_1119, Items.STEEL_FULL_HELM_1157, Items.TEAM_30_CAPE_4373), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.STEEL_FULL_HELM_1157, Items.TEAM_31_CAPE_4375), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_PLATEBODY_1119, Items.TEAM_29_CAPE_4371), + listOf(Items.STEEL_PLATESKIRT_1083, Items.STEEL_PLATEBODY_1119, Items.TEAM_30_CAPE_4373), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.TEAM_31_CAPE_4375), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.STEEL_2H_SWORD_1311, Items.TEAM_34_CAPE_4381), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.STEEL_SCIMITAR_1325, Items.TEAM_35_CAPE_4383), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.STEEL_BATTLEAXE_1365, Items.TEAM_35_CAPE_4383), + listOf(Items.STEEL_PLATELEGS_1069, Items.STEEL_CHAINBODY_1105, Items.STEEL_BATTLEAXE_1365, Items.STEEL_SQ_SHIELD_1177, Items.TEAM_35_CAPE_4383), + listOf(), // No equipment + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.IRON_KITESHIELD_1191, Items.IRON_LONGSWORD_1293, Items.TEAM_36_CAPE_4385), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.IRON_2H_SWORD_1309, Items.TEAM_37_CAPE_4387), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.IRON_SCIMITAR_1323, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.IRON_BATTLEAXE_1363, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_CHAINBODY_1101, Items.IRON_KITESHIELD_1191, Items.IRON_LONGSWORD_1293, Items.TEAM_33_CAPE_4379), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.TEAM_39_CAPE_4391), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.TEAM_37_CAPE_4387), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_PLATEBODY_1115, Items.IRON_SCIMITAR_1323, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_PLATEBODY_1115, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_CHAINBODY_1101, Items.TEAM_33_CAPE_4379), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_PLATEBODY_1115, Items.IRON_FULL_HELM_1153, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATESKIRT_1081, Items.IRON_CHAINBODY_1101, Items.IRON_FULL_HELM_1153, Items.TEAM_32_CAPE_4377), + listOf(Items.IRON_PLATELEGS_1067, Items.IRON_CHAINBODY_1101, Items.IRON_FULL_HELM_1153, Items.TEAM_33_CAPE_4379), + listOf(), // No equipment + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_PLATEBODY_1117,Items.BRONZE_FULL_HELM_1155,Items.BRONZE_KITESHIELD_1189,Items.BRONZE_LONGSWORD_1291,Items.TEAM_27_CAPE_4367), + listOf(Items.BRONZE_PLATESKIRT_1087,Items.BRONZE_PLATEBODY_1117,Items.BRONZE_FULL_HELM_1155,Items.BRONZE_KITESHIELD_1189,Items.BRONZE_LONGSWORD_1291,Items.TEAM_27_CAPE_4367), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_PLATEBODY_1117,Items.BRONZE_FULL_HELM_1155,Items.BRONZE_2H_SWORD_1307, Items.TEAM_28_CAPE_4369), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_CHAINBODY_1103), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_2H_SWORD_1307), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_SCIMITAR_1321), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_BATTLEAXE_1375), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_BATTLEAXE_1375,Items.BRONZE_SQ_SHIELD_1173), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_PLATEBODY_1117, Items.TEAM_27_CAPE_4367), + listOf(Items.BRONZE_PLATELEGS_1075,Items.BRONZE_PLATEBODY_1117,Items.BRONZE_FULL_HELM_1155, Items.TEAM_27_CAPE_4367), + listOf(Items.BRONZE_PLATESKIRT_1087,Items.BRONZE_CHAINBODY_1103), + listOf(Items.BRONZE_PLATESKIRT_1087,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_FULL_HELM_1155), + listOf(Items.BRONZE_PLATESKIRT_1087,Items.BRONZE_CHAINBODY_1103,Items.BRONZE_FULL_HELM_1155, Items.TEAM_28_CAPE_4369), + listOf(Items.BRONZE_PLATESKIRT_1087,Items.BRONZE_PLATEBODY_1117, Items.TEAM_28_CAPE_4369), + listOf(), // No equipment + listOf(Items.LEATHER_BODY_1129,Items.LEATHER_CHAPS_1095), + listOf(), // No equipment + listOf(Items.STUDDED_CHAPS_1097,Items.STUDDED_BODY_1133), + listOf(Items.MIME_LEGS_3059,Items.MIME_TOP_3058,Items.MIME_BOOTS_3061,Items.MIME_GLOVES_3060,Items.MIME_MASK_3057), + listOf(Items.MIME_LEGS_3059,Items.MIME_TOP_3058,Items.MIME_BOOTS_3061,Items.MIME_GLOVES_3060), + listOf(Items.MONKS_ROBE_542,Items.MONKS_ROBE_544), + listOf(Items.CHICKEN_LEGS_11022,Items.CHICKEN_WINGS_11020,Items.CHICKEN_HEAD_11021,Items.CHICKEN_FEET_11019), + listOf(Items.TOY_KITE_12844,Items.CHOCATRICE_CAPE_12634), + listOf(), // No equipment + listOf(Items.WHITE_APRON_1005, Items.CHEFS_HAT_1949), + listOf(Items.TEAM_10_CAPE_4333), + listOf(Items.TEAM_2_CAPE_4317), + listOf(), // No equipment + listOf(), // No equipment + listOf(), // No equipment + listOf()) + + val AVGSETS = arrayOf( + listOf(Items.SKELETAL_TOP_6139, Items.SKELETAL_BOTTOMS_6141), + listOf(Items.SKELETON_LEGGINGS_9923, Items.SKELETON_SHIRT_9924, Items.SKELETON_MASK_9925), + listOf(Items.BLACK_ELE_SHIRT_10400,Items.BLACK_ELE_LEGS_10402,Items.BROWN_HEADBAND_2649), + listOf(Items.RED_ELE_SHIRT_10404,Items.RED_ELE_LEGS_10406), + listOf(Items.COMBAT_ROBE_TOP_100_12971, Items.COMBAT_ROBE_BOTTOM_100_12978), + listOf(Items.COMBAT_ROBE_TOP_100_12971, Items.COMBAT_ROBE_BOTTOM_100_12978, Items.COMBAT_HOOD_100_12964), + listOf(Items.COMBAT_ROBE_TOP_100_12971, Items.COMBAT_ROBE_BOTTOM_100_12978, Items.RUNE_BERSERKER_SHIELD_100_12929), + listOf(Items.COMBAT_ROBE_TOP_100_12971, Items.COMBAT_ROBE_BOTTOM_100_12978, Items.ADAMANT_BERSERKER_SHIELD_100_12915), + listOf(Items.DRUIDIC_MAGE_TOP_100_12894, Items.DRUIDIC_MAGE_BOTTOM_100_12901, Items.DRUIDIC_MAGE_HOOD_100_12887), + listOf(), // No equipment + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_PLATEBODY_1121, Items.MITHRIL_KITESHIELD_1197, Items.MITHRIL_LONGSWORD_1299, Items.TEAM_29_CAPE_4371), + listOf(Items.MITHRIL_PLATESKIRT_1085, Items.MITHRIL_PLATEBODY_1121, Items.MITHRIL_KITESHIELD_1197, Items.MITHRIL_LONGSWORD_1299, Items.TEAM_30_CAPE_4373), + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_PLATEBODY_1121, Items.TEAM_29_CAPE_4371), + listOf(Items.MITHRIL_PLATESKIRT_1085, Items.MITHRIL_PLATEBODY_1121, Items.TEAM_30_CAPE_4373), + listOf(Items.MITHRIL_PLATESKIRT_1085, Items.MITHRIL_CHAINBODY_1109, Items.TEAM_30_CAPE_4373), + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_CHAINBODY_1109, Items.TEAM_31_CAPE_4375), + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_CHAINBODY_1109, Items.MITHRIL_2H_SWORD_1315, Items.TEAM_34_CAPE_4381), + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_CHAINBODY_1109, Items.MITHRIL_SCIMITAR_1329, Items.TEAM_35_CAPE_4383), + listOf(Items.MITHRIL_PLATELEGS_1071, Items.MITHRIL_CHAINBODY_1109, Items.MITHRIL_BATTLEAXE_1369, Items.TEAM_36_CAPE_4385), + listOf(), // No equipment + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_PLATEBODY_1124,Items.ADAMANT_KITESHIELD_1199,Items.ADAMANT_LONGSWORD_1301, Items.TEAM_29_CAPE_4371), + listOf(Items.ADAMANT_PLATESKIRT_1091,Items.ADAMANT_PLATEBODY_1124,Items.ADAMANT_KITESHIELD_1199,Items.ADAMANT_LONGSWORD_1301, Items.TEAM_30_CAPE_4373), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_PLATEBODY_1124, Items.TEAM_29_CAPE_4371), + listOf(Items.ADAMANT_PLATESKIRT_1091,Items.ADAMANT_PLATEBODY_1124, Items.TEAM_30_CAPE_4373), + listOf(Items.ADAMANT_PLATESKIRT_1091,Items.ADAMANT_CHAINBODY_1111, Items.TEAM_30_CAPE_4373), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_CHAINBODY_1111, Items.TEAM_31_CAPE_4375), + listOf(Items.ADAMANT_PLATESKIRT_1091,Items.ADAMANT_CHAINBODY_1111,Items.ADAMANT_FULL_HELM_1161, Items.TEAM_30_CAPE_4373), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_CHAINBODY_1111,Items.ADAMANT_FULL_HELM_1161, Items.TEAM_31_CAPE_4375), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_CHAINBODY_1111,Items.ADAMANT_2H_SWORD_1317, Items.TEAM_34_CAPE_4381), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_CHAINBODY_1111,Items.ADAMANT_SCIMITAR_1331, Items.TEAM_35_CAPE_4383), + listOf(Items.ADAMANT_PLATELEGS_1073,Items.ADAMANT_CHAINBODY_1111,Items.ADAMANT_BATTLEAXE_1371, Items.TEAM_35_CAPE_4383), + listOf(), // No equipment + listOf(Items.GREEN_DHIDE_BODY_1135,Items.GREEN_DHIDE_CHAPS_1099,Items.GREEN_DHIDE_VAMB_1065,Items.GREEN_DHIDE_COIF_100_12936,Items.AVAS_ACCUMULATOR_10499), + listOf(Items.GREEN_DHIDE_BODY_1135,Items.GREEN_DHIDE_CHAPS_1099,Items.GREEN_DHIDE_VAMB_1065,Items.GREEN_DHIDE_COIF_100_12936,Items.ADAMANT_CROSSBOW_9183,Items.AVAS_ACCUMULATOR_10499), + listOf(Items.TEAM_10_CAPE_4333), + listOf(Items.TEAM_1_CAPE_4315), + listOf(), // No equipment + listOf(), // No equipment + listOf()) + + val RICHSETS = arrayOf( + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_PLATEBODY_1127,Items.RUNE_KITESHIELD_1201,Items.RUNE_FULL_HELM_1163,Items.RUNE_LONGSWORD_1303,Items.TEAM_29_CAPE_4371), + listOf(Items.RUNE_PLATESKIRT_1093,Items.RUNE_PLATEBODY_1127,Items.RUNE_KITESHIELD_1201,Items.RUNE_FULL_HELM_1163,Items.RUNE_LONGSWORD_1303,Items.TEAM_30_CAPE_4373), + listOf(Items.RUNE_PLATESKIRT_1093,Items.RUNE_PLATEBODY_1127, Items.TEAM_29_CAPE_4371), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_PLATEBODY_1127,Items.RUNE_FULL_HELM_1163,Items.TEAM_29_CAPE_4371), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_PLATEBODY_1127,Items.RUNE_BERSERKER_SHIELD_100_12929,Items.TEAM_29_CAPE_4371), + listOf(Items.RUNE_PLATESKIRT_1093,Items.RUNE_PLATEBODY_1127,Items.TEAM_30_CAPE_4373), + listOf(Items.RUNE_PLATESKIRT_1093,Items.RUNE_CHAINBODY_1113,Items.TEAM_30_CAPE_4373), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_CHAINBODY_1113,Items.TEAM_31_CAPE_4375), + listOf(Items.RUNE_PLATESKIRT_1093,Items.RUNE_CHAINBODY_1113,Items.RUNE_FULL_HELM_1163,Items.TEAM_30_CAPE_4373), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_CHAINBODY_1113,Items.RUNE_FULL_HELM_1163,Items.TEAM_31_CAPE_4375), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_CHAINBODY_1113,Items.RUNE_2H_SWORD_1319,Items.TEAM_34_CAPE_4381), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_CHAINBODY_1113,Items.RUNE_SCIMITAR_1333,Items.TEAM_35_CAPE_4383), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_CHAINBODY_1113,Items.RUNE_BATTLEAXE_1373,Items.TEAM_35_CAPE_4383), + listOf(Items.RUNE_PLATELEGS_1079,Items.RUNE_PLATEBODY_1127,Items.ABYSSAL_WHIP_4151,Items.TEAM_29_CAPE_4371), + listOf(), // No equipment + listOf(Items.RUNE_PLATEBODY_G_2615,Items.RUNE_PLATELEGS_G_2618,Items.RUNE_KITESHIELD_G_2621,Items.RUNE_FULL_HELMG_2619, Items.TEAM_27_CAPE_4367), + listOf(Items.RUNE_PLATEBODY_T_2623,Items.RUNE_PLATELEGS_T_2625,Items.RUNE_KITESHIELD_T_2629,Items.RUNE_FULL_HELM_T_2627, Items.TEAM_27_CAPE_4367), + listOf(Items.GILDED_PLATEBODY_3481,Items.GILDED_PLATELEGS_3483,Items.CAPE_1030), + listOf(Items.ZAMORAK_PLATEBODY_2653,Items.ZAMORAK_PLATELEGS_2655,Items.ZAMORAK_FULL_HELM_2657), + listOf(Items.SARADOMIN_PLATEBODY_2661,Items.SARADOMIN_PLATELEGS_2663,Items.SARADOMIN_FULL_HELM_2665), + listOf(Items.GUTHIX_PLATEBODY_2669,Items.GUTHIX_PLATELEGS_2671,Items.GUTHIX_FULL_HELM_2673), + listOf(Items.ZAMORAK_PLATEBODY_2653,Items.ZAMORAK_PLATELEGS_2655,Items.ZAMORAK_FULL_HELM_2657,Items.ZAMORAK_CAPE_2414), + listOf(Items.SARADOMIN_PLATEBODY_2661,Items.SARADOMIN_PLATELEGS_2663,Items.SARADOMIN_FULL_HELM_2665,Items.SARADOMIN_CAPE_2412), + listOf(Items.GUTHIX_PLATEBODY_2669,Items.GUTHIX_PLATELEGS_2671,Items.GUTHIX_FULL_HELM_2673,Items.GUTHIX_CAPE_2413), + listOf(Items.THIRD_AGE_ROBE_10340,Items.THIRD_AGE_ROBE_TOP_10338,Items.SANTA_HAT_1050), + listOf(), // No equipment + listOf(Items.DRAGON_PLATELEGS_4087,Items.DRAGON_CHAINBODY_3140,Items.ABYSSAL_WHIP_4151,Items.DRAGON_FULL_HELM_11335), + listOf(Items.DRAGON_PLATELEGS_4087,Items.DRAGON_CHAINBODY_3140,Items.DRAGONFIRE_SHIELD_11283,Items.DRAGON_FULL_HELM_11335), + listOf(), // No equipment + listOf(Items.BLACK_DHIDE_BODY_2503,Items.BLACK_DHIDE_CHAPS_2497,Items.BLACK_DHIDE_VAMB_2491,Items.RUNE_CROSSBOW_9185,Items.AVAS_ACCUMULATOR_10499), + listOf(Items.BLACK_DHIDE_BODY_2503,Items.BLACK_DHIDE_CHAPS_2497,Items.BLACK_DHIDE_VAMB_2491,Items.BLACK_DHIDE_COIF_100_12957,Items.RUNE_CROSSBOW_9185,Items.RUNE_KITESHIELD_1201), + listOf(Items.ELITE_BLACK_PLATEBODY_14492,Items.ELITE_BLACK_PLATELEGS_14490, Items.ELITE_BLACK_FULL_HELM_14494), + listOf(Items.BATTLE_ROBE_BOTTOM_100_12880,Items.BATTLE_ROBE_TOP_100_12873,Items.PURPLE_PARTYHAT_1046), + listOf(Items.ZURIELS_ROBE_BOTTOM_13862,Items.ZURIELS_ROBE_TOP_13858,Items.ZURIELS_HOOD_13864,Items.ZURIELS_HOOD_13864,Items.ZURIELS_STAFF_13867), + listOf(Items.VESTAS_CHAINBODY_13887,Items.VESTAS_PLATESKIRT_13893), + listOf(), // No equipment + listOf(), // No equipment + listOf()) +} 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..89912d9f1 100644 --- a/Server/src/main/core/game/consumable/Potion.java +++ b/Server/src/main/core/game/consumable/Potion.java @@ -1,10 +1,16 @@ package core.game.consumable; import content.data.consumables.Consumables; -import content.region.kandarin.barcrawl.BarcrawlManager; +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.Item; +import core.game.system.task.Pulse; +import core.game.world.GameWorld; +import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; public class Potion extends Drink { @@ -13,7 +19,7 @@ public class Potion extends Drink { private static final Audio SOUND = new Audio(2401, 1, 1); public Potion(final int[] ids, final ConsumableEffect effect, final String... messages) { - super(ids, effect, messages); + super(ids, effect, new Animation(829), messages); } @Override @@ -22,15 +28,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 +46,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 @@ -55,16 +59,22 @@ 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."); - } else if (dosesLeft == 1) { - player.getPacketDispatch().sendMessage("You have 1 dose of your potion left."); - } else { - player.getPacketDispatch().sendMessage("You have finished your potion."); - } + // Remaining dosages message should be delayed - https://youtu.be/n6CCf4Rj8Lg?t=79 + queueScript(player, 2, QueueStrength.SOFT, false, (Integer stage) -> { + if (dosesLeft > 1) { + player.getPacketDispatch().sendMessage("You have " + dosesLeft + " doses of potion left."); + } else if (dosesLeft == 1) { + player.getPacketDispatch().sendMessage("You have 1 dose of potion left."); + } else { + player.getPacketDispatch().sendMessage("You have finished your potion."); + } + return stopExecuting(player); + }); } - 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..b27ecf700 100644 --- a/Server/src/main/core/game/container/impl/BankContainer.java +++ b/Server/src/main/core/game/container/impl/BankContainer.java @@ -1,12 +1,10 @@ package core.game.container.impl; -import core.api.IfaceSettingsBuilder; -import core.game.container.access.InterfaceContainer; -import kotlin.ranges.IntRange; -import org.rs09.consts.Vars; import core.ServerConstants; +import core.api.IfaceSettingsBuilder; import core.game.component.Component; import core.game.container.*; +import core.game.container.access.InterfaceContainer; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.IronmanMode; import core.game.node.item.Item; @@ -15,8 +13,11 @@ import core.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; +import kotlin.ranges.IntRange; +import org.rs09.consts.Vars; + +import static core.api.ContentAPIKt.*; -import java.nio.ByteBuffer; /** * Represents the bank container. @@ -126,15 +127,12 @@ public final class BankContainer extends Container { }); 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) { @@ -152,38 +150,16 @@ public final class BankContainer extends Container { BankContainer.this.close(); return true; }); - refresh(listener); player.getInterfaceManager().openSingleTab(new Component(763)); - player.getInventory().getListeners().add(player.getBank().listener); + refresh(listener); player.getInventory().refresh(); - player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player); + player.getInventory().getListeners().add(player.getBank().listener); + setVarp(player, 1249, lastAmountX); player.getPacketDispatch().sendIfaceSettings(1278, 73, 762, 0, SIZE); int settings = new IfaceSettingsBuilder().enableOptions(new IntRange(0,5)).enableExamine().enableSlotSwitch().build(); player.getPacketDispatch().sendIfaceSettings(settings, 0, 763, 0, 27); player.getPacketDispatch().sendRunScript(1451, ""); open = true; - this.player.getBank().setTabConfigurations(player); - - } - - @Override - public long save(ByteBuffer buffer) { - buffer.putInt(lastAmountX); - buffer.put((byte) tabStartSlot.length); - for (int j : tabStartSlot) { - buffer.putShort((short) j); - } - return super.save(buffer); - } - - @Override - public int parse(ByteBuffer buffer) { - lastAmountX = buffer.getInt(); - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - tabStartSlot[i] = buffer.getShort(); - } - return super.parse(buffer); } /** @@ -222,14 +198,9 @@ public final class BankContainer extends Container { } item = new Item(item.getId(), amount, item.getCharge()); - boolean unnote = !item.getDefinition().isUnnoted(); + Item add = item.toUnnotedItem(); - Item add = unnote ? new Item(item.getDefinition().getNoteId(), amount, item.getCharge()) : item; - if (unnote && !add.getDefinition().isUnnoted()) { - add = item; - } - - int maxCount = super.getMaximumAdd(add); + int maxCount = getMaximumAdd(add); if (amount > maxCount) { add.setAmount(maxCount); item.setAmount(maxCount); @@ -239,7 +210,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 +218,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 +256,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 +272,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 +339,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 +363,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 +405,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 +413,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 +437,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 +447,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 +455,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; } /** @@ -548,6 +466,11 @@ public final class BankContainer extends Container { return open; } + @Override + public int getMaximumAdd(Item item) { + return super.getMaximumAdd(item.toUnnotedItem()); + } + /** * Listens to the bank container. * @author Emperor @@ -571,22 +494,22 @@ public final class BankContainer extends Container { public void update(Container c, ContainerEvent event) { if (c instanceof BankContainer) { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 762, 64000, 95, event.getItems(), false, event.getSlots())); + player.getBank().setTabConfigurations(); + player.getBank().sendBankSpace(); } else { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 763, 64000, 93, event.getItems(), false, event.getSlots())); } - player.getBank().setTabConfigurations(); - player.getBank().sendBankSpace(); } @Override public void refresh(Container c) { if (c instanceof BankContainer) { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 762, 64000, 95, c.toArray(), c.capacity(), false)); + player.getBank().setTabConfigurations(); + player.getBank().sendBankSpace(); } else { PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 763, 64000, 93, c.toArray(), 28, false)); } - player.getBank().setTabConfigurations(); - player.getBank().sendBankSpace(); } } } diff --git a/Server/src/main/core/game/container/impl/EquipmentContainer.java b/Server/src/main/core/game/container/impl/EquipmentContainer.java index a059a0be9..d398d9f3b 100644 --- a/Server/src/main/core/game/container/impl/EquipmentContainer.java +++ b/Server/src/main/core/game/container/impl/EquipmentContainer.java @@ -1,5 +1,6 @@ package core.game.container.impl; +import content.global.skill.skillcapeperks.SkillcapeEquipmentPlugin; import content.global.skill.skillcapeperks.SkillcapePerks; import core.game.container.Container; import core.game.container.ContainerEvent; @@ -8,7 +9,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; @@ -17,9 +17,12 @@ import core.plugin.Plugin; import org.jetbrains.annotations.Nullable; import core.game.interaction.InteractionListeners; import core.game.system.config.ItemConfigParser; +import org.rs09.consts.Components; import java.util.ArrayList; +import static core.api.ContentAPIKt.*; + /** * Represents the equipment container. * @author Emperor @@ -209,7 +212,7 @@ public final class EquipmentContainer extends Container { for(Item currentItem : currentItems) { Plugin plugin = currentItem.getDefinition().getConfiguration("equipment", null); if (plugin != null) { - Object object = plugin.fireEvent("unequip", player, currentItem, newItem); + Object object = plugin.fireEvent("unequip", player, currentItem); if (object != null && !((Boolean) object)) { canContinue = false; break; @@ -274,6 +277,8 @@ public final class EquipmentContainer extends Container { updateDefenceAnimation = true; } else if (slot == EquipmentContainer.SLOT_SHIELD) { updateDefenceAnimation = true; + } else if (slot == EquipmentContainer.SLOT_CAPE) { + SkillcapeEquipmentPlugin.updateCapePerks(player, c, slot); } } if (updateDefenceAnimation) { @@ -302,11 +307,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 +327,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]; } } @@ -351,7 +353,9 @@ public final class EquipmentContainer extends Container { bonuses[10] += increase; } player.getProperties().setBonuses(bonuses); - update(player); + if (player.getInterfaceManager().hasMainComponent(Components.EQUIP_SCREEN2_667)) { + update(player); + } } /** @@ -359,10 +363,6 @@ public final class EquipmentContainer extends Container { * @param player The player to update for. */ public static void update(Player player) { - if (!player.getInterfaceManager().hasMainComponent(667)) { - return; - } - PacketRepository.send(WeightUpdate.class, player.getPacketDispatch().getContext()); int index = 0; int[] bonuses = player.getProperties().getBonuses(); for (int i = 36; i < 50; i++) { @@ -373,6 +373,7 @@ public final class EquipmentContainer extends Container { String bonusValue = bonus > -1 ? ("+" + bonus) : Integer.toString(bonus); player.getPacketDispatch().sendString(BONUS_NAMES[index++] + bonusValue, 667, i); } + player.getSettings().updateWeight(); player.getPacketDispatch().sendString("Attack bonus", 667, 34); } } diff --git a/Server/src/main/core/game/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..c73e3dbab 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,14 +151,17 @@ 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) { - val topic = topics[0] + if(validTopics.size == 0) { + return true + } + else if (validTopics.size == 1) { + val topic = topics.filter { it.text == validTopics[0] }[0] if(topic.toStage is DialogueFile) { val topicFile = topic.toStage as DialogueFile interpreter!!.dialogue.loadFile(topicFile) @@ -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..ccc4a94c0 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(); } @@ -319,54 +323,91 @@ public final class DialogueInterpreter { /** * Send a message with an item next to it. - * @param itemId The item id. + * Accepts item IDs. */ 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]; - } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); - return player.getInterfaceManager().getChatbox(); + return sendItemMessage(new Item(itemId), messages); } /** * Send a message with an item next to it. + * Accepts Items. */ 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]; + // Select interface based on number of messages - 241 (1 line) to 244 (4 lines) + int interfaceId = 240 + messages.length; + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, false); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); + // Hide or empty the title, since double item messages do not have them. (Child 3) + player.getPacketDispatch().sendString("", interfaceId, 3); + // Loop and print messages on Child 4,5,6,7 based on messages count. + for(int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, 4+i); } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), 131, 2); + // Set the first Item (child 1) + ItemDefinition itemDef = ItemDefinition.forId(item.getId()); + player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), interfaceId, 2); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, itemDef.getModelZoom() / 2, itemDef.getModelRotationX(), itemDef.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45); + // Hide the second item which seems to be used for double items (child 1) + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); + // These are old interfaces which made no sense to use them. +// player.getPacketDispatch().sendString(message, 131, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); +// player.getPacketDispatch().sendString(message, 173, 4); +// player.getPacketDispatch().sendString("", 173, 3); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 173, 1); +// player.getPacketDispatch().sendString(message, 519, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 519, 0); +// player.getPacketDispatch().sendString(message, 757, 2); +// player.getPacketDispatch().sendString("", 757, 1); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 757, 0); +// player.getPacketDispatch().sendString(message, 760, 0); +// player.getPacketDispatch().sendItemOnInterface(itemId, 1, 760, 1); return player.getInterfaceManager().getChatbox(); } /** - * Send a message with an item next to it. - * @param message The message. + * Send a message with two items next to it. + * Accepts item IDs. */ - public Component sendDoubleItemMessage(int first, int second, String message) { - player.getInterfaceManager().openChatbox(131); - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(first, 1, 131, 0); - player.getPacketDispatch().sendItemOnInterface(second, 1, 131, 2); - return player.getInterfaceManager().getChatbox(); + public Component sendDoubleItemMessage(int first, int second, String... message) { + return sendDoubleItemMessage(new Item(first), new Item(second), message); } /** - * Send a message with an item next to it. - * @param message The message. + * Send a message with two items next to it. + * Accepts Items. + * Note that interface 241 to 244 contains 2 childs for images, which is used for sendDoubleItemMessage. + * @param first The first item to display. + * @param second The second item to display. + * @param messages The array of messages, one message per line. */ - public Component sendDoubleItemMessage(Item first, Item second, String message) { - player.getInterfaceManager().openChatbox(131); - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(first.getId(), first.getAmount(), 131, 0); - player.getPacketDispatch().sendItemOnInterface(second.getId(), second.getAmount(), 131, 2); + public Component sendDoubleItemMessage(Item first, Item second, String... messages) { + // Select interface based on number of messages - 241 (1 line) to 244 (4 lines) + int interfaceId = 240 + messages.length; + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, false); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); + // Hide or empty the title, since double item messages do not have them. + player.getPacketDispatch().sendString("", interfaceId, 3); + // Loop and print messages on child 4,5,6,7 based on messages count. + for(int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, 4+i); + } + // Set the first item + ItemDefinition itemDef = ItemDefinition.forId(first.getId()); + player.getPacketDispatch().sendItemOnInterface(first.getId(), first.getAmount(), interfaceId, 1); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 1, (int)(itemDef.getModelZoom() / 1.5), itemDef.getModelRotationX(), itemDef.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 1, 40, 40); + // Set the second items + ItemDefinition itemDef2 = ItemDefinition.forId(second.getId()); + player.getPacketDispatch().sendItemOnInterface(second.getId(), second.getAmount(), interfaceId, 2); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, (int)(itemDef2.getModelZoom() / 1.5), itemDef2.getModelRotationX(), itemDef2.getModelRotationY()); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 60, 65); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } @@ -389,7 +430,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 +438,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 +453,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 +475,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,27 +499,49 @@ public final class DialogueInterpreter { * @return The chatbox component. */ public Component sendDialogues(int npcId, int expression, String... messages) { + return sendDialogues(npcId, expression, false, messages); + } + + /** + * Send dialogues based on the amount of specified messages. + * @param npcId The npc id. + * @param expression The entity's facial expression. + * @param messages The messages. + * @param hide should the continue button be hidden? + * @return The chatbox component. + */ + public Component sendDialogues(int npcId, int expression, boolean hide, String... messages) { if (messages.length < 1 || messages.length > 4) { + System.err.println("Invalid amount of messages: " + messages.length); return null; } boolean npc = npcId > -1; int interfaceId = (npc ? 240 : 63) + messages.length; + interfaceId += hide ? 4 : 0; if (expression == -1) { expression = FacialExpression.HALF_GUILTY.getAnimationId(); } + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); player.getPacketDispatch().sendAnimationInterface(expression, interfaceId, 2); if (npc) { + player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1); + player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 2); player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2); + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45); + player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, 2150, 40, 1882); player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3); } else { + player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 426, 45); // 423 is 47 * 9 player.getPacketDispatch().sendPlayerOnInterface(interfaceId, 2); player.getPacketDispatch().sendString(player.getUsername(), interfaceId, 3); } for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(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); + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) + player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } @@ -502,6 +561,7 @@ public final class DialogueInterpreter { for (int i = 0; i < options.length; i++) { player.getPacketDispatch().sendString(options[i].toString(), interfaceId, i + 2); } + // Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title) player.getInterfaceManager().openChatbox(interfaceId); return player.getInterfaceManager().getChatbox(); } @@ -572,4 +632,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..1d39ce22b --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueLabeller.kt @@ -0,0 +1,398 @@ +package core.game.dialogue + +import core.api.Event +import core.api.InputType +import core.api.face +import core.api.openDialogue +import core.api.splitLines +import core.game.component.Component +import core.game.component.Component.setUnclosable +import core.game.event.DialogueCloseEvent +import core.game.event.EventHook +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item + +/** Alias FacialExpression to ChatAnim for compatibility. */ +typealias ChatAnim = FacialExpression +/** Alias InputType to InputType for compatibility. */ +typealias InputType = InputType +/** Create container class DialogueOption for [DialogueLabeller.options] */ +class DialogueOption( + val goto: String, // Required: Label to go to if player selects option. + val option: String, // Required: Printed text on the option list. + val spokenText: String = option, // Optional: Option selected will be spoken text unless provided here. + val expression: ChatAnim = ChatAnim.NEUTRAL, // Optional: Player expression to show. + val skipPlayer: Boolean = false, // Optional: Skips the player spoken text (if wildly different) + val optionIf: ((player: Player, npc: NPC) -> Boolean)? = null // Optional: Function to show/not show option. +) + +/** + * DialogueLabeller is another way to organize a dialogue file. + * It shares the same dialogue layout as another project for portability. + * - Uses [stage] is for the current loop and [super.stage] as the "next" stage + * - Have [dialogueCounter] assign stages and guards for each option/player/npc dialogue + * - Assign [exec] and [goto] the same [dialogueCounter] to execute all in one pass + * - Save [label] to a HashMap to refer to when using [goto] + * - Loops again when a [goto] is encountered, ends when no [stageHit] during a stage + * + * ! WARNING: DO NOT use functions in DialogueFile as it will cause unexpected behavior. + * + * ! HELP: There are snippets below that you can copy and modify to use as part of your code. + */ +abstract class DialogueLabeller : DialogueFile() { + + companion object { + /** + * Makes NPC stop in its tracks and look at player. + * An alternative to setting up DialoguePlugin(player) to be used in InteractionListener. + */ + fun open(player: Player, dialogue: Any, npc: NPC) { + face(npc, player.location) + npc.setDialoguePlayer(player) // This prevents random walking in [NPC.java handleTickActions()] + npc.getWalkingQueue().reset() + npc.getPulseManager().clear() + openDialogue(player, dialogue, npc) + } + } + + /** Maps labels to stage numbers, to make jumps. */ + val labelStageMap = HashMap () + /** Assigns the number to each dialogue line. */ + var dialogueCounter = 0 + /** Set [stage] to start off (1 when there is an initial label). */ + var startingStage: Int? = null + /** Keeps the current stage for the whole cycle. */ + override var stage = -1 + /** Assigns the number to each stage. */ + var stageHit = false + /** Jump to next stage number when hitting a goto. */ + var jumpTo: Int? = null + /** ButtonID on every click */ + var buttonID: Int? = null + /** Filtered options from option stage 1 for storing the order for option stage 2 and 3 */ + var filteredOptions: List = listOf() + /** ButtonID when user clicks on an option */ + var optButton: Int? = null + /** Input value after a user enters a value */ + var optInput: Any? = null + + /** Implement this function instead of overriding handle. */ + abstract fun addConversation() + + /** Helper functions to create an individual stage for each of the dialogue stages. */ + private fun assignIndividualStage(unclosable: Boolean = false, callback: () -> Component?) { + if (startingStage == null) { startingStage = 0 } + if (stage == dialogueCounter && jumpTo == null) { // Run this stage when the stage equals to the dialogueCounter of this dialogue + val component = callback() // CALLBACK FUNCTION + if (unclosable && component != null) { + setUnclosable(player, component) + } + super.stage++ // Increment the stage to the next stage (only applies after a pass) + stageHit = true // Flag that the stage was hit, so that it doesn't close the dialogue + } + dialogueCounter++ // Increment the dialogueCounter to assign each line of dialogue + } + + /** Formats a vararg messages or falls back to message to an array for spread function. */ + private fun formatMessages(messages: Array?, message: String = ""): Array { + return if (messages == null || messages.isEmpty()) { + splitLines(message) + } else if (messages.size > 1) { + messages + } else { + splitLines(messages[0]) // A single line message is similar to a splitLine returning 1 line. + } + } + + /** Does absolutely nothing but to make it look like the other API. */ + fun assignToIds(npcid: Int) { /* super.npc = NPC(npcid) */ } + + /** Marks the start of a series of dialogue that can be jumped to using a [goto]. You can nest after label(...) with a {} purely for organization only. */ + fun label(vararg label: String, nesting: () -> Unit = {}) { + if (startingStage == null) { startingStage = 1 } + dialogueCounter++ + for (l in label) { labelStageMap[l] = dialogueCounter } + nesting() + } + + /** Jumps to a [label] after a series of dialogue. */ + fun goto(label: String) { + if (stage == dialogueCounter && jumpTo == null) { + jumpTo = labelStageMap[label] + } + } + + /** Jumps to a [label] inside an [exec]. */ + fun loadLabel(player: Player, label: String) { + goto(label) + } + + /** + * Executes the callback between stages and can be used for branching with [loadLabel]. + * You can chain as many exec as you like since they read sequentially. + * You can also read [options] and [input] values here via [optButton] and [optInput]. + */ + fun exec(callback: (player: Player, npc: NPC) -> Unit) { + if (startingStage == null) { startingStage = 0 } + if (stage == dialogueCounter && jumpTo == null) { + callback(player!!, npc!!) + } + } + + /** Manual stage. For custom creation of an individual stage. Must call interpreter in some form. **/ + fun manual(unclosable: Boolean = false, callback: (player: Player, npc: NPC) -> Component?) { + assignIndividualStage(unclosable) { return@assignIndividualStage callback(player!!, npc!!) } + } + + /** Dialogue player/playerl. Shows player chathead with text. **/ + fun player(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String, unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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, unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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, unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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, unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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 = "", unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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 = "", unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@assignIndividualStage interpreter!!.sendDoubleItemMessage(item, item2, *formatMessages(messages, message)) } + } + + /** Dialogue line/linel. Simply shows text. **/ + fun line(vararg messages: String, unclosable: Boolean = false) { + assignIndividualStage(unclosable) { return@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", unclosable: Boolean = false) { + // Filter out options that aren't shown. + // Stage Part 1: Options List Dialogue + assignIndividualStage(unclosable) { + filteredOptions = options.filter{ if (it.optionIf != null) { it.optionIf.invoke(player!!, npc!!) } else { true } } + if (filteredOptions.size == 1) { // If there is only one option left, jump to that option. + jumpTo = labelStageMap[filteredOptions[0].goto] + return@assignIndividualStage null + } + return@assignIndividualStage interpreter!!.sendOptions(title, *filteredOptions.map{ it.option }.toTypedArray()) + } + // Stage Part 2: Show spoken text. + val opt = if (buttonID != null && buttonID in 1..filteredOptions.size) { filteredOptions[buttonID!! - 1] } else { null } + assignIndividualStage(unclosable) { + var component: Component? = null + if (opt?.skipPlayer == true) { + jumpTo = stage + 1 + } else { + component = interpreter!!.sendDialogues(player, opt?.expression ?: ChatAnim.NEUTRAL, *(splitLines(opt?.spokenText ?: " "))) + } + optButton = buttonID // transfer the buttonID to a temp memory for the next stage + return@assignIndividualStage component + } + // Stage Part 3: Jump To goto + if (stage == dialogueCounter && optButton != null && optButton in 1..filteredOptions.size) { + jumpTo = labelStageMap[filteredOptions[optButton!! - 1].goto] + } + dialogueCounter++ + } + @Deprecated("Use options(DialogueOption()) and not options(string).", ReplaceWith("options(DialogueOption(options))")) + override fun options(vararg options: String?, title: String) { throw Exception("Deprecated DialogueLabel: Use options(DialogueOption()) and not options(string).") } + + /** Dialogue input. Shows the input dialogue with an input box for the user to type in. Read [optInput] for the value. **/ + fun input(type: InputType, prompt: String = "Enter the amount") { + assignIndividualStage { + // These are similar to calling sendInputDialogue + when (type) { + InputType.AMOUNT -> interpreter!!.sendInput(true, prompt) + InputType.NUMERIC -> interpreter!!.sendInput(false, prompt) + InputType.STRING_SHORT -> interpreter!!.sendInput(true, prompt) // Only 12 letters + InputType.STRING_LONG -> interpreter!!.sendLongInput(prompt) // Very long text, can overflow. + InputType.MESSAGE -> interpreter!!.sendMessageInput(prompt) + } + if (type == InputType.AMOUNT) { + player!!.setAttribute("parseamount", true) + } + // This runscript is the same runscript as the one in ContentAPI sendInputDialogue + player!!.setAttribute("runscript") { value: Any -> + optInput = value + // The next line is a hack. Because this prompt overlays the actual chat box, we trigger the next dialogue with a call to handle. + interpreter!!.handle(player!!.interfaceManager.chatbox.id, 2) + } + player!!.setAttribute("input-type", type) + return@assignIndividualStage null + } + } + /** Dialogue input. Shows the input dialogue with an input box for the user to type in. Read [optInput] in an [exec] function for the value. **/ + fun input(numeric: Boolean, prompt: String = "Enter the amount") { input( if (numeric) { InputType.NUMERIC } else { InputType.STRING_SHORT }, prompt) } + + /** Runs arbitrary code when the dialogue closes, once. **/ + fun afterClose(callback: (player: Player) -> Unit) { + val hook = object : EventHook { + override fun process(entity: Entity, event: DialogueCloseEvent) { + val you = entity as Player + you.unhook(this) + callback(you) + } + } + player!!.hook(Event.DialogueClosed, hook) + } + + /** Calls another dialogue file. Always use this to open another dialogue file instead of calling openDialogue() in exec{} due to interfaces clashing. **/ + fun open(player: Player, dialogue: Any, vararg args: Any) { + assignIndividualStage { + core.api.openDialogue(player, dialogue, *args) + return@assignIndividualStage null + } + } + + /** WARNING: DIALOGUE LABELLER WILL BREAK IN CERTAIN FUNCTIONS. USE open() instead. */ + fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { + core.api.openDialogue(player, dialogue, *args) + } + + /** Hook onto the handle function of DialogueFile. This function gets called every loop with a super.stage. */ + override fun handle(componentID: Int, buttonID: Int) { + this.buttonID = buttonID + startingStage = null + /** This -1 stage is to read labels into a hashmap and to find the starting stage. */ + if (stage == -1) { + dialogueCounter = 0 + addConversation() // Force all labels to be recorded into hashmap. + super.stage = startingStage ?: 0 + } + for (i in 0..10) { // Limit to 10 jumpTo PER DIALOGUE LINE to prevent infinite looping/ping-ponging jumps. + if (jumpTo != null) { // If jumpTo is set, set the super.stage stage as the new jumpTo stage. + super.stage = jumpTo as Int + jumpTo = null + } + stageHit = false + stage = super.stage // [stage] is for the current loop. [super.stage] is treated as the "next" stage. + dialogueCounter = 0 + addConversation() // MAIN CALLBACK FUNCTION + // If there is no jumpTo set, or jumpTo is set to the same stage as this(infinite loop), exit. + if (jumpTo == null || jumpTo == stage) { + break + } + } + // If a dialogue stage is not hit, end the dialogue. + if (!stageHit) { + end() + } + } +} + +/* +// COPY PASTA SNIPPETS SECTION FOR QUICK BOILERPLATES + +// STANDARD: Initializing with DialoguePlugin. +@Initializable +class DonieDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return DonieDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DonieDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONIE_2238) + } +} +class DonieDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.DONIE_2238) + + npc(ChatAnim.FRIENDLY, "Hello there, can I help you?") + goto("nowhere") + } +} + +// NEW! (EXPERIMENTAL): Initializing with InteractionListener. +class SomeDudeDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.PRIEST_OF_GUTHIX_8555, IntType.NPC, "talk-to") { player, node -> + DialogueLabeller.open(player, SomeDudeDialogueLabellerFile(), node as NPC) + return@on true + } + } +} + +// Exec with quest stage branches. +exec { player, npc -> + when(getQuestStage(player, SomeQuest.questName)) { + 100 -> loadLabel(player, "SomeQuestStage100") + 10, 20 -> loadLabel(player, "SomeQuestStage10") + else -> { + loadLabel(player, "SomeQuestStage0") + } + } +} + +// Exec to move quest stage up. +exec { player, npc -> + if (getQuestStage(player, SomeQuest.questName) == 0) { + setQuestStage(player, SomeQuest.questName, 10) + } +} + +// Exec to add item, set attribute. +exec { player, npc -> + if (removeItem(player, Items.ITEM_1)) { + addItemOrDrop(player, Items.ITEM_2) + } + if (getAttribute(player, attributeSomething, null) == null) { + setAttribute(player, attributeSomething, player.location) + } +} + +// Open to another dialogue file. +npc("I'm going to another file.") +open(player!!, SomeDialogueFile2(), npc!!) +... +class SomeDialogueFile2 : DialogueLabeller() { + override fun addConversation() { + npc("This is another file.") + } +} + +// Options with all the different controls. +options( + DialogueOption("Label1", "Line 1 Say", expression=ChatAnim.THINKING), + DialogueOption("Label2", "Line 2 Huh", skipPlayer=true), + DialogueOption("Label3", "Line 3 Blah", spokenText="I say something else", expression=ChatAnim.FRIENDLY), + DialogueOption("Label4", "Line 4 What") { player, npc -> + return@DialogueOption false // Don't show + } +) + +*/ \ No newline at end of file diff --git a/Server/src/main/core/game/dialogue/DialoguePlugin.java b/Server/src/main/core/game/dialogue/DialoguePlugin.java index 26fb26129..18d17dd37 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." ); @@ -273,8 +276,8 @@ public abstract class DialoguePlugin implements Plugin { * Method used to send options. * @param options the options. */ - public void options(final String... options) { - interpreter.sendOptions("Select an Option", options); + public Component options(final String... options) { + return interpreter.sendOptions("Select an Option", options); } /** @@ -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..8b4f2eb8c 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,37 @@ public enum FacialExpression { CHILD_THINKING(7176), CHILD_SAD(7177), CHILD_GUILTY(7178), - CHILD_SUSPICIOUS(7179); //TODO: More? + CHILD_SUSPICIOUS(7179), + CHILD_SURPRISED(7180), + + //Familiar Chatheads + GRAAHK_NOD(6551), + GHRAAK_SHAKE_MILD(6553), + GHRAAK_SHAKE_VIGOROUS(6555), + ; //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; potentially all familiars; 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..acc07905c 100644 --- a/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt +++ b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt @@ -19,25 +19,11 @@ open class SkillDialogueHandler( /** * Represents the skill dialogue type. */ - val type: SkillDialogue?, vararg data: Any) { - /** - * Gets the player. - * @return The player. - */ - - /** - * Gets the type. - * @return The type. - */ - - /** - * Gets the passed data. - * @return the data. - */ - /** - * Represents the object data passed through. - */ - val data: Array + val type: SkillDialogue?, + /** + * The items that can be created through this dialog. + */ + vararg val items: Item) { /** * Method used to open a skill dialogue. @@ -70,7 +56,7 @@ open class SkillDialogueHandler( * @return the amount. */ open fun getAll(index: Int): Int { - return player.inventory.getAmount(data[0] as Item) + return player.inventory.getAmount(items[0]) } /** @@ -107,7 +93,7 @@ open class SkillDialogueHandler( private val length: Int) { ONE_OPTION(309, 5, 1) { override fun display(player: Player, handler: SkillDialogueHandler) { - val item = handler.data[0] as Item + val item = handler.items[0] player.packetDispatch.sendString("



" + item.name, 309, 6) player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 309, 2) PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 309, 6, 60, 20)) @@ -123,12 +109,48 @@ open class SkillDialogueHandler( } } }, + MAKE_SET_ONE_OPTION(582, 4, 1) { + override fun display(player: Player, handler: SkillDialogueHandler) { + val item = handler.items[0] + + // 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 -> 10 + else -> 10 + } + } + }, TWO_OPTION(303, 7, 2) { override fun display(player: Player, handler: SkillDialogueHandler) { var item: Item player.interfaceManager.openChatbox(306) - for (i in handler.data.indices) { - item = handler.data[i] as Item + for (i in handler.items.indices) { + item = handler.items[i] player.packetDispatch.sendString("



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



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



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



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



" + handler.getName(item), 306, 10 + 4 * i) player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 306, 2 + i) PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 306, 2 + i, positions[i][0], positions[i][1])) @@ -325,14 +347,4 @@ open class SkillDialogueHandler( */ const val SKILL_DIALOGUE = 3 shl 16 } - - /** - * Constructs a new `SkillDialogueHandler` `Object`. - * @param player the player. - * @param type the type. - * @param data the data. - */ - init { - this.data = data as Array - } } \ No newline at end of file diff --git a/Server/src/main/core/game/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..692759fd9 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)) @@ -179,16 +180,7 @@ abstract class DiaryEventHookBase(private val diaryType: DiaryType) : MapArea, L ) } - private fun findIndexFor(level: DiaryLevel): Int { - val levelName = level.name.lowercase().replaceFirstChar { c -> c.uppercase() } - val levelIndex = diaryType.levelNames.indexOf(levelName) - - if (levelIndex < 0) { - throw IllegalArgumentException("'$levelName' was not found in diary '$diaryType'.") - } - - return levelIndex - } + private fun findIndexFor(level: DiaryLevel): Int = getDiaryLevelIndex(diaryType, level) protected open fun onAreaVisited(player: Player) { areaTasks.forEach { @@ -207,6 +199,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..b11d6241b 100644 --- a/Server/src/main/core/game/event/Events.kt +++ b/Server/src/main/core/game/event/Events.kt @@ -6,7 +6,9 @@ import core.game.node.Node 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.SpellBookManager.SpellbookChangeSource 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 +17,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 +34,8 @@ 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 SpellbookChangeEvent(val oldSpellBook: SpellBook, val newSpellBook: SpellBook, val source: SpellbookChangeSource) : 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 +47,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..4472ed2f1 100644 --- a/Server/src/main/core/game/ge/GrandExchange.kt +++ b/Server/src/main/core/game/ge/GrandExchange.kt @@ -1,17 +1,16 @@ 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.colorize +import core.tools.Log +import core.tools.SystemLogger import java.lang.Integer.max import java.util.concurrent.LinkedBlockingDeque @@ -81,31 +80,24 @@ class GrandExchange : StartupListener, Commands { } override fun defineCommands() { - define("addbotoffer", Privilege.ADMIN) {player, strings -> + define("addbotoffer", Privilege.ADMIN, "::addbotoffer item-id amount", "Adds a GE bot sell offer for the given item ID and amount.") {player, strings -> val id = strings[1].toInt() val amount = strings[2].toInt() addBotOffer(id, amount) notify(player, "Added ${amount}x ${getItemName(id)} to the bot offers.") } - define("bange", Privilege.ADMIN) {player, strings -> + define("bange", Privilege.ADMIN, "::bange item-id", "Bans/blacklists the specified item from GE trades.") {player, strings -> val id = strings[1].toInt() PriceIndex.banItem(id) notify(player, "Banned ${getItemName(id)} from GE trade.") } - define("allowge", Privilege.ADMIN) {player, strings -> + define("allowge", Privilege.ADMIN, "::allowge item-id", "Allows/whitelists the specified item to be traded on the GE.") {player, strings -> val id = strings[1].toInt() PriceIndex.allowItem(id) notify(player, "Allowed ${getItemName(id)} for GE trade.") } - - define("geprivacy", Privilege.STANDARD) {player, _ -> - val current = getAttribute(player, "ge-exclude", false) - val new = !current - notify(player, "Your name is now ${if (new) colorize("%RHIDDEN") else colorize("%RSHOWN")}.") - setAttribute(player, "/save:ge-exclude", new) - } } companion object { @@ -129,15 +121,11 @@ class GrandExchange : StartupListener, Commands { @JvmStatic fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { - var base = max(PriceIndex.getValue(itemID), getItemDefPrice(itemID)) + var base = PriceIndex.getValue(itemID) if (from_bot) base = (max(BotPrices.getPrice(itemID), base) * 1.10).toInt() return base } - private fun getItemDefPrice(itemID: Int): Int { - return max(itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, itemDefinition(itemID).value) - } - @JvmStatic fun getOfferStats(itemID: Int, sale: Boolean) : String { @@ -202,7 +190,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 +208,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 } @@ -231,8 +221,9 @@ class GrandExchange : StartupListener, Commands { //GrandExchangeRecords.getInstance(player).update(offer) if (offer.sell && !player.isArtificial) { - val username = if (getAttribute(player, "ge-exclude", false)) "?????" else player.username - Repository.sendNews(username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE.") + Repository.sendGrandExchangeNews( + player.username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE." + ) } if (ServerConstants.I_AM_A_CHEATER) { @@ -274,9 +265,6 @@ class GrandExchange : StartupListener, Commands { seller.completedAmount += amount buyer.completedAmount += amount - if(seller.amountLeft < 1 && seller.player != null) - seller.player!!.audioManager.send(Audio(4042,1,1)) - seller.addWithdrawItem(995, amount * if(sellerBias) buyer.offeredValue else seller.offeredValue) buyer.addWithdrawItem(seller.itemID, amount) @@ -306,17 +294,18 @@ class GrandExchange : StartupListener, Commands { } */ - seller.update() - val sellerPlayer = Repository.uid_map[seller.playerUID] - sellerPlayer?.let { GrandExchangeRecords.getInstance(sellerPlayer).visualizeRecords() } - buyer.update() - val buyerPlayer = Repository.uid_map[buyer.playerUID] - buyerPlayer?.let { GrandExchangeRecords.getInstance(buyerPlayer).visualizeRecords() } + for (entity in arrayOf(buyer, seller)) { + entity.update() + val player = Repository.uid_map[entity.playerUID] ?: continue + val records = GrandExchangeRecords.getInstance(player) + records.visualizeRecords() + records.updateNotification = true + } } private fun canUpdatePriceIndex(seller: GrandExchangeOffer, buyer: GrandExchangeOffer): Boolean { 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 +344,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..eee8594dd 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. @@ -143,10 +146,7 @@ class GrandExchangeOffer() { visualize(player) stmt.close() - val username = if (getAttribute(player ?: return@run, "ge-exclude", false)) "?????" - else player?.username ?: "?????" - - Discord.postNewOffer(sell, itemID, offeredValue, amount, username) + Discord.postNewOffer(sell, itemID, offeredValue, amount, player?.username ?: return@run) } } } @@ -237,4 +237,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..e970c01bb 100644 --- a/Server/src/main/core/game/ge/GrandExchangeRecords.kt +++ b/Server/src/main/core/game/ge/GrandExchangeRecords.kt @@ -22,6 +22,7 @@ import java.util.* class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer, LoginListener { var history = arrayOfNulls(5) val offerRecords = arrayOfNulls(6) + var updateNotification = false override fun login(player: Player) { val instance = GrandExchangeRecords(player) @@ -212,7 +213,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 +253,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/ge/GrandExchangeTimer.kt b/Server/src/main/core/game/ge/GrandExchangeTimer.kt new file mode 100644 index 000000000..2e68ca5f4 --- /dev/null +++ b/Server/src/main/core/game/ge/GrandExchangeTimer.kt @@ -0,0 +1,24 @@ +package core.game.ge + +import core.api.hasAwaitingGrandExchangeCollections +import core.api.playJingle +import core.api.sendMessage +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.RSTimer + +class GrandExchangeTimer : RSTimer(500, "GE periodic poll", isSoft = true, isAuto = true) { + override fun run(entity: Entity) : Boolean { + if (entity !is Player) return false + val player = entity + val records = GrandExchangeRecords.getInstance(player) + if (records.updateNotification) { + records.updateNotification = false + if (hasAwaitingGrandExchangeCollections(player)) { + sendMessage(player, "One or more of your Grand Exchange offers have been updated.") + playJingle(player, 284) + } + } + return true + } +} diff --git a/Server/src/main/core/game/global/Skillcape.java b/Server/src/main/core/game/global/Skillcape.java index 619b87f04..4ae5ef8a5 100644 --- a/Server/src/main/core/game/global/Skillcape.java +++ b/Server/src/main/core/game/global/Skillcape.java @@ -1,5 +1,6 @@ package core.game.global; +import content.global.skill.construction.decoration.pohstorage.StorableFamily; import core.game.container.Container; import core.game.dialogue.FacialExpression; import core.game.dialogue.FacialExpression; @@ -66,6 +67,25 @@ public final class Skillcape { } } } + + // trim any skillcapes in the player's POH storage + content.global.skill.construction.decoration.pohstorage.StorageState poh = player.getPOHStorageState(); + content.global.skill.construction.decoration.pohstorage.StorageContainer rack = poh.getContainer(StorableFamily.CAPE_RACK_SKILL); + + // count the capes + java.util.List toReplace = new java.util.ArrayList<>(); + for (int itemId : rack.getItems()) { + if (getCapeIndex(new Item(itemId)) != -1) { + toReplace.add(itemId); + } + } + + // replace the untrimmed IDs with trimmed IDs + for (int oldId : toReplace) { + int skillIndex = getCapeIndex(new Item(oldId)); + rack.withdraw(oldId); + rack.addItem(getTrimmed(skillIndex).getId()); + } } /** 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..e3cab3e34 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 @@ -162,6 +164,7 @@ object InteractionListeners { @JvmStatic fun run(id: Int, player: Player, node: Node, isEquip: Boolean): Boolean{ + player.scripts.removeWeakScripts() player.dispatch(InteractionEvent(node, if(isEquip) "equip" else "unequip")) if(isEquip){ return equipListeners["equip:$id"]?.invoke(player,node) ?: true @@ -172,6 +175,7 @@ object InteractionListeners { @JvmStatic fun run(used: Node, with: Node, type: IntType, player: Player): Boolean{ + player.scripts.removeWeakScripts() val flag = when(type){ IntType.NPC, IntType.PLAYER -> DestinationFlag.ENTITY IntType.SCENERY -> DestinationFlag.OBJECT @@ -214,14 +218,16 @@ 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 } @JvmStatic fun run(id: Int, type: IntType, option: String, player: Player, node: Node): Boolean{ + player.scripts.removeWeakScripts() + val flag = when(type){ IntType.PLAYER -> DestinationFlag.ENTITY IntType.GROUNDITEM -> DestinationFlag.ITEM @@ -232,32 +238,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 +296,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..b378a8a1f 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -4,18 +4,27 @@ 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; +import core.game.world.map.RegionManager; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.net.packet.PacketRepository; 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 +87,8 @@ public abstract class MovementPulse extends Pulse { private Function2 overrideMethod; + private Location previousLoc; + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -167,6 +178,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,27 +190,77 @@ 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); + if (canInteractWithoutMoving()) { + if (interactImmediately()) { + stop(); + return true; + } + } + 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 (Math.max(Math.abs(ml.getX() - interactLocation.getX()), Math.abs(ml.getY() - interactLocation.getY())) <= radius) { + if (interactLocation == null) + return false; + boolean atInteractLocation = Math.max(Math.abs(ml.getX() - interactLocation.getX()), Math.abs(ml.getY() - interactLocation.getY())) <= radius; + // Check if already in a valid interaction position for entity destinations + boolean canInteractFromCurrentPosition = false; + if (!atInteractLocation && destination instanceof Entity) { + Entity target = (Entity) destination; + Location dl = target.getLocation(); + boolean onSameTile = ml.getX() == dl.getX() && ml.getY() == dl.getY() && ml.getZ() == dl.getZ(); + if (!onSameTile) { + canInteractFromCurrentPosition = Pathfinder.canInteract( + ml.getX(), ml.getY(), mover.size(), + dl.getX(), dl.getY(), target.size(), target.size(), + 0, // walkFlag - "can interact from any unblocked direction" + ml.getZ(), + RegionManager::getClippingFlag + ); + } + } + if (atInteractLocation || canInteractFromCurrentPosition) { try { if (near || pulse()) { if (mover instanceof Player) { @@ -214,6 +280,73 @@ public abstract class MovementPulse extends Pulse { return false; } + /** + * Checks if the mover can interact with an entity destination from their + * current position without needing to move. + * + * @return true if the mover can interact without moving + */ + private boolean canInteractWithoutMoving() { + if (!(destination instanceof Entity)) { + return false; + } + Entity target = (Entity) destination; + Location ml = mover.getLocation(); + Location dl = target.getLocation(); + if (ml.getX() == dl.getX() && ml.getY() == dl.getY() && ml.getZ() == dl.getZ()) { + return false; + } + if (isInsideEntity(mover.getLocation())) { + return false; + } + if (target.getWalkingQueue().hasPath()) { // For moving entities, allow interaction from 1 tile away + int distance = Math.max( + Math.abs(ml.getX() - dl.getX()), + Math.abs(ml.getY() - dl.getY()) + ); + if (distance <= target.size()) { + return true; + } + } + return Pathfinder.canInteract( + ml.getX(), ml.getY(), mover.size(), + dl.getX(), dl.getY(), target.size(), target.size(), + 0, // walkFlag - "can interact from any unblocked direction" + ml.getZ(), + RegionManager::getClippingFlag + ); + } + + /** + * Immediately performs the interaction without pathfinding. + * Called when the player is already in a valid interaction position. + * + * @return true if interaction was successful and pulse should stop + */ + private boolean interactImmediately() { + if (destination instanceof Entity) { + mover.face((Entity) destination); + } + try { + if (pulse()) { + if (mover instanceof Player) { + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext((Player) mover)); + } + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + 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 +359,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 +461,63 @@ 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); - } - } - } - last = destination.getLocation(); + 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 < points.length; i++) { + Location closestBorder = getClosestBorderToPoint(points[i], loc.getZ()); + + if (!RegionManager.isTeleportPermitted(closestBorder)) { // A nasty hack to discard invalid intersection points + continue; + } + int moverDist = Math.max(Math.abs(ml.getX() - closestBorder.getX()), Math.abs(ml.getY() - closestBorder.getY())); + float movementRatio = moverDist / (float) ((i + 1) / (mover.getWalkingQueue().isRunning() ? 2 : 1)); + if (predictiveIntersection == null && movementRatio <= 1.0) { //try to predict an intersection point on the path if possible + predictiveIntersection = points[i]; + break; + } + // Otherwise, we target the farthest point along target's planned movement that's within 1 tick's running, + // this ensures the player will run to catch up to the target if able. + if (moverDist <= 2) { + p = points[i]; + } + } + if (predictiveIntersection != null) + p = predictiveIntersection; + + Location endLoc = getClosestBorderToPoint(p, loc.getZ()); + + if (!RegionManager.isTeleportPermitted(endLoc)) { // Basically a prayer + return loc; + } + return endLoc; + } + } + } + return loc; + } + + private Location getClosestBorderToPoint (Point p, int plane) { + Vector pathDiff = Vector.betweenLocs (destination.getLocation(), Location.create(p.getX(), p.getY(), plane)); + Location predictedCenterPos = (destination.getMathematicalCenter().plus(pathDiff)).toLocation(plane); + Vector toPlayerNormalized = Vector.betweenLocs(predictedCenterPos, mover.getCenterLocation()).normalized(); + return predictedCenterPos.transform(toPlayerNormalized.times(destination.size() + 1)); + } + + + private Location findBorderLocation() { + return findBorderLocation(destination.getLocation()); } /** @@ -337,12 +525,12 @@ public abstract class MovementPulse extends Pulse { * * @return The location to walk to. */ - private Location findBorderLocation() { + private Location findBorderLocation(Location centerDestLoc) { int size = destination.size(); - Location centerDest = destination.getLocation().transform(size >> 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..2c638f35f --- /dev/null +++ b/Server/src/main/core/game/interaction/ScriptProcessor.kt @@ -0,0 +1,301 @@ +package core.game.interaction + +import core.api.* +import core.game.bots.AIPlayer +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +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.tools.Log +import java.io.PrintWriter +import java.io.StringWriter +import java.lang.Integer.max + +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 anyExecuted = false + val strongInQueue = hasTypeInQueue(QueueStrength.STRONG) + + 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..ffaa8f461 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. @@ -78,7 +78,7 @@ public abstract class Entity extends Node { /** * The pulse manager. */ - private final PulseManager pulseManager = new PulseManager(); + private final PulseManager pulseManager = new PulseManager(this); /** * The impact handler. @@ -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); @@ -434,6 +450,9 @@ public abstract class Entity extends Node { if (!entity.getZoneMonitor().continueAttack(this, style, message)) { return false; } + if (!this.getZoneMonitor().continueAttack(entity, style, message)) { + return false; + } return true; } @@ -473,10 +492,25 @@ public abstract class Entity extends Node { } /** - * Checks if an entity can continue it's attack. - * @param target the target. - * @param style the style. - * @return {@code True} if so. + * Checks if an entity can continue its attack. + * + *

This method is called during attack validation to determine if the attacking entity should be + * allowed to continue attacking the target. It's invoked by + * {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)} + * as part of the attack validation chain.

+ * + *

The default implementation returns {@code true}, allowing the attack to proceed. + * Subclasses should override this method to implement entity-specific attack restrictions.

+ * + *

This method is distinct from {@link #isAttackable(Entity, CombatStyle, boolean)}, which + * checks if this entity can be attacked by another entity. The {@code continueAttack} method + * checks if this entity can attack the given target.

+ * + * @param target the target entity being attacked + * @param style the combat style being used + * @param message whether to send a message to the player explaining why the attack cannot continue + * + * @return {@code true} if the entity can continue attacking the target */ public boolean continueAttack(Entity target, CombatStyle style, boolean message) { return true; @@ -616,21 +650,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 +934,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 +947,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 +996,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/BattleState.java b/Server/src/main/core/game/node/entity/combat/BattleState.java index e75aac19d..4a12b5966 100644 --- a/Server/src/main/core/game/node/entity/combat/BattleState.java +++ b/Server/src/main/core/game/node/entity/combat/BattleState.java @@ -9,6 +9,7 @@ import core.game.node.entity.combat.spell.CombatSpell; /** * Represents an entity's current battle state. + * * @author Emperor */ public final class BattleState { @@ -99,6 +100,7 @@ public final class BattleState { /** * Constructs a new {@code BattleState} {@Code Object} + * * @param victim The victim entity. */ public BattleState(Entity entity, Entity victim) { @@ -134,6 +136,7 @@ public final class BattleState { /** * Gets the estimatedHit. + * * @return The estimatedHit. */ public int getEstimatedHit() { @@ -142,6 +145,7 @@ public final class BattleState { /** * Sets the estimated hit. + * * @param estimatedHit The estimated hit to set. */ public void setEstimatedHit(int estimatedHit) { @@ -164,6 +168,7 @@ public final class BattleState { /** * Gets the recoilDamage. + * * @return The recoilDamage. */ public int getRecoilDamage() { @@ -172,6 +177,7 @@ public final class BattleState { /** * Sets the recoilDamage. + * * @param recoilDamage The recoilDamage to set. */ public void setRecoilDamage(int recoilDamage) { @@ -236,6 +242,7 @@ public final class BattleState { /** * Gets the maximumHit. + * * @return The maximumHit. */ public int getMaximumHit() { @@ -244,6 +251,7 @@ public final class BattleState { /** * Sets the maximumHit. + * * @param maximumHit The maximumHit to set. */ public void setMaximumHit(int maximumHit) { @@ -252,6 +260,7 @@ public final class BattleState { /** * Gets the rangeWeapon. + * * @return The rangeWeapon. */ public RangeWeapon getRangeWeapon() { @@ -260,6 +269,7 @@ public final class BattleState { /** * Sets the rangeWeapon. + * * @param rangeWeapon The rangeWeapon to set. */ public void setRangeWeapon(RangeWeapon rangeWeapon) { @@ -268,6 +278,7 @@ public final class BattleState { /** * Gets the ammunition. + * * @return The ammunition. */ public Ammunition getAmmunition() { @@ -276,6 +287,7 @@ public final class BattleState { /** * Sets the ammunition. + * * @param ammunition The ammunition to set. */ public void setAmmunition(Ammunition ammunition) { @@ -284,6 +296,7 @@ public final class BattleState { /** * Gets the frozen. + * * @return The frozen. */ public boolean isFrozen() { @@ -292,6 +305,7 @@ public final class BattleState { /** * Sets the frozen. + * * @param frozen The frozen to set. */ public void setFrozen(boolean frozen) { @@ -300,6 +314,7 @@ public final class BattleState { /** * Gets the style. + * * @return The style. */ public CombatStyle getStyle() { @@ -308,6 +323,7 @@ public final class BattleState { /** * Sets the style. + * * @param style The style to set. */ public void setStyle(CombatStyle style) { @@ -316,6 +332,7 @@ public final class BattleState { /** * Gets the armourEffect. + * * @return The armourEffect. */ public ArmourSet getArmourEffect() { @@ -324,6 +341,7 @@ public final class BattleState { /** * Sets the armourEffect. + * * @param armourEffect The armourEffect to set. */ public void setArmourEffect(ArmourSet armourEffect) { @@ -332,6 +350,7 @@ public final class BattleState { /** * Gets the attacking entity. + * * @return The entity. */ public Entity getAttacker() { @@ -340,10 +359,9 @@ public final class BattleState { public int getTotalDamage() { int hit = Math.max(estimatedHit, 0) + Math.max(secondaryHit, 0); - if (targets != null) { for (BattleState s : targets) { - if (s != null) { + if (s != null && s != this) { hit += Math.max(s.getEstimatedHit(), 0) + Math.max(s.getSecondaryHit(), 0); } } 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..074a656e9 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,9 +296,13 @@ class CombatPulse( } setVictim(victim) entity.onAttack(victim as Entity?) + if (isAttacking) { + victim.scripts.removeWeakScripts() + } - if (!isAttacking) + if (!isAttacking) { entity.pulseManager.run(this) + } } /** @@ -351,6 +357,7 @@ class CombatPulse( override fun start() { super.start() entity!!.face(victim) + entity.walkingQueue.reset() } override fun stop() { @@ -424,6 +431,7 @@ class CombatPulse( } handler.adjustBattleState(entity, victim!!, state) handler.addExperience(entity, victim, state) + handler.postSwing(entity, victim, state) handler.visualize(entity, victim, state) if (delay - 1 < 1) { handler.visualizeImpact(entity, victim, state) @@ -438,17 +446,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..d4324b233 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") } @@ -244,16 +247,22 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { return InteractionType.STILL_INTERACT } - private fun canStepTowards(entity: Entity, victim: Entity): InteractionType { + protected fun canStepTowards(entity: Entity, victim: Entity): InteractionType { val closestVictimTile = victim.getClosestOccupiedTile(entity.location) val closestEntityTile = entity.getClosestOccupiedTile(closestVictimTile) val dir = closestEntityTile.deriveDirection(closestVictimTile) ?: return InteractionType.STILL_INTERACT //if we cannot derive a direction, it's because both tiles are the same, so hand off control to the main logic which already handles this case var next = closestEntityTile - while (next.getDistance(closestVictimTile) > 3) { //skip the initial gap in distance if it exists, because standard pathfinding would already stop us before this point if something was between us and the NPC or vice versa + //Skip the initial gap in distance if it exists, because standard pathfinding would already stop us before this point if something was between us and the NPC or vice versa. + //A fixed-direction walk can only arrive within its starting distance in steps; on oblique approaches it + //passes beside the target and never converges, so the walk is hard-capped at that many steps. + val maxSkipSteps = next.getDistance(closestVictimTile).toInt() + 1 + for (i in 0 until maxSkipSteps) { + if (next.getDistance(closestVictimTile) <= 3) break next = next.transform(dir) } + if (next.getDistance(closestVictimTile) > 3) return InteractionType.STILL_INTERACT //never converged (oblique approach), so defer to the range checks instead var result: InteractionType = InteractionType.STILL_INTERACT val maxIterations = next.getDistance(closestVictimTile).toInt() @@ -323,18 +332,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 +363,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 +444,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 +492,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 +525,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 } } @@ -525,6 +537,12 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { } } + /** + * Hook for operations that conceptually happen during swing but could mess with experience granting logic if they + * happened earlier. + */ + open fun postSwing(entity: Entity?, victim: Entity?, state: BattleState?) {} + /** * Gets the formated hit. * @param attacker The attacking entity. @@ -601,7 +619,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 +662,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 +675,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..b3a6a6bec 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, true)) { + p.sendMessage("Your ring of life saves you and in the process is destroyed."); + } } } } @@ -246,35 +255,48 @@ public final class ImpactHandler { if (entity instanceof Player) { return killer; } + if (killer instanceof AIPlayer) return killer; + int damage = -1; - for (Entity e : impactLog.keySet()) { - if (e == this.entity) { - continue; + if (playerImpactLog.isEmpty()) { + for (Entity e : npcImpactLog.keySet()) { + if (e == this.entity) { + continue; + } + int amount = npcImpactLog.get(e); + if (amount > damage) { + damage = amount; + entity = e; + } } - int amount = impactLog.get(e); - if (amount > damage || (entity instanceof NPC && e instanceof Player)) { + return entity; + } + + int player = 0; //needs to be fake-initialized because java is dumb + for (int uid : playerImpactLog.keySet()) { + int amount = playerImpactLog.get(uid); + if (amount > damage) { damage = amount; - entity = e; + player = uid; } } - return entity; + return Repository.getPlayerByUid(player); } /** - * Gets the impact log. - * @return The impact log. + * Gets the npc impact log. + * @return The npc impact log. */ - public Map getImpactLog() { - return impactLog; + public Map getNpcImpactLog() { + return npcImpactLog; } /** - * Gets the impact log filtered for Player objects - * @return The impact log of each Player and their damage + * Gets the player impact log. + * @return The player impact log. */ - public Map getPlayerImpactLog() { - return impactLog.entrySet().stream().filter(entry -> entry.getKey() instanceof Player).collect( - Collectors.toMap(m -> m.getKey().asPlayer(), m -> m.getValue())); + public Map getPlayerImpactLog() { + return playerImpactLog; } /** @@ -388,4 +410,4 @@ public final class ImpactHandler { public static enum HitsplatType { MISS, NORMAL, POISON, DISEASE, NORMAL_1, VENOM; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt index 13d0164b8..0062c20d9 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,7 @@ package core.game.node.entity.combat +import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.summoning.SummoningPouch import core.game.node.entity.Entity import core.game.node.entity.combat.equipment.ArmourSet import core.game.node.entity.combat.spell.SpellType @@ -15,8 +17,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)) { @@ -61,7 +63,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 +138,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 +185,32 @@ 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 + effectiveDefenceLevel *= familiarDefenceBonus(victim) + 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 { @@ -199,6 +222,20 @@ open class MagicSwingHandler return 1.0 } + /** + * Check to see whether the player is receiving a defensive bonus from an active familiar. + * - Wolpertinger => +5% + */ + private fun familiarDefenceBonus(e: Entity?): Double { + if (e !is Player) return 1.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 1.0 + return if (fam.pouchId == SummoningPouch.WOLPERTINGER_POUCH.pouchId) 1.05 else 1.0 + } + override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { if (state?.spell != null && entity is Player) { state.spell.addExperience(entity, state.totalDamage) 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..7f9b668d8 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -3,6 +3,8 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks import content.global.skill.slayer.SlayerEquipmentFlags import content.global.skill.slayer.SlayerManager +import content.global.skill.slayer.Tasks +import content.global.skill.summoning.SummoningPouch import core.api.* import core.api.EquipmentSlot import core.game.container.impl.EquipmentContainer @@ -13,9 +15,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,39 +26,39 @@ 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) { - 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 - var type = InteractionType.STILL_INTERACT - var goodRange = canMelee(entity, victim, distance) - if (!goodRange && victim.properties.combatPulse.getVictim() !== entity && victim.walkingQueue.isMoving && entity.size() == 1) { - type = InteractionType.MOVE_INTERACT - distance += if (entity.walkingQueue.isRunningBoth) 2 else 1 - goodRange = canMelee(entity, victim, distance) - } - if (!isProjectileClipped(entity, victim, !usingHalberd(entity))) { - return InteractionType.NO_INTERACT - } - val isRunning = entity.walkingQueue.runDir != -1 - val enemyRunning = victim.walkingQueue.runDir != -1 - // THX 4 fix tom <333. - if (super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { - val maxDistance = if (isRunning) if (enemyRunning) 3 else 4 else 2 - if (entity.walkingQueue.isMoving - && entity.location.getDistance(victim.location) <= maxDistance) { - return type - } else if (goodRange) { - if (type == InteractionType.STILL_INTERACT) entity.walkingQueue.reset() - return type - } - } - return InteractionType.NO_INTERACT - } + : 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 + var type = InteractionType.STILL_INTERACT + var goodRange = canMelee(entity, victim, distance) + if (!goodRange && victim.properties.combatPulse.getVictim() !== entity && victim.walkingQueue.isMoving && entity.size() == 1) { + type = InteractionType.MOVE_INTERACT + distance += if (entity.walkingQueue.isRunningBoth) 2 else 1 + goodRange = canMelee(entity, victim, distance) + } + if (!isProjectileClipped(entity, victim, !usingHalberd(entity))) { + return InteractionType.NO_INTERACT + } + val isRunning = entity.walkingQueue.runDir != -1 + val enemyRunning = victim.walkingQueue.runDir != -1 + // THX 4 fix tom <333. + if (super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { + val maxDistance = if (isRunning) if (enemyRunning) 3 else 4 else 2 + if (entity.walkingQueue.isMoving && entity.location.getDistance(victim.location) <= maxDistance && goodRange) { + return type + } else if (goodRange) { + if (canStepTowards(entity, victim) == InteractionType.NO_INTERACT) return InteractionType.NO_INTERACT + if (type == InteractionType.STILL_INTERACT) entity.walkingQueue.reset() + return type + } + } + return InteractionType.NO_INTERACT + } override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { var hit = 0 @@ -64,11 +66,14 @@ 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) } @@ -111,16 +116,23 @@ open class MeleeSwingHandler val name = entity.equipment.getNew(3).name var damage = -1 if (name.contains("(p++") || name.contains("(s)") || name.contains("(kp)")) { - damage = 68 + damage = 30 } else if (name.contains("(p+)")) { - damage = 58 + damage = 25 } else if (name.contains("(p)")) { - damage = 48 + damage = 20 } if (damage > -1 && RandomFunction.random(10) < 4) { - victim.stateManager.register(EntityState.POISONED, false, damage, entity) + applyPoison (victim, entity, damage) } } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) + } } super.adjustBattleState(entity, victim, state) } @@ -128,60 +140,86 @@ 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)?.let { task -> + val victimId = entity.properties.combatPulse?.getVictim()?.id ?: 0 + task.ids.contains(victimId) || (task == Tasks.KALPHITES && (victimId == 1158)) // Kalphite Queen phase 1 + } == true) { + effectiveAttackLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape + if (getSlayerTask(entity)?.dragon == true && inEquipment(entity, Items.DRAGON_SLAYER_GLOVES_12862)) + effectiveAttackLevel *= 1.1 + } + + return effectiveAttackLevel.toInt() + } + is NPC -> { + val attackLevel = entity.skills.getLevel(Skills.ATTACK) + 9 + return attackLevel * styleAttackBonus + } } - return floor(effectiveAttackLevel).toInt() + return 0 + + } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { - val level = entity!!.skills.getLevel(Skills.STRENGTH) - var bonus = entity.properties.bonuses[11] - var prayer = 1.0 - if (entity is Player) { - prayer += entity.prayer.getSkillBonus(Skills.STRENGTH) - } - var cumulativeStr = floor(level * prayer) - if (entity.properties.attackStyle.style == WeaponInterface.STYLE_AGGRESSIVE) { - cumulativeStr += 3.0 - } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) { - cumulativeStr += 1.0 + entity ?: return 0 + + var styleStrengthBonus = entity.properties.bonuses[11] + 64 + when (entity) { + is Player -> { + var effectiveStrengthLevel = entity.skills.getLevel(Skills.STRENGTH).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) + effectiveStrengthLevel = floor(effectiveStrengthLevel + (entity.prayer.getSkillBonus(Skills.STRENGTH) * effectiveStrengthLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_AGGRESSIVE) effectiveStrengthLevel += 3 + else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveStrengthLevel += 1 + effectiveStrengthLevel += 8 + effectiveStrengthLevel *= getSetMultiplier(entity, Skills.STRENGTH) + effectiveStrengthLevel = floor(effectiveStrengthLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) + effectiveStrengthLevel *= styleStrengthBonus + else effectiveStrengthLevel *= 64 + if (getSlayerTask(entity)?.let { task -> + val victimId = entity.properties.combatPulse?.getVictim()?.id ?: 0 + task.ids.contains(victimId) || (task == Tasks.KALPHITES && (victimId == 1158)) // Kalphite Queen phase 1 + } == true) { + effectiveStrengthLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape + } + + return (floor((0.5 + (effectiveStrengthLevel / 640.0))) * modifier).toInt() + } + is NPC -> { + val strengthLevel = entity.skills.getLevel(Skills.STRENGTH) + 9 + return (floor((0.5 + (strengthLevel * styleStrengthBonus / 640.0))) * modifier).toInt() + } } - //Strength skillcape perk - if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT, entity) && getItemFromEquipment(entity, EquipmentSlot.WEAPON)?.definition?.getRequirement(Skills.STRENGTH) != 0) - bonus = ceil(bonus * 1.20).toInt() - - 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 +227,20 @@ 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) + effectiveDefenceLevel *= familiarDefenceBonus(victim) + 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,13 +250,17 @@ 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 } + var multiplier = 1.0 if(e is Player && e.isWearingVoid(CombatStyle.MELEE) && (skillId == Skills.ATTACK || skillId == Skills.STRENGTH)) { - return 1.1 + multiplier += 0.1 } - return 1.0 + if (e is Player && e.properties.armourSet === ArmourSet.BERSERKER_NECKLACE_SETS && skillId == Skills.STRENGTH) { + multiplier += 0.2 + } + return multiplier } override fun getArmourSet(e: Entity?): ArmourSet? { @@ -230,6 +273,9 @@ open class MeleeSwingHandler if (ArmourSet.VERAC.isUsing(e)) { return ArmourSet.VERAC } + if (ArmourSet.BERSERKER_NECKLACE_SETS.isUsing(e)) { + return ArmourSet.BERSERKER_NECKLACE_SETS + } return if (ArmourSet.TORAG.isUsing(e)) { ArmourSet.TORAG } else super.getArmourSet(e) @@ -245,6 +291,25 @@ open class MeleeSwingHandler || name == "Skeleton" || name == "Zogre" || name == "Spiritual Mage") } + /** + * Check to see whether the player is receiving a defensive bonus from an active familiar. + * - Iron Titan => +10% + * - Steel Titan => +15% + */ + private fun familiarDefenceBonus(e: Entity?): Double { + if (e !is Player) return 1.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 1.0 + return when (fam.pouchId) { + SummoningPouch.IRON_TITAN_POUCH.pouchId -> 1.10 + SummoningPouch.STEEL_TITAN_POUCH.pouchId -> 1.15 + else -> 1.0 + } + } + companion object { /** * Checks if the entity is using a halberd. 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..a3d6d5fec 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) { /** @@ -27,7 +27,7 @@ open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttac protected var current: SwitchAttack /** - * The current attack. + * The next attack. */ protected var next: SwitchAttack @@ -119,6 +119,9 @@ open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttac override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { current.handler.addExperience(entity, victim, state) } + override fun postSwing(entity: Entity?, victim: Entity?, state: BattleState?) { + current.handler.postSwing(entity, victim, state) + } override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { if (current.isUseHandler) { current.handler.visualizeImpact(entity, victim, state) 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..5eeed5f21 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,8 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.summoning.SummoningPouch +import core.api.* import core.game.container.impl.EquipmentContainer import core.game.node.entity.Entity import core.game.node.entity.combat.equipment.* @@ -9,17 +11,17 @@ 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 @@ -29,11 +31,10 @@ import kotlin.math.floor * @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,7 +47,7 @@ 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 @@ -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 } @@ -91,10 +101,15 @@ open class RangeSwingHandler } if(state.estimatedHit > victim.skills.lifepoints) state.estimatedHit = victim.skills.lifepoints if(state.estimatedHit + state.secondaryHit > victim.skills.lifepoints) state.secondaryHit -= ((state.estimatedHit + state.secondaryHit) - victim.skills.lifepoints) - useAmmo(entity, state, victim.location) return 1 + ceil(entity.location.getDistance(victim.location) * 0.3).toInt() } + override fun postSwing(entity: Entity?, victim: Entity?, state: BattleState?) { + // Using the last piece of ammo in swing causes experience calculations to assume we're using unarmed combat + // which gives wrong experience values. Delay ammo consumption until later to avoid this problem. + useAmmo(entity!!, state!!, victim!!.location) + } + /** * Configures the range data. * @param entity The entity. @@ -106,7 +121,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 +138,14 @@ 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) + } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) } } super.adjustBattleState(entity, victim, state) @@ -160,7 +182,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 +204,100 @@ 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 += familiarOffenceBonus(entity) + 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 += familiarOffenceBonus(entity) + 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 @@ -236,6 +309,22 @@ open class RangeSwingHandler } else super.getArmourSet(e) } + /** + * Check to see whether the player is receiving an offensive bonus from an active familiar. + * - Geyser Titan => 1 + 3% of ranged level + */ + private fun familiarOffenceBonus(e: Entity?): Double { + if (e !is Player) return 0.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 0.0 + return if (fam.pouchId == SummoningPouch.GEYSER_TITAN_POUCH.pouchId) { + 1.0 + ceil(e.skills.getLevel(Skills.RANGE) * 0.03) + } else 0.0 + } + companion object { /** * Checks if the entity has the ammunition needed to proceed. @@ -282,7 +371,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 @@ -342,35 +430,5 @@ open class RangeSwingHandler } 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..5b7a7e3af 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 @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import org.rs09.consts.Items; /** * Represents the types of armour sets. @@ -21,7 +22,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 +79,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 +101,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); } @@ -129,6 +130,15 @@ public enum ArmourSet { } return super.isUsing(e); } + }, + /** + * Berserker Necklace sets + */ + BERSERKER_NECKLACE_SETS(null, new int[][] { { Items.BERSERKER_NECKLACE_11128 }, { Items.TOKTZ_XIL_AK_6523, Items.TOKTZ_XIL_EK_6525, Items.TZHAAR_KET_EM_6527, Items.TZHAAR_KET_OM_6528 } }) { + @Override + public boolean isUsing(Entity e) { + return super.isUsing(e); + } }; /** @@ -148,8 +158,9 @@ public enum ArmourSet { */ private ArmourSet(Graphics endGraphic, int[][] set) { this.endGraphic = endGraphic; - this.sets = new Item[4][5]; + this.sets = new Item[set.length][]; for (int i = 0; i < set.length; i++) { + sets[i] = new Item[set[i].length]; for (int k = 0; k < sets[i].length; k++) { sets[i][k] = new Item(set[i][k]); } @@ -197,7 +208,7 @@ public enum ArmourSet { } } } - return hits == 4; + return hits == sets.length; } -} \ No newline at end of file +} 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..71d26d8b6 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,24 +216,32 @@ 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) { victim.graphics(graphics); } } - + /** * Checks if the effect can fire. + * * @param state the state. + * * @return {@code True} if so. */ public boolean canFire(BattleState state) { - return RandomFunction.random(13) == 5; + // Never fires on slayer monster that player lacks level requirement for. Inauthentic(?) + // and should be considered a TODO: temporary abuse prevention measure (bolt effect on slayer monsters) + boolean rollSuccess = RandomFunction.random(13) == 5; + if (!(state.getVictim() instanceof NPC)) return rollSuccess; + if (!(state.getAttacker() instanceof Player) || ((NPC) state.getVictim()).getTask() == null) return rollSuccess; + if (state.getVictim().asNpc().getTask().levelReq > state.getAttacker().asPlayer().getSkills().getLevel(Skills.SLAYER)) return false; + return rollSuccess; } /** @@ -269,5 +265,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..cb74f3fa6 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. @@ -318,9 +321,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); } }; @@ -729,4 +732,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..e330489a9 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/Grave.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/Grave.kt @@ -1,5 +1,6 @@ package core.game.node.entity.combat.graves +import content.global.handlers.item.equipment.BarrowsEquipment import core.api.clearHintIcon import core.api.registerHintIcon import core.api.sendMessage @@ -49,7 +50,7 @@ class Grave : AbstractNPC { this.ownerUid = player.details.uid this.ownerUsername = player.username - this.location = location + this.location = player.getAttribute("/save:original-loc",location) this.isRespawn = false this.isWalks = false this.isNeverWalks = true @@ -65,7 +66,11 @@ class Grave : AbstractNPC { continue } - val finalItem = GraveController.checkTransform(item) + val finalItem = if (BarrowsEquipment.isBarrowsItem(item.id) && !BarrowsEquipment.isFullyRepaired(item.id)) { + BarrowsEquipment.graveDeathDurabilityReduction(item) ?: item + } else { + GraveController.checkTransform(item) + } val gi = GroundItemManager.create(finalItem, this.location, player) gi.isRemainPrivate = true diff --git a/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt b/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt index 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..1a9105873 100644 --- a/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt +++ b/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt @@ -1,8 +1,9 @@ package core.game.node.entity.combat.graves import org.rs09.consts.NPCs +import content.data.Quests -enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: String = "", val text: String) { +enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: Quests? = null, val text: String) { MEM_PLAQUE(NPCs.GRAVE_MARKER_6565, 0, 2, false, text = "In memory of @name,
who died here."), FLAG(NPCs.GRAVE_MARKER_6568, 50, 2, false, text = MEM_PLAQUE.text), SMALL_GS(NPCs.GRAVESTONE_6571, 500, 2, false, text = "In loving memory of our dear friend @name,
who died in this place @mins ago."), @@ -12,9 +13,9 @@ enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, va SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 4, true, text = "@name,
an enlightened servant of Saradomin,
perished in this place."), ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 4, true, text = "@name,
a most bloodthirsty follower of Zamorak,
perished in this place."), GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 4, true, text = "@name,
who walked with the Balance of Guthix,
perished in this place."), - BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = "Land of the Goblins", text = "@name,
a vicious warrior dedicated to Bandos,
perished in this place. "), - ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = "Temple of Ikov", text = "@name,
a follower of the Law of Armadyl,
perished in this place."), - ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = "Desert Treasure", text = "@name,
servant of the Unknown Power,
perished in this place."), + BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = Quests.LAND_OF_THE_GOBLINS, text = "@name,
a vicious warrior dedicated to Bandos,
perished in this place. "), + ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = Quests.TEMPLE_OF_IKOV, text = "@name,
a follower of the Law of Armadyl,
perished in this place."), + ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = Quests.DESERT_TREASURE, text = "@name,
servant of the Unknown Power,
perished in this place."), ANGEL_DEATH(NPCs.MEMORIAL_STONE_6601, 500000, 5, true, text = "Ye frail mortals who gaze upon this sight,
forget not the fate of @name, once mighty, now
surrendered to the inescapable grasp of destiny.
Requiescat in pace."); companion object { diff --git a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java index 225855b11..30cf2f238 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java @@ -1,5 +1,6 @@ package core.game.node.entity.combat.spell; +import content.global.skill.summoning.familiar.Familiar; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.InteractionType; @@ -13,12 +14,16 @@ import core.game.node.entity.player.link.SpellBookManager; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.map.RegionManager; +import core.game.world.map.zone.impl.WildernessZone; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import org.rs09.consts.Sounds; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + /** * Represents a combat magic spell. * @author Emperor @@ -103,27 +108,42 @@ public abstract class CombatSpell extends MagicSpell { } - /** - * Gets a list of possible targets for a multihitting spell. - * @param entity The caster of the spell. - * @param target The victim. - * @param max The max amount of victims. - * @return The list of targets. - */ - public List getMultihitTargets(Entity entity, Entity target, int max) { - List list = new ArrayList<>(20); - list.add(target); - boolean npc = target instanceof NPC; - for (Entity e : npc ? RegionManager.getSurroundingNPCs(target) : RegionManager.getSurroundingPlayers(target)) { - if (e != target && e != entity && CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) { - list.add(e); + /** + * Gets a list of valid targets for a multihitting spell. + * @param entity The caster of the spell. + * @param target The primary victim. + * @param max The maximum number of extra victims that may be hit. + * @return The list of targets (the primary target is always at index 0). + */ + public List getMultihitTargets(Entity entity, Entity target, int max) { + List victims = new ArrayList<>(20); + victims.add(target); + + List surrounding = new ArrayList<>(); + surrounding.addAll(RegionManager.getSurroundingPlayers(target)); + surrounding.addAll(RegionManager.getSurroundingNPCs(target)); + + for (Entity e : surrounding) { + if (e == target || e == entity) { + continue; + } + if (CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) == InteractionType.NO_INTERACT || !e.isAttackable(entity, CombatStyle.MAGIC, false)) { + continue; + } + if (e instanceof Familiar) { + Player owner = ((Familiar) e).getOwner(); + if (owner != entity && WildernessZone.getInstance().continueAttack(entity, owner, CombatStyle.MAGIC, true)) { + victims.add(e); + } + } else { + victims.add(e); } if (--max < 1) { break; } } - return list; - } + return victims; + } /** * Visualizes the impact. @@ -132,18 +152,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 @@ -175,7 +190,7 @@ public abstract class CombatSpell extends MagicSpell { entity.animate(animation); } } - sendAudio(entity); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override @@ -237,4 +252,4 @@ public abstract class CombatSpell extends MagicSpell { return SPLASH_GRAPHIC; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java b/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java index 53d017c75..544372bba 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,7 +1,7 @@ package core.game.node.entity.combat.spell; -import core.game.component.Component; -import core.game.node.entity.skill.Skills; +import content.global.skill.magic.SpellUtils; +import core.game.event.SpellCastEvent; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatSwingHandler; @@ -9,17 +9,17 @@ import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; 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.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; import core.tools.RandomFunction; import java.util.ArrayList; -import java.util.List; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Represents a magic spell. @@ -120,17 +120,13 @@ public abstract class MagicSpell implements Plugin { p.faceTemporary((Entity) target, 1); } if (spell.cast(p, target)) { - if (book != SpellBook.LUNAR && p.getAttribute("spell:swap", 0) != 0) { - p.removeAttribute("spell:swap"); - p.getSpellBookManager().setSpellBook(SpellBook.LUNAR); - p.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId())); - } if (!combatSpell) { p.getSkills().addExperience(Skills.MAGIC, spell.getExperience(p), true); } 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 +154,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); } /** @@ -227,16 +223,17 @@ public abstract class MagicSpell implements Plugin { if (runes == null) { return true; } - List toRemove = new ArrayList<>(20); - for (Item item : runes) { - if (!hasRune(p, item, toRemove, message)) { - return false; + Item missing = SpellUtils.hasRunes(p, runes); + if (missing != null) { + if (message) { + p.getPacketDispatch().sendMessage("You don't have enough " + missing.getName() + "s to cast this spell."); } + return false; } if (remove) { - toRemove.forEach(i -> { - p.getInventory().remove(i); - }); + ArrayList toRemove = p.getAttribute("spell:runes", new ArrayList<>()); + toRemove.forEach(i -> p.getInventory().remove(i)); + p.removeAttribute("spell:runes"); } return true; } @@ -259,49 +256,6 @@ public abstract class MagicSpell implements Plugin { return true; } - /** - * Checks if the player has a rune to remove. - * @param p the player. - * @param item the item. - * @param toRemove the list of items to remove. - * @param message the message. - * @return {@code True} if so. - */ - 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()); - if(!hasBaseRune){ - int baseAmt = p.getInventory().getAmount(item.getId()); - if(baseAmt > 0){ - toRemove.add(new Item(item.getId(),p.getInventory().getAmount(item.getId()))); - } - int amtRemaining = item.getAmount() - baseAmt; - List possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(item.getId())); - for(CombinationRune r : possibleComboRunes){ - if(p.getInventory().containsItem(new Item(r.id)) && amtRemaining > 0){ - int amt = p.getInventory().getAmount(r.id); - if(amtRemaining < amt){ - toRemove.add(new Item(r.id,amtRemaining)); - amtRemaining = 0; - continue; - } - amtRemaining -= p.getInventory().getAmount(r.id); - toRemove.add(new Item(r.id,p.getInventory().getAmount(r.id))); - } - } - 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; - } - /** * Adds the experience for casting this spell. * @param entity The entity to reward with experience. @@ -314,11 +268,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 +284,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 +354,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..828a88804 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,10 @@ 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.*; + /** * Represents the spell types. * @author Emperor @@ -107,10 +108,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 +153,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 +253,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..430c4d551 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,11 @@ 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 +118,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 +153,8 @@ public final class Animator { */ public void reset() { animate(RESET_A); + entity.clocks[Clocks.getANIMATION_END()] = 0; + ticks = 0; } /** @@ -158,7 +162,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 +208,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/PulseManager.java b/Server/src/main/core/game/node/entity/impl/PulseManager.java index 74a78e7cc..6f93e1250 100644 --- a/Server/src/main/core/game/node/entity/impl/PulseManager.java +++ b/Server/src/main/core/game/node/entity/impl/PulseManager.java @@ -17,6 +17,12 @@ import java.util.HashMap; */ public final class PulseManager { + private final Entity entity; + + public PulseManager(Entity entity) { + this.entity = entity; + } + /** * The movement pulse. */ @@ -61,6 +67,9 @@ public final class PulseManager { } public void clear() { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + currentPulses.forEach((type, pulse) -> { if (type != PulseType.STRONG && pulse != null) pulse.stop(); }); @@ -70,6 +79,9 @@ public final class PulseManager { * Clears the pulses. */ public boolean clear(PulseType pulseType) { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + Pulse pulse = currentPulses.get(pulseType); if (pulse != null) { diff --git a/Server/src/main/core/game/node/entity/impl/WalkingQueue.java b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java index 5b81e2aef..fbd9d2e36 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,9 @@ public final class WalkingQueue { if (player.getSettings().getWeight() > 0.0) { rate *= 1 + (player.getSettings().getWeight() / 100); } + if (hasTimerActive(player, "hamstrung")) { + rate *= 4; + } return rate; } @@ -296,6 +320,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 +368,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 +406,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..18334251e 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. */ @@ -355,28 +383,49 @@ public class NPC extends Entity { this.movementIndex = 0; this.pathBoundMovement = true; } - + @Override 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(); + state.setPoisonDamage(-1); + if (state.getStyle() == CombatStyle.MAGIC) { + state.setEstimatedHit(-1); + } } + if (task != null && entity instanceof Familiar && task.levelReq > ((Familiar) entity).getOwner().getSkills().getLevel(Skills.SLAYER)) { + state.neutralizeHits(); + state.setPoisonDamage(-1); + if (state.getStyle() == CombatStyle.MAGIC) { + state.setEstimatedHit(-1); + } + } + } + + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (isInvisible()) { + return false; + } + 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); } @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if ((entity instanceof Player && !definition.hasAction("attack")) || isInvisible()) { + public boolean continueAttack(Entity target, CombatStyle style, boolean message) { + if (isInvisible()) { return false; } - if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { - if(message) { - ((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster."); - } - } - return super.isAttackable(entity, style, message); - } + return true; + } @Override public int getDragonfireProtection(boolean fire) { @@ -394,8 +443,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 +462,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,17 +488,21 @@ 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(); getLocks().unlockMovement(); - 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 +539,9 @@ public class NPC extends Entity { return false; } + public int getNextWalk() { + return nextWalk; + } /** * Sets the next walk. */ @@ -496,6 +561,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(); @@ -507,22 +580,41 @@ public class NPC extends Entity { return definition.getConfiguration(NPCConfigParser.POISON_IMMUNE, false); } + public boolean isPoisonous() { + return definition.getConfiguration(NPCConfigParser.POISONOUS, false); + } + + public int poisonSeverity() { + return definition.getConfiguration(NPCConfigParser.POISON_AMOUNT, 0); + } + @Override public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); 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 +645,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 +660,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 +739,27 @@ 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; + } + + /** + * Transforms this NPC with HP carried over from pre-transformed entity. + * HP adjustments (ie healing/scaling HP to new maximum) should be done manually per-NPC + * @param id The new NPC id. + */ + public NPC transformWithHpCarryover(int id) { + int lp = getSkills().getLifepoints(); + this.transform(id); + getSkills().setLifepoints(lp); return this; } @@ -698,18 +779,14 @@ public class NPC extends Entity { */ protected Location getMovementDestination() { if (!pathBoundMovement || movementPath == null || movementPath.length < 1) { - Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); + int radius = getWalkRadius() / 2; + Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(RandomFunction.random(-radius, radius+1), RandomFunction.random(-radius, radius+1), 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..e4fa6676b --- /dev/null +++ b/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt @@ -0,0 +1,159 @@ +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 { + return !(attacker is Player && !self.definition.hasAction("attack") && !self.definition.hasAction("smash-ice")) + } + + /** + * 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..50f93be3f 100644 --- a/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java +++ b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java @@ -72,9 +72,9 @@ public class AggressiveBehavior { if (entity instanceof NPC && target instanceof Player) { NPC npc = (NPC) entity; if (npc.getAggressiveHandler() != null && npc.getAggressiveHandler().isAllowTolerance() && !WildernessZone.isInZone(npc)) { - if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { - return false; - } + if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { + return false; + } } } int level = target.getProperties().getCurrentCombatLevel(); @@ -84,9 +84,9 @@ public class AggressiveBehavior { return true; } - public boolean ignoreCombatLevelDifference() { - return false; - } + public boolean ignoreCombatLevelDifference() { + return false; + } /** * Gets the priority flag. @@ -132,9 +132,9 @@ public class AggressiveBehavior { */ public Entity getLogicalTarget(Entity entity, List possibleTargets) { Entity target = null; - int comparingFlag = Integer.MAX_VALUE; + double comparingFlag = Double.MAX_VALUE; for (Entity e : possibleTargets) { - int flag = getPriorityFlag(e); + double flag = (double)getPriorityFlag(e) + Math.random(); if (flag <= comparingFlag) { comparingFlag = flag; target = e; diff --git a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java index 5a7291c25..baacc117b 100644 --- a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java @@ -42,7 +42,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 +74,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; } /** @@ -139,7 +147,7 @@ public final class NPCDropTables { List players = RegionManager.getLocalPlayers(npc, 16); List looters = new ArrayList<>(20); for (Player p : players) { - if (p != null && p.getCommunication().getClan() != null && p.getCommunication().getClan() == player.getCommunication().getClan() && p.getCommunication().isLootShare() && p.getCommunication().getLootRequirement().ordinal() >= p.getCommunication().getClan().getLootRequirement().ordinal() && npc.getImpactHandler().getImpactLog().containsKey(p)) { + if (p != null && p.getCommunication().getClan() != null && p.getCommunication().getClan() == player.getCommunication().getClan() && p.getCommunication().isLootShare() && p.getCommunication().getLootRequirement().ordinal() >= p.getCommunication().getClan().getLootRequirement().ordinal()) { looters.add(p); } } diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index 9a4f28dec..d21c591fc 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -1,15 +1,21 @@ package core.game.node.entity.player; +import content.global.handlers.item.equipment.BarrowsEquipment; import content.global.handlers.item.equipment.special.SalamanderSwingHandler; +import content.global.skill.construction.decoration.pohstorage.POHStorageProxyContainer; +import content.global.skill.construction.decoration.pohstorage.StorageState; import content.global.skill.runecrafting.PouchManager; +import content.global.skill.slayer.SlayerEquipmentFlags; +import core.api.ContentAPIKt; import core.game.component.Component; import core.game.container.Container; -import core.game.container.ContainerType; import core.game.container.impl.BankContainer; import core.game.container.impl.EquipmentContainer; import core.game.container.impl.InventoryListener; 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 +25,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 +39,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 +47,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 +59,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 +73,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,11 +123,30 @@ 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; + + private StorageState POHStorageState; + + public final Container POHStorageProxyContainer = new POHStorageProxyContainer(this); + + public StorageState getPOHStorageState() { + if (POHStorageState == null) { + POHStorageState = new StorageState(this); + } + return POHStorageState; + } + + public void setPOHStorageState(StorageState state) { + this.POHStorageState = state; + } + + /** * The inventory. */ private final Container inventory = new Container(28).register(new InventoryListener(this)); @@ -146,21 +171,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 +224,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 +241,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 +270,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 +314,20 @@ 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; + /** * Constructs a new {@code Player} {@code Object}. * @param details The player's details. @@ -324,36 +335,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); @@ -370,9 +384,7 @@ public class Player extends Entity { getZoneMonitor().clear(); HouseManager.leave(this); UpdateSequence.getRenderablePlayers().remove(this); - details.save(); sendLogoutEvents(); - Repository.getDisconnectionQueue().add(this); checkForWealthUpdate(true); } @@ -439,12 +451,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 +464,35 @@ 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. + // Does not run if the player is currently in the middle of a kidnap sequence (to avoid false positives while the teleport is taking place). + if (GameWorld.getTicks() % 10 == 0 && ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null && !ContentAPIKt.getAttribute(this, "kidnapped-by-random", false)) { + int rid = location.getRegionId(); + Region r = RegionManager.forId(rid); + if (!(r instanceof DynamicRegion) && !getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) { + log(this.getClass(), Log.ERR, "Player " + getUsername() + " has the original-loc attribute set but isn't actually off-map! This indicates a bug in the code that set that attribute. The original-loc is " + getAttribute("/save:original-loc") + ", the current region is " + rid + ". Good luck debugging!"); + ContentAPIKt.removeAttribute(this, "original-loc"); + } + } } private void checkForWealthUpdate(boolean force) { @@ -491,6 +515,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 +560,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 +572,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 +609,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 +618,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,54 +632,72 @@ public class Player extends Entity { if (this.isArtificial() && killer instanceof NPC) { return; } + if (killer instanceof Player && !Objects.equals(killer.getName(), getName())) { // the latter happens if you died via typeless damage from an external cause, e.g. bugs in a dark cave without a light source + ContentAPIKt.sendMessage((Player) killer, "You have defeated " + getUsername() + "."); + } getPacketDispatch().sendMessage("Oh dear, you are dead!"); incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS); 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", this, i); + } + 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("), "); + if (GraveController.shouldCrumble(item.getId())) continue; if (GraveController.shouldRelease(item.getId())) continue; - item = GraveController.checkTransform(item); - GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + + if (!BarrowsEquipment.isBarrowsItem(item.getId()) && !item.getDefinition().isTradeable()) { + if (killer instanceof Player) { + int value = item.getDefinition().getAlchemyValue(true); + if (getStatLevel(killer, Skills.MAGIC) < 55) value /= 2; + coins += Math.max(0, value - 250); + continue; + } else stayPrivate = true; + } + if (BarrowsEquipment.isBarrowsItem(item.getId())) { + if (!BarrowsEquipment.isBroken(item.getId())) { + int brokenItemId = Objects.requireNonNull(BarrowsEquipment.getDefinition(item.getId())).getBrokenId(); + item = new Item(brokenItemId, item.getAmount()); + } + } else { + item = GraveController.checkTransform(item); + } + GroundItem gi = GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + gi.setRemainPrivate(stayPrivate); + } + 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); @@ -656,6 +705,7 @@ public class Player extends Entity { } equipment.clear(); + SlayerEquipmentFlags.updateFlags(this); inventory.clear(); inventory.addAll(c[0]); familiarManager.dismiss(); @@ -663,6 +713,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 +770,52 @@ 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 super.isAttackable(entity, style, message); + } else return false; + } return super.isAttackable(entity, style, message); } + @Override + public boolean continueAttack(Entity target, CombatStyle style, boolean message) { + if (target instanceof NPC) { + NPC npc = (NPC) target; + NPC shown = npc.getShownNPC(this); + boolean canAttack = shown.getDefinition().hasAction("attack") + || shown.getDefinition().hasAction("smash-ice"); + if (!canAttack && !npc.isIgnoreAttackRestrictions(this)) { + return false; + } + } + if (target instanceof Player) { + Player p = (Player) target; + if (p.getSkullManager().isWilderness() && skullManager.isWilderness()) { + if (GameWorld.getSettings() == null || !GameWorld.getSettings().getWild_pvp_enabled()) { + return false; + } + if (p.getSkullManager().hasWildernessProtection()) { + return false; + } + return !skullManager.hasWildernessProtection(); + } else { + return false; + } + } + return true; + } + @Override public boolean isPoisonImmune() { - return getAttribute("poison:immunity", -1) > GameWorld.getTicks(); + return timers.getTimer("poison:immunity") != null; } @Override @@ -761,7 +855,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 +999,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)); } /** @@ -1103,6 +1196,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 +1269,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 +1331,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 +1397,6 @@ public class Player extends Entity { return "Player [name=" + name + ", getRights()=" + getRights() + "]"; } - public String getCustomState() { return customState; } @@ -1324,25 +1422,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..95009c23a 100644 --- a/Server/src/main/core/game/node/entity/player/info/Rights.java +++ b/Server/src/main/core/game/node/entity/player/info/Rights.java @@ -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,8 +25,10 @@ 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) { + 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..fb50ee051 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -1,12 +1,10 @@ package core.game.node.entity.player.info.login; -import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; 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 +12,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 +25,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. @@ -44,22 +47,17 @@ public final class LoginConfiguration { */ private static final Component LOBBY_PANE = new Component(549); + /** + * The lobby interface component. + */ + private static final Component LOBBY_INTERFACE = new Component(378); + /** * The lobby message of the week models & constant to be set for auto selecting the models */ private static final int[] MESSAGE_MODEL = {15, 16, 17, 18, 19, 20, 21, 22, 23, 405, 447, 622, 623, 679, 715, 800}; private static int messModel; - /** - * The lobby interface close event. - */ - private static final Component LOBBY_INTERFACE = new Component(378).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - return player.getLocks().isLocked("login"); - } - }); - /** * Constructs a new {@Code LoginConfiguration} {@Code Object} */ @@ -98,25 +96,25 @@ public final class LoginConfiguration { } } Repository.getLobbyPlayers().add(player); - player.getPacketDispatch().sendString(getLastLogin(player), 378, 116); - player.getPacketDispatch().sendString("Welcome to " + GameWorld.getSettings().getName(), 378, 115); - player.getPacketDispatch().sendString(" ", 378, 37); - player.getPacketDispatch().sendString("Want to stay up to date with the latest news and updates? Join our
discord by using the link below!", 378, 38); - player.getPacketDispatch().sendString(" ", 378, 39); - player.getPacketDispatch().sendString("Discord Invite", 378, 14); - player.getPacketDispatch().sendString("Discord Invite", 378, 129); - player.getPacketDispatch().sendString("Credits", 378, 94); - player.getPacketDispatch().sendString(player.getDetails().getCredits() + "", 378, 96); - player.getPacketDispatch().sendString(" ", 378, 229); - player.getPacketDispatch().sendString("Want to contribute to 2009scape?
Visit the github using the link below!", 378, 230); - player.getPacketDispatch().sendString(" ", 378, 231); - player.getPacketDispatch().sendString("Github", 378, 240); - player.getPacketDispatch().sendString(GameWorld.getSettings().getMessage_string(), messModel, getMessageChild(messModel)); - player.getPacketDispatch().sendString("You can gain more credits by voting, reporting bugs and various other methods of contribution.", 378, 93); + setInterfaceText(player, "Welcome to " + ServerConstants.SERVER_NAME, 378, 115); + setInterfaceText(player, getLastLogin(player), 378, 116); + setInterfaceText(player, "", 378, 37); + setInterfaceText(player, "Want to stay up to date with the latest news and updates? Join our discord by using the link on our website!", 378, 38); + setInterfaceText(player, "", 378, 39); + setInterfaceText(player, "Discord Invite", 378, 14); + setInterfaceText(player, "Discord Invite", 378, 129); + setInterfaceText(player, "You can gain more credits by reporting bugs and various other methods of contribution.", 378, 93); + setInterfaceText(player, player.getDetails().getCredits() + "", 378, 96); + setInterfaceText(player, "Credits", 378, 94); + setInterfaceText(player, "", 378, 229); + setInterfaceText(player, "Want to contribute to " + ServerConstants.SERVER_NAME + "? Visit the GitLab using the link on our website!", 378, 230); + setInterfaceText(player, "", 378, 231); + setInterfaceText(player, "Github", 378, 240); + setInterfaceText(player, GameWorld.getSettings().getMessage_string(), messModel, getMessageChild(messModel)); player.getInterfaceManager().openWindowsPane(LOBBY_PANE); player.getInterfaceManager().setOpened(LOBBY_INTERFACE); - PacketRepository.send(Interface.class, new InterfaceContext(player, 549, 2, 378, true)); - PacketRepository.send(Interface.class, new InterfaceContext(player, 549, 3, messModel, true));//UPDATE `configs` SET `value`=FLOOR(RAND()*(25-10)+10) WHERE key_="messageInterface" + PacketRepository.send(Interface.class, new InterfaceContext(player, LOBBY_PANE.getId(), 2, 378, true)); + PacketRepository.send(Interface.class, new InterfaceContext(player, LOBBY_PANE.getId(), 3, messModel, true));//UPDATE `configs` SET `value`=FLOOR(RAND()*(25-10)+10) WHERE key_="messageInterface" player.getDetails().setLastLogin(System.currentTimeMillis()); } @@ -126,33 +124,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; - 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. + if (hasRequirement(player, Quests.KINGS_RANSOM, false)) { + setVarbit(player, 3909, 8, false); } if(ServerConstants.RULES_AND_INFO_ENABLED) RulesAndInfo.openFor(player); - /*if (GameWorld.getSettings().isPvp()) { - player.getPacketDispatch().sendString("", 226, 1); - }*/ /*if (TutorialSession.getExtension(player).getStage() != 73) { TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), true); }*/ @@ -185,21 +199,14 @@ public final class LoginConfiguration { * @param player the player. Fullscreen mode Object id: */ public static final void welcome(final Player player) { - if (GameWorld.getSettings().isPvp()) { - player.getPacketDispatch().sendString("", 226, 0); - } if (player.isArtificial()) { - return; } - player.getPacketDispatch().sendMessage("Welcome to " + GameWorld.getSettings().getName() + "."); - //player.getPacketDispatch().sendMessage("You are currently playing in beta version 1.2"); + player.getPacketDispatch().sendMessage("Welcome to " + ServerConstants.SERVER_NAME + "."); if (player.getDetails().isMuted()) { player.getPacketDispatch().sendMessage("You are muted."); player.getPacketDispatch().sendMessage("To prevent further mutes please read the rules."); } -// ResourceAIPManager.get().load(player); -// ResourceAIPManager.get().save(player); } /** @@ -209,7 +216,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(); @@ -219,14 +226,14 @@ public final class LoginConfiguration { player.getPacketDispatch().sendRunEnergy(); player.getFamiliarManager().login(); player.getInterfaceManager().openDefaultTabs(); - player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3); - player.getConfigManager().init(); + setInterfaceText(player, "Friends List - " + ServerConstants.SERVER_NAME + " " + GameWorld.getSettings().getWorldId(), 550, 3); + setInterfaceText(player, "When you have finished playing " + ServerConstants.SERVER_NAME + ", always use the button below to logout safely.", 182, 0); player.getQuestRepository().syncronizeTab(player); player.getInterfaceManager().close(); player.getEmoteManager().refresh(); 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()); } /** @@ -240,11 +247,11 @@ public final class LoginConfiguration { return 8; } else if (interfaceId == 16) { return 6; - } else if (interfaceId == 17 || interfaceId == 15 || interfaceId == 18 || interfaceId == 19 || interfaceId == 21 || interfaceId == 22 || interfaceId == 447 || interfaceId == 405) { - return 4; } else if (interfaceId == 20 || interfaceId == 623) { return 5; - } else if (interfaceId == 23 || interfaceId == 800) { + } else if (interfaceId == 15 || interfaceId == 18 || interfaceId == 19 || interfaceId == 21 || interfaceId == 22 || interfaceId == 447 || interfaceId == 405) { + return 4; + } else if (interfaceId == 17 || interfaceId == 23 || interfaceId == 800) { return 3; } else if (interfaceId == 715) { return 2; @@ -269,12 +276,8 @@ public final class LoginConfiguration { * @return the last login. */ public static String getLastLogin(Player player) { - String lastIp = player.getDetails().accountInfo.getLastUsedIp(); - if (lastIp.equals("")) { - lastIp = player.getDetails().getIpAddress(); - } - player.getDetails().accountInfo.setLastUsedIp(player.getDetails().getIpAddress()); - String string = "You last logged in @timeAgo from: " + lastIp; + player.getPacketDispatch().sendLastLoginInfo(); + String string = "You last logged in @timeAgo from:"; long time = player.getDetails().getLastLogin(); Date lastLogin = new Date(time); Date current = new Date(); @@ -300,4 +303,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..649db34a5 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt @@ -1,11 +1,12 @@ package core.game.node.entity.player.info.login -import core.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,14 +37,17 @@ class LoginParser(val details: PlayerDetails) { override fun pulse(): Boolean { try { if (details.session.isActive) { + player.properties.spawnLocation = getAttribute(player, "/save:spawnLocation", ServerConstants.HOME_LOCATION) loginListeners.forEach(Consumer { listener: LoginListener -> listener.login(player) }) //Run our login hooks parser.runContentHooks() //Run our saved-content-parsing hooks player.details.session.setObject(player) + player.getDetails().accountInfo.lastUsedIp = player.getDetails().getIpAddress() if (reconnect) { reconnect(player) } else { flag(AuthResponse.Success) player.init() + reinitVarps(player) } } else { Repository.removePlayer(player) @@ -70,7 +73,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 +102,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..74d82e646 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,15 @@ 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 +35,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() + parsePOHStorable() } fun runContentHooks() @@ -98,6 +93,12 @@ class PlayerSaveParser(val player: Player) { player.pouchManager.parse(saveFile!!["pouches"] as JSONArray) } + fun parsePOHStorable() { + val storableRaw = saveFile?.get("pohstorage") ?: return + val storableJson = storableRaw as JSONObject + player.getPOHStorageState().readJson(storableJson) + } + fun parseAttributes() { if (saveFile!!.containsKey("attributes")) { val attrData = saveFile!!["attributes"] as JSONArray @@ -120,7 +121,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 +179,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 +197,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 +283,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 +313,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,13 +335,10 @@ class PlayerSaveParser(val player: Player) { val skillData = saveFile!!["skills"] as JSONArray player.skills.parse(skillData) player.skills.experienceGained = saveFile!!["totalEXP"].toString().toDouble() - player.skills.experienceMutiplier = saveFile!!["exp_multiplier"].toString().toDouble() - if (GameWorld.settings?.default_xp_rate != 5.0) { - player.skills.experienceMutiplier = GameWorld.settings?.default_xp_rate!! - } + player.skills.experienceMultiplier = saveFile!!["exp_multiplier"].toString().toDouble() val divisor: Double - if(player.skills.experienceMutiplier >= 10 && !player.attributes.containsKey("permadeath")){ //exclude permadeath HCIMs from XP squish - divisor = player.skills.experienceMutiplier / 5.0 + if(player.skills.experienceMultiplier >= 10 && !player.attributes.containsKey("permadeath")){ //exclude permadeath HCIMs from XP squish + divisor = player.skills.experienceMultiplier / 5.0 player.skills.correct(divisor) } if (saveFile!!.containsKey("milestone")) { @@ -361,5 +354,11 @@ class PlayerSaveParser(val player: Player) { player.settings.parse(settingsData) } - -} \ No newline at end of file + fun parseVersion() { + saveFile ?: return + player.version = 0 + if (saveFile!!.containsKey("version")) { + player.version = saveFile!!["version"].toString().toInt() + } + } +} diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt index 59d00d48f..351c8dc34 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,62 @@ class PlayerSaver (val player: Player){ saveStatManager(saveFile) saveAttributes(saveFile) savePouches(saveFile) + savePOHStorageRoom(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 savePOHStorageRoom(root: JSONObject) { + root.put("pohstorage", player.getPOHStorageState().toJson()) + } + + 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 +119,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) @@ -252,33 +266,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 +311,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() @@ -338,7 +327,6 @@ class PlayerSaver (val player: Player){ fun saveGlobalData(root: JSONObject){ val globalData = JSONObject() - globalData.put("tutorialStage",player.savedData.globalData.tutorialStage.toString()) globalData.put("homeTeleportDelay",player.savedData.globalData.homeTeleportDelay.toString()) globalData.put("lumbridgeRope",player.savedData.globalData.hasTiedLumbridgeRope()) globalData.put("apprentice",player.savedData.globalData.hasSpokenToApprentice()) @@ -567,19 +555,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 +602,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 +629,22 @@ 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) } } 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..62873b245 100644 --- a/Server/src/main/core/game/node/entity/player/link/GlobalData.java +++ b/Server/src/main/core/game/node/entity/player/link/GlobalData.java @@ -10,12 +10,6 @@ import org.json.simple.JSONObject; * @author 'Vexia */ public final class GlobalData { - - /** - * Represents the tutorial stage. - */ - private int tutorialStage; - /** * Represents the home teleport delay. */ @@ -298,7 +292,6 @@ public final class GlobalData { private boolean macroDisabled = false; public void parse(JSONObject data){ - tutorialStage = Integer.parseInt( data.get("tutorialStage").toString()); homeTeleportDelay = Long.parseLong(data.get("homeTeleportDelay").toString()); lumbridgeRope = (boolean) data.get("lumbridgeRope"); apprentice = (boolean) data.get("apprentice"); @@ -467,22 +460,6 @@ public final class GlobalData { playerTestStage = stage; } - /** - * Gets the tutorialStage. - * @return The tutorialStage. - */ - public int getTutorialStage() { - return tutorialStage; - } - - /** - * Sets the tutorialStage. - * @param tutorialStage The tutorialStage to set. - */ - public void setTutorialStage(int tutorialStage) { - this.tutorialStage = tutorialStage; - } - /** * Gets the homeTeleportDelay. * @return The homeTeleportDelay. @@ -821,8 +798,8 @@ public final class GlobalData { } /** - * Gets the read plagues. - * @return The plague + * Gets the read plaques. + * @return The plaques */ public boolean[] getReadPlaques() { return readPlaques; diff --git a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java index 47574f569..4a0fb00ae 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); @@ -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/IronmanMode.java b/Server/src/main/core/game/node/entity/player/link/IronmanMode.java index 2573b3fe4..3aa5e2a7a 100644 --- a/Server/src/main/core/game/node/entity/player/link/IronmanMode.java +++ b/Server/src/main/core/game/node/entity/player/link/IronmanMode.java @@ -5,7 +5,7 @@ package core.game.node.entity.player.link; * @author Vexia */ public enum IronmanMode { - // HARDCORE_DEAD has to be before Ultimate so that it does not adopt it's restrictions (on the basis of >= in IronmanManager.java?) + // HARDCORE_DEAD has to be before Ultimate so that it does not adopt its restrictions (on the basis of >= in IronmanManager.java) NONE(-1), STANDARD(5), HARDCORE(6), ULTIMATE(7); /** diff --git a/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java index 33f6f29e3..de6bb8233 100644 --- a/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java +++ b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java @@ -1,9 +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.SystemLogger; +import core.tools.Log; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -11,13 +10,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 +46,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 +66,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 +201,12 @@ public final class PacketDispatch { * @param childId The child id. */ public void sendPlayerOnInterface(int interfaceId, int childId) { - PacketRepository.send(DisplayModel.class, new DisplayModelContext(player, interfaceId, childId)); + // fixme right now for iface 68-71 the player is massive + // The zoom for the other windows is 2150 + // for these 4 individuals it should be 796 but dmc.setZoom doesn't work + DisplayModelContext dmc = new DisplayModelContext(player, interfaceId, childId); + dmc.setZoom(796); // this appears to do nothing + PacketRepository.send(DisplayModel.class, dmc); } /** @@ -216,6 +223,14 @@ public final class PacketDispatch { PacketRepository.send(DisplayModel.class, new DisplayModelContext(player, ModelType.MODEL, modelID,zoom,interfaceId,childId,new Object())); } + public void sendRepositionOnInterface(int interfaceId, int childId, int positionX, int positionY){ + PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, interfaceId, childId, positionX, positionY)); + } + + public void sendAngleOnInterface(int interfaceId, int childId, int zoom, int pitch, int yaw){ + PacketRepository.send(InterfaceSetAngle.class, new DefaultContext(player, pitch, zoom, yaw, interfaceId, childId)); + } + /** * Send the item on interface packet. * @param itemId The item id. @@ -297,7 +312,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 +321,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 +329,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 +395,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)); } @@ -415,8 +430,11 @@ public final class PacketDispatch { PacketRepository.send(CSConfigPacket.class, new CSConfigContext(player, id, value, type, params)); } - public void resetInterface(int id) - { + public void resetInterface(int id) { PacketRepository.send(ResetInterface.class, new InterfaceContext(player, 0, 0, id, false)); } + + public void sendLastLoginInfo() { + PacketRepository.send(LastLoginInfo.class, new PlayerContext(player)); + } } diff --git a/Server/src/main/core/game/node/entity/player/link/QuestData.java b/Server/src/main/core/game/node/entity/player/link/QuestData.java index aa58c395f..110c89e01 100644 --- a/Server/src/main/core/game/node/entity/player/link/QuestData.java +++ b/Server/src/main/core/game/node/entity/player/link/QuestData.java @@ -5,7 +5,6 @@ import core.game.node.item.Item; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; import java.util.Arrays; /** @@ -97,19 +96,6 @@ public final class QuestData { witchsExperimentStage = Integer.parseInt( data.get("witchsExperimentStage").toString()); } - /** - * Saves the desert treasure node. - * @param buffer The buffer. - */ - private final void saveDesertTreasureNode(ByteBuffer buffer) { - buffer.put((byte) 8); - for (int i = 0; i < desertTreasure.length; i++) { - Item item = desertTreasure[i]; - buffer.putShort((short) item.getId()); - buffer.put((byte) item.getAmount()); - } - } - /** * Gets the draynorLever. * @return The draynorLever. diff --git a/Server/src/main/core/game/node/entity/player/link/SavedData.java b/Server/src/main/core/game/node/entity/player/link/SavedData.java index fc62f3a72..a0e005592 100644 --- a/Server/src/main/core/game/node/entity/player/link/SavedData.java +++ b/Server/src/main/core/game/node/entity/player/link/SavedData.java @@ -2,8 +2,6 @@ package core.game.node.entity.player.link; import core.game.node.entity.player.Player; -import java.nio.ByteBuffer; - /** * Represents a managing class of saved data related to ingame interactions, * such as questing data, npc talking data, etc. @@ -39,45 +37,6 @@ public class SavedData { this.player = player; } - /** - * Method used to save an activity var that isn't valued at default. - * @param buffer the buffer. - * @param var the variable to save. - */ - public static final void save(final ByteBuffer buffer, final Object var, final int index) { - if (var instanceof Integer ? (int) var != 0 : var instanceof Double ? (double) var != 0.0 : var instanceof Byte ? (byte) var != 0 : var instanceof Short ? (short) var != 0 : var instanceof Long ? (long) var != 0L : var instanceof Boolean ? (boolean) var != false : var != null) { - buffer.put((byte) index); - if (var instanceof Integer) { - buffer.putInt((int) var); - } else if (var instanceof Byte) { - buffer.put((byte) var); - } else if (var instanceof Short) { - buffer.putShort((short) var); - } else if (var instanceof Long) { - buffer.putLong((long) var); - } else if (var instanceof Boolean) { - buffer.put((byte) 1); - } else if (var instanceof Double) { - buffer.putDouble((double) var); - } else if (var instanceof double[]) { - double[] doubleArray = ((double[]) var); - for (int i = 0; i < doubleArray.length; i++) { - buffer.putDouble(doubleArray[i]); - } - } else if (var instanceof boolean[]) { - boolean[] booleanArray = ((boolean[]) var); - for (int i = 0; i < booleanArray.length; i++) { - buffer.put((byte) (booleanArray[i] ? 1 : 0)); - } - } else if (var instanceof int[]) { - int[] intArray = ((int[]) var); - for (int i = 0; i < intArray.length; i++) { - buffer.putInt(intArray[i]); - } - } - } - } - /** * Gets the boolean value. * @param value the value. @@ -87,15 +46,6 @@ public class SavedData { return value == 1; } - /** - * Gets the boolean value. - * @param buffer the buffer. - * @return the value. - */ - public static boolean getBoolean(ByteBuffer buffer) { - return getBoolean(buffer.get()); - } - /** * Gets the activityData. * @return The activityData. diff --git a/Server/src/main/core/game/node/entity/player/link/Settings.java b/Server/src/main/core/game/node/entity/player/link/Settings.java index 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/SpellBookManager.java b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java index f5dbc2de4..72fd3005a 100644 --- a/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java +++ b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java @@ -4,9 +4,11 @@ import core.game.component.Component; import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.player.Player; - import java.util.HashMap; import java.util.Map; +import java.util.Objects; + +import static core.api.ContentAPIKt.setVarbit; /** * Represents a managing class of a players spell book. @@ -15,7 +17,7 @@ import java.util.Map; public final class SpellBookManager { /** - * Represents the current interface if of the spellbook. + * Represents the current interface if of the spell book. */ private int spellBook = SpellBook.MODERN.getInterfaceId(); @@ -23,25 +25,26 @@ public final class SpellBookManager { * Constructs a new {@code SpellBookManager} {@code Object}. */ public SpellBookManager() { - /** + /* * empty. */ } /** * Sets the spell book. - * @param book + * @param book the spell book. */ public void setSpellBook(SpellBook book) { this.spellBook = book.getInterfaceId(); } /** - * Updates the - * @param player + * Updates the spell book. + * @param player the player. */ public void update(Player player) { - player.getInterfaceManager().openTab(new Component(SpellBook.forInterface(this.spellBook).getInterfaceId())); + player.getInterfaceManager().openTab(new Component(spellBook)); + setVarbit(player, 357, Objects.requireNonNull(SpellBook.forInterface(spellBook)).ordinal()); } /** @@ -53,7 +56,17 @@ public final class SpellBookManager { } /** - * Represents a characters spellbook. + * All the possible ways a SpellBook can get changed. + */ + public enum SpellbookChangeSource { + ALTAR, + MAGIC_CAPE_PERK, + SPELLBOOK_SWAP_CAST, + SPELLBOOK_SWAP_RESTORE + } + + /** + * Represents a characters spell book. * @author 'Vexia * @author Emperor */ @@ -131,5 +144,4 @@ public final class SpellBookManager { return spells.get(buttonId); } } - } \ No newline at end of file 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..723b62b48 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. @@ -23,16 +26,6 @@ public class TeleportManager { */ public static final int WILDY_TELEPORT = 1 << 16 | 8; - /** - * The animations used in the home teleport. - */ - private final static int[] HOME_ANIMATIONS = {1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535}; - - /** - * The graphics used in the home teleport. - */ - private final static int[] HOME_GRAPHICS = {775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535}; - /** * The entity being handled. */ @@ -59,7 +52,6 @@ public class TeleportManager { */ public TeleportManager(Entity entity) { this.entity = entity; - lastTeleport = TeleportType.HOME.getPulse(entity, ServerConstants.HOME_LOCATION); } /** @@ -90,18 +82,22 @@ 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 && 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) { Player p = (Player) entity; + p.scripts.setDelay(GameWorld.getTicks() + 12); p.getDialogueInterpreter().close(); } } @@ -110,16 +106,11 @@ public class TeleportManager { } this.teleportType = teleportType; entity.getWalkingQueue().reset(); - lastTeleport = currentTeleport; currentTeleport = type.getPulse(entity, location); entity.getPulseManager().clear(); - if (type == TeleportType.HOME) { - entity.getPulseManager().run(type.getPulse(entity, location)); - } else { - entity.lock(12); - entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); - GameWorld.getPulser().submit(currentTeleport); - } + entity.lock(12); + entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); + GameWorld.getPulser().submit(currentTeleport); if (entity instanceof Player) { ((Player) entity).getInterfaceManager().close(); } @@ -129,7 +120,7 @@ public class TeleportManager { /** * Fires a random event. * @param entity The entity teleporting. - * @param location The destination lcoation. + * @param location The destination location. */ public static void fireRandom(Entity entity, Location location) { if (entity instanceof Player && entity.getTeleporter().getTeleportType() == 0) { @@ -137,22 +128,6 @@ public class TeleportManager { } } - /** - * Get the home teleport audio based on tick count. - * @param count - */ - private static int getAudio(int count){ - switch(count){ - case 0: - return 193; - case 4: - return 194; - case 11: - return 195; - } - return -1; - } - /** * Gets the entity. * @return the Entity @@ -161,14 +136,6 @@ public class TeleportManager { return entity; } - /** - * Gets the last teleport pulse. - * @return the Pulse - */ - public final Pulse getLastTeleport() { - return lastTeleport; - } - /** * Gets the current teleport pulse. * @return the Pulse @@ -189,24 +156,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 +190,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 +224,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 +257,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)); @@ -328,61 +290,10 @@ public class TeleportManager { }; } }, - HOME(new TeleportSettings(4847, 4857, 800, 804)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int count; - Player player; - - @Override - public boolean pulse() { - switch (count) { - case 18: - player.getProperties().setTeleportLocation(location); - return true; - default: - entity.asPlayer().getAudioManager().send(getAudio(count)); - player.getPacketDispatch().sendGraphic(HOME_GRAPHICS[count]); - player.getPacketDispatch().sendAnimation(HOME_ANIMATIONS[count]); - break; - } - count++; - return false; - } - @Override - public void start() { - player = ((Player) entity); - /*if (player.getSavedData().getGlobalData().getHomeTeleportDelay() > System.currentTimeMillis() && !player.isDonator()) { - long milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - int minutes = (int) Math.round(milliseconds / 120000); - if (minutes > 15) { - player.getSavedData().getGlobalData().setHomeTeleportDelay(System.currentTimeMillis() + 1200000); - milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - minutes = (int) Math.round(milliseconds / 120000); - } - if (minutes != 0) { - player.getPacketDispatch().sendMessage("You need to wait another " + minutes + " " + (minutes == 1 ? "minute" : "minutes") + " to cast this spell."); - stop(); - return; - } - }*/ - super.start(); - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - player.graphics(new Graphics(-1)); - } - }; - } - }, OBELISK(new TeleportSettings(8939, 8941, 661, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override @@ -406,15 +317,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 +343,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 +351,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 +371,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 +394,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 +415,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 +455,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 +470,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 +492,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 +526,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 +563,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 +714,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..875798165 100644 --- a/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java @@ -114,13 +114,12 @@ public class AchievementDiary { } child++; } - // sendString(player, builder.toString(), 11); - //Changes the size of the scroll bar - //player.getPacketDispatch().sendRunScript(1207, "i", new Object[] { 330 }); - //sendString(player, builder.toString(), 11); if (!player.getInterfaceManager().isOpened()) { player.getInterfaceManager().open(new Component(DIARY_COMPONENT)); } + // Changes the size of the scroll bar (see 1207.cs2 for more) + // (args1: 1 is to start from top of scroll) (args0: child-12 lines to display) + player.getPacketDispatch().sendRunScript(1207, "ii", 1, child - 10); } /** @@ -207,7 +206,7 @@ public class AchievementDiary { } if(complete){ completedLevels.add(level); - } else if(completedLevels.contains(level)) completedLevels.remove(level); + } else if(completedLevels.contains(level)) completedLevels.remove((Object) level); } } @@ -225,18 +224,6 @@ public class AchievementDiary { drawStatus(player); } - public boolean checkComplete(DiaryLevel level){ - if(type != DiaryType.LUMBRIDGE && level == DiaryLevel.BEGINNER){ - return false; - } - - if(level == DiaryLevel.BEGINNER){ - return completedLevels.contains(level.ordinal()); - } - - return completedLevels.contains(level.ordinal() - 1); - } - /** * Sends a string on the diary interface. * @param player the player. @@ -489,7 +476,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 +484,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 +530,18 @@ public class AchievementDiary { /** * Checks if a player can claim the rewards for the given level of the given diary - * Checks to make sure the player hasn't completed the next level. + * Checks to make sure the player hasn't claimed the next level. * @param player the player to check * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. * @param level the level to check * @return whether or not the player can claim the rewards */ public static boolean canClaimLevelRewards(Player player, DiaryType type, int level) { - return !hasCompletedLevel(player, type, level + 1) && hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); + if (level == 2) + // Cannot be a higher level to claim + return hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); + else + return !hasClaimedLevelRewards(player, type, level + 1) && hasCompletedLevel(player, type, level) && !hasClaimedLevelRewards(player, type, level); } /** diff --git a/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java index a91f4b5bc..dbbb06e81 100644 --- a/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java @@ -1,8 +1,8 @@ package core.game.node.entity.player.link.diary; import core.game.node.entity.player.Player; -import org.rs09.consts.Items; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * An achievement diary type. @@ -34,14 +34,14 @@ public enum DiaryType { "Use Vigroy and Hajedy's cart service", "Earn 100% favour in the village of Tai Bwo Wannai Cleanup", // todo tai bwo wannai cleanup "Cook a spider on stick", // todo tai bwo wannai cleanup - "Charter the Lady of the Waves from Cairn Isle to Port Khazard", // todo verify + "Charter the Lady of the Waves from Cairn Isle to Port Khazard", "Cut a log from a teak tree", "Cut a log from a mahogany tree", "Catch a karambwan", // todo need Tai Bwo Wannai Trio "Exchange gems, a gout tuber, trading sticks for a machete", // todo "Use the gnome glider to travel to Karamja", - "Grow a healthy fruit tree in the patch near Brimhaven", // todo verify - "Trap a Horned Graahk", // todo need to implement pitfall trapping + "Grow a healthy fruit tree in the patch near Brimhaven", + "Trap a Horned Graahk", "Chop the vines to gain deeper access to Brimhaven Dungeon", "Cross the lava using the stepping stones within Brimhaven

Dungeon", "Climb the stairs within Brimhaven Dungeon", @@ -118,7 +118,7 @@ public enum DiaryType { "Select a colour for a new kitten", // TODO need ring of charos(a) and garden of tranquility to start "Use the shortcut under the wall, north-west of the Grand

Exchange", "Enter the A Soul's Bane rift", - "Teleport to the Digsite using a Digsite pendant", // TODO need Digsite and museum + "Teleport to the Digsite using a Digsite pendant", "Craft an earth tiara on the Earth Altar", "Pickpocket a guard in the Varrock Palace courtyard", "Use the teleport to Varrock spell", @@ -140,7 +140,7 @@ public enum DiaryType { "Craft an air battlestaff", "Give your player-owned house a tropical wood or fancy stone

finish at the Varrock estate agent's", "Make a Varrock teleport tablet on a mahogany lectern", - "Obtain a new set of Family Crest gauntlets from Dimintheis", // TODO need family crest + "Obtain a new set of Family Crest gauntlets from Dimintheis", "Make a Waka Canoe near Edgeville", "Use the Home Teleport spell in the Ancient Magicks spellbook

to teleport to Edgeville", "Use the skull sceptre to teleport to Barbarian Village" 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..2bda2ca57 100644 --- a/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java +++ b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.link.emote; +import content.data.Quests; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.player.info.Rights; import core.game.world.map.Direction; @@ -14,6 +15,7 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import static core.api.ContentAPIKt.playJingle; /** * Represents an emote. * @@ -50,10 +52,10 @@ public enum Emotes { SHRUG(8, Animation.create(2113)), CHEER(9, Animation.create(862)), BECKON(10, Animation.create(864)), - //Switch laugh id 11, to 12, and switch Jump id 12 to 11. Id's were mismatched. - //This caused the LAUGH to proc JUMP_FOR_JOY and JUMP_FOR_JOY to proc LAUGH! :) - JUMP_FOR_JOY(11, Animation.create(2109)), - LAUGH(12, Animation.create(861)), + //Switch laugh id 11, to 12, and switch Jump id 12 to 11. Id's were mismatched. + //This caused the LAUGH to proc JUMP_FOR_JOY and JUMP_FOR_JOY to proc LAUGH! :) + JUMP_FOR_JOY(11, Animation.create(2109)), + LAUGH(12, Animation.create(861)), YAWN(13, Animation.create(2111)) { @Override public void play(Player player) { @@ -105,7 +107,7 @@ public enum Emotes { public void play(Player player) { if(player.getLocation().getRegionId() == 13206 && !player.getAttribute("mistag-greeted", false)) { RegionManager.getLocalNpcs(player).forEach(npc -> { - if (npc.getId() == 2084 && npc.getLocation().withinDistance(player.getLocation(), 3) && player.getQuestRepository().getQuest("Lost Tribe").getStage(player) == 45) { + if (npc.getId() == 2084 && npc.getLocation().withinDistance(player.getLocation(), 3) && player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 45) { player.getDialogueInterpreter().open(2084,npc,"greeting"); player.setAttribute("/save:mistag-greeted",true); } @@ -166,7 +168,12 @@ public enum Emotes { } }, SNOWMAN_DANCE(40, Animation.create(7531), "This emote can be unlocked by playing a Christmas holiday event."), - AIR_GUITAR(41, Animation.create(2414), Graphics.create(1537), "This emote can be accessed by unlocking 200 pieces of music."), + AIR_GUITAR(41, Animation.create(2414), Graphics.create(1537), "This emote can be accessed by unlocking 200 pieces of music.") { + public void play(Player player) { + playJingle(player, 302); + super.play(player); + } + }, SAFETY_FIRST(42, Animation.create(8770), Graphics.create(1553), "You can't use this emote yet. Visit the Stronghold of Player safety to
unlock it."), EXPLORE(43, Animation.create(9990), Graphics.create(1734), "You can't use this emote yet. You must complete all the Lumbridge
and Draynor beginner tasks to unlock it."), TRICK(44, Animation.create(10530), Graphics.create(1863), "This emote can be unlocked by playing a Halloween holiday event."), @@ -174,7 +181,7 @@ public enum Emotes { GIVE_THANKS(46, "This emote can be unlocked by playing a Thanksgiving holiday event.") { @Override public void play(final Player player) { - GameWorld.getPulser().submit(new Pulse(1, player) { + GameWorld.getPulser().submit(new Pulse(1, player) { int counter; @Override @@ -291,7 +298,7 @@ public enum Emotes { } /** - * Handles the reward button for an emote. + * Handles the button for an emote. * * @param player the player. * @param buttonId the button id. @@ -415,4 +422,4 @@ public enum Emotes { return lockedMessage; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java b/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java index 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..41f7dfbf2 100644 --- a/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java +++ b/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java @@ -1,16 +1,21 @@ package core.game.node.entity.player.link.prayer; +import content.data.Quests; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillBonus; -import core.game.node.entity.skill.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 +23,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)), + THICK_SKIN(1, 12, 83, 5, PrayerCategory.BABY_BLUE, Sounds.THICK_SKIN_2690, new SkillBonus(Skills.DEFENCE, 0.05)), + BURST_OF_STRENGTH(4, 12, 84, 7, PrayerCategory.GREEN, Sounds.STRENGTH_BURST_2688, new SkillBonus(Skills.STRENGTH, 0.05)), + CLARITY_OF_THOUGHT(7, 12, 85, 9, PrayerCategory.PINK, Sounds.CLARITY_2664, new SkillBonus(Skills.ATTACK, 0.05)), + SHARP_EYE(8, 12, 862, 11, PrayerCategory.LIME_GREEN, Sounds.SHARP_EYE_2685, new SkillBonus(Skills.RANGE, 0.05)), + MYSTIC_WILL(9, 12, 863, 13, PrayerCategory.LIME_GREEN, Sounds.MYSTIC_WILL_2670, new SkillBonus(Skills.MAGIC, 0.05)), + ROCK_SKIN(10, 6, 86, 15, PrayerCategory.BABY_BLUE, Sounds.ROCK_SKIN_2684, new SkillBonus(Skills.DEFENCE, 0.1)), + SUPERHUMAN_STRENGTH(13, 6, 87, 17, PrayerCategory.GREEN, Sounds.SUPERHUMAN_STRENGTH_2689, new SkillBonus(Skills.STRENGTH, 0.1)), + IMPROVED_REFLEXES(16, 6, 88, 19, PrayerCategory.PINK, Sounds.IMPROVED_REFLEXES_2662, new SkillBonus(Skills.ATTACK, 0.1)), + RAPID_RESTORE(19, 26, 89, 21, PrayerCategory.PURPLE, Sounds.RAPID_RESTORE_2679), + RAPID_HEAL(22, 18, 90, 23, PrayerCategory.PURPLE, Sounds.RAPID_HEAL_2678), + PROTECT_ITEMS(25, 18, 91, 25, PrayerCategory.DARK_GREEN, Sounds.PROTECT_ITEMS_1982), + HAWK_EYE(26, 6, 864, 27, PrayerCategory.LIME_GREEN, Sounds.HAWK_EYE_2666, new SkillBonus(Skills.RANGE, 0.1)), + MYSTIC_LORE(27, 6, 865, 29, PrayerCategory.LIME_GREEN, Sounds.MYSTIC_2668, new SkillBonus(Skills.MAGIC, 0.1)), + STEEL_SKIN(28, 3, 92, 31, PrayerCategory.BABY_BLUE, Sounds.STEEL_SKIN_2687, new SkillBonus(Skills.DEFENCE, 0.15)), + ULTIMATE_STRENGTH(31, 3, 93, 33, PrayerCategory.GREEN, Sounds.ULTIMATE_STRENGTH_2691, new SkillBonus(Skills.STRENGTH, 0.15)), + INCREDIBLE_REFLEXES(34, 3, 94, 35, PrayerCategory.PINK, Sounds.INCREDIBLE_REFLEXES_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)); + 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, 4, 97, 41, PrayerCategory.LIGHT_BROWN, Sounds.PROTECT_FROM_MELEE_2676), + EAGLE_EYE(44, 3, 866, 43, PrayerCategory.LIME_GREEN, Sounds.EAGLE_EYE_2665, new SkillBonus(Skills.RANGE, 0.15)), + MYSTIC_MIGHT(45, 3, 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, 2, 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)), + PIETY(70, 2, 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)); /** * Represents the a cache of objects related to prayers in order to decide @@ -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 @@ -217,11 +222,20 @@ public enum PrayerType { * @return True if it is permitted. */ public boolean permitted(final Player player) { - if (player.getSkills().getStaticLevel(Skills.PRAYER) < getLevel() || player.getSkills().getStaticLevel(Skills.DEFENCE) < defenceReq) { - player.getAudioManager().send(2673); - player.getDialogueInterpreter().sendDialogue("You need a Prayer level of " + getLevel() + (player.getSkills().getStaticLevel(Skills.DEFENCE) < defenceReq ? (" and a Defence level of " + defenceReq) : "") + " to use " + StringUtils.formatDisplayName(name().toLowerCase().replace("_", " ")) + "."); + if (!hasLevelStat(player, Skills.PRAYER, level) || !hasLevelStat(player, Skills.DEFENCE, defenceReq)) { + sendDialogue(player, "You need a " + (!hasLevelStat(player, Skills.PRAYER, level) ? "Prayer level of " + level + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? " and a " : "") : "") + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? "Defence level of " + defenceReq : "") + " to use " + StringUtils.formatDisplayName(name().toLowerCase().replace("_", " ")) + "."); return false; } + + if (defenceReq == 65 || defenceReq == 70) { + if (!hasRequirement(player, Quests.KINGS_RANSOM)) { + return false; + } else { + if (getVarbit(player, 3909) != 8) { + setVarbit(player, 3909, 8, false); + } + } + } return true; } @@ -231,20 +245,18 @@ 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()); - - if (this == PrayerType.PIETY - && new ZoneBorders(2732, 3467, 2739, 3471, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 3); - } + playAudio(player, sound.id); + 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..e210f04c4 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; } /** @@ -278,17 +337,9 @@ public abstract class Quest implements Plugin { return questPoints; } - /** - * Gets the configs. - * @return the configs. - */ - public int[] getConfigs() { - return configs; - } - @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..22fab5af3 100644 --- a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java @@ -161,7 +161,8 @@ public final class TradeContainer extends Container { String targetMac = target.getDetails().getMacAddress(); String playerHost = player.getDetails().getCompName(); String targetHost = target.getDetails().getCompName(); - if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759) { + // Ironman trading exceptions for Shield of Arrav and Heroes Quest + if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759 || item.getId() == 1586 || item.getId() == 1577) { return true; } if (player.getIronmanManager().isIronman() || target != null && target.getIronmanManager().isIronman()) { diff --git a/Server/src/main/core/game/node/entity/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..06a978589 100644 --- a/Server/src/main/core/game/node/entity/skill/Skills.java +++ b/Server/src/main/core/game/node/entity/skill/Skills.java @@ -1,32 +1,32 @@ package core.game.node.entity.skill; -import content.global.skill.skillcapeperks.SkillcapePerks; -import core.game.event.DynamicSkillLevelChangeEvent; -import core.game.event.XPGainEvent; import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; import content.global.handlers.item.equipment.brawling_gloves.BrawlingGlovesManager; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.ServerConstants; +import core.game.event.DynamicSkillLevelChangeEvent; +import core.game.event.XPGainEvent; import core.game.node.entity.Entity; 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.info.PlayerMonitor; 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.game.world.GameWorld; +import core.game.world.repository.Repository; import core.net.packet.PacketRepository; import core.net.packet.context.SkillContext; import core.net.packet.out.SkillLevel; +import core.plugin.CorePluginTypes.XPGainPlugins; import kotlin.Pair; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import core.game.node.entity.player.info.PlayerMonitor; -import core.game.world.GameWorld; -import core.game.world.repository.Repository; -import core.plugin.CorePluginTypes.XPGainPlugins; import org.rs09.consts.Items; -import java.nio.ByteBuffer; import java.util.ArrayList; +import static core.api.ContentAPIKt.getWorldTicks; import static java.lang.Math.floor; import static java.lang.Math.max; @@ -39,7 +39,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 +105,6 @@ public final class Skills { */ private double experienceGained = 0; - /** - * The restoration pulses. - */ - private final SkillRestoration[] restoration; - /** * If a lifepoints update should occur. */ @@ -125,6 +120,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 +132,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 +161,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 +170,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 +252,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 +273,8 @@ public final class Skills { lastUpdateXp = this.experience.clone(); lastUpdate = GameWorld.getTicks(); } + lastTrainedSkill = slot; + lastXpGain = getWorldTicks(); } /** @@ -311,7 +287,7 @@ public final class Skills { private double getExperienceMod(int slot, double experience, boolean playerMod, boolean multiplyer) { //Keywords for people ctrl + Fing the project //xprate xp rate xp multiplier skilling rate - return experienceMutiplier; + return experienceMultiplier; /*if (!(entity instanceof Player)) { return 1.0; } @@ -385,46 +361,38 @@ public final class Skills { int staticLevel = getStaticLevel(i); setLevel(i, staticLevel); } - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(2674); - } rechargePrayerPoints(); } - /** - * Parses the skill data from the buffer. - * @param buffer The byte buffer. - */ - public void parse(ByteBuffer buffer) { - for (int i = 0; i < 24; i++) { - experience[i] = ((double) buffer.getInt() / 10D); - dynamicLevels[i] = buffer.get() & 0xFF; - if (i == HITPOINTS) { - lifepoints = dynamicLevels[i]; - } else if (i == PRAYER) { - prayerPoints = dynamicLevels[i]; - } - staticLevels[i] = buffer.get() & 0xFF; - } - experienceGained = buffer.getInt(); - } - public void parse(JSONArray skillData){ for(int i = 0; i < skillData.size(); i++){ JSONObject skill = (JSONObject) skillData.get(i); int id = Integer.parseInt( skill.get("id").toString()); - dynamicLevels[id] = Integer.parseInt( skill.get("dynamic").toString()); - if (id == HITPOINTS) { - lifepoints = dynamicLevels[i]; - } else if (id == PRAYER) { - prayerPoints = dynamicLevels[i]; - } - staticLevels[id] = Integer.parseInt( skill.get("static").toString()); + dynamicLevels[id] = Integer.parseInt(skill.get("dynamic").toString()); + staticLevels[id] = Integer.parseInt(skill.get("static").toString()); experience[id] = Double.parseDouble(skill.get("experience").toString()); + int version = entity instanceof Player ? entity.asPlayer().version : ServerConstants.CURRENT_SAVEFILE_VERSION; + if (i == HITPOINTS) { + if (version < 3 && !skill.containsKey("lifepoints")) { //!1881 + lifepoints = dynamicLevels[id]; + dynamicLevels[id] = staticLevels[id]; + } else { + lifepoints = Integer.parseInt(skill.get("lifepoints").toString()); + } + } + if (i == PRAYER) { + if (version < 3 && !skill.containsKey("prayerPoints")) { //!1881 + prayerPoints = dynamicLevels[id]; + dynamicLevels[id] = staticLevels[id]; + } else { + prayerPoints = Double.parseDouble(skill.get("prayerPoints").toString()); + } + } } } public void correct(double divisor){ + // XP squish for legacy x20 accounts for(int i = 0; i < staticLevels.length; i++){ experience[i] /= divisor; staticLevels[i] = getStaticLevelByExperience(i); @@ -436,40 +404,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 +588,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,21 +820,13 @@ 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. */ public int getMasteredSkills() { int count = 0; - for (int i = 0; i < 23; i++) { + for (int i = 0; i < staticLevels.length; i++) { if (getStaticLevel(i) >= 99) { count++; } 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..c95077106 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. @@ -127,6 +135,19 @@ public class Item extends Node{ return getId(); } + /** + * Converts noted items into unnoted items. + * + * @return The unnoted version of the item. Returns the original Item if already unnoted. + */ + public Item toUnnotedItem() { + if (definition.isUnnoted()) { + return this; + } else { + return new Item(definition.getNoteId(), getAmount(), getCharge()); + } + } + /** * @return the id */ @@ -188,14 +209,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 +258,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..9d8425993 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() } @@ -63,22 +62,23 @@ class Shop(val title: String, val stock: Array, val general: Boolean = fun showTab(player: Player, main: Boolean) { - val cont = if (main) getAttribute(player, "shop-cont", null) ?: return else playerStock + val mainCont = getAttribute(player, "shop-cont", null) ?: return + val cont = if (main) mainCont else playerStock if(!main) { - cont.listeners.remove(listenerInstances[player.details.uid]) + mainCont.listeners.remove(listenerInstances[player.details.uid]) playerStock.listeners.add(listenerInstances[player.details.uid]) } else { playerStock.listeners.remove(listenerInstances[player.details.uid]) - cont.listeners.add(listenerInstances[player.details.uid]) + mainCont.listeners.add(listenerInstances[player.details.uid]) } 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 +97,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 +135,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,11 +157,23 @@ 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) - else -> getGPCost(Item(item.id, 1), if (isMainStock) stock[item.slot].amount else playerStock[slot].amount, if (isMainStock) item.amount else playerStock[slot].amount) + 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 -> { + val fixedPrice = fixedPriceItems[item.id] + fixedPrice ?: getGPCost(Item(item.id, 1), + if (isMainStock) stock[item.slot].amount else playerStock[slot].amount, + if (isMainStock) item.amount else playerStock[slot].amount) + } } return Item(currency, price) @@ -170,6 +183,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,28 +200,32 @@ 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) - else -> getGPSell(Item(shopItemId, 1), stockAmt, currentAmt) - } + val price = when (currency) { + // selling items authentically return 10x less tokkul (floored/truncated) than the item's shop price + Items.TOKKUL_6529 -> (item.definition.getConfiguration(ItemConfigParser.TOKKUL_PRICE, 1) / 10.0).toInt() + Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, 1) + Items.CASTLE_WARS_TICKET_4067 -> item.definition.getConfiguration(ItemConfigParser.CASTLE_WARS_TICKET_PRICE, 1) + else -> { + val fixedPrice = fixedPriceItems[shopItemId] + fixedPrice ?: getGPSell(Item(shopItemId, 1), stockAmt, currentAmt) + } + } if(!general && stockAmt == 0 && shopSlot == -1) { @@ -223,9 +247,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 +292,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") @@ -278,19 +302,26 @@ class Shop(val title: String, val stock: Array, val general: Boolean = if(cost.id == -1) sendMessage(player, "This shop cannot sell that item.").also { return TransactionStatus.Failure("Shop cannot sell this item") } if(currency == Items.COINS_995){ - var amt = item.amount - var inStockAmt = inStock.amount - while(amt-- > 1) - cost.amount += getGPCost(Item(item.id, 1), if (isMainStock) stock[slot].amount else playerStock[slot].amount, --inStockAmt) + val fixedPrice = fixedPriceItems[item.id] + if (fixedPrice != null) { + // Fixed price items: simple multiplication + cost.amount = fixedPrice * item.amount + } else { + // Dynamic pricing: calculate cost for each item as stock depletes + var amt = item.amount + var inStockAmt = inStock.amount + while(amt-- > 1) + cost.amount += getGPCost(Item(item.id, 1), if (isMainStock) stock[slot].amount else playerStock[slot].amount, --inStockAmt) + } } else { - cost.amount = cost.amount * item.amount + 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 +352,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 +387,18 @@ 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) - if(currency == Items.COINS_995 && item.amount > 1){ - val id = if(!item.definition.isUnnoted) item.noteChange else item.id - val (isPlayerStock, shopSlot) = getStockSlot(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.") + } + + val fixedPrice = fixedPriceItems[id] + if (fixedPrice != null) { + profit.amount = fixedPrice * item.amount + } else if(currency == Items.COINS_995 && item.amount > 1){ var amt = item.amount var inStockAmt = container!![shopSlot]?.amount ?: playerStock.getAmount(id) while(amt-- > 1) @@ -424,14 +465,20 @@ class Shop(val title: String, val stock: Array, val general: Boolean = return Pair(isPlayerStock, shopSlot) } - companion object { - //General stores globally share player stock (weird quirk, right?) - val generalPlayerStock = Container(40, ContainerType.SHOP) - val listenerInstances = HashMap() - } + companion object { + //General stores globally share player stock (weird quirk, right?) + val generalPlayerStock = Container(40, ContainerType.SHOP) + val listenerInstances = HashMap() + + // Items with fixed prices that don't fluctuate based on stock (both buy & sell price) + val fixedPriceItems = mapOf( + Items.SPIRIT_SHARDS_12183 to 25, + Items.POUCH_12155 to 1 + ) + } sealed class TransactionStatus { class Success : TransactionStatus() 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..b79b354f2 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,26 @@ 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 { + val restockRate = tokens.getOrNull(2)?.toIntOrNull() ?: 100 + items.add(ShopItem(item, amount.toInt(), restockRate)) + 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 +100,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 +113,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 +141,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 +151,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 +192,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 +211,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 +238,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 +261,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) } @@ -248,15 +284,15 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe } override fun defineCommands() { - define("openshop", Privilege.ADMIN) { player, args -> + define("openshop", Privilege.ADMIN, "::openshop shop-id", "Opens the shop interface for shop-id.") { player, args -> if(args.size < 2) reject(player, "Usage: ::openshop shopId") val shopId = args[1].toInt() shopsById[shopId]?.openFor(player) } - define("shopscript") { player, args -> + define("shopscript", usage = "::shopscript object", description = "Runs CS2 script 25 with string \"vg\" and objects \"object, 92\"") { player, args -> val arg1 = args[1].toInt() 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..813b16fe3 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,18 @@ 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..."); + if (Server.getWebSocketServer() != null) { + Server.getWebSocketServer().stop(1000, "Server shutting down"); + Server.setWebSocketServer(null); + } + log(this.getClass(), Log.INFO, "Stopping all pulses..."); GameWorld.getMajorUpdateWorker().stop(); for (Iterator it = Repository.getPlayers().iterator(); it.hasNext();) { try { @@ -72,7 +79,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 +88,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 +101,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/Command.kt b/Server/src/main/core/game/system/command/Command.kt index fdbc56d90..c76e42a61 100644 --- a/Server/src/main/core/game/system/command/Command.kt +++ b/Server/src/main/core/game/system/command/Command.kt @@ -2,18 +2,28 @@ package core.game.system.command import core.game.node.entity.player.Player import core.ServerConstants +import core.api.sendChat +import core.game.node.entity.player.info.Rights import core.game.world.GameWorld import kotlin.collections.ArrayList /** * Base class for Commands in the new system. Can pass a lambda as part of the constructor or after the constructor. - * @author Ceikry + * @author Ceikry, Player Name */ class Command(val name: String, val privilege: Privilege, val usage: String = "UNDOCUMENTED", val description: String = "UNDOCUMENTED", val handle: (Player, Array) -> Unit) { fun attemptHandling(player: Player, args: Array?){ args ?: return - if(player.rights.ordinal >= privilege.ordinal || GameWorld.settings?.isDevMode == true || ServerConstants.I_AM_A_CHEATER){ + val hasRights = player.rights == Rights.ADMINISTRATOR || (ServerConstants.PLAYER_COMMANDS && player.rights.ordinal >= privilege.ordinal) + val isDev = GameWorld.settings?.isDevMode == true + val isCheater = ServerConstants.I_AM_A_CHEATER + if (hasRights || isDev || isCheater) { handle(player,args) + } else if (ServerConstants.PLAYER_COMMANDS || args[0] == "bank") { + // Authentically, if you attempt to use a command you shouldn't have access to, the game is supposed to say the below line. + // We do this for all our custom commands if they are enabled (in which case this will only trigger for commands above your privilege level), + // otherwise we only do it for the one command we have evidence existed authentically, "bank" (https://www.reddit.com/r/2007scape/comments/3n1c4d/hey_everyone_i_just_tried_to_do_something_very/) + sendChat(player, "Hey, everyone, I just tried to do something very silly!") } } } 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..17d98c883 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) @@ -189,7 +197,7 @@ class VisualCommand : CommandPlugin() { return true } location = if (args.size > 2) Location.create(args[1]!!.toInt(), args[2]!!.toInt(), player!!.location.z) else player!!.location - `object` = RegionManager.getObject(location) + `object` = RegionManager.getObject(location!!) if (`object` == null) { player!!.debug("error: object not found in region cache.") return true @@ -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..d06258333 100644 --- a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -1,5 +1,10 @@ package core.game.system.command.sets +import core.api.animate +import core.api.delayScript +import core.api.queueScript +import core.api.stopExecuting +import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation @@ -27,6 +32,28 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { player.animate(animation) } + define("anims", Privilege.ADMIN, "::anims <(opt) Duration Per Animation>", "Plays animations from the From ID to the To ID, with a delay of 3 between animations, unless specified otherwise"){ player, args -> + + if (args.size < 3) { + reject(player, "Syntax error: ::anims <(opt) Duration Per Animation>") + } + val animationFrom = args[1].toInt() + val animationTo = args[2].toInt() + val animationDelay = (args.getOrNull(3) ?: "3").toInt() + + queueScript(player, 1, QueueStrength.STRONG) { stage: Int -> + val animationId = animationFrom + stage + animate(player, animationId, true) + notify(player, "Playing animation $animationId") + + if (animationId == animationTo) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayScript(player, animationDelay) + } + } + /** * Force the player to loop animation */ @@ -56,11 +83,23 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { if (args.size < 2) { reject(player, "Syntax error: ::ranim ") } - try { - player.appearance.setAnimations(Animation.create(args[1].toInt())) - player.appearance.sync() - } catch (e: NumberFormatException) { - reject(player, "Syntax error: ::ranim ") + if (args.size > 2) { + GameWorld.Pulser.submit(object : Pulse(3, player) { + var id = args[1].toInt() + override fun pulse(): Boolean { + player.appearance.setAnimations(Animation.create(id)) + player.appearance.sync() + player.sendChat("Current: $id") + return ++id >= args[2].toInt() + } + }) + } else { + try { + player.appearance.setAnimations(Animation.create(args[1].toInt())) + player.appearance.sync() + } catch (e: NumberFormatException) { + reject(player, "Syntax error: ::ranim ") + } } } diff --git a/Server/src/main/core/game/system/command/sets/AudioCommandSet.kt b/Server/src/main/core/game/system/command/sets/AudioCommandSet.kt new file mode 100644 index 000000000..70e797b0c --- /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", usage = "::playsong song-id", description = "Plays the music track song-id."){player,args -> + if(args.size < 2){ + reject(player,"Usage: ::playsong ") + } + 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", usage = "::playjingle jingle-id", description = "Plays the sound effect with ID jingle-id."){player,args -> + if(args.size < 2){ + reject(player,"Usage: ::playjingle ") + } + 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", usage = "::playid song-id", description = "Plays a music track by musicId instead of numeric music track ID."){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, description = "Unlocks every music track for you."){ 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. Loopable with optional integer argument.") { player, args -> + if (args.size < 2 || args.size > 3) + reject(player, "Usage: ::audio 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..825dd326d 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,8 @@ 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.* +import core.game.interaction.QueueStrength @Initializable class ConfigCommandSet : CommandSet(Privilege.ADMIN){ @@ -11,14 +13,14 @@ class ConfigCommandSet : CommandSet(Privilege.ADMIN){ /** * Sets a range of configs to a maximum value */ - define("sconfigrange"){player, args -> + define("sconfigrange", usage = "::sconfigrange start-id end-id", description = "Sets each varp from start-id (inclusive) up to end-id (exclusive) to Integer.MAX_VALUE."){player, args -> if (args.size < 3) { reject(player, "usage: sconfigrange idlo idhi") } 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) } } @@ -26,14 +28,14 @@ class ConfigCommandSet : CommandSet(Privilege.ADMIN){ /** * Sets a range of configs to 0 */ - define("sconfigrange0"){player, args -> + define("sconfigrange0", usage = "::sconfigrange0 start-id end-id", description = "Sets each varp from start-id (inclusive) up to end-id (exclusive) to 0."){player, args -> if (args.size < 3) { reject(player, "usage: sconfigrange0 idlo idhi") } 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") } } @@ -43,11 +45,33 @@ class ConfigCommandSet : CommandSet(Privilege.ADMIN){ */ define("iface", Privilege.ADMIN, "::iface Interface ID", "Opens the interface with the given ID."){player, args -> if (args.size < 2) { - reject(player, "usage: iface id") + reject(player, "usage: ::iface ") return@define } val id = args[1].toIntOrNull() ?: reject(player, "INVALID INTERFACE ID") player.interfaceManager.openComponent(id as Int) } + + define("ifaces", Privilege.ADMIN, "::ifaces <(opt) Duration Per Interface>", "Opens interfaces from the From ID to the To ID, with a delay of 3 between each, unless specified otherwise"){ player, args -> + + if (args.size < 3) { + reject(player, "Syntax error: ::anims <(opt) Duration Per Interface>") + } + val ifaceFrom = args[1].toInt() + val ifaceTo = args[2].toInt() + val ifaceDelay = (args.getOrNull(3) ?: "3").toInt() + + queueScript(player, 1, QueueStrength.WEAK) { stage: Int -> + val ifaceId = ifaceFrom + stage + player.interfaceManager.openComponent(ifaceId) + notify(player, "Opening interface $ifaceId") + + if (ifaceId == ifaceTo) { + return@queueScript stopExecuting(player) + } + + return@queueScript delayScript(player, ifaceDelay) + } + } } } 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..25ef32fef 100644 --- a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt @@ -1,12 +1,16 @@ package core.game.system.command.sets -import core.api.sendMessage +import content.global.activity.jobs.JobManager +import content.global.skill.construction.decoration.pohstorage.Storable +import content.global.skill.construction.decoration.pohstorage.StorableFamily +import core.api.* import core.cache.Cache import core.cache.def.impl.DataMap import core.cache.def.impl.NPCDefinition import core.cache.def.impl.VarbitDefinition import core.cache.def.impl.Struct import core.game.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.SpellBookManager import core.game.node.entity.player.link.diary.DiaryType @@ -17,29 +21,120 @@ 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.map.RegionManager.getLocalEntitys +import core.game.world.repository.Repository +import org.json.simple.JSONArray +import kotlin.collections.set +import content.global.activity.penguinhns.Penguin +import content.global.activity.penguinhns.PenguinHNSEvent +import content.global.activity.penguinhns.PenguinManager +import content.global.activity.penguinhns.PenguinManager.Companion.penguins +import content.global.activity.penguinhns.PenguinManager.Companion.spawner +import content.global.activity.penguinhns.PenguinManager.Companion.tagMapping +import content.global.activity.penguinhns.PenguinManager.Companion.updateStoreFile +import core.ServerStore.Companion.toJSONArray +import org.rs09.consts.NPCs @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("cleardiary", Privilege.ADMIN) { player, _ -> + /** + * Adds a subset of POH storable item takeIds to the player's bank, filtered by StorableFamily type. These are stored in the costume room or bookcase. + * Usage: ::pohstorableitems [TYPE] + * Example: ::pohstorableitems BOOKCASE + * Example: ::pohstorableitems TREASURE_CHEST_MED + */ + define("pohstorableitems", Privilege.ADMIN, "::pohstorableitems TYPE", "Adds POH storable items to bank. To see TYPEs, use the command with no argument.") { player, args -> + // Check for the argument (StorableFamily type) + val typeArg = if (args.size > 1) args[1].uppercase() else null + + val allStorables = Storable.values() + + // Filter the list of items based on the provided argument + val itemsToProcess = if (typeArg != null) { + try { + val targetFamily = StorableFamily.valueOf(typeArg) + allStorables.filter { it.type == targetFamily } + } catch (e: IllegalArgumentException) { + sendMessage(player, "Error: Unknown StorableFamily type '$typeArg'. Valid types are:") + StorableFamily.values().forEach { family -> + sendMessage(player, family.toString()) + } + return@define + } + } else { + // If no argument is provided, default to all storables + sendMessage(player, "Valid types are:") + StorableFamily.values().forEach { family -> + sendMessage(player, family.toString()) + } + return@define + } + + if (itemsToProcess.isEmpty()) { + sendMessage(player, "No storable items found for type: '$typeArg'.") + return@define + } + + var totalItemsAdded = 0 + + // Process the filtered list of storables + itemsToProcess.forEach { storable -> + storable.takeIds.forEach { itemId -> + if (player.bank.add(Item(itemId))) { + totalItemsAdded++ + } + } + } + return@define + } + + 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, description = "Resets all achievement diary progress.") { player, _ -> for (type in DiaryType.values()) { val diary = player.achievementDiaryManager.getDiary(type) for (level in 0 until diary.levelStarted.size) { @@ -51,6 +146,15 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { sendMessage(player, "All achievement diaries cleared successfully.") } + define("clearjob", Privilege.ADMIN, description = "Clears your current job assignment.") { 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}") } @@ -64,37 +168,38 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { player.spellBookManager.update(player) } - define("killme", Privilege.ADMIN, "", "Does exactly what it says on the tin.") { player, _ -> + define("killme", Privilege.ADMIN, description = "Literally kills you in-game even") { player, _ -> player.impactHandler.manualHit(player, player.skills.lifepoints, HitsplatType.NORMAL) } - define("struct") {player, args -> + define("struct", usage = "::struct struct-id", description = "Logs the struct definition for the given cache id.") {player, args -> 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 -> + define("datamap", usage = "::datamap data-map-id", description = "Logs the data map definition for the given cache id.") {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, _ -> val dump = File("structs.txt") val writer = BufferedWriter(FileWriter(dump)) val index = Cache.getIndexes()[2] - val containers = index.information.containers[26].filesIndexes - for(fID in containers) - { - val file = index.getFileData(26, fID) - if(file != null){ - val def = Struct.parse(fID, file) - if(def.dataStore.isEmpty()) continue //no data in struct. - writer.write(def.toString()) - writer.newLine() + val containers = index.information.containers[26]!!.filesIndexes + if (containers != null) { + for(fID in containers) { + val file = index.getFileData(26, fID) + if(file != null){ + val def = Struct.parse(fID, file) + if(def.dataStore.isEmpty()) continue //no data in struct. + writer.write(def.toString()) + writer.newLine() + } } } writer.flush() @@ -110,15 +215,16 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { for(cID in containers) { - val fileIndexes = index.information.containers[cID].filesIndexes - for(fID in fileIndexes) - { - val file = index.getFileData(cID, fID) - if(file != null){ - val def = DataMap.parse((cID shl 8) or fID, file) - if(def.keyType == '?') continue //Empty definition - only a 0 present in the cachefile data. - writer.write(def.toString()) - writer.newLine() + val fileIndexes = index.information.containers[cID]!!.filesIndexes + if (fileIndexes != null) { + for(fID in fileIndexes) { + val file = index.getFileData(cID, fID) + if(file != null){ + val def = DataMap.parse((cID shl 8) or fID, file) + if(def.keyType == '?') continue //Empty definition - only a 0 present in the cachefile data. + writer.write(def.toString()) + writer.newLine() + } } } } @@ -126,23 +232,35 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { writer.close() } - define("rolldrops", Privilege.ADMIN, "::rolldrops NPC ID AMOUNT", "Rolls the given NPC drop table AMOUNT times.") { player: Player, args: Array -> - if(args.size < 2){ - reject(player,"Usage: ::rolldrops npcid amount") + define( + "rolldrops", + Privilege.ADMIN, + "::rolldrops NPC ID AMOUNT [true|false]", + "Rolls the given NPC drop table AMOUNT times. Add true to persist the items in your bank." + ) { player: Player, args: Array -> + if (args.size < 3) { + reject(player, "Usage: ::rolldrops npcid amount [true|false]") } - val container = player.dropLog val npcId = args[1].toInt() val amount = args[2].toInt() + val container = when (args.getOrNull(3)) { + "true" -> player.bank + "false", null -> { + player.dropLog.clear() + player.dropLog + } - container.clear() + else -> { + reject(player, "Usage: ::rolldrops npcid amount [true|false]") + return@define + } - 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) } @@ -165,8 +283,230 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { } } - define("testpacket") { player, _ -> + define("testpacket", description = "Sends the reset-interface packet to the client.") { player, _ -> PacketWriteQueue.write(ResetInterface(), PlayerContext(player)) } - } -} \ No newline at end of file + + 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 1000 of each rune type. We love casting spells" ) { 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, description = "Marks your current tile as the force-movement start point.") {player, _ -> + setAttribute(player, "fmstart", Location.create(player.location)) + } + + define ("fmend", Privilege.ADMIN, description = "Marks your current tile as the force-movement destination.") {player, _ -> + setAttribute(player, "fmend", Location.create(player.location)) + } + + define ("fmspeed", Privilege.ADMIN, "::fmspeed start-speed", "Sets the starting force-movement speed (client cycles between steps) used by ::testfm.") {player, args -> + setAttribute(player, "fmspeed", args[1].toIntOrNull() ?: 10) + } + + define ("fmspeedend", Privilege.ADMIN, "::fmspeedend end-speed", "Sets the ending speed (client cycles between steps) used by ::testfm.") {player, args -> + setAttribute(player, "fmspeedend", args[1].toIntOrNull() ?: 10) + } + + define("testfm", Privilege.ADMIN, description = "Runs the configured force-movement from the saved start to end point.") { 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, "::fmanim animation-id", "Sets the animation id to play during ::testfm.") {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[optional]", "Sets your (or player_names) 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", "Sets all skills to 99s for 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!!) + } + + define("renewpenguins", Privilege.ADMIN, "", "Generates a fresh set of weekly penguins") { player, _ -> + val spawnedOrdinals = (PenguinHNSEvent.getStoreFile()["spawned-penguins"] as JSONArray).map { it.toString().toInt() } + val penguinNPCs = arrayListOf(NPCs.BARREL_8104, NPCs.BUSH_8105,NPCs.CACTUS_8107,NPCs.CRATE_8108,NPCs.ROCK_8109,NPCs.TOADSTOOL_8110) + + spawnedOrdinals.forEach { + val peng = Penguin.values()[it] + val nearNPCs = getLocalEntitys(peng.location,1) + nearNPCs.forEach { npc -> + if (npc.id in penguinNPCs) { + poofClear(npc as NPC) + } + } + } + penguins = spawner.spawnPenguins(10) + PenguinHNSEvent.getStoreFile()["spawned-penguins"] = penguins.toJSONArray() + tagMapping.clear() + for (p in penguins) { + tagMapping.put(p, JSONArray()) + val pengCoord = Penguin.values()[p].location + player.debug("Penguin spawned at:$pengCoord") + } + updateStoreFile() + player.debug("Penguin positions have been renewed") + } + + define("spawnpenguin",Privilege.ADMIN,"::spawnPenguin Ordinal","Adds a new Penguin spawn to this weeks list based on the ordinal provided 0-64"){player,args-> + if (args.size!=2) reject (player,"Usage: ::spawnpenguin Ordinal") + val ordinal = args[1].toIntOrNull() + if (ordinal == null) reject(player,"Ordinal must be an integer.") + if (ordinal!! > 64 || ordinal < 0) reject(player,"Ordinal must be in the range 0-64 inclusive.") + + val store = PenguinHNSEvent.getStoreFile() + val ordinals = (store["spawned-penguins"] as? JSONArray) + ?.map { it.toString().toInt() } + ?.toMutableList() + ?: mutableListOf() + val peng = Penguin.values()[ordinal] + NPC(peng.id,peng.location) + .also { PenguinManager.npcs.add(it); it.isNeverWalks = true; it.isWalks = false }.init() + tagMapping.clear() + for (p in ordinals) { + tagMapping.put(p, JSONArray()) + } + updateStoreFile() + val pengCoords = peng.location + player.debug("Penguin spawned at:$pengCoords") + } + + } +} 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..05b59c153 100644 --- a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt @@ -1,48 +1,120 @@ 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.ServerStore +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.node.entity.player.info.login.PlayerSaver +import core.game.node.item.Item +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.RandomFunction +import core.tools.Log +import org.json.simple.JSONArray 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.world.GameWorld -import core.tools.END_DIALOGUE +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) { var npcs: List = ArrayList() + private val TREASURE_KEY = "fun_command_buried_treasures" + private val treasures = ArrayList() + + init { + loadTreasures() + } + + /** + * I hope ceikry doesn't punish me for this ~ used SkillcapePerks for a base idea tried to make it modular. + * Supports persistence across reboots for bury, buryitem, and burymessage supporting all command arguments. + */ + private fun loadTreasures() { + + val archive = ServerStore.getArchive(TREASURE_KEY) + if (!archive.containsKey("data")) return + + try { + val array = archive["data"] as JSONArray + + for (obj in array) { + + val json = obj as JSONObject + val x = (json["x"] as Long).toInt() + val y = (json["y"] as Long).toInt() + val z = (json["z"] as Long).toInt() + val loc = Location.create(x, y, z) + + val type = TreasureType.valueOf(json["type"] as String) + val hiderName = json["hiderName"] as? String ?: "Unknown" + val hideName = json["hideName"] as? Boolean ?: false + val customMessage = json["customMessage"] as? String + val message = json["message"] as? String ?: "" + + val items = ArrayList() + if (json.containsKey("items")) { + val itemsJson = json["items"] as JSONArray + for (i in itemsJson) { + val itemObj = i as JSONObject + val id = (itemObj["id"] as Long).toInt() + val amount = (itemObj["amount"] as Long).toInt() + items.add(Item(id, amount)) + } + } + + val treasure = BuriedTreasure(loc, type, items, message, hiderName, hideName, customMessage) + treasures.add(treasure) + registerTreasureListener(treasure) + } + log(FunCommandSet::class.java, Log.DEBUG, "Loaded ${treasures.size} buried treasures.") + } catch (e: Exception) { + log(FunCommandSet::class.java, Log.ERR, "Failed to load buried treasures: ${e.message}") + e.printStackTrace() + } + } override fun defineCommands() { /** * 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 [end-animation-id]", "Makes nearby NPCs play animation-id, or loop that id up to 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 +175,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 } }) } @@ -124,16 +207,16 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { /** * Opens up the makeover interface */ - define("makeover", Privilege.MODERATOR){ player, _ -> + define("makeover", Privilege.MODERATOR, description = "Opens the makeover interface for your character."){ player, _ -> CharacterDesign.open(player) } /** * Copies your current appearance and equipment as JSON to the clipboard */ - define("dumpappearance", Privilege.MODERATOR){ player, _ -> + define("dumpappearance", Privilege.MODERATOR, description = "Copies your appearance and equipment as JSON to the clipboard."){ 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() @@ -149,40 +232,275 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { /** * Bury inventory at current location */ - define("bury"){player, _ -> + define("bury", description = "Buries your entire inventory at your current location for treasure hunters. Required true/false to hide players name and an optional custom message."){player, args -> if(player.inventory.isEmpty){ reject(player, "You have no items to bury.") } + if (args.size < 2) { + reject(player, "Usage: ::bury true | false [custom news message]") + return@define + } + + val hideName = when(args[1].lowercase()) { + "true" -> true + "false" -> false + else -> { + reject(player, "Invalid argument. Usage: ::bury true | false [custom news message]") + return@define + } + } + + val customMessage = if (args.size > 2) { + args.slice(2 until args.size).joinToString(" ") + } else { + null + } + player.dialogueInterpreter.open(object : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { when(stage){ - 0 -> dialogue("This will bury your whole inventory in this spot.","Are you sure?").also { stage++ } + 0 -> dialogue("This will bury your whole inventory at this spot.","Are you sure?").also { stage++ } 1 -> options("Yes","No").also { stage++ } 2 -> when(buttonID){ - 1 -> bury(player).also { end() } - 2 -> stage = END_DIALOGUE + 1 -> bury(player, hideName, customMessage).also { end() } + 2 -> end() } } } }) } + + /** + * Bury a secret message at current location for a player to find. + */ + define("burymessage", description = "Buries a secret message at your current location."){ player, args -> + if (args.size < 2) { + reject(player, "Usage: ::burymessage [message]") + return@define + } + + val message = args.slice(1 until args.size).joinToString(" ") + val loc = Location.create(player.location) + + if(SpadeDigListener.listeners.containsKey(loc)){ + reject(player, "There is already something buried here.") + return@define + } + + val treasure = BuriedTreasure(loc, TreasureType.MESSAGE, emptyList(), message, player.username, false, null) + treasures.add(treasure) + saveTreasures() + registerTreasureListener(treasure) + + notify(player, "You have buried the message '$message' at $loc") + log(FunCommandSet::class.java, Log.DEBUG, "Message buried by ${player.username} at $loc: \"$message\"") + } + + /** + * Bury a specific item at current location + */ + define("buryitem", description = "Buries a specific item at your current location."){ player, args -> + if (args.size < 3) { + reject(player, "Usage: ::buryitem item id true | false [custom message]") + return@define + } + + val itemId = args[1].toIntOrNull() + if (itemId == null) { + reject(player, "Invalid item ID.") + return@define + } + + val hideName = when(args[2].lowercase()) { + "true" -> true + "false" -> false + else -> { + reject(player, "Invalid argument. Usage: ::buryitem item id true | false [custom message]") + return@define + } + } + + val customMessage = if (args.size > 3) { + args.slice(3 until args.size).joinToString(" ") + } else { + null + } + + player.dialogueInterpreter.open(object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> dialogue("This will bury a ${Item(itemId).name} at this spot.","Are you sure?").also { stage++ } + 1 -> options("Yes","No").also { stage++ } + 2 -> when(buttonID){ + 1 -> buryItem(player, itemId, hideName, customMessage).also { end() } + 2 -> end() + } + } + } + }) + + + } + + 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 Ice 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)) + } + } + + /** + * Toggles pet hunger to off, normal, or dev + * 0 = No hunger/growth mode (hunger/growth does not progress) + * 1 = Normal hunger/growth mode (1x speed) + * 2 = Dev hunger/growth mode (100x speed) + */ + define("petrate", Privilege.ADMIN, "::petrate 0-2", "Sets pet hunger and growth to off, normal, or dev."){ player, args -> + if(args.size < 2) { + notify(player, "Pet mode is currently ${player.getAttribute("petrate", 1)}") + return@define + } + val mode = args[1].toIntOrNull() ?: (-1).also { reject(player, "Please enter a valid number") } + if (mode in 0 .. 2) { + player.setAttribute("petrate", mode) + notify(player, "Setting pet mode to $mode") + } + else { + reject(player, "Only modes 0-2 are valid") + } + } + + /** + * Toggle instant woodcutting. + */ + define("instachop", Privilege.ADMIN, "", "Fells trees after a single log is gathered."){ player, _ -> + player.setAttribute("instachop", !player.getAttribute("instachop", false)) + notify(player,"Instachop mode " + if (player.getAttribute("instachop", false)) "on." else "off.") + } + } - fun bury(player: Player){ + fun bury(player: Player, hideName: Boolean = false, customMessage: String? = null){ + val loc = Location.create(player.location) - val inv = player.inventory.toArray().filterNotNull() - SpadeDigListener.registerListener(player.location){ p -> - for(item in inv){ - addItemOrDrop(p, item.id, item.amount) - sendMessage(p, "You dig and find ${if(item.amount > 1) "some" else "a"} ${item.name}") - } - sendNews("${player.username} has found the hidden treasure! Congratulations!!!") - SpadeDigListener.listeners.remove(loc) + if(SpadeDigListener.listeners.containsKey(loc)){ + notify(player, "There is already something buried here.") + return } + + val inv = player.inventory.toArray().filterNotNull().toList() + if (inv.isEmpty()) { + notify(player, "You have nothing to bury.") + return + } + + val treasure = BuriedTreasure(loc, TreasureType.ITEM, inv, "", player.username, hideName, customMessage) + treasures.add(treasure) + saveTreasures() + registerTreasureListener(treasure) + player.inventory.clear() notify(player, "You have buried your loot at ${loc.toString()}") + log(FunCommandSet::class.java, Log.DEBUG, "Loot buried by ${player.username} at $loc. Masked: $hideName") } + fun buryItem(player: Player, itemId: Int, hideName: Boolean, customMessage: String? = null) { + val loc = Location.create(player.location) + if(SpadeDigListener.listeners.containsKey(loc)){ + notify(player, "There is already something buried here.") + return + } + + val treasure = BuriedTreasure(loc, TreasureType.ITEM, listOf(Item(itemId, 1)), "", player.username, hideName, customMessage) + treasures.add(treasure) + saveTreasures() + registerTreasureListener(treasure) + + notify(player, "You have buried item ${Item(itemId).name} at $loc") + log(FunCommandSet::class.java, Log.DEBUG, "Item $itemId buried by ${player.username} at $loc. Masked: $hideName") + } + + private fun registerTreasureListener(t: BuriedTreasure) { + + SpadeDigListener.registerListener(t.location) { p -> + log(FunCommandSet::class.java, Log.DEBUG, "SpadeDigListener triggered for ${p.username} at ${t.location}") + + if (t.type == TreasureType.ITEM) { + for(item in t.items){ + addItemOrDrop(p, item.id, item.amount) + sendMessage(p, "You dig and find ${if(item.amount > 1) "some" else "a"} ${item.name}") + } + val finderName = if (t.hideName) "Someone" else p.username + val message = t.customMessage ?: "$finderName has found the hidden treasure! Congratulations!!!" + sendNews(message) + } else { + sendMessage(p, t.message) + } + + SpadeDigListener.listeners.remove(t.location) + treasures.remove(t) + saveTreasures() + } + } + + private fun saveTreasures() { + + val array = JSONArray() + + for (t in treasures) { + val json = JSONObject() + json["x"] = t.location.x + json["y"] = t.location.y + json["z"] = t.location.z + json["type"] = t.type.name + json["hiderName"] = t.hiderName + json["hideName"] = t.hideName + if (t.customMessage != null) json["customMessage"] = t.customMessage + json["message"] = t.message + + val itemsArray = JSONArray() + for (i in t.items) { + val itemObj = JSONObject() + itemObj["id"] = i.id + itemObj["amount"] = i.amount + itemsArray.add(itemObj) + } + json["items"] = itemsArray + + array.add(json) + } + val archive = ServerStore.getArchive(TREASURE_KEY) + archive["data"] = array + } + + private data class BuriedTreasure( + val location: Location, + val type: TreasureType, + val items: List, + val message: String, + val hiderName: String, + val hideName: Boolean, + val customMessage: String? + ) + + private enum class TreasureType { ITEM, MESSAGE } + } 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..0727f65db 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,67 @@ package core.game.system.command.sets +import content.global.handlers.iface.* +import content.global.skill.summoning.familiar.Familiar +import content.global.skill.cooking.fermenting.BrewGrowth +import content.global.skill.farming.timers.* +import content.minigame.fishingtrawler.TrawlerLoot +import content.region.misthalin.draynor.quest.anma.AnmaCutscene +import core.ServerConstants import core.api.* -import core.api.InputType +import core.api.utils.permadeath import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.VarbitDefinition +import core.game.bots.AIRepository import core.game.component.Component +import core.game.ge.GrandExchange import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.node.scenery.Scenery +import core.game.system.command.Command +import core.game.system.command.CommandMapping +import core.game.system.command.Privilege import core.game.system.communication.CommunicationInfo import core.game.world.map.RegionManager +import core.game.world.repository.GE_NEWS_MUTE_ATTRIBUTE import core.game.world.map.build.DynamicRegion +import core.game.world.repository.Repository +import core.game.node.entity.combat.equipment.WeaponInterface 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 org.rs09.consts.Items import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection +private const val INFINITE_SPECIAL_ATTRIBUTE = "infinite-special" +private const val ALLOW_ADMIN_AGGRESSION_ATTRIBUTE = "/save:allow_admin_aggression" + +private fun resolveBooleanToggle(player: Player, args: Array, attribute: String): Boolean? { + return when { + args.size < 2 -> !player.getAttribute(attribute, false) + args[1].equals("true", ignoreCase = true) -> true + args[1].equals("false", ignoreCase = true) -> false + else -> null + } +} + +private fun setBooleanAttribute(player: Player, attribute: String, enabled: Boolean, label: String) { + if (enabled) { + setAttribute(player, attribute, true) + } else { + removeAttribute(player, attribute) + } + sendMessage(player, "$label is now ${if (enabled) "enabled" else "disabled"}.") +} + @Initializable class MiscCommandSet : CommandSet(Privilege.ADMIN){ override fun defineCommands() { @@ -49,10 +73,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 +85,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, _ -> + define("anmacs", Privilege.ADMIN, description = "Starts the AnmaCutscene sequence for testing.") { player, _ -> AnmaCutscene(player).start() } + define("setsaveversion", Privilege.ADMIN, "::setsaveversion version", "Overrides your save version to version so migration logic can run.") { 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 +122,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 @@ -145,17 +149,69 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player, "Do you mean ::loc?") } - define("calcmaxhit", Privilege.STANDARD, "", "Calculates and shows you your current max hit.") { player, _ -> + define("calcmaxhit", Privilege.STANDARD, description = "Calculates and prints your current maximum 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()})") + } + + define("calcdefence", Privilege.STANDARD, "::calcdefence NPC ID", "Calculates and prints your current raw defence values for Melee, Ranged and Magic against a given NPC.") { player, args -> + val meleeHandler = core.game.node.entity.combat.MeleeSwingHandler() + val rangeHandler = core.game.node.entity.combat.RangeSwingHandler() + val magicHandler = core.game.node.entity.combat.MagicSwingHandler() + var attacker: core.game.node.entity.Entity = player + if (args.size > 1) { + val npcId = args[1].toInt() + val npc = NPC(npcId) + npc.initConfig() + attacker = npc + notify(player, "Calculating defence against NPC: ${npc.name} (ID=$npcId)") + val bonuses = npc.properties.bonuses + var maxBonus = Int.MIN_VALUE + var maxIndex = 0 + for (i in 0..4) { + if (i in bonuses.indices) { + val v = bonuses[i] + if (v > maxBonus) { + maxBonus = v + maxIndex = i + } + } + } + if (maxBonus == Int.MIN_VALUE) { + maxBonus = 0 + maxIndex = 0 + } + npc.properties.attackStyle = WeaponInterface.AttackStyle(WeaponInterface.STYLE_CONTROLLED,maxIndex) + } else { + notify(player, "Calculating defence against yourself (no NPC specified).") + } + fun hitChance(accuracy: Int, defence: Int): Double { + return if (accuracy > defence) { + 1.0 - ((defence + 2.0) / (2.0 * (accuracy + 1.0))) + } else { + accuracy / (2.0 * (defence + 1.0)) + } + } + val meleeAccuracy = meleeHandler.calculateAccuracy(attacker) + val meleeDefence = meleeHandler.calculateDefence(player, attacker) + val meleeChance = hitChance(meleeAccuracy, meleeDefence) + notify(player, "Your melee defence: $meleeDefence; Attacker accuracy: $meleeAccuracy; Chance to be hit: $meleeChance") + val rangeAccuracy = rangeHandler.calculateAccuracy(attacker) + val rangeDefence = rangeHandler.calculateDefence(player, attacker) + val rangeChance = hitChance(rangeAccuracy, rangeDefence) + notify(player, "Your range defence: $rangeDefence; Attacker accuracy: $rangeAccuracy; Chance to be hit: $rangeChance") + val magicAccuracy = magicHandler.calculateAccuracy(attacker) + val magicDefence = magicHandler.calculateDefence(player, attacker) + val magicChance = hitChance(magicAccuracy, magicDefence) + notify(player, "Your magic defence: $magicDefence; Attacker accuracy: $magicAccuracy; Chance to be hit: $magicChance") } /** * Empty a player's inventory * ADMIN only (for obvious reasons) */ - define("empty", Privilege.ADMIN, "", "Empties your inventory."){player,_-> + define("empty", Privilege.ADMIN, "", "Empties/clears your inventory."){player,_-> player.inventory.clear() player.inventory.refresh() } @@ -213,29 +269,44 @@ 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)) } /** - * Shows the player a list of currently active GE sell offers + * Shows the player a list of currently active GE sell offers or toggles muting sell offer News announcements. */ - define("ge", Privilege.STANDARD, "::ge MODE (Modes: buying, selling, search, bots, botsearch)", "Various commands for viewing GE offers.") { player, args -> - if(args.size < 2){ - reject(player, "Usage: ::ge mode", "Available modes: buying, selling, search, bots, botsearch") + define( + "ge", + Privilege.STANDARD, + "::ge MODE (Modes: buying, selling, search, bots, botsearch, mute)", + "Various commands for viewing GE offers." + ) { player, args -> + if (args.size < 2) { + reject(player, "Usage: ::ge mode", "Available modes: buying, selling, search, bots, botsearch, mute") } - val mode = args[1] - when(mode){ + 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) + "mute" -> { + val currentlyMuted = getAttribute(player, GE_NEWS_MUTE_ATTRIBUTE, false) + if (currentlyMuted) { + removeAttribute(player, GE_NEWS_MUTE_ATTRIBUTE) + } else { + setAttribute(player, GE_NEWS_MUTE_ATTRIBUTE, true) + } + sendMessage(player, "GE sell offer news is now ${if (currentlyMuted) "visible" else "hidden"}.") } - else -> reject(player, "Invalid mode used. Available modes are: buying, selling, search") + + else -> reject( + player, + "Invalid mode used. Available modes are: buying, selling, search, bots, botsearch, mute" + ) } } /** @@ -248,54 +319,117 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ */ define("commands", Privilege.STANDARD, "::commands page", "Lists all the commands (you are here.)"){player, args -> val page = if (args.size > 1) (args[1].toIntOrNull() ?: 1) - 1 else 0 - var lineid = 11 - var pages = CommandMapping.getPageIndices(player.rights.ordinal) - var end = if (page < (pages.size - 1)) pages[page + 1] else CommandMapping.getCommands().size - - player.interfaceManager.close() + val pages = CommandMapping.getPageIndices(player.rights.ordinal) + val end = if (page < (pages.size - 1)) pages[page + 1] else CommandMapping.getCommands().size if (page < 0) { reject(player, "Usage: ::commands page") } - for (i in 0..310) { - player.packetDispatch.sendString("", Components.QUESTJOURNAL_SCROLL_275, i) + if (page > pages.size) { + reject(player, "That page number is too high, you don't have access to that many.") } - player.packetDispatch.sendString( - "Commands" + if (pages.size > 1) " (${page + 1}/${pages.size})" else "", - Components.QUESTJOURNAL_SCROLL_275, - 2 - ) + val title = "Commands" + if (pages.size > 1) " (${page + 1}/${pages.size})" else "" + var lineId = setupScrollInterface(player, title) - for(i in pages[page] until end) { - var command = CommandMapping.getCommands()[i] - var title = "${command.name}" - var rights = command.privilege.ordinal - var icon = rights - 1 + val command = CommandMapping.getCommands()[i] + lineId = displayCommandInScroll(player, command, player.rights.ordinal, lineId) - - if (rights > player.rights.ordinal) continue - - if (rights > 0) - title = "() $title" - - player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (command.usage.isNotEmpty()) - player.packetDispatch.sendString("Usage: ${command.usage}", Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (command.description.isNotEmpty()) - player.packetDispatch.sendString(command.description, Components.QUESTJOURNAL_SCROLL_275, lineid++) - - player.packetDispatch.sendString("-------------------------------", Components.QUESTJOURNAL_SCROLL_275, lineid++) - - if (lineid > 306) { - player.packetDispatch.sendString("To view the next page, use ::commands ${page + 2}", Components.QUESTJOURNAL_SCROLL_275, lineid) + if (lineId > 306) { + player.packetDispatch.sendString("To view the next page, use ::commands ${page + 2}", Components.QUESTJOURNAL_SCROLL_275, lineId) break } + } + player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) + } + /* + * Search for commands + */ + define("commandsearch", Privilege.STANDARD, "::commandsearch search term [--chat]", "Searches for commands containing the given term."){player, args -> + if (args.size < 2) { + reject(player, "Usage: ::commandsearch [--chat]") + return@define + } + val rawArguments = args.copyOfRange(1, args.size) + val chatMode = rawArguments.any { it.equals("--chat", ignoreCase = true) } + val searchRaw = rawArguments.filterNot { it.equals("--chat", ignoreCase = true) } + if (searchRaw.isEmpty()) { + reject(player, "Usage: ::commandsearch [--chat]") + return@define + } + val search = searchRaw.joinToString(" ").lowercase() + if (search.length < 2) { + reject(player, "Search term must be at least 2 characters long") + return@define + } + val playerRights = player.rights.ordinal + val allMatchingCommands = CommandMapping.getCommands() + .filter { command -> + command.name.lowercase().contains(search) || + command.usage.lowercase().contains(search) || + command.description.lowercase().contains(search) + } + if (allMatchingCommands.isEmpty()) { + notify(player, "No commands found matching '$search'") + return@define + } + val accessibleCommands = allMatchingCommands + .filter { it.privilege.ordinal <= playerRights } + .sortedBy { it.name } + val inaccessibleCount = allMatchingCommands.count { it.privilege.ordinal > playerRights } + if (chatMode) { + for (command in accessibleCommands) { + val cmdargs = if (command.usage.isNotEmpty()) { + val parts = command.usage.split(" ", limit = 2) + if (parts.size > 1) parts[1] else "" + } else { + "" + } + val formattedMessage = if (cmdargs.isNotEmpty()) { + "CMD: ${command.name} ARGS: $cmdargs DESC: ${command.description}" + } else { + "CMD: ${command.name} DESC: ${command.description}" + } + val chunks = chunkText(formattedMessage, 70) + chunks.forEach { notify(player, it) } + } + if (accessibleCommands.isEmpty()) { + notify(player, "Found $inaccessibleCount command(s) matching '$search' but you don't have permission to use any of them") + } else { + val summary = "Found ${accessibleCommands.size} accessible command(s) for '$search'" + if (inaccessibleCount > 0) { + notify(player, summary) + notify(player, "($inaccessibleCount additional command(s) require higher privileges)") + } else { + notify(player, summary) + } + } + return@define + } + var lineId = setupScrollInterface(player, "Command Search: '$search'") + if (accessibleCommands.isEmpty()) { + player.packetDispatch.sendString("No accessible commands found.", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + for (command in accessibleCommands) { + lineId = displayCommandInScroll(player, command, playerRights, lineId) + if (lineId > 306) { + player.packetDispatch.sendString( + "Results truncated. Use ::commandsearch $search --chat", + Components.QUESTJOURNAL_SCROLL_275, + lineId + ) + break + } + } + if (inaccessibleCount > 0 && lineId <= 306) { + player.packetDispatch.sendString( + "$inaccessibleCount more command(s) need higher privileges.", + Components.QUESTJOURNAL_SCROLL_275, + lineId + ) } player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) } @@ -327,56 +461,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,7 +471,23 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } } - define("log", Privilege.ADMIN){player,_ -> + /** + * 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, description = "Adds your current coordinates to the location log buffer."){player,_ -> var log: ArrayList? = player.getAttribute("loc-log") log = log ?: ArrayList() val locString = "{${player.location.x},${player.location.y},${player.location.z},1,0}" @@ -395,7 +495,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ player.setAttribute("loc-log",log) } - define("logdone"){player,_ -> + define("logdone", description = "Copies the queued location log to your clipboard and clears it."){player,_ -> val log: ArrayList? = player.getAttribute("loc-log") log ?: return@define @@ -421,30 +521,30 @@ 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,_ -> + define("fillbank", Privilege.ADMIN, description = "Fills your bank with sequential item IDs."){player,_ -> for(i in 0 until ServerConstants.BANK_SIZE){ player.bank.add(Item(i)) } } - define("emptybank", Privilege.ADMIN, "", "Right as it says on the tin."){player,_ -> + define("emptybank", Privilege.ADMIN, description = "Clears your bank contents."){player,_ -> player.bank.clear() player.bank.update() } - define("setconfig", Privilege.ADMIN, "", "DEPRECATED: Use setvarp or setvarbit."){player,args -> + define("setconfig", Privilege.ADMIN, "::setconfig config-id value", "DEPRECATED: use ::setvarp or ::setvarbit. Sets the raw varp config-id to value."){player,args -> if(args.size < 3){ reject(player,"Syntax: ::setconfig configID value") } 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 -> + define("getobjectvarp", usage = "::getobjectvarp object-id", description = "Looks up which varp controls the state of object-id."){player,args -> if(args.size < 2){ reject(player,"Syntax: ::getobjectvarp objectid") } @@ -452,98 +552,50 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player, "${VarbitDefinition.forObjectID(SceneryDefinition.forId(objectID).varbitID).varpId}") } - define("define_varbit", Privilege.ADMIN, "::define_varbit VARBIT ID", "Prints information about the given varbit."){ player, args -> + define("define_varbit", Privilege.ADMIN, "::define_varbit ", "Prints detailed information about ."){ player, args -> if(args.size < 2) { reject(player, "Syntax: ::define_varbit varbitId") } 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", ""){ + define("setvarbit", Privilege.ADMIN, "::setvarbit varbit-id value", "Sets varbit-id to value for your player."){ player,args -> if(args.size != 3){ - reject(player,"Usage: ::setvarbit varbit value") + reject(player,"Usage: ::setvarbit ") } val index = args[1].toIntOrNull() val value = args[2].toIntOrNull() if(index == null || value == null){ - reject(player,"Usage ::setvarbit index offset value") + reject(player,"Usage ::setvarbit ") } - 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 ") + } + 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", "Prints the current value of VARBIT ID.") { + player, args -> + if (args.size != 2) + reject(player, "Usage: ::getvarbit ") + 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 +611,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,45 +629,41 @@ 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, _ -> + define("addcredits", Privilege.ADMIN, description = "Adds 100 ::shop credits to your account."){ 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 -> + define("getnpcparent", usage = "::getnpcparent npc-id", description = "Finds the parent NPC whose child list includes npc-id."){player,args -> if(args.size < 2){ reject(player,"Usage: ::getnpcparent npcid") } @@ -636,32 +684,53 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player,"No parent NPC found.") } } - define("infinitespecial", Privilege.ADMIN){ player, args -> - val usageStr = "Usage: ::infinitespecial true|false" - if(args.size < 2){ - reject(player, usageStr) - } - when(args[1]){ - "true" -> player.setAttribute("infinite-special", true) - "false" -> player.removeAttribute("infinite-special") - else -> reject(player, usageStr) + define( + "infinitespecial", + Privilege.ADMIN, + "::infinitespecial [true|false]", + "Toggles infinite special attack energy for your player (special-attack flag)." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, INFINITE_SPECIAL_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::infinitespecial [true|false]") + return@define } + setBooleanAttribute(player, INFINITE_SPECIAL_ATTRIBUTE, enabled, "Infinite special attack") } - define("allow_aggro", Privilege.ADMIN) { player, args -> - val usageStr = "Usage: ::allow_aggro true | false" - if(args.size < 2) { - reject(player, usageStr) + define( + "infinitespecialmove", + Privilege.ADMIN, + "::infinitespecialmove [true|false]", + "Toggles infinite Summoning familiar special move points for your player." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, Familiar.INFINITE_SPECIAL_MOVE_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::infinitespecialmove [true|false]") + return@define } - when(args[1]) { - "true" -> player.setAttribute("allow_admin_aggression", true) - "false" -> player.removeAttribute("allow_admin_aggression") - else -> reject(player, usageStr) - + setBooleanAttribute( + player, + Familiar.INFINITE_SPECIAL_MOVE_ATTRIBUTE, + enabled, + "Infinite familiar special move energy" + ) + } + define( + "allowaggro", + Privilege.ADMIN, + "::allowaggro [true|false]", + "Toggles NPCs aggroing on you as an admin." + ) { player, args -> + val enabled = resolveBooleanToggle(player, args, ALLOW_ADMIN_AGGRESSION_ATTRIBUTE) + if (enabled == null) { + reject(player, "Usage: ::allowaggro [true|false]") + return@define } + setBooleanAttribute(player, ALLOW_ADMIN_AGGRESSION_ATTRIBUTE, enabled, "Admin NPC aggression") } 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 +748,194 @@ 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 true/false", + "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]}") + + } + define("brewkit", Privilege.ADMIN, "brewkit 0-10", + "Give yourself everything you need to brew ales."){ player, args -> + if (args.size == 1) { + sendMessage(player, "Kit numbers: 0 - Dwarven Stout, 1 - Asgarnian Ale, 2 - Greenman's Ale,") + sendMessage(player, "Kit numbers: 3 - Wizard's Mind Bomb, 4 - Dragon Bitter, 5 - Moonlight Mead,") + sendMessage(player, "Kit numbers: 6 - Axeman's Folly, 7 - Chef's Delight, 8 - Slayer's Respite,") + sendMessage(player, "Kit numbers: 9 - Cider, 10 - Kelda Stout") + return@define + } + val item = + when (args[1].toInt()) { + 0 -> Items.HAMMERSTONE_HOPS_5994 + 1 -> Items.ASGARNIAN_HOPS_5996 + 2 -> Items.CLEAN_HARRALANDER_255 + 3 -> Items.YANILLIAN_HOPS_5998 + 4 -> Items.KRANDORIAN_HOPS_6000 + 5 -> Items.MUSHROOM_6004 + 6 -> Items.OAK_ROOTS_6043 + 7 -> Items.CHOCOLATE_DUST_1975 + 8 -> Items.WILDBLOOD_HOPS_6002 + 9 -> Items.APPLE_MUSH_5992 + 10 -> Items.KELDA_HOPS_6113 + else -> { + reject(player, "Invalid brew kit specified.") + return@define + } + } + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929, if(args[1].toInt() == 9) 0 else 2) + addItemOrDrop(player, Items.BARLEY_MALT_6008, if(args[1].toInt() == 9) 0 else 2) + addItemOrDrop(player, item, if(args[1].toInt() == 6 || args[1].toInt() == 10) 1 else 4) + addItemOrDrop(player, Items.ALE_YEAST_5767) + return@define + } + + define("brew", Privilege.ADMIN, "brew mode", + "0: force natural step, 1: force good step without maturity, 2: force good step with maturity, 3: force bad step"){ player, args -> + var forceStep = false + var forceGood = false + var forceMature = false + var forceBad = false + if (args.size == 1){ + forceStep = true + } + if (args.size != 1){ + when(args[1].toIntOrNull()){ + 0 -> forceStep = true + 1 -> forceGood = true + 2 -> forceMature = true + 3 -> forceBad = true + else -> reject(player, "${args[1]} is not a valid option.") + } + } + + val vats = getOrStartTimer(player).getVats() + for (vat in vats){ + vat.brew(forceStep, forceGood, forceMature, forceBad) + } + } + } + + fun setupScrollInterface(player: Player, title: String): Int { + player.interfaceManager.close() + for (i in 0..310) { + player.packetDispatch.sendString("", Components.QUESTJOURNAL_SCROLL_275, i) + } + player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, 2) + return 11 + } + + fun displayCommandInScroll(player: Player, command: Command, playerRights: Int, lineId: Int): Int { + val privilegeLevel = command.privilege.ordinal + if (privilegeLevel > playerRights) return lineId + val titleIcon = privilegeLevel - 1 + var title = command.name + if (privilegeLevel > 0) { + title = "() $title" + } + var currentLineId = lineId + player.packetDispatch.sendString(title, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + if (command.usage.isNotEmpty()) { + val usageText = "Usage: ${command.usage}" + val usageChunks = chunkText(usageText, 70) + for (chunk in usageChunks) { + player.packetDispatch.sendString(chunk, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + } + } + if (command.description.isNotEmpty()) { + val descChunks = chunkText(command.description, 70) + for (chunk in descChunks) { + player.packetDispatch.sendString(chunk, Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + } + } + player.packetDispatch.sendString("-------------------------------", Components.QUESTJOURNAL_SCROLL_275, currentLineId++) + return currentLineId + } + + fun chunkText(text: String, maxLength: Int = 70): List { + if (text.length <= maxLength) return listOf(text) + val chunks = mutableListOf() + var remaining = text + while (remaining.length > maxLength) { + var breakPoint = maxLength + for (i in maxLength downTo 1) { + if (remaining[i - 1] == ' ') { + breakPoint = i + break + } + } + chunks.add(remaining.take(breakPoint).trim()) + remaining = remaining.substring(breakPoint).trim() + } + if (remaining.isNotEmpty()) { + chunks.add(remaining) + } + return chunks + } + + fun setPlaqueReadStatus(player: Player, status: Boolean){ + // For some reason the loop has to be this way to have read write access + for (i in 0 until player.savedData.globalData.readPlaques.size){ + player.savedData.globalData.readPlaques[i] = status + } + } fun showGeBotsearch(player: Player, searchTerm: String) @@ -700,17 +951,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 +974,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 +1003,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 +1032,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 +1079,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..f5b22a4dd --- /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", description = "Opens the in-game model viewer toolkit."){ 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 + } + } +} 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..8fd7550ef 100644 --- a/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt @@ -37,10 +37,10 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ * Kick a player * ============================================================================================================= */ - define("kick", Privilege.MODERATOR){ player, args -> + define("kick", Privilege.MODERATOR, "::kick ", "Disconnects the specified player from the game."){ 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..e168c7a1e --- /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", usage = "::getattribute [player] attribute", description = "Prints the value of the chosen attribute on you or another player. Omit [player] to use on self.") { 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", usage = "::setattribute [player] attribute value", description = "Sets a simple attribute on you or another player.") { 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..c8c414c38 100644 --- a/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt @@ -1,11 +1,13 @@ package core.game.system.command.sets +import core.api.log import core.game.component.Component import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.QuestRepository -import core.game.system.command.Command import core.plugin.Initializable import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.tools.Log @Initializable class QuestCommandSet : CommandSet(Privilege.ADMIN){ @@ -13,36 +15,81 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){ /** * Completes all quests */ - define("allquest"){player,_-> + define("allquest", description = "Instantly completes every quest for your account."){player,_-> for (quest in QuestRepository.getQuests().values) { - quest.finish(player) + try { + quest.finish(player) + } catch (e: Exception){ + e.message?.let { log(this.javaClass, Log.WARN, it) } + continue + } } } /** * Displays the currently implemented quests with debug information */ - define("quest"){player,_ -> - sendQuestsDebug(player) + define("quest", + usage = "::quest [-p username] [quest-filter]", + description = "Shows quest stages for you, or for [username] if -p is used. Add optional text to filter by quest name." + ) { player, args -> + + var target = player + var filter: String? = null + + if (args.size > 1) { + if (args[1].equals("-p", ignoreCase = true)) { + // need at least 3 arguments if -p is used: command, -p, username + if (args.size < 3) { + reject(player, "ERROR: Missing player name. Usage: ::quest -p [quest-filter]") + return@define + } + + val playerName = args[2] + target = Repository.getPlayerByName(playerName) ?: run { + reject(player, "ERROR: Username '$playerName' not found.") + return@define + } + + // arguments after the player name are the quest filter + if (args.size > 3) { + filter = args.drop(3).joinToString(" ") + } + } else { + // no -p, so the arguments are the quest filter + filter = args.drop(1).joinToString(" ") + } + } + sendQuestsDebug(player, target, filter) } /** * Sets stage of quest */ - define("setqueststage"){player,args -> - if (args.size < 2) { + define("setqueststage", usage = "::setqueststage quest-index stage", description = "Sets quest quest-index to stage stage; use ::quest to find indices."){player,args -> + 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") + } } /** * Displays the currently implemented quests */ - define("quests", Privilege.STANDARD){ player, _ -> + define("quests", Privilege.STANDARD, description = "Opens the quests interface listing current implementations."){ player, _ -> sendQuests(player) } } @@ -60,26 +107,57 @@ 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. + * @param lookupUser the target player to view quests for. + * @param questFilter optional string to filter quests by name. */ - private fun sendQuestsDebug(player: Player?) { - player!!.interfaceManager.open(Component(275)) + private fun sendQuestsDebug(admin: Player?, lookupUser: Player?, questFilter: String? = null) { + admin!!.interfaceManager.open(Component(275)) + + // clear interface lines 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) - 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++) + + // title + admin.packetDispatch.sendString("${lookupUser!!.username}'s Quest Debug", 275, 2) + + // filtered quests + val questsToDisplay = QuestRepository.getQuests().values.filter { q -> + questFilter.isNullOrBlank() || q.quest.toString().contains(questFilter, ignoreCase = true) + } + + // no quests to filter + if (questsToDisplay.isEmpty()) { + admin.packetDispatch.sendString("No quests found matching: $questFilter", 275, lineId++) + return + } + + for (q in questsToDisplay) { + // can't display more than 308 lines + if (lineId >= 308) { + admin.packetDispatch.sendString("List truncated (too many results).", 275, lineId) + break + } + + 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: ${stage}", 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..0f2e32426 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){ @@ -11,7 +15,7 @@ class SlayerCommandSet : CommandSet(Privilege.ADMIN){ /** * Finishes a player's slayer task (the correct way) */ - define("finishtask"){player,_ -> + define("finishtask", description = "Spawns a finisher NPC so your current Slayer task ends instantly."){player,_ -> notify(player, "Kill the npc that spawned to finish your task.") SlayerManager.getInstance(player).amount = 1 val finisher = NPC(SlayerManager.getInstance(player).task?.npcs?.get(0) ?: 0, player.location) @@ -22,7 +26,7 @@ class SlayerCommandSet : CommandSet(Privilege.ADMIN){ /** * Set slayer points */ - define("setslayerpoints"){player,args -> + define("setslayerpoints", usage = "::setslayerpoints amount", description = "Sets your Slayer points total to amount."){player,args -> if(args.size < 2){ reject(player,"Usage: ::setslayerpoints amount") } @@ -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..468bdaf96 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 @@ -18,27 +20,48 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ /** * Spawns an npc with the given ID */ - define("npc"){player,args -> + define("npc", usage = "::npc npc-id [amount] [iswalks]", description = "Spawns one or more NPCs at your location, optionally letting them walk."){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) } /** * Spawns an item with the given ID */ - define("item"){player,args -> + define("item", usage = "::item item-id [amount]", description = "Spawns the specified item stack into your inventory."){player,args -> if (args.size < 2) { reject(player,"You must specify an item ID") return@define @@ -58,20 +81,47 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ player.inventory.add(item) } + + /** + * Spawns multiple items with the given ID + */ + define("items", usage = "::items start-id end-id [amount]", description = "Loops through item ids from 'start-id' to 'end-id' and spawns 'amount' of them in your inventory."){player,args -> + if (args.size < 3) { + reject(player,"You must specify start and end item ids.") + return@define + } + val start = args[1].toIntOrNull() ?: return@define + val end = args[2].toIntOrNull() ?: return@define + var amount = (args.getOrNull(3) ?: "1").toInt() + for (id in start..end) { + if (id > Cache.getItemDefinitionsSize()) { + reject(player,"Item ID '$id' out of range.") + return@define + } + val item = Item(id, amount) + val max = player.inventory.getMaximumAdd(item) + if (amount > max) { + amount = max + } + item.setAmount(amount) + player.inventory.add(item) + } + } + /** * Spawn object with given ID at the player's location */ - define("object"){player,args -> + define("object", usage = "::object object-id [type] [rotation]", description = "Spawns an object at your tile with optional type and rotation."){player,args -> if (args!!.size < 2) { reject(player,"syntax error: id (optional) type rotation or rotation") return@define } 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 -> + define("objectgrid", usage = "::objectgrid start-id end-id type rotation", description = "Spawns a 10-wide grid cycling through the given object id range.") { player, args -> if(args!!.size != 5) { reject(player, "Usage: objectgrid beginId endId type rotation") return@define 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..ad8cf75da 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", usage = "::stats [player-name]", description = "Opens the statistics book for you or the specified player."){ 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..d4e48ca9c 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 @@ -22,7 +20,7 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) { /** * Start an update countdown */ - define("update") { player, args -> + define("update", usage = "::update [seconds]", description = "Flags the world for an update, optionally using [seconds] as the countdown.") { player, args -> if (args.size > 1) { SystemManager.getUpdater().setCountdown(args[1].toInt()) } @@ -32,7 +30,7 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) { /** * Cancel an update countdown */ - define("cancelupdate") { player, _ -> + define("cancelupdate", description = "Cancels any active update countdown.") { player, _ -> SystemManager.getUpdater().cancel() } @@ -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 -> @@ -233,9 +230,79 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) { player.inventory.add(Item(Items.ROTTEN_POTATO_5733)) } - define("shutdown", Privilege.ADMIN) { player, _ -> + define("shutdown", Privilege.ADMIN, description = "Immediately terminates the server process. Do NOT test on a remote server you don't own.") { player, _ -> 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..d57ac9031 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,21 +63,11 @@ 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. */ fun init(): GroundItem { + if (respawnRate shr 16 == 0) respawnRate = respawnRate or (respawnRate shl 16) return GroundItemManager.create(this) } @@ -116,4 +114,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..58e3e6ebf 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) } } @@ -65,8 +76,6 @@ object ServerConfigParser { isQuickChat = false, isLootshare = false, msAddress = data.getString("server.msip"), - default_xp_rate = data.getDouble("world.default_xp_rate"), - allow_slayer_reroll = data.getBoolean("world.allow_slayer_reroll"), enable_default_clan = data.getBoolean("world.enable_default_clan"), enable_bots = data.getBoolean("world.enable_bots"), autostock_ge = data.getBoolean("world.autostock_ge"), @@ -78,6 +87,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 +136,62 @@ 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_GLOBAL_CHAT = data.getBoolean("world.enable_global_chat", false) + ServerConstants.MAX_PATHFIND_DISTANCE = data.getLong("server.max_pathfind_dist", 25L).toInt() + ServerConstants.XP_RATES = data.getBoolean("world.xp_rates", false) + ServerConstants.IRONMAN = data.getBoolean("world.ironman", false) + ServerConstants.PLAYER_STOCK_CLEAR_INTERVAL = data.getLong("world.playerstock_clear_mins", 180L).toInt() + ServerConstants.PLAYER_STOCK_RECIRCULATE = data.getBoolean("world.playerstock_bot_offers", true) + ServerConstants.BOTSTOCK_LIMIT = data.getLong("world.botstock_limit", 5000L).toInt() + ServerConstants.BETTER_AGILITY_PYRAMID_GP = data.getBoolean("world.better_agility_pyramid_gp", false) + ServerConstants.GRAFANA_PATH = data.getPath("integrations.grafana_log_path") + ServerConstants.GRAFANA_LOGGING = data.getBoolean("integrations.grafana_logging", false) + ServerConstants.GRAFANA_TTL_DAYS = data.getLong("integrations.grafana_log_ttl_days", 7L).toInt() + ServerConstants.BETTER_DFS = data.getBoolean("world.better_dfs", false) + ServerConstants.NEW_PLAYER_ANNOUNCEMENT = data.getBoolean("world.new_player_announcement", false) + ServerConstants.INAUTHENTIC_CANDLELIGHT_RANDOM = data.getBoolean("world.inauthentic_candlelight_random", false) + ServerConstants.HOLIDAY_EVENT_RANDOMS = data.getBoolean("world.holiday_event_randoms", false) + ServerConstants.FORCE_HALLOWEEN_EVENTS = data.getBoolean("world.force_halloween_randoms", false) + ServerConstants.FORCE_CHRISTMAS_EVENTS = data.getBoolean("world.force_christmas_randoms", false) + ServerConstants.FORCE_EASTER_EVENTS = data.getBoolean("world.force_easter_randoms", false) + ServerConstants.APRIL_FOOLS_EVENT = data.getBoolean("world.april_fools_event", false) + ServerConstants.FORCE_APRIL_FOOLS = data.getBoolean("world.force_april_fools", false) + ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 530).toInt() + ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", false) + ServerConstants.WILDERNESS_EXCLUSIVE_LOOT = data.getBoolean("world.wilderness_exclusive_loot", false) + ServerConstants.SHOOTING_STAR_RING = data.getBoolean("world.shooting_star_ring", false) + ServerConstants.RING_OF_WEALTH_TELEPORT = data.getBoolean("world.ring_of_wealth_teleport", false) + ServerConstants.SECOND_BANK = data.getBoolean("world.second_bank", false) + ServerConstants.PLAYER_COMMANDS = data.getBoolean("world.player_commands", false) + ServerConstants.BOOSTED_TRAWLER_REWARDS = data.getBoolean("world.boosted_trawler_rewards", false) + ServerConstants.CONNECTIVITY_CHECK_URL = data.getString("server.connectivity_check_url", "https://google.com,https://2009scape.org") + ServerConstants.CONNECTIVITY_TIMEOUT = data.getLong("server.connectivity_timeout", 500L).toInt() + ServerConstants.WEBSOCKET_ENABLED = data.getBoolean("server.websocket_enabled", false) + ServerConstants.WEBSOCKET_PORT = data.getLong("server.websocket_port", 0L).toInt() + ServerConstants.WEBSOCKET_TLS_ENABLED = data.getBoolean("server.websocket_tls_enabled", false) + ServerConstants.WEBSOCKET_TLS_KEYSTORE_PATH = data.getString("server.websocket_tls_keystore_path", "") + ServerConstants.WEBSOCKET_TLS_KEYSTORE_PASSWORD = data.getString("server.websocket_tls_keystore_password", "") + + 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..4f9ba872a --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/AntiMacro.kt @@ -0,0 +1,182 @@ +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 = false + var nextRandom: RandomEvents? = null + + override fun run(entity: Entity): Boolean { + 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) + 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..23a2fa7e5 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Frozen.kt @@ -0,0 +1,50 @@ +package core.game.system.timer.impl + +import core.game.system.timer.* +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.repository.Repository +import org.json.simple.* + +class Frozen : PersistTimer (1, "frozen", flags = arrayOf(TimerFlag.ClearOnDeath)) { + var shouldApplyImmunity = false + + override fun save (root: JSONObject, entity: Entity) { + root["ticksLeft"] = (nextExecution - getWorldTicks()).toString() + root["applyImmunity"] = shouldApplyImmunity + } + + override fun parse (root: JSONObject, entity: Entity) { + runInterval = root["ticksLeft"].toString().toInt() + shouldApplyImmunity = root["applyImmunity"] as? Boolean ?: false + } + + override fun beforeRegister (entity: Entity) { + if (hasTimerActive(entity)) { + removeTimer(entity, this) + return + } + if (hasTimerActive(entity)) { + removeTimer(entity, this) + return + } + if (entity is Player) { + sendMessage(entity as Player, "You have been frozen!") + } + } + + override fun run (entity: Entity) : Boolean { + if (shouldApplyImmunity) { + registerTimer (entity, spawnTimer(7)) + } else (entity as? Player)?.debug ("Can't apply immunity") + return false + } + + override fun getTimer (vararg args: Any) : RSTimer { + val inst = Frozen() + inst.runInterval = args.getOrNull(0) as? Int ?: 10 + inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false + return inst + } +} diff --git a/Server/src/main/core/game/system/timer/impl/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..ececf51f6 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Poison.kt @@ -0,0 +1,82 @@ +package core.game.system.timer.impl + +import content.global.skill.summoning.familiar.Familiar +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.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +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 + 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.") + // this is ass, and preventing poisoning slayer monster player lacks lv req for should be done but prob not here & not like this. same for familiars + if (entity is NPC && entity.task != null && entity.task.levelReq > damageSource.skills.getLevel(Skills.SLAYER)) { + (damageSource as? Player)?.debug("[Poison] -> Removed poison from ${entity.name} ${entity.location} due to Slayer reqs!") + removeTimer(entity, this) + } + } + if (damageSource is Familiar) { + val owner : Player? = (damageSource as Familiar).owner + if (owner != null && entity is NPC && entity.task != null && entity.task.levelReq > owner.skills.getLevel(Skills.SLAYER)) { + owner.debug("[Poison] -> Removed poison from ${entity.name} ${entity.location} due to Slayer reqs!") + removeTimer(entity, this) + } + } + } + + override fun run (entity: Entity) : Boolean { + entity.scripts.removeWeakScripts() + 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..c90608e58 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/PoisonImmunity.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 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 == 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..f14d46abe --- /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.") + } + + 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..f8e8bcd76 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Skulled.kt @@ -0,0 +1,32 @@ +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 onRemoval (entity: Entity) { + if (entity !is Player) return + entity.skullManager.reset() + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = Skulled() + t.runInterval = args.getOrNull(0) as? Int ?: 500 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt b/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt new file mode 100644 index 000000000..b689e3a0a --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt @@ -0,0 +1,99 @@ +package core.game.system.timer.impl + +import core.api.Event.DialogueClosed +import core.api.Event.SpellCast +import core.api.Event.SpellbookChanged +import core.api.clearLogoutListener +import core.api.registerLogoutListener +import core.api.removeTimer +import core.game.event.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager.SpellBook +import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource +import core.game.system.timer.PersistTimer +import core.tools.minutesToTicks + +/** + * A timer dedicated to the handling of the Lunar spell Spellbook Swap. + */ +class SpellbookSwap : PersistTimer(runInterval = minutesToTicks(2), identifier = "spellbook:swap") { + + private val spellCastHook = object : EventHook { + override fun process(entity : Entity, event : SpellCastEvent) { + if (event.spellBook == SpellBook.LUNAR && event.spellId == 12) return // Ignore spellbook swap itself + revertSpellbook(entity) + removeTimer(entity) + } + } + + private val spellBookChangeHook = object : EventHook { + override fun process(entity : Entity, event : SpellbookChangeEvent) { + if (event.source != SpellbookChangeSource.SPELLBOOK_SWAP_CAST + && event.source != SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE) { + removeTimer(entity) + } + } + } + + private val dialogueCloseHook = object : EventHook { + override fun process(entity : Entity, event : DialogueCloseEvent) { + if (event.dialogue?.getIds()?.contains(3264731) != true) return // Spellbook swap selection dialogue + if (entity !is Player) return + if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) { + entity.removeAttribute("spell:runes") + removeTimer(entity) + } + } + } + + override fun run(entity : Entity) : Boolean { + revertSpellbook(entity) + return false + } + + override fun onRegister(entity : Entity) { + entity.hook(SpellCast, spellCastHook) + entity.hook(SpellbookChanged, spellBookChangeHook) + entity.hook(DialogueClosed, dialogueCloseHook) + if (entity is Player) { + registerLogoutListener(entity, "spellbook:swap") { player -> + revertSpellbook(player) + } + } + } + + override fun onRemoval(entity : Entity) { + entity.unhook(spellCastHook) + entity.unhook(spellBookChangeHook) + entity.unhook(dialogueCloseHook) + if (entity is Player) { + clearLogoutListener(entity, "spellbook:swap") + } + } + + private fun revertSpellbook(entity : Entity) { + if (entity !is Player) return + if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) return + // Clear autocast + val weaponInterface = entity.getExtension(WeaponInterface::class.java) + if (weaponInterface != null && entity.properties.autocastSpell != null) { + weaponInterface.selectAutoSpell(-1, true) + } + // Close autocast selection interface if it's still open + if (entity.getAttribute("autocast_select", false)) { + entity.removeAttribute("autocast_select") + entity.removeAttribute("autocast_component") + if (weaponInterface != null) { + entity.interfaceManager.openTab(weaponInterface) + } + } + entity.dispatch(SpellbookChangeEvent( + SpellBook.forInterface(entity.spellBookManager.spellBook), + SpellBook.LUNAR, + SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE)) + entity.spellBookManager.setSpellBook(SpellBook.LUNAR) + entity.spellBookManager.update(entity) + } +} 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..e113dbbe3 100644 --- a/Server/src/main/core/game/world/GameSettings.kt +++ b/Server/src/main/core/game/world/GameSettings.kt @@ -69,8 +69,6 @@ class GameSettings * The address of the Management server. */ var msAddress: String, - var default_xp_rate: Double, - var allow_slayer_reroll: Boolean, var enable_default_clan: Boolean, var enable_bots: Boolean, var autostock_ge: Boolean, @@ -82,20 +80,22 @@ 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 - * 15 & 22 = keys & lock || 16 = fly swat || 17 = person with question marks || 18 & 447 = wise old man - * 19 = man & woman with mouth closed || 20 = man & lock & key || 21 = closed chests - * 23 = snowmen || 405 = Construction houses || 622 = Two sets of 3 people range, mage, melee - * 623 = Woodcutting || 679 = Summoning || 715 = Easter || 800 = Halloween - * Any value that isn't one listed above = random selection + * 15 & 22 = Lock w/key & keys || 16 = Fly swatters || 17 = Man & woman with question marks + * 18 & 447 = Wise old man & woman || 19 = Man with mouth zipped & woman talking || 20 = Man & lock w/key + * 21 = Chests that open & close || 23 = Christmas || 405 = Construction houses + * 622 = Two sets 3 people range, mage, melee 623 = Woodcutting || 679 = Summoning || 715 = Easter || 800 = Halloween */ var message_model: Int, /**"Lobby" interface * The message of the week text - * The "child" for writing text to these interfaces is located inside of LoginConfiguration.java + * The "child" for writing text to these interfaces is located inside LoginConfiguration.java * method: getMessageChild */ var message_string: String @@ -124,8 +124,6 @@ class GameSettings val activity = data["activity"].toString() val pvpWorld = data["pvpWorld"] as Boolean val msip = data["msip"].toString() - val default_xp_rate = data["default_xp_rate"].toString().toDouble() - val allow_slayer_reroll = data["allow_slayer_reroll"] as Boolean val enable_default_clan = data["enable_default_clan"] as Boolean val enable_bots = data["enable_bots"] as Boolean val autostock_ge = data["autostock_ge"] as Boolean @@ -136,6 +134,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() @@ -152,8 +153,6 @@ class GameSettings false, false, msip, - default_xp_rate, - allow_slayer_reroll, enable_default_clan, enable_bots, autostock_ge, @@ -165,6 +164,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 +190,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 deleted file mode 100644 index 53c5562a7..000000000 --- a/Server/src/main/core/game/world/map/Location.java +++ /dev/null @@ -1,547 +0,0 @@ -package core.game.world.map; - -import core.game.interaction.DestinationFlag; -import core.game.node.Node; -import core.game.world.map.path.Path; -import core.game.world.map.path.Pathfinder; -import core.tools.RandomFunction; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a location on the world map. - * @author Emperor - */ -public final class Location extends Node { - - /** - * The x-coordinate. - */ - private int x; - - /** - * The y-coordinate. - */ - private int y; - - /** - * The plane. - */ - private int z; - - /** - * Constructs a new {@code Location} {@code Object}. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @param z The z-coordinate. - */ - public Location(int x, int y, int z) { - super(null, null); - super.destinationFlag = DestinationFlag.LOCATION; - this.x = x; - this.y = y; - if (z < 0) { - z += 4; - } - this.z = z; - } - - /** - * Constructs a new {@code Location} {@code Object} - * @param x The x-coordinate. - * @param y The y-coordinate. - */ - public Location(int x, int y) { - this(x, y, 0); - } - - /** - * Constructs a new {@code Location} {@code Object}. - * @param x The x-coordinate. - * @param y The y coordinate. - * @param z The z-coordinate. - * @param randomizer The amount we should randomize the x and y coordinates - * with (x + random(randomizer), y + random(randomizer)). - */ - public Location(int x, int y, int z, int randomizer) { - this(x + RandomFunction.getRandom(randomizer), y + RandomFunction.getRandom(randomizer), z); - } - - /** - * Construct a new Location. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @param z The z-coordinate. - * @return The constructed location. - */ - public static Location create(int x, int y, int z) { - return new Location(x, y, z); - } - - public static Location create(int x, int y) { - return new Location(x, y, 0); - } - - /** - * Creates a new instance of the given location. - * @param location The given location. - * @return The new instance. - */ - public static Location create(Location location) { - return create(location.getX(), location.getY(), location.getZ()); - } - - /** - * Creates a location instance with coordinates being the difference between - * {@code other} & {@code location}. - * @param location The first location. - * @param other The other location. - * @return The delta location. - */ - public static Location getDelta(Location location, Location other) { - return Location.create(other.x - location.x, other.y - location.y, other.z - location.z); - } - - /** - * Gets a random location near the main location. - * @param main The main location. - * @param radius The radius. - * @param reachable If the locations should be able to reach eachother. - * @return The location. - */ - public static Location getRandomLocation(Location main, int radius, boolean reachable) { - Location location = RegionManager.getTeleportLocation(main, radius); - if (!reachable) { - return location; - } - Path path = Pathfinder.find(main, location, false, Pathfinder.DUMB); - if (!path.isSuccessful()) { - location = main; - if (!path.getPoints().isEmpty()) { - Point p = path.getPoints().getLast(); - location = Location.create(p.getX(), p.getY(), main.getZ()); - } - } - return location; - } - - @Override - public Location getLocation() { - return this; - } - - /** - * Checks if this location is right next to the node (assuming the node is - * size 1x1). - * @param node The node to check. - * @return {@code True} if this location is 1 tile north, west, south or - * east of the node location. - */ - public boolean isNextTo(Node node) { - Location l = node.getLocation(); - if (l.getY() == y) { - return l.getX() - x == -1 || l.getX() - x == 1; - } - if (l.getX() == x) { - return l.getY() - y == -1 || l.getY() - y == 1; - } - return false; - } - - /** - * Gets the region id. - * @return The region id. - */ - public int getRegionId() { - return (x >> 6) << 8 | (y >> 6); - } - - /** - * Compares the users region with the one given - * @return True if user is in given region - */ - public boolean isInRegion(int region) { - return getRegionId() == region; - } - - /** - * Gets the location incremented by the given coordinates. - * @param dir The direction to transform this location. - * @return The location. - */ - public Location transform(Direction dir) { - return transform(dir, 1); - } - - /** - * Gets the location incremented by the given coordinates. - * @param dir The direction to transform this location. - * @param steps The amount of steps to move in this direction. - * @return The location. - */ - public Location transform(Direction dir, int steps) { - return new Location(x + (dir.getStepX() * steps), y + (dir.getStepY() * steps), this.z); - } - - /** - * Gets the location incremented by the given coordinates. - * @param l incremental location - * @return The location. - */ - public Location transform(Location l) { - return new Location(x + l.getX(), y + l.getY(), this.z + l.getZ()); - } - - /** - * Gets the location incremented by the given coordinates. - * @param diffX The x-difference. - * @param diffY The y-difference. - * @param z The height difference. - * @return The location. - */ - public Location transform(int diffX, int diffY, int z) { - return new Location(x + diffX, y + diffY, this.z + z); - } - - /** - * Checks if the other location is within viewing distance. - * @param other The other location. - * @return If you're within the other distance. - */ - public boolean withinDistance(Location other) { - return withinDistance(other, MapDistance.RENDERING.getDistance()); - } - - /** - * Returns if a player is within a specified distance. - * @param other The other location. - * @param dist The amount of distance. - * @return If you're within the other distance. - */ - public boolean withinDistance(Location other, int dist) { - if (other.z != z) { - return false; - } - - int a = (other.x - x); - int b = (other.y - y); - double product = Math.sqrt((a*a) + (b*b)); - return product <= dist; - } - - /** - * Returns if a player is within a specified distance using max norm distance. - * @param other The other location. - * @param dist The amount of distance. - * @return If you're within the other distance. - */ - public boolean withinMaxnormDistance(Location other, int dist) { - if (other.z != z) { - return false; - } - - int a = Math.abs(other.x - x); - int b = Math.abs(other.y - y); - double max = Math.max(a, b); - return max <= dist; - } - - /** - * Returns the distance between you and the other. - * @param other The other location. - * @return The amount of distance between you and other. - */ - public double getDistance(Location other) { - int xdiff = this.getX() - other.getX(); - int ydiff = this.getY() - other.getY(); - return Math.sqrt(xdiff * xdiff + ydiff * ydiff); - } - - /** - * Returns the distance between the first and the second specified distance. - * @param first The first location. - * @param second The other location. - * @return The amount of distance between first and other. - */ - public static double getDistance(Location first, Location second) { - int xdiff = first.getX() - second.getX(); - int ydiff = first.getY() - second.getY(); - return Math.sqrt(xdiff * xdiff + ydiff * ydiff); - } - - /** - * Gets the 8 tiles surrounding this location as an ArrayList - */ - public ArrayList getSurroundingTiles() { - ArrayList locs = new ArrayList<>(); - - locs.add(transform(0,1,0)); //N - locs.add(transform(1,1,0)); //NE - locs.add(transform(1,0,0)); //E - locs.add(transform(1,-1,0)); //SE - locs.add(transform(0,-1,0)); //S - locs.add(transform(-1,-1,0));//SW - locs.add(transform(-1,0,0));//W - locs.add(transform(-1,1,0));//NW - - return locs; - } - - /** - * Gets a square of 3 x 3 tiles as an ArrayList - */ - public ArrayList get3x3Tiles() { - ArrayList locs = new ArrayList<>(); - locs.add(transform(0,0,0)); //Center - locs.add(transform(0,1,0)); //N - locs.add(transform(1,1,0)); //NE - locs.add(transform(1,0,0)); //E - locs.add(transform(1,-1,0)); //SE - locs.add(transform(0,-1,0)); //S - locs.add(transform(-1,-1,0));//SW - locs.add(transform(-1,0,0));//W - locs.add(transform(-1,1,0));//NW - return locs; - } - - /** - * Gets the x position on the region chunk. - * @return The x position on the region chunk. - */ - public int getChunkOffsetX() { - int x = getLocalX(); - //return x - ((x / RegionChunk.SIZE) * RegionChunk.SIZE); - return x & 7; - } - - /** - * Gets the y position on the region chunk. - * @return The y position on the region chunk. - */ - public int getChunkOffsetY() { - int y = getLocalY(); - //return y - ((y / RegionChunk.SIZE) * RegionChunk.SIZE); - return y & 7; - } - - /** - * Gets the base location for the chunk this location is in. - * @return The base location. - */ - public Location getChunkBase() { - return create(getRegionX() << 3, getRegionY() << 3, z); - } - - /** - * Gets the region x-coordinate. - * @return The region x-coordinate. - */ - public int getRegionX() { - return x >> 3; - } - - /** - * Gets the region y-coordinate. - * @return The region y-coordinate. - */ - public int getRegionY() { - return y >> 3; - } - - /** - * Gets the local x-coordinate on the current region in [0, 64). - * @return The local x-coordinate. - */ - public int getLocalX() { - return x & 63; - } - - /** - * Gets the local y-coordinate on the current region in [0, 64). - * @return The local y-coordinate. - */ - public int getLocalY() { - return y & 63; - } - - /** - * Gets the scene x-coordinate in [48, 55] (note that 104/2 = 52). - * @return The local x-coordinate. - */ - public int getSceneX() { - return x - ((getRegionX() - 6) << 3); - } - - /** - * Gets the local y-coordinate in [48, 55] (note that 104/2 = 52). - * @return The local y-coordinate. - */ - public int getSceneY() { - return y - ((getRegionY() - 6) << 3); - } - - /** - * Gets the local x-coordinate. - * @param loc The location containing the regional coordinates. - * @return The local x-coordinate. - */ - public int getSceneX(Location loc) { - return x - ((loc.getRegionX() - 6) << 3); - } - - /** - * Gets the local y-coordinate. - * @param loc The location containing the regional coordinates. - * @return The local y-coordinate. - */ - public int getSceneY(Location loc) { - return y - ((loc.getRegionY() - 6) << 3); - } - - /** - * Gets the chunk's x-coordinate (0-7). - * @return The x in the (8x8) region. - */ - public int getChunkX() { - return getLocalX() >> 3; - } - - /** - * Gets the chunk's y-coordinate (0-7). - * @return The y in the (8x8) region. - */ - public int getChunkY() { - return getLocalY() >> 3; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Location)) { - return false; - } - Location loc = (Location) other; - return loc.x == x && loc.y == y && loc.z == z; - } - - /** - * Checks if these coordinates equal this location. - * @param x the x. - * @param y the y. - * @param z the x. - * @return {@code True} if so. - */ - public boolean equals(int x, int y, int z) { - return equals(new Location(x, y, z)); - } - - @Override - public String toString() { - return "[" + x + ", " + y + ", " + z + "]"; - } - - public static Location fromString(String locString) { - String trimmed = locString.replace("[", "").replace("]", ""); - String[] tokens = trimmed.split(","); - return Location.create( - Integer.parseInt(tokens[0].trim()), - Integer.parseInt(tokens[1].trim()), - Integer.parseInt(tokens[2].trim()) - ); - } - - @Override - public int hashCode() { - return z << 30 | x << 15 | y; - } - - /** - * Gets the x. - * @return The x. - */ - public int getX() { - return x; - } - - /** - * Sets the x. - * @param x The x to set. - */ - public void setX(int x) { - this.x = x; - } - - /** - * Gets the y. - * @return The y. - */ - public int getY() { - return y; - } - - /** - * Sets the y. - * @param y The y to set. - */ - public void setY(int y) { - this.y = y; - } - - /** - * Gets the z. - * @return The z. - */ - public int getZ() { - return z % 4; - } - - /** - * Sets the z. - * @param z The z to set. - */ - public void setZ(int z) { - this.z = z; - } - - @NotNull - public List getStepComponents(Direction dir) { - List output = new ArrayList<>(2); - int stepX = dir.getStepX(); - int stepY = dir.getStepY(); - - if (stepX != 0) output.add(transform(stepX, 0, 0)); - if (stepY != 0) output.add(transform(0, stepY, 0)); - return output; - } - - public Direction deriveDirection(Location location) { - int diffX = location.x - this.x; - int diffY = location.y - this.y; - - diffX = diffX >= 0 ? Math.min(diffX, 1) : -1; - diffY = diffY >= 0 ? Math.min(diffY, 1) : -1; - - StringBuilder sb = new StringBuilder(); - - if (diffY != 0) { - if (diffY > 0) { - sb.append("NORTH"); - } else { - sb.append("SOUTH"); - } - } - - if (diffX != 0) { - if (sb.length() > 0) sb.append("_"); - if (diffX > 0) { - sb.append("EAST"); - } else { - sb.append("WEST"); - } - } - - if (sb.length() == 0) return null; - return Direction.valueOf(sb.toString()); - } -} diff --git a/Server/src/main/core/game/world/map/Location.kt b/Server/src/main/core/game/world/map/Location.kt new file mode 100644 index 000000000..a202e4a49 --- /dev/null +++ b/Server/src/main/core/game/world/map/Location.kt @@ -0,0 +1,257 @@ +package core.game.world.map + +import core.api.utils.Vector +import core.game.interaction.DestinationFlag +import core.game.node.Node +import core.game.world.map.RegionManager.getTeleportLocation +import core.game.world.map.path.Pathfinder +import core.tools.RandomFunction +import kotlin.math.* + +/** + * Specify a position (x,y,z), implements [Node]. + * - Coordinates x and y can be negative for delta values. Values ±16,383. + * - Coordinate z (level) has a quirk, this class doesn't let you init with a negative z. Values 0 - 3. + */ +/* +Official Terms + - Zones: 8x8 tiles + - Map Squares: 64x64 tiles + - Build Area: 104x104 tiles (13 x 13 chunks centered around player's chunk) - client's loaded area + +Some Definitions: + - Cardinal: Four Base Direction (N E S W) + - Diagonal: Four Diagonal directions (NE SE SW NW) + - Compass: All 8 directions (For this game, compass in relation to 8-point Cardinal+Diagonal) + - Euclidean distance: Straight-line length of two points. Direct calculation. e.g. [2,3] distance=√13(≈3.61) + - Manhattan distance: Cardinal length of two points. Count of steps along grid lines. e.g. [2,3] distance=5 + - Chebyshev distance: Cardinal+Diagonal length of two points. Count of king moves to make. e.g. [2,3] distance=3 + */ +class Location(var hash: LocationHash) : Node(null, null) { + @JvmOverloads + constructor(x: Int, y: Int, z: Int = 0) : this( + LocationHash(x, y, (z % 4 + 4) % 4) // The previous z calc didn't make sense since they play around with negative numbers. + ) + constructor(location: Location) : this(location.hash) + constructor(x: Int, y: Int, z: Int, randomizer: Int) : + this(x + RandomFunction.getRandom(randomizer), y + RandomFunction.getRandom(randomizer), z) + init { super.destinationFlag = DestinationFlag.LOCATION } // This is to set the type of [Node]. + + // Map properties directly to the underlying hash for convenience + var x: Int + get() = hash.x + set(value) { hash = LocationHash(value, y, z) } + + var y: Int + get() = hash.y + set(value) { hash = LocationHash(x, value, z) } + + var z: Int + get() = hash.z + set(value) { hash = LocationHash(x, y, (value % 4 + 4) % 4) } + + companion object { + // Alternate static constructors + @JvmStatic @JvmOverloads fun create(x: Int, y: Int, z: Int = 0): Location = Location(x, y, z) + @JvmStatic fun create(location: Location): Location = Location(location) + /** @return new Location parsed from a string format "[x, y, z]". */ + fun fromString(locString: String): Location { + val coordinates = locString.removeSurrounding("[", "]").split(",").map { it.trim().toInt() } + return create(coordinates[0], coordinates[1], coordinates[2]) + } + + /** @return Straight-line(Euclidean) distance between two locations. */ + @JvmStatic fun getDistance(first: Location, second: Location): Double { + val xDiff: Int = first.x - second.x + val yDiff: Int = first.y - second.y + return sqrt((xDiff * xDiff + yDiff * yDiff).toDouble()) + } + + /** @return A location representing the delta between two locations. This is not a good function because it doesn't care about z. */ + @JvmStatic fun getDelta(location: Location, other: Location): Location = create(other.x - location.x, other.y - location.y, other.z - location.z) + + /** @return A random location within radius, optionally verifying reachability. */ + fun getRandomLocation(main: Location, radius: Int, reachable: Boolean): Location { + val loc = getTeleportLocation(main, radius) + if (!reachable) return loc + val path = Pathfinder.find(main, loc, false, Pathfinder.DUMB) + if (!path.isSuccessful && path.points.isNotEmpty()) { + return create(path.points.last.x, path.points.last.y, main.z) + } else if (path.isSuccessful) { + return loc + } else { + return main + } + } + } + + // Function overrides of Node class and Java class + override fun getLocation(): Location = this + override fun toString(): String = "[$x, $y, $z]" + override fun hashCode(): Int = hash.hash + override fun equals(other: Any?): Boolean { + return when (other) { + is Location -> other.hash == this.hash + is LocationHash -> other == this.hash + else -> false + } + } + fun equals(x: Int, y: Int, z: Int): Boolean = hash == LocationHash(x, y, z) + + // Derived variables. Please note the following isn't a 1-1 representation of what is officially used. + + // Region - (Officially: Map Square) 64x64 + /** Unique index "key" (Officially: region_uid) for a 64x64 area. Made of x and y joined bitwise together. Used in xteas.json. */ + val regionId: Int get() = x shr 6 shl 8 or (y shr 6) // Cuts off the end 6 bits of both x y and joins them together. E.g. 10392 + /** The chunk x index for this position (x/8). For sets of 8x8 area. THIS IS NAMED WRONG, THIS IS A CHUNK(8) NOT REGION(64). */ + val regionX: Int get() = x shr 3 // shr 3 is essentially divide by 8 + /** The chunk y index for this position (y/8). For sets of 8x8 area. THIS IS NAMED WRONG, THIS IS A CHUNK(8) NOT REGION(64). */ + val regionY: Int get() = y shr 3 // shr 3 is essentially divide by 8 + // Local - x,y position inside a region + /** The relative x position inside a region(64x64). */ + val localX: Int get() = x and 63 // and 63 is essentially modulo 64 + /** The relative y position inside a region(64x64). */ + val localY: Int get() = y and 63 // and 63 is essentially modulo 64 + /** @return Check if it is in the same region id. */ + fun isInRegion(region: Int): Boolean = regionId == region + + // Chunk - (Officially: Zones) 8x8 + val chunkBase: Location get() = create(regionX shl 3, regionY shl 3, z) + /** The chunk x index for this position RELATIVE to a region[localX](x/8). THIS IS NOT GLOBAL CHUNK X. */ + val chunkX: Int get() = localX shr 3 // shr 3 is essentially divide by 8 (note this is localX) + /** The chunk y index for this position RELATIVE to a region[localY](y/8). THIS IS NOT GLOBAL CHUNK Y. */ + val chunkY: Int get() = localY shr 3 // shr 3 is essentially divide by 8 (note this is localX) + /** The relative x position inside a chunk(8x8). */ + val chunkOffsetX: Int get() = localX and 7 // and 7 is essentially modulo 8 + /** The relative y position inside a chunk(8x8). */ + val chunkOffsetY: Int get() = localY and 7 // and 7 is essentially modulo 8 + + // Scene - (Officially: Build Area) 104x104 - Made of 13x13 chunks(8x8) + /** The relative x position inside a scene(104x104). Used for client viewport rendering. */ + val sceneX: Int get() = x - (regionX - 6 shl 3) // Centers the current region at [7chunk,7chunk] + /** The relative y position inside a scene(104x104). Used for client viewport rendering. */ + val sceneY: Int get() = y - (regionY - 6 shl 3) // Centers the current region at [7chunk,7chunk] + /** @return The local scene x-coordinate relative to another location's regionX. */ + fun getSceneX(loc: Location): Int = x - (loc.regionX - 6 shl 3) + /** @return The local scene y-coordinate relative to another location's regionY. */ + fun getSceneY(loc: Location): Int = y - (loc.regionY - 6 shl 3) + + + + /** @return The location incremented by another location's coordinates. (POTENTIAL Z PROBLEMS HERE) */ + // TODO: THE FOLLOWING TRANSFORM HAS SHIT Z HANDLING. + fun transform(l: Location): Location = Location(x + l.x, y + l.y, (z + l.z) % 4) + /** @return The location incremented by specific x, y, z differences. */ + fun transform(diffX: Int, diffY: Int, z: Int): Location = Location(x + diffX, y + diffY, (this.z + z) % 4) + /** @return The location incremented by direction and steps. */ + @JvmOverloads fun transform(dir: Direction, steps: Int = 1): Location = Location(x + dir.stepX * steps, y + dir.stepY * steps, z) + /** Location transformed by a Vector. Limit usage to more complicated functionality. */ + fun transform(vector: Vector): Location = create(x + floor(vector.x).toInt(), y + floor(vector.y).toInt()) + + + /** @return True if this location is 1 tile N, W, S, or E of the node. THIS CAN BE REFACTORED OUT. */ + fun isNextTo(node: Node): Boolean { + val l = node.location + return if (l.y == y) abs(l.x - x) == 1 else if (l.x == x) abs(l.y - y) == 1 else false + } + + /** @return True if this other location is within dist of this location. */ + @JvmOverloads fun withinDistance(other: Location, dist: Int = MapDistance.RENDERING.distance): Boolean { + if (other.z != z) return false + return getDistance(this, other) <= dist + } + + /** @return True if within specified distance using max norm (Chebyshev) distance. */ + fun withinMaxnormDistance(other: Location, dist: Int): Boolean = if (other.z != z) false else max(abs(other.x - x), abs(other.y - y)) <= dist + + /** @return The straight-line(Euclidean) distance between this and another location. */ + fun getDistance(other: Location): Double = getDistance(this, other) + + /** @returns ArrayList of the 8 cardinal and diagonal tiles. Order is important and follows [Direction] indexing **/ + val surroundingTiles: ArrayList get() { + val locations = ArrayList() + locations.add(transform(-1, -1, 0)) + locations.add(transform(0, -1, 0)) + locations.add(transform(1, -1, 0)) + locations.add(transform(1, 0, 0)) + locations.add(transform(1, 1, 0)) + locations.add(transform(0, 1, 0)) + locations.add(transform(-1, 1, 0)) + locations.add(transform(-1, 0, 0)) + return locations + } + + /** @returns ArrayList of the 4 cardinal direction tiles. Order is important and follows [Direction] indexing **/ + val cardinalTiles: ArrayList get() { + val locations = ArrayList() + locations.add(transform(-1, 0, 0)) + locations.add(transform(0, -1, 0)) + locations.add(transform(1, 0, 0)) + locations.add(transform(0, 1, 0)) + return locations + } + + + /** + * Gets the directional components of a movement step. + * TODO: would prefer this in the other place. This doesn't exactly belong here since this isn't a common function for other instances of Location + * + * we can move diagonally but there's no melee attacking diagonally. its gotta be from N, W, S or E. + * if we are moving diagonally, we then need to check if we can attack from one of the valid directions. + * in other words we need to check the two tiles that share a corner with our destination and make sure they can be attacked from + * + * picture this: you are at (0,0). im at (1,1). you want to strangle my head off, so you got to check + * 1. to the west of destination: (1,1) + (-1,0) = (0,1) or north from where you started + * 2. to the south of the destination: (1,1) + (0,-1) = (1,0) or east from where you started + * this function is used only by CombatSwingHandler.kt and assumes that we return the X tiles, west or east, FIRST + * and then the Y tiles South and North. + */ + fun getStepComponents(dir: Direction): List { + val output = ArrayList(2) + if (dir.stepX != 0) output.add(transform(-dir.stepX, 0, 0)) + if (dir.stepY != 0) output.add(transform(0, -dir.stepY, 0)) + return output + } + + /** @return the Direction of the another location relative to this one. */ + fun deriveDirection(location: Location): Direction? { + val dx = (location.x - x).let { if (it >= 0) min(it, 1) else -1 } + val dy = (location.y - y).let { if (it >= 0) min(it, 1) else -1 } + val sb = StringBuilder() + if (dy != 0) sb.append(if (dy > 0) "NORTH" else "SOUTH") + if (dx != 0) { if (sb.isNotEmpty()) sb.append("_"); sb.append(if (dx > 0) "EAST" else "WEST") } + return if (sb.isEmpty()) null else Direction.valueOf(sb.toString()) + } + + /** + * Locations as an Integer rather than a Location object. This is for space efficiency. + * Inner class, because this is only used here, and to avoid adding another standard until this replaces Location + * + * 32 bits for an int (z z x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y) + * - Rightmost set of 15 bits is for y (can handle negative numbers) + * - Middle set of 15 bits is for x (can handle negative numbers) + * - Leftmost set of 2 bits is for z + * + * Since x and y occupy 15 bits (0 to 32,767) it becomes => 1 sign + 14 bits (e.g., 16,384) for range of ±16,383. + */ + @JvmInline + value class LocationHash(val hash: Int) { + constructor(x: Int, y: Int, z: Int = 0) : this( + ((z and 0x3) shl 30) or + ((x and 0x7FFF) shl 15) or + (y and 0x7FFF) + ) + val x: Int get() { + val raw = (hash ushr 15) and 0x7FFF + // If the 15th bit is set, it's a negative number in 15-bit range + return if (raw >= 0x4000) raw - 0x8000 else raw + } + + val y: Int get() { + val raw = hash and 0x7FFF + // If the 15th bit is set, it's a negative number + return if (raw >= 0x4000) raw - 0x8000 else raw + } + val z: Int get() = hash ushr 30 + } +} 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..9fb7b848b 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.x - xdist && + player.location.x <= l.x + xdist && + player.location.y >= l.y - ydist && + player.location.y <= l.y + 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..f83724ec0 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. @@ -242,13 +257,21 @@ public final class RegionPlane { public void add(GroundItem item) { Location l = item.getLocation(); RegionChunk c = getRegionChunk(l.getLocalX() / RegionChunk.SIZE, l.getLocalY() / RegionChunk.SIZE); + // This adds it to the chunk to be interactable with. if (!c.getItems().add(item)) { return; } GroundItem g = (GroundItem) item; if (g.isPrivate()) { if (g.getDropper() != null) { - PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(g.getDropper(), item)); + // Limit it to the region(8x8 square) you are in +/-2 regions, as the chunk loader will load it all over again. + // This is based on MapChunkRenderer which seems to "update" 2 chunks away. + // Look at RegionChunk.java ConstructGroundItem.write(buffer, item); + if (Math.abs(g.getDropper().getLocation().getRegionX() - l.getRegionX()) <= 2 && + Math.abs(g.getDropper().getLocation().getRegionY() - l.getRegionY()) <= 2 && + g.getDropper().getLocation().getZ() == l.getZ()) { + PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(g.getDropper(), item)); + } } return; } @@ -418,4 +441,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/MapZone.java b/Server/src/main/core/game/world/map/zone/MapZone.java index 630d06194..7adb16dfc 100644 --- a/Server/src/main/core/game/world/map/zone/MapZone.java +++ b/Server/src/main/core/game/world/map/zone/MapZone.java @@ -130,12 +130,25 @@ public abstract class MapZone implements Zone { } /** - * Checks if the entity is able to continue attacking the target. - * @param e the attacking entity. - * @param target The target. - * @param style The combat style used. - * @param message If a message should be send. - * @return {@code True} if so. + * Checks if the entity is able to continue attacking the target within this zone. + * + *

This method is called during attack validation to determine if zone-specific rules + * allow the attack to proceed. It's invoked by + * {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)} + * for each zone the attacking entity is in.

+ * + *

The default implementation returns {@code true}, allowing the attack to proceed. + * Subclasses should override this method to implement zone-specific attack restrictions.

+ *

This method is part of the attack validation chain: + * {@link Entity#isAttackable(Entity, CombatStyle, boolean)} -> + * {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)} -> + * {@code MapZone.continueAttack()} + {@link Entity#continueAttack(Entity, CombatStyle, boolean)}

+ * + * @param e the attacking entity + * @param target the target node being attacked + * @param style the combat style being used + * @param message whether to send a message to the player explaining why the attack cannot continue + * @return {@code true} if the zone allows the attack to continue */ public boolean continueAttack(Entity e, Node target, CombatStyle style, boolean message) { return true; @@ -164,14 +177,23 @@ public abstract class MapZone implements Zone { if (multi || e.isIgnoreMultiBoundaries(t) || e.getZoneMonitor().isIgnoreMultiBoundaries(t)) { return true; } + Entity attacker = e instanceof Familiar ? ((Familiar) e).getOwner() : e; Entity target = t.getAttribute("combat-attacker", e); - if (t.getAttribute("combat-time", -1L) > time && target != e && target.isActive()) { + if (target instanceof Familiar) { + target = ((Familiar) target).getOwner(); + } + if (t.getAttribute("combat-time", -1L) > time && target != attacker && target.isActive()) { if (message && e instanceof Player) { ((Player) e).getPacketDispatch().sendMessage("Someone else is already fighting this" + (t instanceof Player ? " player." : ".")); } return false; } - if (e.getAttribute("combat-time", -1L) > time && (target = e.getAttribute("combat-attacker", t)) != t && target.isActive()) { + Entity attackerOnE = e.getAttribute("combat-attacker", t); + if (attackerOnE instanceof Familiar) { + attackerOnE = ((Familiar) attackerOnE).getOwner(); + } + Entity tResolved = t instanceof Familiar ? ((Familiar) t).getOwner() : t; + if (e.getAttribute("combat-time", -1L) > time && attackerOnE != tResolved && attackerOnE.isActive()) { if (t.getId() == 1614 || t.getId() == 1613) { return true; } 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..65b4a0322 100644 --- a/Server/src/main/core/game/world/map/zone/ZoneMonitor.java +++ b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java @@ -5,6 +5,7 @@ import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.music.MusicEntry; import core.game.node.entity.player.link.music.MusicZone; import core.game.node.entity.player.link.request.RequestType; import core.game.node.item.Item; @@ -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)) { if (entity.isPlayer()) { entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); } @@ -217,6 +252,33 @@ 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; + } + /** * Checks if the death should start for an entity. * @param entity the entity. @@ -336,21 +398,25 @@ public final class ZoneMonitor { for (Iterator it = musicZones.iterator(); it.hasNext();) { MusicZone zone = it.next(); if (!zone.getBorders().insideBorder(l.getX(), l.getY())) { - zone.leave(player, false); - it.remove(); + if (zone.leave(player, false)) { + it.remove(); + } } } - for (MusicZone zone : player.getViewport().getRegion().getMusicZones()) { - if (!zone.getBorders().insideBorder(l.getX(), l.getY())) { - continue; - } - if (!musicZones.contains(zone)) { + Region r = player.getViewport().getRegion(); + for (MusicZone zone : r.getMusicZones()) { + if (zone.getBorders().insideBorder(l.getX(), l.getY())) { zone.enter(player); - musicZones.add(zone); + return; } } - if (musicZones.isEmpty() && !player.getMusicPlayer().isPlaying()) { - player.getMusicPlayer().playDefault(); + int music = r.getMusic(); + if (music == -1) { + if (!player.getMusicPlayer().isPlaying()) { + player.getMusicPlayer().playDefault(); + } + } else { + player.getMusicPlayer().unlock(music, true); } } diff --git a/Server/src/main/core/game/world/map/zone/ZoneRestriction.java b/Server/src/main/core/game/world/map/zone/ZoneRestriction.java index 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..cce6ab1d4 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,37 @@ 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()); + String npcString = e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? "the Chaos Elemental" : ("a " + e.asNpc().getName().toLowerCase()); + Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from " + npcString + "!"); + } + for (int j : PVP_GEAR) { + boolean chance = RandomFunction.roll(pvpGearRate); + if (chance) { + Item reward; + if (j == Items.MORRIGANS_JAVELIN_13879 || j == Items.MORRIGANS_THROWING_AXE_13883) { + reward = new Item(j, RandomFunction.random(15, 50)); + } else { + reward = new Item(j); } + GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); + String npcString = e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? "the Chaos Elemental" : ("a " + e.asNpc().getName().toLowerCase()); + Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from " + npcString + "!"); } } } @@ -134,8 +145,6 @@ public final class WildernessZone extends MapZone { return super.interact(e, target, option); } - - @Override public boolean enter(Entity e) { if (e instanceof Player) { @@ -146,17 +155,6 @@ public final class WildernessZone extends MapZone { p.getSkullManager().setWilderness(true); p.getSkullManager().setLevel(getWilderness(p)); } - for (int i = 0; i < 7; i++) { - if (i == 5 || i == 3) { - continue; - } - if(p.getAttributes().containsKey("overload") || p.getSkills().getLevel(i) > 118){ - if (p.getSkills().getLevel(i) > p.getSkills().getStaticLevel(i)) { - p.getSkills().setLevel(i, p.getSkills().getStaticLevel(i)); - p.removeAttribute("overload"); - } - } - } if (p.getFamiliarManager().hasFamiliar() && !p.getFamiliarManager().hasPet()) { Familiar familiar = p.getFamiliarManager().getFamiliar(); familiar.transform(); @@ -210,7 +208,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()) { @@ -265,6 +263,23 @@ public final class WildernessZone extends MapZone { return false; } } + if (e instanceof Player && target instanceof NPC && ((NPC) target).getName().contains("Revenant")) { + Player p = (Player) e; + NPC npc = (NPC) target; + int level = p.getSkullManager().getLevel(); + int npcWildLevel = getWilderness(npc); + if (npcWildLevel < level) { + level = npcWildLevel; + } + int combat = p.getProperties().getCurrentCombatLevel(); + int npcCombat = npc.getDefinition().getCombatLevel(); + if (combat - level > npcCombat || combat + level < npcCombat) { + if (message) { + p.getPacketDispatch().sendMessage("The level difference between you and your opponent is too great."); + } + return false; + } + } return true; } @@ -290,17 +305,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..0b97f197c 100644 --- a/Server/src/main/core/game/world/repository/Repository.kt +++ b/Server/src/main/core/game/world/repository/Repository.kt @@ -1,15 +1,17 @@ package core.game.world.repository -import core.game.node.entity.npc.NPC import content.region.wilderness.handlers.revenants.RevenantNPC +import core.ServerConstants +import core.api.sendMessage +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.ServerConstants import core.game.world.update.UpdateSequence -import java.util.* import java.util.concurrent.CopyOnWriteArrayList +const val GE_NEWS_MUTE_ATTRIBUTE = "/save:ge:news-mute" + /** * The repository holding all node lists, etc in the game world. * @author Emperor @@ -55,20 +57,7 @@ object Repository { */ @JvmStatic val disconnectionQueue = DisconnectionQueue() - /** - * Sends a market update message to all players. - * @param string The string. - * @param color The color. - */ - @JvmOverloads - fun sendMarketUpdate(string: String, icon: Int = 12, color: String = "") { - val players: Array = playerNames.values.toTypedArray() - val size = players.size - for (i in 0 until size) { - val player = players[i] as Player ?: continue - player.sendMessage("" + color + "Market Update: " + string) - } - } + /** * Send a news message to all players. * @param string The string. @@ -76,11 +65,23 @@ object Repository { */ @JvmStatic fun sendNews(string: String, icon: Int = 12, color: String = "CC6600") { + sendNews(string, icon, color, false) + } + + @JvmStatic + fun sendGrandExchangeNews(string: String, icon: Int = 12, color: String = "CC6600") { + sendNews(string, icon, color, true) + } + + @JvmStatic + fun sendNews(string: String, icon: Int, color: String, isGeNews: Boolean) { + if (!ServerConstants.ENABLE_GLOBAL_CHAT) return val players: Array = playerNames.values.toTypedArray() val size = players.size for (i in 0 until size) { - val player = players[i] as Player ?: continue - player.sendMessage("News: $string") + val player = players[i] as? Player ?: continue + if (isGeNews && player.getAttribute(GE_NEWS_MUTE_ATTRIBUTE, false)) continue + sendMessage(player, "News: $string") } } @@ -142,9 +143,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 +160,6 @@ object Repository { playerNames.remove(player.name) UpdateSequence.renderablePlayers.remove(player) player.session.disconnect() - player.clear(true) } /** @@ -193,6 +192,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 +204,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/PlayerRenderer.kt b/Server/src/main/core/game/world/update/PlayerRenderer.kt index 0fb1068bc..e192463d7 100644 --- a/Server/src/main/core/game/world/update/PlayerRenderer.kt +++ b/Server/src/main/core/game/world/update/PlayerRenderer.kt @@ -107,7 +107,7 @@ object PlayerRenderer { if (offsetX < 0) { offsetX += 32 } - val appearance = info.appearanceStamps[other.index and 0x800] != other.updateMasks.appearanceStamp + val appearance = info.appearanceStamps[other.index] != other.updateMasks.appearanceStamp val update = appearance || other.updateMasks.isUpdateRequired || other.updateMasks.hasSynced() buffer.putBits(1, if (update) 1 else 0) buffer.putBits(5, offsetX) @@ -117,7 +117,7 @@ object PlayerRenderer { info.localPlayers.add(other) if (update) { if (appearance) { - info.appearanceStamps[other.index and 0x800] = other.updateMasks.appearanceStamp + info.appearanceStamps[other.index] = other.updateMasks.appearanceStamp } writeMaskUpdates(player, other, flags, appearance, true) } 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..a83f0235c --- /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 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) + val encodedText = CP1252.toBytes(context.text) + chatBuf[0] = encodedText.size.toByte() + val offset = 1 + StringUtils.encryptPlayerChat ( + chatBuf, + 0, 1, + encodedText.size, + encodedText + ) + 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..e1108a473 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt @@ -0,0 +1,277 @@ +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, aprilfools") + 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()) + } + } + "aprilfools" -> { + ServerConstants.FORCE_APRIL_FOOLS = true + for (p in Repository.players) { + notify(p, colorize("%GApril Fools is now active!")) + } + } + 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/aprilfools/AprilFoolsEvent.kt b/Server/src/main/core/game/worldevents/holiday/aprilfools/AprilFoolsEvent.kt new file mode 100644 index 000000000..4a4f11a72 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/aprilfools/AprilFoolsEvent.kt @@ -0,0 +1,40 @@ +package core.game.worldevents.holiday.aprilfools + +import core.ServerConstants +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes +import core.game.system.command.Privilege +import core.tools.colorize +import java.util.Calendar + +class AprilFoolsEvent : Commands, LoginListener { + private fun isActive(): Boolean { + if (ServerConstants.FORCE_APRIL_FOOLS) return true + if (!ServerConstants.APRIL_FOOLS_EVENT) return false + val cal = Calendar.getInstance() + return cal.get(Calendar.MONTH) == Calendar.APRIL && cal.get(Calendar.DAY_OF_MONTH) == 1 + } + + override fun login(player: Player) { + if (!isActive()) return + sendMessage(player, colorize("Use command ::wheel to spin the new prize wheel!", "00ffff")) + } + + override fun defineCommands() { + define("wheel", Privilege.STANDARD) { player, args -> + if (!isActive()) { + sendMessage(player, colorize("-->%R${args[0]}: command not found")) + return@define + } + if (getAttribute(player, "aprilfools:wheelspun", false)) { + sendMessage(player, "I'm not falling for that again.") + return@define + } + sendNews("${player.username} just tried to spin the wheel!") + sendChat(player, "I'm spinning!") + emote(player, Emotes.SPIN) + setAttribute(player, "aprilfools:wheelspun", 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..f0b5a76c3 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt @@ -0,0 +1,53 @@ +package core.game.worldevents.holiday.christmas.randoms + +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..6b129b906 100644 --- a/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt @@ -1,117 +1,320 @@ package core.game.worldevents.holiday.easter -import core.game.node.entity.npc.NPC +import core.ServerConstants +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.event.EventHook +import core.game.event.XPGainEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item -import core.game.system.task.Pulse +import core.game.world.GameWorld import core.game.world.map.Direction import core.game.world.map.Location -import org.rs09.consts.Items -import org.rs09.consts.NPCs +import core.game.world.map.path.Pathfinder import core.game.worldevents.WorldEvent -import core.game.system.config.GroundSpawnLoader -import core.game.world.GameWorld -import core.game.world.repository.Repository -import core.tools.secondsToTicks +import core.tools.RandomFunction +import core.tools.StringUtils +import core.tools.colorize +import org.rs09.consts.Items import java.util.* -class EasterEvent : WorldEvent("easter") { - val LUMBRIDGE_SPOTS = arrayOf(Location.create(3190, 3240, 0), - Location.create(3219, 3204, 0),Location.create(3212, 3201, 0),Location.create(3205, 3209, 0), - Location.create(3205, 3217, 0),Location.create(3211, 3213, 0),Location.create(3206, 3229, 0), - Location.create(3212, 3226, 0),Location.create(3212, 3244, 0),Location.create(3202, 3252, 0), - Location.create(3197, 3220, 0),Location.create(3189, 3207, 0),Location.create(3229, 3200, 0), - Location.create(3228, 3205, 0),Location.create(3251, 3212, 0),Location.create(3232, 3237, 0)) +class EasterEvent : WorldEvent("easter"), TickListener, InteractionListener, LoginListener, Commands { + private val spawnedItems = ArrayList() + private var timeUntilNextEggSpawn = 0 + private var currentLoc = "" - val DRAYNOR_SPOTS = arrayOf(Location.create(3085, 3242, 0),Location.create(3083, 3236, 0),Location.create(3093, 3224, 0), - Location.create(3099, 3241, 0),Location.create(3095, 3255, 0),Location.create(3089, 3264, 0),Location.create(3089, 3265, 0), - Location.create(3088, 3268, 0),Location.create(3090, 3274, 0),Location.create(3100, 3281, 0),Location.create(3116, 3265, 0), - Location.create(3123, 3272, 0),Location.create(3079, 3261, 0)) + override fun tick() { + if (timeUntilNextEggSpawn-- > 0) return + timeUntilNextEggSpawn = EGG_SPAWN_TIME - val FALADOR_SPOTS = arrayOf( - Location.create(2961, 3332, 0),Location.create(2967, 3336, 0),Location.create(2974, 3329, 0), - Location.create(2979, 3346, 0),Location.create(2970, 3348, 0),Location.create(2955, 3375, 0),Location.create(2942, 3386, 0), - Location.create(2937, 3385, 0),Location.create(3005, 3370, 0),Location.create(3006, 3383, 0),Location.create(3007, 3387, 0), - Location.create(2985, 3391, 0),Location.create(2984, 3381, 0),Location.create(2980, 3384, 0),Location.create(3025, 3345, 0), - Location.create(3060, 3389, 0),Location.create(3052, 3385, 0)) + for (egg in spawnedItems) + { + GroundItemManager.destroy(egg) + } + spawnedItems.clear() - val EDGEVILLE_SPOTS = arrayOf(Location.create(3077, 3487, 0),Location.create(3082, 3487, 0), - Location.create(3089, 3481, 0),Location.create(3084, 3479, 0),Location.create(3108, 3499, 0), - Location.create(3110, 3517, 0),Location.create(3091, 3512, 0),Location.create(3092, 3507, 0), - Location.create(3081, 3513, 0),Location.create(3079, 3513, 1),Location.create(3080, 3508, 1), - Location.create(3108, 3499, 0),Location.create(3093, 3467, 0)) + val (locName, locData) = getRandomLocations() + currentLoc = locName - val TREE_GNOME_STRONGHOLD_SPOTS = arrayOf( - Location.create(2480, 3507, 0),Location.create(2486, 3513, 0),Location.create(2453, 3512, 0), - Location.create(2442, 3484, 0),Location.create(2438, 3486, 0),Location.create(2441, 3506, 0), - Location.create(2471, 3482, 0),Location.create(2482, 3478, 0),Location.create(2480, 3469, 0), - Location.create(2489, 3440, 0),Location.create(2470, 3417, 0),Location.create(2478, 3402, 0), - Location.create(2486, 3407, 0),Location.create(2492, 3404, 0),Location.create(2493, 3413, 0), - Location.create(2446, 3395, 0),Location.create(2422, 3398, 0),Location.create(2421, 3402, 0),Location.create(2418, 3398, 0), - Location.create(2401, 3415, 0),Location.create(2397, 3436, 0),Location.create(2409, 3448, 0),Location.create(2482, 3391, 0)) + for (loc in locData) + { + val gi = GroundItemManager.create(GroundItem( + Item(eggs.random()), + loc, + EGG_SPAWN_TIME, + null + )) + gi.forceVisible = true + spawnedItems.add(gi) + } - val locations = arrayOf("Lumbridge","Draynor Village","Falador","Edgeville","Tree Gnome Stronghold") - - val spawnedItems = ArrayList() - val eggs = arrayOf(Items.EASTER_EGG_11027,Items.EASTER_EGG_11028,Items.EASTER_EGG_11029,Items.EASTER_EGG_11030) - var recentLoc = "" - - override fun checkActive(): Boolean { - val isApril = Calendar.getInstance().get(Calendar.MONTH) == Calendar.APRIL - val isBefore9th = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) < 9 - return false + sendNews("Eggs have appeared in $locName!") } - override fun initialize() { - super.initialize() - val easterBunny = NPC(NPCs.EASTER_BUNNY_7197) - easterBunny.isNeverWalks = true - easterBunny.direction = Direction.WEST - easterBunny.location = Location.create(3225, 3212, 0) - for(i in 0 until 5){ - val bunny = NPC(NPCs.EASTER_BUNNY_3688) - bunny.location = Location.create(3223, 3213, 0) - bunny.isWalks = true - bunny.walkRadius = 4 - bunny.init() + override fun login(player: Player) { + if (spawnedItems.isNotEmpty()) + sendMessage(player, colorize("%GEggs were last spotted in $currentLoc!")) + player.hook(Event.XpGained, xpEventHook) + } + + override fun defineCommands() { + define("eggspawntest") {player, _ -> + notify(player, "Spawning 10 eggs nearby...") + repeat(10) { spawnEggFor(player) } } - easterBunny.init() - GameWorld.settings?.message_model = 715 - GameWorld.settings?.message_string = "Happy Easter!" - GameWorld.Pulser.submit(object : Pulse(){ - override fun pulse(): Boolean { - if(delay == 1){ - delay = secondsToTicks(if(GameWorld.settings?.isDevMode == true) 60 else 3600) - } - for(item in spawnedItems){ - GroundItemManager.destroy(item) - } - spawnedItems.clear() - val locName = locations.random() - val locs = when(locName){ - "Lumbridge" -> LUMBRIDGE_SPOTS - "Draynor Village" -> DRAYNOR_SPOTS - "Falador" -> FALADOR_SPOTS - "Edgeville" -> EDGEVILLE_SPOTS - "Tree Gnome Stronghold" -> TREE_GNOME_STRONGHOLD_SPOTS - else -> LUMBRIDGE_SPOTS - }.toMutableList() + } - Repository.sendNews("A bunch of eggs have been spotted in $locName!") - recentLoc = locName - val itemLocs = ArrayList() - for(i in 0 until locs.size / 2){ - val loc = locs.random() - locs.remove(loc) - itemLocs.add(loc) - } + object xpEventHook : EventHook + { + override fun process(entity: Entity, event: XPGainEvent) { + if (entity !is Player) return - for(i in itemLocs){ - spawnedItems.add(GroundSpawnLoader.GroundSpawn(-1, Item(eggs.random()),i).init()) - } - return false + val lastRoll = getAttribute(entity, LAST_EGG_ROLL, 0) + if (getWorldTicks() - lastRoll < 10) return + setAttribute(entity, LAST_EGG_ROLL, getWorldTicks()) + + val activeEggRate = if (GameWorld.settings!!.isDevMode) EGG_RATE_DEV else EGG_RATE + + if (RandomFunction.roll(activeEggRate)) + spawnEggFor(entity) + } + } + + + fun getRandomLocations() : Pair> + { + val toReturn = ArrayList() + val name = locNames.random() + val locs = locationsForName(name) + for (loc in locs) + if (RandomFunction.nextBool()) toReturn.add(loc) + return Pair(name, toReturn) + } + + override fun checkActive(cal: Calendar): Boolean { + return cal.get(Calendar.MONTH) == Calendar.APRIL || ServerConstants.FORCE_EASTER_EVENTS + } + + private fun onEggBroken (player: Player) + { + val eggsBroken = getAttribute(player, EGGS_BROKEN, 0) + 1 + + if (eggsBroken == 10) + { + player.emoteManager.unlock(Emotes.BUNNY_HOP) + Emotes.BUNNY_HOP.play(player) + sendMessage(player, colorize("%RYou have unlocked the 'Bunny Hop' emote!")) + } + else if (eggsBroken == 15) + { + addItemOrDrop(player, BUN_EARS) + sendMessage(player, colorize("%RYou have been given bunny ears!")) + } + else if (eggsBroken % 5 == 0) + { + var trackUnlocked = false; + for (track in tracks) + { + if (player.musicPlayer.hasUnlocked(track)) + continue + player.musicPlayer.unlock(track) + trackUnlocked = true + break } - }) + if (!trackUnlocked) + giveRandomLoot(player) + } + else giveRandomLoot(player) + + setAttribute(player, EGGS_BROKEN, eggsBroken) + } + + private fun giveRandomLoot (player: Player) + { + //Give some loot + val loot = lootTable.roll(player)[0] + addItemOrDrop(player, loot.id, loot.amount) + val term = if (loot.amount == 1) + if (StringUtils.isPlusN(loot.name)) "an" + else "a" + else "some" + + val name = if (loot.amount == 1) loot.name else StringUtils.plusS(loot.name) + sendMessage(player, "Inside the egg you find $term ${name.lowercase()}.") + } + + override fun defineListeners() { + on (eggs, IntType.GROUNDITEM, "take") {player, node -> + if (node !is GroundItem) return@on true + queueScript(player, strength = QueueStrength.NORMAL) { stage -> + when (stage) + { + 0 -> { + animate(player, STOMP_ANIM) + return@queueScript delayScript(player, 1) + } + 1 -> { + val item = GroundItemManager.get(node.id, node.location, null) + if (item == null || !item.isActive) return@queueScript stopExecuting(player) + sendGraphics(gfxForEgg(node.id), node.location) + GroundItemManager.destroy(node) + spawnedItems.remove(node) + onEggBroken(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript keepRunning(player) + } + return@on true + } + on (eggs, IntType.ITEM, "release") {player, node -> + if (node !is Item) return@on true + queueScript(player, strength = QueueStrength.NORMAL) { stage -> + when (stage) + { + 0 -> { + animate(player, STOMP_ANIM) + return@queueScript delayScript(player, 1) + } + 1 -> { + if (!removeItem(player, node)) return@queueScript stopExecuting(player) + sendGraphics(gfxForEgg(node.id), player.location) + onEggBroken(player) + return@queueScript stopExecuting(player) + } + } + return@queueScript keepRunning(player) + } + return@on true + } + } + + companion object + { + const val LOC_LUM = "Lumbridge" + const val LOC_DRAYNOR = "Draynor Village" + const val LOC_FALADOR = "Falador" + const val LOC_EDGE = "Edgeville" + const val LOC_TGS = "Tree Gnome Stronghold" + const val EGG_SPAWN_TIME = 5_000 + const val EGG_A = 11027 + const val EGG_B = 11028 + const val EGG_C = 11029 + const val EGG_D = 11030 + const val GFX_A = 1040 + const val GFX_B = 1045 + const val GFX_C = 1042 + const val GFX_D = 1043 + const val STOMP_ANIM = 10017 + const val BUN_EARS = 14658 + const val TRACK_BSB = 502 + const val TRACK_EJ = 273 + const val TRACK_FB = 603 + const val EGG_RATE = 64 + const val EGG_RATE_DEV = 3 + + val EGGS_BROKEN = "/save:easter${ServerConstants.STARTUP_MOMENT.get(Calendar.YEAR)}:eggs" + val LAST_EGG_ROLL = "easter:lasteggroll" + val tracks = arrayOf(TRACK_BSB, TRACK_EJ, TRACK_FB) + val eggs = intArrayOf(EGG_A, EGG_B, EGG_C, EGG_D) + val locNames = arrayOf(LOC_LUM, LOC_DRAYNOR, LOC_FALADOR, LOC_EDGE, LOC_TGS) + val LUMBRIDGE_SPOTS = arrayOf(Location.create(3190, 3240, 0), + Location.create(3219, 3204, 0),Location.create(3212, 3201, 0),Location.create(3205, 3209, 0), + Location.create(3205, 3217, 0),Location.create(3211, 3213, 0),Location.create(3206, 3229, 0), + Location.create(3212, 3226, 0),Location.create(3212, 3244, 0),Location.create(3202, 3252, 0), + Location.create(3197, 3220, 0),Location.create(3189, 3207, 0),Location.create(3229, 3200, 0), + Location.create(3228, 3205, 0),Location.create(3251, 3212, 0),Location.create(3232, 3237, 0)) + + val DRAYNOR_SPOTS = arrayOf(Location.create(3085, 3242, 0), + Location.create(3083, 3236, 0),Location.create(3093, 3224, 0),Location.create(3099, 3241, 0), + Location.create(3095, 3255, 0),Location.create(3089, 3264, 0),Location.create(3089, 3265, 0), + Location.create(3088, 3268, 0),Location.create(3090, 3274, 0),Location.create(3100, 3281, 0), + Location.create(3116, 3265, 0),Location.create(3123, 3272, 0),Location.create(3079, 3261, 0), + Location.create(3127, 3280, 0),Location.create(3118, 3287, 0),Location.create(3111, 3270, 0), + Location.create(3119, 3277, 0),Location.create(3113, 3283, 0)) + + val FALADOR_SPOTS = arrayOf( + Location.create(2961, 3332, 0),Location.create(2967, 3336, 0),Location.create(2974, 3329, 0), + Location.create(2979, 3346, 0),Location.create(2970, 3348, 0),Location.create(2955, 3375, 0), + Location.create(2942, 3386, 0),Location.create(2937, 3385, 0),Location.create(3005, 3370, 0), + Location.create(3006, 3383, 0),Location.create(3007, 3387, 0),Location.create(2985, 3391, 0), + Location.create(2984, 3381, 0),Location.create(2980, 3384, 0),Location.create(3025, 3345, 0), + Location.create(3060, 3389, 0),Location.create(3052, 3385, 0)) + + val EDGEVILLE_SPOTS = arrayOf(Location.create(3077, 3487, 0),Location.create(3082, 3487, 0), + Location.create(3089, 3481, 0),Location.create(3084, 3479, 0),Location.create(3108, 3499, 0), + Location.create(3110, 3517, 0),Location.create(3091, 3512, 0),Location.create(3092, 3507, 0), + Location.create(3081, 3513, 0),Location.create(3079, 3513, 1),Location.create(3080, 3508, 1), + Location.create(3093, 3467, 0)) + + val TREE_GNOME_STRONGHOLD_SPOTS = arrayOf( + Location.create(2480, 3507, 0),Location.create(2486, 3513, 0),Location.create(2453, 3512, 0), + Location.create(2442, 3484, 0),Location.create(2438, 3486, 0),Location.create(2441, 3506, 0), + Location.create(2471, 3482, 0),Location.create(2482, 3478, 0),Location.create(2480, 3469, 0), + Location.create(2489, 3440, 0),Location.create(2470, 3417, 0),Location.create(2478, 3402, 0), + Location.create(2486, 3407, 0),Location.create(2492, 3404, 0),Location.create(2493, 3413, 0), + Location.create(2446, 3395, 0),Location.create(2422, 3398, 0),Location.create(2421, 3402, 0), + Location.create(2418, 3398, 0),Location.create(2401, 3415, 0),Location.create(2397, 3436, 0), + Location.create(2409, 3448, 0),Location.create(2482, 3391, 0)) + + fun locationsForName (name: String) : Array + { + return when (name) + { + LOC_LUM -> LUMBRIDGE_SPOTS + LOC_DRAYNOR -> DRAYNOR_SPOTS + LOC_FALADOR -> FALADOR_SPOTS + LOC_TGS -> TREE_GNOME_STRONGHOLD_SPOTS + LOC_EDGE -> EDGEVILLE_SPOTS + else -> LUMBRIDGE_SPOTS + } + } + + fun gfxForEgg (egg: Int) : Int + { + return when (egg) + { + EGG_A -> GFX_A + EGG_B -> GFX_B + EGG_C -> GFX_C + EGG_D -> GFX_D + else -> GFX_A + } + } + + fun spawnEggFor (player: Player) + { + val dirs = Direction.values() + val dir = dirs[RandomFunction.random(dirs.size)] + var loc = player.location.transform(dir, 3) + val path = Pathfinder.find(player, loc) + loc = Location.create(path.points.last.x, path.points.last.y, loc.z) + GroundItemManager.create(Item(eggs.random()), loc, player) + sendMessage(player, colorize("%RAn egg has appeared nearby.")) + } + + val lootTable = WeightBasedTable.create( + WeightedItem(Items.EASTER_EGG_1962, 1, 15, 0.10), + WeightedItem(Items.PURPLE_SWEETS_10476, 5, 15, 0.10), + WeightedItem(Items.COINS_995, 500, 2500, 0.15), + WeightedItem(Items.ESS_IMPLING_JAR_11246, 1, 1, 0.05), + WeightedItem(Items.BABY_IMPLING_JAR_11238, 1, 1, 0.05), + WeightedItem(Items.EARTH_IMPLING_JAR_11244, 1, 1, 0.05), + WeightedItem(Items.GOURM_IMPLING_JAR_11242, 1, 1, 0.05), + WeightedItem(Items.MAGPIE_IMPLING_JAR_11252, 1, 1, 0.025), + WeightedItem(Items.ECLECTIC_IMPLING_JAR_11248, 1, 1, 0.025), + WeightedItem(Items.ESS_IMPLING_JAR_11246, 1, 1, 0.025), + WeightedItem(Items.NATURE_IMPLING_JAR_11250, 1, 1, 0.015), + WeightedItem(Items.NINJA_IMPLING_JAR_11254, 1, 1, 0.015), + WeightedItem(Items.DRAGON_IMPLING_JAR_11256, 1, 1, 0.005) + ) } } \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt index 4dd777cf3..2fdc30c28 100644 --- a/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt @@ -1,50 +1,10 @@ package core.game.worldevents.holiday.easter -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.node.item.GroundItemManager -import org.rs09.consts.Items -import org.rs09.consts.NPCs import core.game.interaction.InteractionListener -import core.game.interaction.IntType class EasterEventListeners : InteractionListener { - - val EGG_ATTRIBUTE = "/save:easter:eggs" - val eggs = intArrayOf(Items.EASTER_EGG_11027, Items.EASTER_EGG_11028, Items.EASTER_EGG_11029, Items.EASTER_EGG_11030) - override fun defineListeners() { - on(eggs, IntType.ITEM, "take"){ player, node -> - player.pulseManager.run(object : MovementPulse(player,node, DestinationFlag.ITEM){ - override fun pulse(): Boolean { - if(player.inventory.contains(Items.BASKET_OF_EGGS_4565,1) || player.equipment.contains(Items.BASKET_OF_EGGS_4565,1)){ - val item = GroundItemManager.get(node.id,node.location,null) - GroundItemManager.destroy(item) - player.incrementAttribute(EGG_ATTRIBUTE) - player.sendMessage("You place the egg in the basket. You now have ${player.getAttribute(EGG_ATTRIBUTE,0)} eggs!") - } else { - player.sendMessage("You need to have your egg basket with you to collect these.") - } - return true - } - }) - return@on true - } - - on(Items.BASKET_OF_EGGS_4565, IntType.ITEM, "operate"){ player, node -> - val numEggs = player.getAttribute(EGG_ATTRIBUTE) ?: 0 - player.dialogueInterpreter.sendDialogue("You have $numEggs eggs in the basket.") - return@on true - } - - on(NPCs.EASTER_BUNNY_7197, IntType.NPC, "talk-to"){ player, node -> - val npc = node.asNpc() - npc.faceLocation(player.location) - val NEED_BASKET = !(player!!.inventory.contains(Items.BASKET_OF_EGGS_4565,1) || player!!.equipment.contains(Items.BASKET_OF_EGGS_4565,1) || player!!.bank.contains(Items.BASKET_OF_EGGS_4565,1)) - player.dialogueInterpreter.open(EasterBunnyDialogueFile(NEED_BASKET),npc) - return@on true - } } diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt b/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt deleted file mode 100644 index 50c26daea..000000000 --- a/Server/src/main/core/game/worldevents/holiday/halloween/SimpleHalloweenEvent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package core.game.worldevents.holiday.halloween - -import core.game.worldevents.PluginSet -import core.game.worldevents.WorldEvent -import core.game.world.GameWorld - -class SimpleHalloweenEvent : WorldEvent("hween"){ - override fun checkActive(): Boolean { - return false - } - - override fun initialize() { - plugins = PluginSet( - GrimDialogue() - ) - super.initialize() - GameWorld.settings?.message_model = 800 - GameWorld.settings?.message_string = "A mysterious figure has appeared in Draynor! You should go investigate!" - log("Initialized.") - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/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..037c9ba10 --- /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, 2, 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) { + } +} 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..956b1d9da 100644 --- a/Server/src/main/core/net/IoSession.java +++ b/Server/src/main/core/net/IoSession.java @@ -122,12 +122,45 @@ public class IoSession { * @param service The executor service. */ public IoSession(SelectionKey key, ExecutorService service) { + this(key, service, resolveRemoteAddress(key)); + } + + /** + * Constructs a new {@code IoSession} with an explicit remote address. + * @param key The selection key. + * @param service The executor service. + * @param remoteAddress The remote address. + */ + public IoSession(SelectionKey key, ExecutorService service, String remoteAddress) { this.key = key; this.service = service; - this.address = getRemoteAddress().replaceAll("/", "").split(":")[0]; + this.address = normalizeRemoteAddress(remoteAddress); this.js5Queue = new JS5Queue(this); } + private static String resolveRemoteAddress(SelectionKey key) { + if (key == null || !(key.channel() instanceof SocketChannel)) { + return "127.0.0.1"; + } + try { + return ((SocketChannel) key.channel()).getRemoteAddress().toString(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static String normalizeRemoteAddress(String remoteAddress) { + if (remoteAddress == null || remoteAddress.trim().isEmpty()) { + return "127.0.0.1"; + } + String normalized = remoteAddress.replace("/", ""); + int separator = normalized.lastIndexOf(':'); + if (separator > 0) { + return normalized.substring(0, separator); + } + return normalized; + } + /** * Fires a write event created using the current event producer. * @param context The event context. @@ -213,20 +246,17 @@ public class IoSession { return; } active = false; - 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 (key != null) { + key.cancel(); + SocketChannel channel = (SocketChannel) key.channel(); + channel.socket().close(); + } + if (getPlayer() != null) { + try { + getPlayer().clear(); + } catch (Exception e) { + e.printStackTrace(); + } } object = null; } catch (IOException e) { @@ -440,4 +470,4 @@ public class IoSession { this.clientInfo = clientInfo; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/NioReactor.java b/Server/src/main/core/net/NioReactor.java index 42fea5a1d..fb07b826d 100644 --- a/Server/src/main/core/net/NioReactor.java +++ b/Server/src/main/core/net/NioReactor.java @@ -143,6 +143,21 @@ public final class NioReactor implements Runnable { */ public void terminate() { running = false; + try { + if (channel != null) { + if (channel.getChannel() != null) { + channel.getChannel().close(); + } + if (channel.getSocket() != null) { + channel.getSocket().close(); + } + channel.getSelector().wakeup(); + channel.getSelector().close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + service.shutdownNow(); } } \ No newline at end of file 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..c7d24f36b 100644 --- a/Server/src/main/core/net/lobby/WorldList.java +++ b/Server/src/main/core/net/lobby/WorldList.java @@ -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..c05798490 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -1,88 +1,102 @@ package core.net.packet -import core.game.event.ButtonClickEvent +import content.global.ame.events.maze.MazeInterface +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 content.global.skill.summoning.familiar.FamiliarSpecial +import core.ServerConstants import core.api.getAttribute +import core.api.log import core.api.sendMessage -import core.api.tryPop +import core.api.utils.Vector import core.cache.def.impl.ItemDefinition 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.skill.summoning.familiar.FamiliarSpecial -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.node.scenery.Scenery -import core.game.system.communication.ClanRank -import core.game.system.communication.CommunicationInfo -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.net.amsc.MSPacketRepository -import core.net.packet.context.PlayerContext -import core.net.packet.out.ClearMinimapFlag -import org.rs09.consts.Components -import proto.management.ClanMessage -import proto.management.JoinClanRequest -import proto.management.LeaveClanRequest -import core.ServerConstants +import core.game.event.ButtonClickEvent 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.game.interaction.* +import core.game.node.Node +import core.game.node.entity.combat.spell.MagicSpell +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.tools.SystemLogger +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.item.GroundItemManager +import core.game.node.item.Item +import core.game.node.scenery.Scenery import core.game.system.command.CommandSystem +import core.game.system.communication.ClanRank +import core.game.system.communication.CommunicationInfo +import core.game.system.communication.GlobalChat +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 +import core.game.world.update.flag.EntityFlag +import core.game.world.update.flag.context.ChatMessage +import core.net.amsc.MSPacketRepository +import core.net.packet.context.PlayerContext import core.net.packet.`in`.Packet import core.net.packet.`in`.RunScript +import core.net.packet.out.ClearMinimapFlag +import core.tools.Log import core.worker.ManagementEvents +import org.rs09.consts.Components +import proto.management.ClanMessage +import proto.management.JoinClanRequest +import proto.management.LeaveClanRequest import java.io.PrintWriter import java.io.StringWriter +import java.lang.Math.min import java.util.* object PacketProcessor { - val queue = LinkedList() + private var queue = LinkedList() + private var processingQueue = LinkedList() + private val queueLock = Any() @JvmStatic fun enqueue(pkt: Packet) { - queue.addLast(pkt) + synchronized(queueLock) { + queue.addLast(pkt) + } } @JvmStatic fun processQueue() { - var countThisCycle = queue.size + synchronized(queueLock) { + if (queue.isEmpty()) { + return + } + val queued = queue + queue = processingQueue + processingQueue = queued + } val sw = StringWriter() val pw = PrintWriter(sw) var pkt: Packet - while (countThisCycle-- > 0) { - pkt = queue.tryPop(Packet.NoProcess()) + while (processingQueue.isNotEmpty()) { + pkt = processingQueue.pollFirst() ?: return + if (pkt is Packet.NoProcess) { + synchronized(queueLock) { + queue.clear() + } + processingQueue.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 +171,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 @@ -182,7 +200,7 @@ object PacketProcessor { offer.itemID = pkt.itemId offer.sell = false if (!PriceIndex.canTrade(pkt.itemId)) { - sendMessage(pkt.player, "That item is blacklisted from the grand exchange.") + sendMessage(pkt.player, "That item is blacklisted from the Grand Exchange.") return } offer.player = pkt.player @@ -202,18 +220,35 @@ 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_GLOBAL_CHAT && pkt.message.startsWith("//")) { + if (getAttribute(pkt.player, GlobalChat.ATTR_GLOBAL_MUTE, false)) + return + + val messages = splitChatMessage(pkt.message.substring(2), pkt.player.name.length + 3, false) + for (message in messages) { + if (message.isNotBlank()) + PlayerMonitor.logChat(pkt.player, "global", message) + GlobalChat.process(pkt.player.username, message, Rights.getChatIcon(pkt.player)) + } + return + } + 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) + PlayerMonitor.logChat(pkt.player, "clan", message) + 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 +285,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 +308,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}") } } @@ -288,7 +323,10 @@ object PacketProcessor { } is Packet.TrackingAfkTimeout -> { if (pkt.player.details.rights != Rights.ADMINISTRATOR) + { + pkt.player.isAfkLogout = true pkt.player.packetDispatch.sendLogout() + } } is Packet.TrackingCameraPos -> { //TODO Refactor the player monitor to be actually useful and log this @@ -297,6 +335,7 @@ object PacketProcessor { //TODO see above todo } + else -> {} } } @@ -396,6 +435,11 @@ object PacketProcessor { return if (player.getAttribute("magic:delay", -1) > GameWorld.ticks) return + // Validate spell packet interface matches player's actual spellbook + // This is to prevent player being able to cast spells when client has stale interface (e.g. after Spellbook swap reverts) + if (iface in intArrayOf(192, 193, 430)) { + if (!SpellUtils.validateSpellbookInterface(player, iface)) return + } val book = SpellUtils.getBookFromInterface(iface) if (book != "none") SpellListeners.run(child, type, book, player, target) @@ -436,15 +480,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 +507,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 +544,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 +598,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 +620,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 +645,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 +669,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 +690,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 +704,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 +713,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 +727,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 +744,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 +759,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 +778,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 +821,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..e2656b02a 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,15 @@ 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()); + OUTGOING_PACKETS.put(LastLoginInfo.class, new LastLoginInfo()); } /** @@ -79,15 +88,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); + } } } - -} +} \ No newline at end of file diff --git a/Server/src/main/core/net/packet/PacketWriteQueue.kt b/Server/src/main/core/net/packet/PacketWriteQueue.kt index 294a54452..ed3de3d43 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.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 var packetsToWrite = LinkedList>() + private var packetsToFlush = LinkedList>() + private val queueLock = Any() @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,37 @@ class PacketWriteQueue : TickListener { @JvmStatic fun push(packet: OutgoingPacket, context: T) { - if (queueLock.isHeldByCurrentThread) - packetsToQueue.add(QueuedPacket(packet, context)) - else + if (context == null) { + log(this::class.java, Log.ERR, "${packet::class.java.simpleName} tried to queue with a null context!") + return + } + synchronized(queueLock) { packetsToWrite.add(QueuedPacket(packet, context)) + } } @JvmStatic fun flush() { - queueLock.lock() - - var hasEnded = false - while (!hasEnded) { - try { - val packet = packetsToWrite.pop() - write(packet?.out ?: continue, packet.context ?: continue) - } catch (e: NoSuchElementException) { - hasEnded = true + synchronized(queueLock) { + if (packetsToWrite.isEmpty()) { + return } + val queued = packetsToWrite + packetsToWrite = packetsToFlush + packetsToFlush = queued } - - if (packetsToWrite.isNotEmpty()) { - SystemLogger.logWarn(this::class.java, "Packet queue was NOT empty! Remaining packets: ${packetsToWrite.size}") + val sw = StringWriter() + val pw = PrintWriter(sw) + while (packetsToFlush.isNotEmpty()) { + val pkt = packetsToFlush.pollFirst() ?: continue 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 +68,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..77e86af95 100644 --- a/Server/src/main/core/net/packet/out/CommunicationMessage.java +++ b/Server/src/main/core/net/packet/out/CommunicationMessage.java @@ -6,6 +6,7 @@ import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.PacketHeader; import core.net.packet.context.MessageContext; +import core.tools.CP1252; import core.tools.StringUtils; import core.game.bots.AIPlayer; @@ -21,32 +22,32 @@ 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/LastLoginInfo.kt b/Server/src/main/core/net/packet/out/LastLoginInfo.kt new file mode 100644 index 000000000..1892cf487 --- /dev/null +++ b/Server/src/main/core/net/packet/out/LastLoginInfo.kt @@ -0,0 +1,15 @@ +package core.net.packet.out + +import core.net.packet.IoBuffer +import core.net.packet.OutgoingPacket +import core.net.packet.context.PlayerContext +import java.net.Inet4Address + +class LastLoginInfo : OutgoingPacket { + override fun send(context: PlayerContext) { + val buffer = IoBuffer(164) + buffer.imp4(Inet4Address.getByName(context.player.details.ipAddress).hashCode()) + buffer.cypherOpcode(context.player.session.isaacPair.output) + context.player.session.write(buffer) + } +} \ No newline at end of file diff --git a/Server/src/main/core/net/packet/out/UpdateSceneGraph.java b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java index 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/net/websocket/GameWebSocketServer.kt b/Server/src/main/core/net/websocket/GameWebSocketServer.kt new file mode 100644 index 000000000..dcd7d3f3f --- /dev/null +++ b/Server/src/main/core/net/websocket/GameWebSocketServer.kt @@ -0,0 +1,73 @@ +package core.net.websocket + +import core.api.log +import core.net.IoSession +import core.tools.Log +import org.java_websocket.WebSocket +import org.java_websocket.handshake.ClientHandshake +import org.java_websocket.server.WebSocketServer +import java.net.InetSocketAddress +import java.nio.ByteBuffer +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class GameWebSocketServer(port: Int, poolSize: Int) : WebSocketServer(InetSocketAddress(port)) { + private val service: ExecutorService = Executors.newFixedThreadPool(poolSize) { runnable -> + Thread(runnable, "WebSocketIo").apply { + isDaemon = true + } + } + private val sessions = ConcurrentHashMap() + + init { + // This pool processes websocket frames; it is not a cap on connected webclients. + setTcpNoDelay(true) + setReuseAddr(true) + } + + override fun onOpen(conn: WebSocket, handshake: ClientHandshake) { + sessions[conn] = WebSocketIoSession(conn, service) + } + + override fun onClose(conn: WebSocket, code: Int, reason: String, remote: Boolean) { + val session = sessions.remove(conn) + if (session != null && session.isActive) { + session.disconnect() + } + } + + override fun onMessage(conn: WebSocket, message: String) { + log(javaClass, Log.WARN, "Closing websocket client ${conn.remoteSocketAddress}: text frames are unsupported.") + conn.close(1003, "Binary frames only") + } + + override fun onMessage(conn: WebSocket, message: ByteBuffer) { + val session: IoSession = sessions[conn] ?: run { + conn.close(1011, "Session not initialized") + return + } + val copy = ByteBuffer.allocate(message.remaining()) + copy.put(message) + copy.flip() + service.execute(session.producer.produceReader(session, copy)) + } + + override fun onError(conn: WebSocket?, ex: Exception?) { + if (ex != null) { + log(javaClass, Log.ERR, "WebSocket server error: ${ex.message}") + } + if (conn != null) { + sessions.remove(conn)?.disconnect() + } + } + + override fun onStart() { + log(javaClass, Log.INFO, "WebSocket listener started on port $port.") + } + + override fun stop(timeout: Int, closeMessage: String) { + super.stop(timeout, closeMessage) + service.shutdownNow() + } +} diff --git a/Server/src/main/core/net/websocket/WebSocketIoSession.kt b/Server/src/main/core/net/websocket/WebSocketIoSession.kt new file mode 100644 index 000000000..49693ec09 --- /dev/null +++ b/Server/src/main/core/net/websocket/WebSocketIoSession.kt @@ -0,0 +1,41 @@ +package core.net.websocket + +import core.net.IoSession +import org.java_websocket.WebSocket +import org.java_websocket.exceptions.WebsocketNotConnectedException +import java.nio.ByteBuffer +import java.util.concurrent.ExecutorService + +class WebSocketIoSession( + private val socket: WebSocket, + service: ExecutorService +) : IoSession(null, service, socket.remoteSocketAddress?.toString() ?: "127.0.0.1") { + + override fun queue(buffer: ByteBuffer?) { + if (buffer == null || !socket.isOpen) { + return + } + val copy = buffer.slice() + val data = ByteArray(copy.remaining()) + copy.get(data) + try { + socket.send(data) + } catch (e: WebsocketNotConnectedException) { + disconnect() + } + } + + override fun write() { + // WebSocket sessions bypass the SelectionKey-backed TCP write queue. + } + + override fun disconnect() { + if (!isActive) { + return + } + super.disconnect() + if (socket.isOpen) { + socket.close() + } + } +} diff --git a/Server/src/main/core/net/websocket/WebSocketTls.kt b/Server/src/main/core/net/websocket/WebSocketTls.kt new file mode 100644 index 000000000..981afe060 --- /dev/null +++ b/Server/src/main/core/net/websocket/WebSocketTls.kt @@ -0,0 +1,45 @@ +package core.net.websocket + +import core.ServerConstants +import core.api.log +import core.game.system.config.ServerConfigParser +import core.tools.Log +import org.java_websocket.server.DefaultSSLWebSocketServerFactory +import java.io.FileInputStream +import java.security.KeyStore +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext + +object WebSocketTls { + + private const val KEYSTORE_TYPE = "PKCS12" + + fun configure(server: GameWebSocketServer) { + if (!ServerConstants.WEBSOCKET_TLS_ENABLED) { + return + } + server.setWebSocketFactory(DefaultSSLWebSocketServerFactory(createContext())) + } + + private fun createContext(): SSLContext { + val keystorePath = ServerConstants.WEBSOCKET_TLS_KEYSTORE_PATH.trim() + if (keystorePath.isEmpty()) { + throw IllegalStateException("server.websocket_tls_enabled is true but websocket_tls_keystore_path is blank") + } + val resolvedKeystorePath = ServerConfigParser.parsePath(keystorePath) + val keystorePassword = ServerConstants.WEBSOCKET_TLS_KEYSTORE_PASSWORD.toCharArray() + + val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) + FileInputStream(resolvedKeystorePath).use { input -> + keyStore.load(input, keystorePassword) + } + + val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + keyManagerFactory.init(keyStore, keystorePassword) + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(keyManagerFactory.keyManagers, null, null) + log(WebSocketTls::class.java, Log.INFO, "Configured secure websocket listener using keystore $resolvedKeystorePath.") + return sslContext + } +} 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/CP1252.java b/Server/src/main/core/tools/CP1252.java index 9269b8710..c3f42e54a 100644 --- a/Server/src/main/core/tools/CP1252.java +++ b/Server/src/main/core/tools/CP1252.java @@ -1,18 +1,22 @@ package core.tools; -public class CP1252 { - private static char[] charMap = {'\u20ac', '\0', '\u201a', '\u0192', '\u201e', '\u2026', '\u2020', '\u2021', +public final class CP1252 { + private static final char[] CHAR_MAP = {'\u20ac', '\0', '\u201a', '\u0192', '\u201e', '\u2026', '\u2020', '\u2021', '\u02c6', '\u2030', '\u0160', '\u2039', '\u0152', '\0', '\u017d', '\0', '\0', '\u2018', '\u2019', '\u201c', '\u201d', '\u2022', '\u2013', '\u2014', '\u02dc', '\u2122', '\u0161', '\u203a', '\u0153', '\0', '\u017e', '\u0178'}; + private CP1252() { + /* utility class */ + } + public static char getFromByte(byte value) { int out = value & 0xff; if (out == 0) { throw new IllegalArgumentException("Non cp1252 character 0x" + Integer.toString(out, 16) + " provided"); } if (out >= 128 && out < 160) { - int cp1252 = charMap[out - 128]; + int cp1252 = CHAR_MAP[out - 128]; if (cp1252 == 0) { cp1252 = 63; } @@ -20,4 +24,26 @@ public class CP1252 { } return (char) out; } + + public static byte getByte(char value) { + if ((value > 0 && value < 128) || (value >= 160 && value <= 255)) { + return (byte) value; + } + + for (int i = 0; i < CHAR_MAP.length; i++) { + if (CHAR_MAP[i] != 0 && CHAR_MAP[i] == value) { + return (byte) (i + 128); + } + } + + return 63; + } + + public static byte[] toBytes(CharSequence value) { + byte[] out = new byte[value.length()]; + for (int i = 0; i < value.length(); i++) { + out[i] = getByte(value.charAt(i)); + } + return out; + } } 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..b74092a20 100644 --- a/Server/src/main/core/tools/RandomFunction.java +++ b/Server/src/main/core/tools/RandomFunction.java @@ -35,6 +35,7 @@ public class RandomFunction { /** * Method used to ease the access of the random class. + * In certain cases, it is inclusive from the min value and exclusive from the max values specified. * @param a the minimum random value. * @param b the maximum random value. * @return the value as an {@link Integer}. @@ -49,8 +50,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 +271,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..bc55800b6 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 ""; + } } /** @@ -498,7 +502,7 @@ public final class StringUtils { return ""; int charsDecoded = 0; int i_4_ = 0; - String s = ""; + StringBuilder s = new StringBuilder(totalChars); for (;;) { byte i_7_ = (byte) buffer.get(); if (i_7_ >= 0) @@ -507,7 +511,7 @@ public final class StringUtils { i_4_ = anIntArray241[i_4_]; int i_8_; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (totalChars <= ++charsDecoded) break; i_4_ = 0; @@ -517,7 +521,7 @@ public final class StringUtils { else i_4_++; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (++charsDecoded >= totalChars) break; i_4_ = 0; @@ -527,7 +531,7 @@ public final class StringUtils { else i_4_ = anIntArray241[i_4_]; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (totalChars <= ++charsDecoded) break; i_4_ = 0; @@ -537,7 +541,7 @@ public final class StringUtils { else i_4_ = anIntArray241[i_4_]; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (totalChars <= ++charsDecoded) break; @@ -548,7 +552,7 @@ public final class StringUtils { else i_4_++; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (++charsDecoded >= totalChars) break; i_4_ = 0; @@ -558,7 +562,7 @@ public final class StringUtils { else i_4_ = anIntArray241[i_4_]; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (totalChars <= ++charsDecoded) break; i_4_ = 0; @@ -568,7 +572,7 @@ public final class StringUtils { else i_4_++; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (totalChars <= ++charsDecoded) break; i_4_ = 0; @@ -578,13 +582,13 @@ public final class StringUtils { else i_4_++; if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); if (++charsDecoded >= totalChars) break; i_4_ = 0; } } - return s; + return s.toString(); } catch (RuntimeException runtimeexception) { runtimeexception.printStackTrace(); } 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..e28f1045b --- /dev/null +++ b/Server/src/test/kotlin/content/RandomEventTests.kt @@ -0,0 +1,251 @@ +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 content.region.wilderness.handlers.KingBlackDragonArea +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBuilder +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +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)) + } + } + + @Test fun randomEventShouldNotSpawnInKingBlackDragonLair() { + TestUtils.getMockPlayer("antimacronospawninkbdlair").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + TestUtils.advanceTicks(5, false) + + // Manually configure KBD lair zone like ClassScanner does for MapArea instances + val kbdArea = KingBlackDragonArea() + val zone = object : MapZone(kbdArea.javaClass.simpleName + "MapArea", true, *kbdArea.getRestrictions()) {} + for(border in kbdArea.defineAreaBorders()) zone.register(border) + ZoneBuilder.configure(zone) + + val kbdLocation = Location.create(2273, 4698, 0) // KBD spawn location + p.location = kbdLocation + + Assertions.assertEquals(kbdLocation, p.location) + + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + Assertions.assertNull(AntiMacro.getEventNpc(p), "Random event should not spawn in KBD lair but one was found!") + } + } + + //FIXME + //@Test fun randomEventShouldNotSpawnIfOneAlreadyActive() { + // TestUtils.getMockPlayer("antimacronospawnifalreadyhas").use { p -> + // 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/ZamorakBrewTests.kt b/Server/src/test/kotlin/content/ZamorakBrewTests.kt new file mode 100644 index 000000000..42d6c099a --- /dev/null +++ b/Server/src/test/kotlin/content/ZamorakBrewTests.kt @@ -0,0 +1,67 @@ +package content + +import TestUtils +import content.data.consumables.Consumables +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 ZamorakBrewTests { + companion object { + init { + TestUtils.preTestSetup() + } + } + + @Test + fun zamorakBrewShouldDamageTenPercentOfCurrentLifepointsPlusTwo() { + TestUtils.getMockPlayer("zammybrewdrinker").use { p -> + p.skills.setStaticLevel(Skills.HITPOINTS, 99) + p.skills.lifepoints = 6 + p.inventory.clear() + p.inventory.add(Item(Items.ZAMORAK_BREW4_2450)) + + Consumables.getConsumableById(Items.ZAMORAK_BREW4_2450) + .consumable + .consume(p.inventory.get(0), p) + + Assertions.assertEquals(4, p.skills.lifepoints) + } + } + + @Test + fun zamorakMixDamageShouldBeBasedOnLifepointsBeforeItHeals() { + TestUtils.getMockPlayer("zammymixdrinker").use { p -> + p.skills.setStaticLevel(Skills.HITPOINTS, 99) + p.skills.lifepoints = 6 + p.inventory.clear() + p.inventory.add(Item(Items.ZAMORAK_MIX2_11521)) + + Consumables.getConsumableById(Items.ZAMORAK_MIX2_11521) + .consumable + .consume(p.inventory.get(0), p) + + // Damage on pre-drink lifepoints (10% of 6 + 2 = 2), then heal 6: 6 - 2 + 6 = 10 + Assertions.assertEquals(10, p.skills.lifepoints) + } + } + + @Test + fun zamorakMixShouldNotKillAtLowLifepoints() { + TestUtils.getMockPlayer("zammymixlowhp").use { p -> + p.skills.setStaticLevel(Skills.HITPOINTS, 99) + p.skills.lifepoints = 2 + p.inventory.clear() + p.inventory.add(Item(Items.ZAMORAK_MIX2_11521)) + + Consumables.getConsumableById(Items.ZAMORAK_MIX2_11521) + .consumable + .consume(p.inventory.get(0), p) + + // Hitsplat shows 2 (10% of 2 + 2), but the heal lands first: 2 + 6 - 2 = 6, no death + Assertions.assertEquals(6, p.skills.lifepoints) + } + } +} 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/region/wilderness/handlers/KBDBreathEffectTests.kt b/Server/src/test/kotlin/content/region/wilderness/handlers/KBDBreathEffectTests.kt new file mode 100644 index 000000000..d40bef56b --- /dev/null +++ b/Server/src/test/kotlin/content/region/wilderness/handlers/KBDBreathEffectTests.kt @@ -0,0 +1,56 @@ +package content.region.wilderness.handlers + +import TestUtils +import core.api.getTimer +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.equipment.FireType +import core.game.system.timer.impl.Poison +import core.game.world.map.Location +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class KBDBreathEffectTests { + init { + TestUtils.preTestSetup() + } + + @Test fun toxicBreathShouldPoisonOnImpactNotOnLaunch() { + val kbd = KingBlackDragonNPC(50, Location.create(2273, 4698, 0)) + val handler = kbd.getSwingHandler(false) + // KBD rolls a random FireType per swing, force toxic breath for determinism + val fireTypeField = handler.javaClass.getDeclaredField("fireType") + fireTypeField.isAccessible = true + fireTypeField.set(handler, FireType.TOXIC_BREATH) + + TestUtils.getMockPlayer("kbdbreathtiming").use { player -> + val state = BattleState(kbd, player) + state.estimatedHit = 0 + + handler.adjustBattleState(kbd, player, state) + Assertions.assertNull(getTimer(player), "Breath effect must not apply on the launch tick.") + + handler.impact(kbd, player, state) + Assertions.assertNotNull(getTimer(player), "Breath effect must apply when the attack lands.") + } + } + + @Test fun toxicBreathShouldNotPoisonVictimWhoTeleportedMidFlight() { + val kbd = KingBlackDragonNPC(50, Location.create(2273, 4698, 0)) + val handler = kbd.getSwingHandler(false) + val fireTypeField = handler.javaClass.getDeclaredField("fireType") + fireTypeField.isAccessible = true + fireTypeField.set(handler, FireType.TOXIC_BREATH) + + TestUtils.getMockPlayer("kbdbreathteleport").use { player -> + val state = BattleState(kbd, player) + state.estimatedHit = 0 + + handler.adjustBattleState(kbd, player, state) + // Disable impacts like TeleportManager.send does for a real teleport + player.impactHandler.setDisabledTicks(12) + + handler.impact(kbd, player, state) + Assertions.assertNull(getTimer(player), "Breath effect must not apply to a victim whose impacts are disabled (teleported away).") + } + } +} 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..84f6c640c 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..e789bae24 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,10 +25,8 @@ 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 #enables a default clan for players to join automatically. Should be an account with the same name as @name, with a clan set up already. enable_default_clan = true enable_bots = true @@ -37,7 +35,7 @@ motw_identifier = "0" #text shown for message of the week - @name will be replaced with the name property set above. motw_text = "Welcome to @name!" #the coordinates new players spawn at -new_player_location = "2524,5002,0" +new_player_location = "3094,3107,0" #the location of home teleport home_location = "3222,3218,0" autostock_ge = false @@ -49,6 +47,8 @@ max_adv_bots = 100 enable_doubling_money_scammers = true wild_pvp_enabled = false jad_practice_enabled = false +enable_global_chat = false +enable_castle_wars = false personalized_shops = false [paths] diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index c32123166..308db7417 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -1,4 +1,10 @@ [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" @@ -16,7 +22,20 @@ noauth_default_admin = true #NOTE: If we are not using auth, this determines whe #------------------------------------------------------------------------------------------------------ #The limit on how many different accounts a player can log into per day. daily_accounts_per_ip = 3 -watchdog_enabled = false +watchdog_enabled = true +connectivity_check_url = "https://google.com,https://2009scape.org" +connectivity_timeout = 500 +# Enables a websocket listener for browser clients. The websocket stream carries the +# same raw binary protocol as TCP; each websocket binary frame is treated as a byte chunk. +websocket_enabled = false +# Leave at 0 to use the default websocket port of 53594 + world_id. +websocket_port = 0 +# Enables TLS on the websocket listener so browser clients can connect over wss://. +websocket_tls_enabled = false +# PKCS12 keystore path, e.g. certs/dev-wss.p12. +websocket_tls_keystore_path = "" +# Optional. Leave blank for a PKCS12 file exported with an empty password. +websocket_tls_keystore_password = "" [database] database_name = "global" @@ -25,24 +44,31 @@ 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 = "2009Scape" #name used for announcements of bots selling items on the GE -name_ge = "2009scape" +name_ge = "2009Scape" debug = true dev = true start_gui = false -daily_restart = true +daily_restart = false #world number 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 #enables a default clan for players to join automatically. Should be an account with the same name as @name, with a clan set up already. enable_default_clan = true enable_bots = true @@ -51,28 +77,68 @@ motw_identifier = "0" #text shown for message of the week - @name will be replaced with the name property set above. motw_text = "Welcome to @name!" #the coordinates new players spawn at -new_player_location = "2524,5002,0" +new_player_location = "3094,3107,0" #the location of home teleport home_location = "3222,3218,0" autostock_ge = false -allow_token_purchase = true -skillcape_perks = true +allow_token_purchase = false +skillcape_perks = false increased_door_time = false enable_botting = false max_adv_bots = 100 enable_doubling_money_scammers = true wild_pvp_enabled = false -jad_practice_enabled = true -personalized_shops = true +jad_practice_enabled = false +enable_global_chat = false +#minimum HA value for announcements of bots selling on ge +ge_announcement_limit = 500 +enable_castle_wars = false +personalized_shops = false bots_influence_ge_price = true #verbose cutscene logging (for cutscenes in the new system) verbose_cutscene = false #show the rules the first time a player logs in -show_rules = true +show_rules = false #the number of revenants active at a time revenant_population = 30 #enable auto-buy/auto-sell on the GE. i_want_to_cheat = false +#better agility pyramid gp reward (gp reward = 1000 + ((agility level / 99) * 9000)) +better_agility_pyramid_gp = false +#better dragonfire shield attack (30 second cooldown instead of 2 minutes) +better_dfs = false +#new player announcement +new_player_announcement = false +#enables inauthentic candlelight random event (adds an additional normal random event) +inauthentic_candlelight_random = false +#enables holiday random events (no effect on normal random events) +holiday_event_randoms = false +#force holiday randoms (can only force one at a time) +force_halloween_randoms = false +force_christmas_randoms = false +#enables inauthentic april fools event +april_fools_event = false +force_april_fools = false +#runecrafting formula revision (573 introduced probabilistic multiple runes, 581 extrapolated probabilistic runes past 99) +runecrafting_formula_revision = 530 +#enables the enhanced deep wilderness, where the area past the members' fence applies a red skull that boosts brawler/pvp drop rates +enhanced_deep_wilderness = false +#enables wilderness-exclusive loot, i.e. brawling gloves and PvP gear, from revenants and the Chaos Elemental +wilderness_exclusive_loot = false +#enables the xp rates option on tutorial island +xp_rates = false +#enables the ironman option on tutorial island and the inauthentic game protocol addition to transmit chat icons for ironmen +ironman = false +#enables the custom-content ancient blueprint and ring of the star sprite +shooting_star_ring = false +#enables the inauthentic teleport option on the ring of wealth +ring_of_wealth_teleport = false +#enables second bank +second_bank = false +#enables inauthentic but non-dangerous commands for regular players +player_commands = false +#enables boosted rewards from fishing trawler consisting of key halves and pirate outfit pieces +boosted_trawler_rewards = false [paths] #path to the data folder, which contains the cache subfolder and such @@ -103,4 +169,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/build b/build index 86471b26f..43f08b78b 100755 --- a/build +++ b/build @@ -2,139 +2,90 @@ SCRIPT_DIR=$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P) -BUILD_MS=0 +CLEAN=0 BUILD_GS=0 -CLEAN_MS=0 -CLEAN_GS=0 SKIPTEST="" GS_SRC=$SCRIPT_DIR/Server -MS_SRC=$SCRIPT_DIR/Management-Server -CLEANOPT="" _JAR_DIR="$SCRIPT_DIR/builddir" help() { - echo "Usage: $0 [-h] <-m | -g> [-c ] [-o ]"; + echo "Usage: $0 [-h] [-q] <-g|-c> [-o ]"; echo " -h: Display this message."; - echo " -m: Build the management server."; echo " -g: Build the game server."; - echo " -c: Clean: m: management, g: gameserver. " - echo " Can specify both. Need at least one if present."; - echo " -o: Specify jar-file directory." + echo " -c: Clean previous build."; + echo " -o: Specify jar-file directory."; echo " -q: Quick build - will skip tests."; } error() { - echo $1; - exit -1; -} - -clean_ms() -{ - cd $MS_SRC - - if [[ "$CLEANOPT" == *"m"* ]]; then - echo "Cleaning the management server." - sh mvnw clean || error "Failed to clean the MS." - fi + echo "$1"; + exit 1; } clean_gs() { - cd $GS_SRC; - if [[ "$CLEANOPT" == *"g"* ]]; then + cd $GS_SRC || error "Could not change dir to game server directory." echo "Cleaning the game server." - sh mvnw clean || error "Failed to clean the game server. Giving up." - fi -} - -build_ms() -{ - cd $MS_SRC - - sh mvnw package $SKIPTEST || \ - error "Failed to build the management server. Giving up"; + sh mvnw clean || error "Failed to clean the game server. Giving up." } build_gs() { - cd $GS_SRC; + cd "$GS_SRC" || error "ERROR: Could not chdir to server src. Abort." sh mvnw package $SKIPTEST || \ error "Failed to build the game server. Giving up." } -while getopts "hqmgc:o:d" arg; do +while getopts "hqgc:o:d" arg; do case $arg in h) help exit 0 ;; - m) - BUILD_MS=1 - ;; g) BUILD_GS=1 ;; c) - CLEANOPT=${OPTARG} + CLEAN=1 ;; o) _JAR_DIR=${OPTARG} ;; d) - echo "BUILD_MS=$BUILD_MS" - echo "BUILD_GS=$BUILD_GS" - echo "CLEANOPT=$CLEANOPT" echo "_JAR_DIR=$_JAR_DIR" ;; q) SKIPTEST="-DskipTests" ;; + *) + error "Argument -$arg is not a known or valid argument." + ;; esac done -if [[ $CLEANOPT != "m" ]] \ - && [[ $CLEANOPT != "g" ]] \ - && [[ $CLEANOPT != "mg" ]] \ - && [[ $CLEANOPT != "" ]]; -then - error "Invalid cleaning option '$CLEANOPT'. Valid options are 'm', 'g', 'mg' or none." +if [ $BUILD_GS -eq 0 ] && [ $CLEAN -eq 0 ]; then + error "Need to either build or clean at least. See -h for details." fi -if [ $BUILD_MS -eq 0 ] && [ $BUILD_GS -eq 0 ] && [[ $CLEANOPT == "" ]]; then - error "Need to build or clean at least one of the modules. See -h for details." -fi +# Inside func is cd so mgmt prob fails if no condition +(( CLEAN )) && clean_gs -# Conditionals inside the functions. -clean_gs -clean_ms # ----------- -if [ -d $_JAR_DIR ]; then - rm -r $_JAR_DIR +if [ -d "$_JAR_DIR" ]; then + rm -r "$_JAR_DIR" fi -if [ $BUILD_MS -ne 1 ] && [ $BUILD_GS -ne 1 ]; then - echo "No build options specified. Stop." - exit 0 -fi - -mkdir -p $_JAR_DIR || error "Failed to create build directory."; - -if [ $BUILD_MS -eq 1 ]; then - build_ms - - # Will never execute if build_ms fails because it quits upon failure. - mv -v $MS_SRC/target/*-with-dependencies.jar $_JAR_DIR/ms.jar -fi +mkdir -p "$_JAR_DIR" || error "Failed to create build directory."; if [ $BUILD_GS -eq 1 ]; then build_gs # Will never execute if build_gs fails because it quits upon failure. - mv -v $GS_SRC/target/*-with-dependencies.jar $_JAR_DIR/server.jar + mv -v "$GS_SRC"/target/*-with-dependencies.jar "$_JAR_DIR"/server.jar fi diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..ba0cc0c40 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + server: + build: . + depends_on: + - db + restart: always + volumes: + - "bcache:/app/Server/target" + - "mcache:/root/.m2" + - "./Server/data:/app/Server/data" + - "./config:/app/Server/worldprops" + ports: + - "43594-43600:43594-43600" + + db: + # current LTS (Long-Term Support) version + image: mariadb:11.4-noble + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 30s + interval: 60s + timeout: 10s + retries: 3 + restart: always + volumes: + - "./db:/var/lib/mysql" + - "./Server/db_exports/global.sql:/docker-entrypoint-initdb.d/global.sql" + env_file: + - mysql.env + +volumes: + bcache: + mcache: diff --git a/dumps/530/530_interface_names.txt b/dumps/530/530_interface_names.txt index 8013a2693..77cbeac07 100644 --- a/dumps/530/530_interface_names.txt +++ b/dumps/530/530_interface_names.txt @@ -10,10 +10,15 @@ 9 ahoy_runedraw 10 ahoy_windspeed 11 bank_deposit_box +12 pulsing yellow rectangle 13 bankpin_main 14 bankpin_settings 15 banner_padlock_keys 16 banner_anti_virus +17 nothing +18 heads for the welcome screen? +19 heads for the welcome screen? +20 heads for the welcome screen? 21 banner_scamming 22 banner_security 23 banner_xmas @@ -85,6 +90,7 @@ 89 stab slash lunge block 90 staff with autocast 91 darts or knives +92 nothing 93 whip 94 confirm_destroy 95 sailing_transport_world_map @@ -315,6 +321,7 @@ 320 stats 321 swamp_boatjourney 322 swamp_boatjourney_back +323 tai bwo wannai parcel service 324 tanner 325 target 326 teleport_other @@ -458,7 +465,7 @@ 464 emotes 465 ooglog ogress opens health spa 466 switch diagram -467 menu with big buttons +467 POH_costume room 468 diango item return 469 zep_balloon_map 470 zep_interface @@ -511,6 +518,7 @@ 517 turn off black 518 old follower inv? 519 objbox +520 dream_cyrisus's courage 521 dream_cyrisus 522 dream_monster_stat 523 dream_player_stats @@ -565,6 +573,7 @@ 572 cws_warning_13 573 cws_warning_12 574 cws_warning_1 +575 all fired up beacon status 576 cws_warning_8 577 cws_warning_18 578 cws_warning_15 @@ -575,6 +584,8 @@ 583 cws_doomsayer 584 kr_king_statue 585 kr_pyramid_escape +586 change of address form for the citizens of seers' village +587 meet at camel left half 588 kr_picklock 589 clanjoin 590 clansetup @@ -694,7 +705,21 @@ 704 p safety 6 705 p safety 7 706 fuck this +707 player safety +708 player safety +709 player safety +710 player safety +711 player safety +712 meet at legend's guild from kent +713 slight darkness overlay +714 nothing 715 banner_easter08 +716 summoning_chocatrice +717 incubator controls +718 select an option +719 operating your incubator +720 chocatrice hatching +721 chocatrice hatching 722 summoning_side 723 carpet_info 724 carpet_runesquares @@ -703,6 +728,18 @@ 727 carpet_draughts 728 carpet_main 729 carpet_ticket +730 bounty hunter? +731 bounty hunter? +732 fog rewards shop +733 ivandis flail +734 fairy ring main +735 fairy ring log +736 dragon hatchling forge +737 pick a kitten +738 dillo_tok-xil-im +739 dillo_tzhaar numbers +740 open your stats prompt +741 congratulations! 742 graphics_options 743 sound_options 744 loginscreen @@ -714,42 +751,79 @@ 750 topstat_run 751 filterbuttons 752 chattop +753 nothing 754 pmchat 755 worldmap 756 boardgames_options 757 npcchatlarge 758 canoe_travel +759 display mode prompt 760 tutorial2_objbox +761 tutorial2_sir vant's requests 762 bank_v2_main 763 bank_v2_side +764 tutorial2_minimap tutorial 765 tutorial2_switch_task 766 tutorial2_death 767 bank_v2_help +768 nothing 769 tutorial2_text +770 black screen +771 tutorial character creation +772 returned items screen +773 luc2?_guthix and swirling runes cutscene +774 where do you want to rub the key +775 sum1?_spooky overlay, spirit of summer? +776 sum1?_spooky overlay fade to white +777 sum1?_sepia overlay 778 rcguild_side 779 rcguild_rewards 780 rcguild_map 781 rcguild_overlay +782 telescope star 783 crcs_tightrope 784 crcs_side 785 crcs_rewards 786 crcs_equipment +787 star landing time discovered by board 788 crcs_scoreboard +789 set/clear caller 790 clanwars_end 791 clanwars_setup 792 clanwars_setup_side +793 clanwars_free for all safe +794 varrock census +795 black overlay +796 ghostly mage +797 ancient magicks +798 warning you are currently carrying a lot of items +799 holding pen 800 banner_halloween 801 quickchat_locked 802 sc_tutorial_overlay 803 sc_scores_border +804 nothing 805 sc_item_transfer 806 sc_remaining_clay +807 nothing +808 yellow squares overlay +809 sc_score overlay and timer 810 sc_scores 811 sc_reward_shop +812 clan leader and allied leaders 813 sc_processing 814 luc2_chapter2 +815 luc2_door opening text 816 luc2_telescope_view +817 luc2_door opening +818 luc2_descending cliff +819 luc2_probably +820 luc2_book pages +821 luc2_swirling mass 822 luc2_chapter3 +823 luc2_hand with opening orb +824 luc2_pulsing black screen +825 luc2_ascending cliff 826 luc2_chapter1 827 luc2_lucien_projectiles 828 journal with map? @@ -758,3 +832,8 @@ 831 drawings on ice again 832 again 833 again ALL SEEM TO BE FOR BARB ASSAULT +834 nothing +835 nothing +836 nothing +837 2009scape rewards shop +838 anything beyond this is a crash \ No newline at end of file diff --git a/make-release.py b/make-release.py index b9855d3c7..4b898b4e2 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 -authors: ryannathans +date: {current_date} 00:00:00 +0000 +authors: "Update Bot" excerpt: "There have been some more changes in Gielinor..." +modtype: "Bot" +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/mysql.env.example b/mysql.env.example new file mode 100644 index 000000000..8772412d4 --- /dev/null +++ b/mysql.env.example @@ -0,0 +1,20 @@ +# Prefer no and generating a strong password for the root (+ special) user +MYSQL_ALLOW_EMPTY_PASSWORD="no" + +# You can change the db name also for obscurity. +# Has to be changed in the default.conf and in the global.sql init file then also. +MYSQL_DATABASE="global" + +# Beware: if not using root user, the database 'global' requires granting permissions for that user +# Root user is created automatically so dont provide it here. +#MYSQL_USER="dbuser" +#MYSQL_PASSWORD="CHANGEMEIFNOTUSINGROOTANDINGLOBALCONF" + +# Comment this out if you are using random root pw (below). +MYSQL_ROOT_PASSWORD="ALWAYSCHANGEMEHEREANDINGLOBALCONFIFUSINGROOT" + +# Or you can use a special non-root db user and generate a random pw for the root user instead. +# To improve security +# However, either initial root user access +# or editing Server/db_exports/global.sql to add grants for 'global' to the special user above. +MYSQL_RANDOM_ROOT_PASSWORD="no" diff --git a/run b/run index e1c4fdc10..a854c3200 100755 --- a/run +++ b/run @@ -3,15 +3,11 @@ SCRIPT_DIR=$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P) LOG_DIR=$SCRIPT_DIR/logs BUILD_DIR=$SCRIPT_DIR/builddir GS_SRC=$SCRIPT_DIR/Server -MS_SRC=$SCRIPT_DIR/Management-Server FORCE_REBUILD=0 REFRESH_BUILD=1 -GAMESERVER_ONLY=1 - GS_EXEC="cd $GS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/server.jar" -MS_EXEC="cd $MS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/ms.jar" # 0: parallel # 1: fancy tmux @@ -20,13 +16,12 @@ RUN_MODE=0 help() { - echo "Usage: $0 [-h] [-q] [-r] [-t] [-x] [-g] [-e ] [-o ]" + echo "Usage: $0 [-h] [-q] [-r] [-t] [-x] [-e ] [-o ]" echo " -h: Display this message." echo " -q: Don't perform the incremental build." echo " -r: Force clean rebuild." echo " -t: Only run tests, not the JARs." echo " -x: Run in a fancy tmux session." - echo " -g: Build + run only the game server (no management server)." echo " -e: Write STDERR to 'logs/filename' as well as the terminal."; echo " -o: Write STDOUT to 'logs/filename' as well as the terminal." } @@ -39,31 +34,17 @@ error() rebuild_project() { - if [ $GAMESERVER_ONLY ]; then - $SCRIPT_DIR/build -qgcg - else - $SCRIPT_DIR/build -qmgcmg - fi + "$SCRIPT_DIR/build" -qgc } refresh_project() { - if [ $GAMESERVER_ONLY ]; then - $SCRIPT_DIR/build -qg - else - $SCRIPT_DIR/build -qmg - fi + "$SCRIPT_DIR/build" -qg } verify_binaries_exist() { - if ! [ $GAMESERVER_ONLY ]; then - if [ ! -f $SCRIPT_DIR/builddir/ms.jar ]; then - error "Management server binary does not exist."; - fi - fi - - if [ ! -f $SCRIPT_DIR/build/server.jar ]; then + if [ ! -f "$SCRIPT_DIR/build/server.jar" ]; then error "Game server binary does not exist."; fi } @@ -76,11 +57,7 @@ run_server_parallel() echo "Running in parallel. All logs redirected to proper files." - if [ $GAMESERVER_ONLY ]; then - sh -c "$GS_EXEC" & wait - else - sh -c "$MS_EXEC > $LOG_DIR/ms_stdout.log 2> $LOG_DIR/ms_stderr.log & $GS_EXEC" & wait - fi + sh -c "$GS_EXEC" & wait } run_server_tmux() @@ -89,21 +66,16 @@ run_server_tmux() error "tmux is not installed. Install tmux or don't use this option." fi - if [ $GAMESERVER_ONLY ]; then - tmux new-session "$GS_EXEC" - else - tmux new-session "$MS_EXEC" \; \ - split-window -h "$GS_EXEC" - fi + tmux new-session "$GS_EXEC" } run_server_tests() { - cd "$GS_SRC" || error "Could not change to GameServer source directory." + cd "$GS_SRC" || error "Could not change to game server source directory." sh mvnw test } -while getopts ":ghqrtxe:o:" arg; do +while getopts ":hqrtxe:o:" arg; do case $arg in h) help @@ -127,9 +99,6 @@ while getopts ":ghqrtxe:o:" arg; do x) RUN_MODE=1 ;; - g) - GAMESERVER_ONLY=0 - ;; *) error "Argument -$arg is not a known or valid argument." ;; diff --git a/run-ms.bat b/run-ms.bat deleted file mode 100644 index 494ccb692..000000000 --- a/run-ms.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -cd Management-Server - -if NOT exist DoNotCreateThisFile.txt ( - .\mvnw.cmd package -DskipTests - xcopy /Y target\*-with-dependencies.jar ms.jar* - java -jar ms.jar -) \ No newline at end of file